Something went wrong. Try again.
A nicer S3 CLI
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788import type { CommandSpec } from './command.ts';import { S3Error } from './vendor/ordner.fyi/s3/s3.ts';
// What can go wrong, and the exit code each kind of failure leaves.//// Agents branch on exit codes, so they are few and stable://// 0 done// 1 anything else that went wrong (a server error, a file that is not there)// 2 the command line was wrong: an unknown option, a missing argument// 3 no usable credentials: no profile, or the keys were refused// 4 the thing named does not exist: an unknown bucket, a missing key// 5 the service said to slow down, for longer than is worth waiting// 6 a question needed answering and there was no terminal to ask on//// With --json, every failure is also printed on stdout as// {"error": {"code", "message", "status"?, "exit"}}, so a caller that reads only// stdout still learns what happened.
export const EXIT = { ok: 0, error: 1, usage: 2, auth: 3, notFound: 4, rateLimited: 5, needsInput: 6,} as const;
export class CliError extends Error { exit: number; code: string; constructor(message: string, code = 'Error', exit: number = EXIT.error) { super(message); this.code = code; this.exit = exit; }}
export class UsageError extends CliError { /** The command the mistake was made in, so its help can be pointed at. */ path: CommandSpec[]; constructor(message: string, path: CommandSpec[] = []) { super(message, 'UsageError', EXIT.usage); this.path = path; }}
export class AuthError extends CliError { constructor(message: string, code = 'AuthRequired') { super(message, code, EXIT.auth); }}
export class NotFoundError extends CliError { constructor(message: string, code = 'NotFound') { super(message, code, EXIT.notFound); }}
export class NeedsInputError extends CliError { constructor(message: string) { super(message, 'NeedsInput', EXIT.needsInput); }}
/** * The exit code for an error the S3 client threw (the vendored client's * S3Error, with the service's <Code> and HTTP status), or for anything else. */export function exitOf(error: unknown): number { if (error instanceof CliError) return error.exit; if (error instanceof S3Error) { const { status, code } = error; if (/^(InvalidAccessKeyId|SignatureDoesNotMatch|ExpiredToken|InvalidToken|TokenRefreshRequired|AccessDenied|AllAccessDisabled|AccountProblem|Forbidden)$/.test(code)) return EXIT.auth; if (status === 401 || status === 403) return EXIT.auth; if (status === 404 || /^(NoSuch|NotFound$)/.test(code)) return EXIT.notFound; if (status === 429 || /^(SlowDown|Throttling|TooManyRequests)$/.test(code)) return EXIT.rateLimited; } return EXIT.error;}
/** The code --json reports for an error. */export function codeOf(error: unknown): string { if (error instanceof CliError || error instanceof S3Error) return error.code; return 'InternalError';}