{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/run.ts","../../src/cli/index.ts"],"sourcesContent":["import { configureClientLoggerForService, resolveOpenTagHome, resolveOpenTagHomeLayout } from \"@opentag/client\";\nimport { type Command, CommanderError } from \"commander\";\nimport { resolveChannelEnvironment } from \"../core/channel/environment.js\";\nimport {\n  CommandError,\n  commandExitCode,\n  EXIT_CODES,\n  observeCommandFailures,\n  presentCommand,\n  toCommandError,\n} from \"../core/command/policy.js\";\nimport {\n  installCliProcessErrorReporting,\n  reportCommandFailure,\n  resolveCommandPath,\n} from \"../core/diagnostics/error-reporting.js\";\nimport { createProgram } from \"./program.js\";\n\nexport interface RunCliOptions {\n  argv: readonly string[];\n  env?: NodeJS.ProcessEnv;\n  /** The program to run; the real one by default. Tests register a throwing command on it. */\n  program?: Command;\n  /** Relays one failure; `reportCommandFailure` by default. */\n  report?: typeof reportCommandFailure;\n  /** Ends the process for a Commander usage or help exit; `process.exit` by default. */\n  exit?: (code: number) => void;\n  /** Whether to install the process-level uncaught-failure handlers; on by default. */\n  processHandlers?: boolean;\n}\n\ninterface RunState {\n  command?: string;\n  detachObserver?: () => void;\n}\n\n/**\n * The CLI entry point's whole behaviour, so it can run under test with a real program.\n *\n * A failure is reported from exactly one of two places: the shared execution path, which handles\n * most command failures and returns an exit code, or the catch below, which sees what escaped it.\n * `reportCommandFailure` keeps the two from reporting the same thrown value twice.\n */\nexport async function runCli(options: RunCliOptions): Promise<number> {\n  const argv = [...options.argv];\n  const env = options.env ?? process.env;\n  const json = argv.includes(\"--json\");\n  const report = options.report ?? reportCommandFailure;\n  const state: RunState = {};\n  try {\n    await runProgram(options, argv, env, report, state);\n    return EXIT_CODES.success;\n  } catch (error) {\n    if (error instanceof CommanderError) return exitForUsage(error, json, options.exit);\n    const commandError = toCommandError(error);\n    const exitCode = presentCommand(\n      { ok: false, error: commandError, exitCode: commandExitCode(commandError) },\n      { json },\n    );\n    // After the failure is presented, so reporting can only ever delay the exit, never the answer.\n    await report(error, commandError, { command: state.command, environment: env });\n    return exitCode;\n  } finally {\n    state.detachObserver?.();\n  }\n}\n\nasync function runProgram(\n  options: RunCliOptions,\n  argv: string[],\n  env: NodeJS.ProcessEnv,\n  report: typeof reportCommandFailure,\n  state: RunState,\n): Promise<void> {\n  const program = options.program ?? createProgram({ json: argv.includes(\"--json\") });\n  // The command path only, never its arguments: those are the user's and may carry secrets.\n  state.command = resolveCommandPath(program, argv.slice(2));\n  const environment = resolveChannelEnvironment(env);\n  const home = resolveOpenTagHome(environment);\n  configureClientLoggerForService(resolveOpenTagHomeLayout(home).logs);\n  const context = { command: state.command, environment: env, home };\n  if (options.processHandlers !== false) installCliProcessErrorReporting(context);\n  state.detachObserver = observeCommandFailures((error, commandError) => {\n    void report(error, commandError, context);\n  });\n  await program.parseAsync(argv);\n}\n\n/**\n * Commander usage errors (unknown options, missing required options or arguments) are\n * input-validation failures: they exit 2, and a --json caller receives the same failure envelope\n * as any other command instead of human help text.\n */\nfunction exitForUsage(\n  error: CommanderError,\n  json: boolean,\n  exit: (code: number) => void = (code) => process.exit(code),\n): number {\n  if (error.exitCode !== 0 && json) {\n    const commandError = new CommandError(\n      { code: \"USAGE_ERROR\", category: \"validation\", retryability: \"never\", phase: \"validation\" },\n      error.message,\n    );\n    presentCommand({ ok: false, error: commandError, exitCode: EXIT_CODES.usage }, { json: true });\n  }\n  const code = error.exitCode === 0 ? 0 : EXIT_CODES.usage;\n  exit(code);\n  return code;\n}\n","#!/usr/bin/env node\n\nimport { runCli } from \"./run.js\";\n\n/*\n * The CLI ships as one bundle, so an untranslated stack names `dist/cli/index.mjs` and a line number\n * in it, which tells a reader of an error report nothing about the source it came from. Source maps\n * are consulted when a stack is captured, so asking for them here covers everything this process can\n * go on to throw. `--enable-source-maps` would do the same, but a `#!/usr/bin/env node` shebang\n * cannot portably carry a flag.\n */\nprocess.setSourceMapsEnabled(true);\n\nconst exitCode = await runCli({ argv: process.argv, env: process.env });\nif (exitCode !== 0) process.exitCode = exitCode;\n"],"mappings":";;;;;;;;;;AA2CA,eAAsB,OAAO,SAAyC;CACpE,MAAM,OAAO,CAAC,GAAG,QAAQ,IAAI;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,KAAK,SAAS,QAAQ;CACnC,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,QAAkB,CAAC;CACzB,IAAI;EACF,MAAM,WAAW,SAAS,MAAM,KAAK,QAAQ,KAAK;EAClD,OAAO,WAAW;CACpB,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB,OAAO,aAAa,OAAO,MAAM,QAAQ,IAAI;EAClF,MAAM,eAAe,eAAe,KAAK;EACzC,MAAM,WAAW,eACf;GAAE,IAAI;GAAO,OAAO;GAAc,UAAU,gBAAgB,YAAY;EAAE,GAC1E,EAAE,KAAK,CACT;EAEA,MAAM,OAAO,OAAO,cAAc;GAAE,SAAS,MAAM;GAAS,aAAa;EAAI,CAAC;EAC9E,OAAO;CACT,UAAU;EACR,MAAM,iBAAiB;CACzB;AACF;AAEA,eAAe,WACb,SACA,MACA,KACA,QACA,OACe;CACf,MAAM,UAAU,QAAQ,WAAW,cAAc,EAAE,MAAM,KAAK,SAAS,QAAQ,EAAE,CAAC;CAElF,MAAM,UAAU,mBAAmB,SAAS,KAAK,MAAM,CAAC,CAAC;CACzD,MAAM,cAAc,0BAA0B,GAAG;CACjD,MAAM,OAAO,mBAAmB,WAAW;CAC3C,gCAAgC,yBAAyB,IAAI,CAAC,CAAC,IAAI;CACnE,MAAM,UAAU;EAAE,SAAS,MAAM;EAAS,aAAa;EAAK;CAAK;CACjE,IAAI,QAAQ,oBAAoB,OAAO,gCAAgC,OAAO;CAC9E,MAAM,iBAAiB,wBAAwB,OAAO,iBAAiB;EACrE,OAAY,OAAO,cAAc,OAAO;CAC1C,CAAC;CACD,MAAM,QAAQ,WAAW,IAAI;AAC/B;;;;;;AAOA,SAAS,aACP,OACA,MACA,QAAgC,SAAS,QAAQ,KAAK,IAAI,GAClD;CACR,IAAI,MAAM,aAAa,KAAK,MAAM;EAChC,MAAM,eAAe,IAAI,aACvB;GAAE,MAAM;GAAe,UAAU;GAAc,cAAc;GAAS,OAAO;EAAa,GAC1F,MAAM,OACR;EACA,eAAe;GAAE,IAAI;GAAO,OAAO;GAAc,UAAU,WAAW;EAAM,GAAG,EAAE,MAAM,KAAK,CAAC;CAC/F;CACA,MAAM,OAAO,MAAM,aAAa,IAAI,IAAI,WAAW;CACnD,KAAK,IAAI;CACT,OAAO;AACT;;;ACjGA,QAAQ,qBAAqB,IAAI;AAEjC,MAAM,WAAW,MAAM,OAAO;CAAE,MAAM,QAAQ;CAAM,KAAK,QAAQ;AAAI,CAAC;AACtE,IAAI,aAAa,GAAG,QAAQ,WAAW"}