@obov/mext 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/run.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createApp, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from "../config.js";
1
+ import { createApp, loadCliErrorFormatter, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from "../config.js";
2
2
  import { MextError } from "../errors.js";
3
3
  import { isSegment } from "../ids.js";
4
4
  import { loadProjectInstrumentation } from "../instrumentation.js";
@@ -9,7 +9,7 @@ import { loadRegistry } from "../scan.js";
9
9
  import { analyzeInput, assertCliConvertible, coerceValue, parseArgs } from "./args.js";
10
10
  import { cliPath, renderHelp } from "./help.js";
11
11
  import { createLineReader, readAll } from "./io.js";
12
- import { CliInterrupted, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult } from "./output.js";
12
+ import { CliInterrupted, emitError, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult, } from "./output.js";
13
13
  import { remoteTarget, runRemoteCommand } from "./remote.js";
14
14
  export const RUN_PROG = "mext run";
15
15
  /**
@@ -42,10 +42,12 @@ export async function runCommand(argv, io) {
42
42
  const beforeDashDash = rest.indexOf("--") < 0 ? rest : rest.slice(0, rest.indexOf("--"));
43
43
  const json = lead.json || beforeDashDash.includes("--json");
44
44
  const lines = createLineReader(io);
45
+ let formatter;
45
46
  try {
46
47
  // instrumentation.ts register() = 설정 · 명령어 모듈 import 전 (프로세스당 1회)
47
48
  const instrumentation = await loadProjectInstrumentation(io.cwd);
48
49
  const config = await loadConfig(io.cwd);
50
+ formatter = await loadCliErrorFormatter(io.cwd, config);
49
51
  const registry = await loadRegistry(resolveCommandsDir(io.cwd, config));
50
52
  assertCliConvertible(registry);
51
53
  // mext run help [target...]
@@ -73,10 +75,9 @@ export async function runCommand(argv, io) {
73
75
  // 네임스페이스 (루트 포함) 만 지정 = 명령어 없음
74
76
  const error = new MextError("UNKNOWN_COMMAND", id === "" ? "No command given." : `"${cliPath(id)}" is a namespace. Specify a command.`, { command: id, suggested: [], usage: id === "" ? "help" : `help ${id}` });
75
77
  if (json)
76
- return report(error, io, prog, true);
77
- io.stderr.write(formatError(error, prog));
78
- io.stderr.write(`\n${renderHelp(registry, id, { prog })}`);
79
- return EXIT.usage;
78
+ return report(error, io, prog, true, formatter);
79
+ const text = `${formatError(error, prog)}\n${renderHelp(registry, id, { prog })}`;
80
+ return emitError(io, { body: errorJson(error), text, exitCode: EXIT.usage, json: false }, formatter);
80
81
  }
81
82
  const principal = await resolveLocalPrincipal(config);
82
83
  authorize(entry, principal); // 권한 → 입력 검증 순서 (invoke 와 같음)
@@ -113,7 +114,7 @@ export async function runCommand(argv, io) {
113
114
  }
114
115
  }
115
116
  catch (error) {
116
- return report(error, io, prog, json);
117
+ return report(error, io, prog, json, formatter);
117
118
  }
118
119
  finally {
119
120
  lines.close();
@@ -161,9 +162,8 @@ function printHelp(registry, id, io, prog, json, tools) {
161
162
  io.stdout.write(json ? formatJson(help(registry, id, toolFor ? { toolFor } : {})) : renderHelp(registry, id, { prog }));
162
163
  return EXIT.ok;
163
164
  }
164
- function report(error, io, prog, json) {
165
- io.stderr.write(json ? formatJson(errorJson(error)) : formatError(error, prog));
166
- return exitCodeOf(error);
165
+ function report(error, io, prog, json, formatter) {
166
+ return emitError(io, { body: errorJson(error), text: formatError(error, prog), exitCode: exitCodeOf(error), json }, formatter);
167
167
  }
168
168
  function usageError(message) {
169
169
  return new MextError("INVALID_ARGUMENT", message, {
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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","sourcesContent":["import { createApp, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from \"../config.ts\"\nimport { MextError } from \"../errors.ts\"\nimport { isSegment } from \"../ids.ts\"\nimport { loadProjectInstrumentation } from \"../instrumentation.ts\"\nimport { authorize, findCommand, invoke, type AskHandler } from \"../invoke.ts\"\nimport type { ToolExposure } from \"../mcp/dispatch.ts\"\nimport { help } from \"../mcp/help.ts\"\nimport { resolveCommand, type RegisteredCommand, type Registry } from \"../registry.ts\"\nimport { loadRegistry } from \"../scan.ts\"\nimport { analyzeInput, assertCliConvertible, coerceValue, parseArgs } from \"./args.ts\"\nimport { cliPath, renderHelp } from \"./help.ts\"\nimport { createLineReader, readAll, type CliIo, type LineReader } from \"./io.ts\"\nimport { CliInterrupted, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult } from \"./output.ts\"\nimport { remoteTarget, runRemoteCommand } from \"./remote.ts\"\n\nexport const RUN_PROG = \"mext run\"\n\n/**\n * mext run <ns> <cmd> [flags] — 로컬 실행 (ctx.principal.kind = local, mext.config.ts 의 localPrincipal 로 변경 가능).\n * mext run help [target] · mext run [<ns> [<cmd>]] --help · --json · --yes.\n * --url <url> (또는 MEXT_URL) = 원격 실행 (cli/remote.ts)\n */\nexport async function runCommand(argv: readonly string[], io: CliIo): Promise<number> {\n const remote = remoteTarget(argv, io.env)\n if (remote) return runRemoteCommand(remote, io)\n const prog = RUN_PROG\n const lead = { json: false, yes: false, help: false }\n let i = 0\n for (; i < argv.length; i++) {\n const tok = argv[i]!\n if (!tok.startsWith(\"-\") || tok === \"-\") break\n if (tok === \"--json\") lead.json = true\n else if (tok === \"--yes\") lead.yes = true\n else if (tok === \"--help\" || tok === \"-h\") lead.help = true\n else {\n return report(usageError(`unknown flag ${tok} before the command`), io, prog, argv.includes(\"--json\"))\n }\n }\n const rest = argv.slice(i)\n const beforeDashDash = rest.indexOf(\"--\") < 0 ? rest : rest.slice(0, rest.indexOf(\"--\"))\n const json = lead.json || beforeDashDash.includes(\"--json\")\n\n const lines = createLineReader(io)\n try {\n // instrumentation.ts register() = 설정 · 명령어 모듈 import 전 (프로세스당 1회)\n const instrumentation = await loadProjectInstrumentation(io.cwd)\n const config = await loadConfig(io.cwd)\n const registry = await loadRegistry(resolveCommandsDir(io.cwd, config))\n assertCliConvertible(registry)\n\n // mext run help [target...]\n if (rest[0] === \"help\") {\n const targetTokens = rest.slice(1).filter((t) => !t.startsWith(\"-\"))\n const { id, consumed } = resolvePath(registry, targetTokens)\n if (consumed < targetTokens.length) throw unknownAt(registry, id, targetTokens.slice(consumed))\n return printHelp(registry, id, io, prog, json, config.tools)\n }\n\n const { id, consumed } = resolvePath(registry, rest)\n const entry = id === \"\" ? undefined : resolveCommand(registry, id)\n const next = rest[consumed]\n const acceptsPositional = entry !== undefined && entry.command.positional.length > 0\n if (registry.namespaces.has(id) && next !== undefined && !next.startsWith(\"-\") && !acceptsPositional) {\n throw unknownAt(registry, id, rest.slice(consumed))\n }\n if (entry === undefined && next !== undefined && !next.startsWith(\"-\")) {\n throw unknownAt(registry, id, rest.slice(consumed))\n }\n\n const help = lead.help || beforeDashDash.includes(\"--help\") || beforeDashDash.includes(\"-h\")\n if (help) return printHelp(registry, id, io, prog, json, config.tools)\n\n if (!entry) {\n // 네임스페이스 (루트 포함) 만 지정 = 명령어 없음\n const error = new MextError(\n \"UNKNOWN_COMMAND\",\n id === \"\" ? \"No command given.\" : `\"${cliPath(id)}\" is a namespace. Specify a command.`,\n { command: id, suggested: [], usage: id === \"\" ? \"help\" : `help ${id}` },\n )\n if (json) return report(error, io, prog, true)\n io.stderr.write(formatError(error, prog))\n io.stderr.write(`\\n${renderHelp(registry, id, { prog })}`)\n return EXIT.usage\n }\n\n const principal = await resolveLocalPrincipal(config)\n authorize(entry, principal) // 권한 → 입력 검증 순서 (invoke 와 같음)\n const parsed = await parseArgs(entry, rest.slice(consumed), { readStdin: () => readAll(io.stdin) })\n const yes = lead.yes || parsed.yes\n const app = await createApp(config)\n\n const controller = new AbortController()\n const onAbort = (): void => controller.abort(io.signal?.reason)\n if (io.signal?.aborted) onAbort()\n io.signal?.addEventListener(\"abort\", onAbort, { once: true })\n try {\n const result = await invoke(registry, entry.id, parsed.input, {\n principal,\n signal: controller.signal,\n progress: (update) => io.stderr.write(formatProgress(update)),\n ask: askHandler(io, lines, yes, controller.signal),\n app,\n instrumentation,\n transport: \"local\",\n })\n if (controller.signal.aborted) throw new CliInterrupted()\n io.stdout.write(json ? formatJson(result.value) : formatResult(result.value))\n return EXIT.ok\n } catch (error) {\n if (controller.signal.aborted) throw new CliInterrupted()\n throw error\n } finally {\n io.signal?.removeEventListener(\"abort\", onAbort)\n }\n } catch (error) {\n return report(error, io, prog, json)\n } finally {\n lines.close()\n }\n}\n\n/** 앞쪽 경로 토큰 → 가장 긴 네임스페이스 · 명령어 (별칭 포함) ID */\nexport function resolvePath(registry: Registry, tokens: readonly string[]): { id: string; consumed: number } {\n let id = \"\"\n let consumed = 0\n for (const tok of tokens) {\n if (!isSegment(tok)) break\n const candidate = id === \"\" ? tok : `${id}.${tok}`\n if (!registry.namespaces.has(candidate) && !resolveCommand(registry, candidate)) break\n id = candidate\n consumed++\n }\n return { id, consumed }\n}\n\n/** 해석되지 않은 경로 토큰 → UNKNOWN_COMMAND (suggested · usage) */\nfunction unknownAt(registry: Registry, id: string, tail: readonly string[]): MextError {\n const segs: string[] = []\n for (const t of tail) {\n if (t.startsWith(\"-\") || (segs.length > 0 && !isSegment(t))) break\n segs.push(t)\n if (!isSegment(t)) break\n }\n const unknown = [id, ...segs].filter((s) => s !== \"\").join(\".\")\n try {\n findCommand(registry, unknown)\n } catch (error) {\n if (error instanceof MextError) return error\n throw error\n }\n return new MextError(\"UNKNOWN_COMMAND\", `Unknown command \"${unknown}\".`, { command: unknown, suggested: [], usage: \"help\" })\n}\n\n/** --help --json = MCP help 의 structuredContent 와 같은 데이터 (mext mcp 의 tools 노출 기준) */\nfunction printHelp(registry: Registry, id: string, io: CliIo, prog: string, json: boolean, tools: ToolExposure | undefined): number {\n const toolFor = tools === \"split\" ? (e: RegisteredCommand) => (e.command.readOnly ? \"query\" : \"mutate\") : undefined\n io.stdout.write(json ? formatJson(help(registry, id, toolFor ? { toolFor } : {})) : renderHelp(registry, id, { prog }))\n return EXIT.ok\n}\n\nfunction report(error: unknown, io: CliIo, prog: string, json: boolean): number {\n io.stderr.write(json ? formatJson(errorJson(error)) : formatError(error, prog))\n return exitCodeOf(error)\n}\n\nfunction usageError(message: string): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\"INVALID_ARGUMENT\", message, {\n command: \"\",\n missing_fields: [],\n issues: [{ path: \"\", code: \"unknown_flag\", message }],\n expected_schema: {},\n examples: [],\n })\n}\n\n\n/**\n * ctx.ask. --yes = confirm 승인. TTY 없음 = 즉시 INPUT_UNAVAILABLE.\n * TTY = stderr 프롬프트 + stdin 한 줄 (confirm = y/yes, input = 필드별 입력을 스키마 모양으로 변환)\n */\nfunction askHandler(io: CliIo, lines: LineReader, yes: boolean, signal: AbortSignal): AskHandler {\n return async (request) => {\n if (request.kind === \"confirm\" && yes) return true\n if (!io.isTTY) {\n const hint = request.kind === \"confirm\" ? \" Pass --yes to approve.\" : \" Run it in an interactive terminal.\"\n throw new MextError(\n \"INPUT_UNAVAILABLE\",\n `${request.command} asked \"${request.message}\", but stdin is not a terminal.${hint}`,\n { command: request.command },\n )\n }\n if (request.kind === \"confirm\") {\n const answer = await lines.question(`${request.message} [y/N] `, signal)\n if (signal.aborted) throw new CliInterrupted()\n return /^y(es)?$/i.test((answer ?? \"\").trim())\n }\n io.stderr.write(`${request.message}\\n`)\n const { spec } = analyzeInput(request.schema, request.requestedSchema)\n const out: Record<string, unknown> = {}\n for (const f of spec.fields) {\n const answer = await lines.question(` ${f.key}${f.required ? \"\" : \" (optional)\"}: `, signal)\n if (signal.aborted) throw new CliInterrupted()\n if (answer === undefined || answer === \"\") continue\n if (f.kind.type === \"array\") {\n const kind = f.kind\n out[f.key] = answer.split(\",\").map((s) => {\n const r = coerceValue(kind, s.trim())\n return r.ok ? r.value : s\n })\n continue\n }\n const r = coerceValue(f.kind, answer)\n out[f.key] = r.ok ? r.value : answer\n }\n return out\n }\n}\n"]}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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","sourcesContent":["import { createApp, loadCliErrorFormatter, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from \"../config.ts\"\nimport { MextError } from \"../errors.ts\"\nimport { isSegment } from \"../ids.ts\"\nimport { loadProjectInstrumentation } from \"../instrumentation.ts\"\nimport { authorize, findCommand, invoke, type AskHandler } from \"../invoke.ts\"\nimport type { ToolExposure } from \"../mcp/dispatch.ts\"\nimport { help } from \"../mcp/help.ts\"\nimport { resolveCommand, type RegisteredCommand, type Registry } from \"../registry.ts\"\nimport { loadRegistry } from \"../scan.ts\"\nimport { analyzeInput, assertCliConvertible, coerceValue, parseArgs } from \"./args.ts\"\nimport { cliPath, renderHelp } from \"./help.ts\"\nimport { createLineReader, readAll, type CliIo, type LineReader } from \"./io.ts\"\nimport {\n CliInterrupted,\n emitError,\n EXIT,\n errorJson,\n exitCodeOf,\n formatError,\n formatJson,\n formatProgress,\n formatResult,\n type CliErrorFormatter,\n} from \"./output.ts\"\nimport { remoteTarget, runRemoteCommand } from \"./remote.ts\"\n\nexport const RUN_PROG = \"mext run\"\n\n/**\n * mext run <ns> <cmd> [flags] — 로컬 실행 (ctx.principal.kind = local, mext.config.ts 의 localPrincipal 로 변경 가능).\n * mext run help [target] · mext run [<ns> [<cmd>]] --help · --json · --yes.\n * --url <url> (또는 MEXT_URL) = 원격 실행 (cli/remote.ts)\n */\nexport async function runCommand(argv: readonly string[], io: CliIo): Promise<number> {\n const remote = remoteTarget(argv, io.env)\n if (remote) return runRemoteCommand(remote, io)\n const prog = RUN_PROG\n const lead = { json: false, yes: false, help: false }\n let i = 0\n for (; i < argv.length; i++) {\n const tok = argv[i]!\n if (!tok.startsWith(\"-\") || tok === \"-\") break\n if (tok === \"--json\") lead.json = true\n else if (tok === \"--yes\") lead.yes = true\n else if (tok === \"--help\" || tok === \"-h\") lead.help = true\n else {\n return report(usageError(`unknown flag ${tok} before the command`), io, prog, argv.includes(\"--json\"))\n }\n }\n const rest = argv.slice(i)\n const beforeDashDash = rest.indexOf(\"--\") < 0 ? rest : rest.slice(0, rest.indexOf(\"--\"))\n const json = lead.json || beforeDashDash.includes(\"--json\")\n\n const lines = createLineReader(io)\n let formatter: CliErrorFormatter | undefined\n try {\n // instrumentation.ts register() = 설정 · 명령어 모듈 import 전 (프로세스당 1회)\n const instrumentation = await loadProjectInstrumentation(io.cwd)\n const config = await loadConfig(io.cwd)\n formatter = await loadCliErrorFormatter(io.cwd, config)\n const registry = await loadRegistry(resolveCommandsDir(io.cwd, config))\n assertCliConvertible(registry)\n\n // mext run help [target...]\n if (rest[0] === \"help\") {\n const targetTokens = rest.slice(1).filter((t) => !t.startsWith(\"-\"))\n const { id, consumed } = resolvePath(registry, targetTokens)\n if (consumed < targetTokens.length) throw unknownAt(registry, id, targetTokens.slice(consumed))\n return printHelp(registry, id, io, prog, json, config.tools)\n }\n\n const { id, consumed } = resolvePath(registry, rest)\n const entry = id === \"\" ? undefined : resolveCommand(registry, id)\n const next = rest[consumed]\n const acceptsPositional = entry !== undefined && entry.command.positional.length > 0\n if (registry.namespaces.has(id) && next !== undefined && !next.startsWith(\"-\") && !acceptsPositional) {\n throw unknownAt(registry, id, rest.slice(consumed))\n }\n if (entry === undefined && next !== undefined && !next.startsWith(\"-\")) {\n throw unknownAt(registry, id, rest.slice(consumed))\n }\n\n const help = lead.help || beforeDashDash.includes(\"--help\") || beforeDashDash.includes(\"-h\")\n if (help) return printHelp(registry, id, io, prog, json, config.tools)\n\n if (!entry) {\n // 네임스페이스 (루트 포함) 만 지정 = 명령어 없음\n const error = new MextError(\n \"UNKNOWN_COMMAND\",\n id === \"\" ? \"No command given.\" : `\"${cliPath(id)}\" is a namespace. Specify a command.`,\n { command: id, suggested: [], usage: id === \"\" ? \"help\" : `help ${id}` },\n )\n if (json) return report(error, io, prog, true, formatter)\n const text = `${formatError(error, prog)}\\n${renderHelp(registry, id, { prog })}`\n return emitError(io, { body: errorJson(error), text, exitCode: EXIT.usage, json: false }, formatter)\n }\n\n const principal = await resolveLocalPrincipal(config)\n authorize(entry, principal) // 권한 → 입력 검증 순서 (invoke 와 같음)\n const parsed = await parseArgs(entry, rest.slice(consumed), { readStdin: () => readAll(io.stdin) })\n const yes = lead.yes || parsed.yes\n const app = await createApp(config)\n\n const controller = new AbortController()\n const onAbort = (): void => controller.abort(io.signal?.reason)\n if (io.signal?.aborted) onAbort()\n io.signal?.addEventListener(\"abort\", onAbort, { once: true })\n try {\n const result = await invoke(registry, entry.id, parsed.input, {\n principal,\n signal: controller.signal,\n progress: (update) => io.stderr.write(formatProgress(update)),\n ask: askHandler(io, lines, yes, controller.signal),\n app,\n instrumentation,\n transport: \"local\",\n })\n if (controller.signal.aborted) throw new CliInterrupted()\n io.stdout.write(json ? formatJson(result.value) : formatResult(result.value))\n return EXIT.ok\n } catch (error) {\n if (controller.signal.aborted) throw new CliInterrupted()\n throw error\n } finally {\n io.signal?.removeEventListener(\"abort\", onAbort)\n }\n } catch (error) {\n return report(error, io, prog, json, formatter)\n } finally {\n lines.close()\n }\n}\n\n/** 앞쪽 경로 토큰 → 가장 긴 네임스페이스 · 명령어 (별칭 포함) ID */\nexport function resolvePath(registry: Registry, tokens: readonly string[]): { id: string; consumed: number } {\n let id = \"\"\n let consumed = 0\n for (const tok of tokens) {\n if (!isSegment(tok)) break\n const candidate = id === \"\" ? tok : `${id}.${tok}`\n if (!registry.namespaces.has(candidate) && !resolveCommand(registry, candidate)) break\n id = candidate\n consumed++\n }\n return { id, consumed }\n}\n\n/** 해석되지 않은 경로 토큰 → UNKNOWN_COMMAND (suggested · usage) */\nfunction unknownAt(registry: Registry, id: string, tail: readonly string[]): MextError {\n const segs: string[] = []\n for (const t of tail) {\n if (t.startsWith(\"-\") || (segs.length > 0 && !isSegment(t))) break\n segs.push(t)\n if (!isSegment(t)) break\n }\n const unknown = [id, ...segs].filter((s) => s !== \"\").join(\".\")\n try {\n findCommand(registry, unknown)\n } catch (error) {\n if (error instanceof MextError) return error\n throw error\n }\n return new MextError(\"UNKNOWN_COMMAND\", `Unknown command \"${unknown}\".`, { command: unknown, suggested: [], usage: \"help\" })\n}\n\n/** --help --json = MCP help 의 structuredContent 와 같은 데이터 (mext mcp 의 tools 노출 기준) */\nfunction printHelp(registry: Registry, id: string, io: CliIo, prog: string, json: boolean, tools: ToolExposure | undefined): number {\n const toolFor = tools === \"split\" ? (e: RegisteredCommand) => (e.command.readOnly ? \"query\" : \"mutate\") : undefined\n io.stdout.write(json ? formatJson(help(registry, id, toolFor ? { toolFor } : {})) : renderHelp(registry, id, { prog }))\n return EXIT.ok\n}\n\nfunction report(error: unknown, io: CliIo, prog: string, json: boolean, formatter?: CliErrorFormatter): number {\n return emitError(io, { body: errorJson(error), text: formatError(error, prog), exitCode: exitCodeOf(error), json }, formatter)\n}\n\nfunction usageError(message: string): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\"INVALID_ARGUMENT\", message, {\n command: \"\",\n missing_fields: [],\n issues: [{ path: \"\", code: \"unknown_flag\", message }],\n expected_schema: {},\n examples: [],\n })\n}\n\n\n/**\n * ctx.ask. --yes = confirm 승인. TTY 없음 = 즉시 INPUT_UNAVAILABLE.\n * TTY = stderr 프롬프트 + stdin 한 줄 (confirm = y/yes, input = 필드별 입력을 스키마 모양으로 변환)\n */\nfunction askHandler(io: CliIo, lines: LineReader, yes: boolean, signal: AbortSignal): AskHandler {\n return async (request) => {\n if (request.kind === \"confirm\" && yes) return true\n if (!io.isTTY) {\n const hint = request.kind === \"confirm\" ? \" Pass --yes to approve.\" : \" Run it in an interactive terminal.\"\n throw new MextError(\n \"INPUT_UNAVAILABLE\",\n `${request.command} asked \"${request.message}\", but stdin is not a terminal.${hint}`,\n { command: request.command },\n )\n }\n if (request.kind === \"confirm\") {\n const answer = await lines.question(`${request.message} [y/N] `, signal)\n if (signal.aborted) throw new CliInterrupted()\n return /^y(es)?$/i.test((answer ?? \"\").trim())\n }\n io.stderr.write(`${request.message}\\n`)\n const { spec } = analyzeInput(request.schema, request.requestedSchema)\n const out: Record<string, unknown> = {}\n for (const f of spec.fields) {\n const answer = await lines.question(` ${f.key}${f.required ? \"\" : \" (optional)\"}: `, signal)\n if (signal.aborted) throw new CliInterrupted()\n if (answer === undefined || answer === \"\") continue\n if (f.kind.type === \"array\") {\n const kind = f.kind\n out[f.key] = answer.split(\",\").map((s) => {\n const r = coerceValue(kind, s.trim())\n return r.ok ? r.value : s\n })\n continue\n }\n const r = coerceValue(f.kind, answer)\n out[f.key] = r.ok ? r.value : answer\n }\n return out\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { type CliIo } from "../cli/io.ts";
2
+ import { type CliErrorFormatter } from "../cli/output.ts";
2
3
  import type { ClientManifest } from "./manifest.ts";
3
4
  /**
4
5
  * 고객 CLI 런타임 (§5). mext build 가 이 모듈 + 설정 (CustomerCliConfig) 을 .mext/cli/bin.js 하나로 번들한다.
@@ -20,6 +21,11 @@ export interface CustomerCliConfig {
20
21
  /** 번들 형태의 내장 manifest. 없음 = 범용 형태 */
21
22
  manifest?: ClientManifest;
22
23
  }
23
- export declare function runCustomerCli(config: CustomerCliConfig, argv: readonly string[], partialIo?: Partial<CliIo>): Promise<number>;
24
+ /** bin.js 에 번들되는 코드 (설정과 달리 JSON 이 아님) */
25
+ export interface CustomerCliHooks {
26
+ /** cli.errors 모듈의 default export */
27
+ formatError?: CliErrorFormatter;
28
+ }
29
+ export declare function runCustomerCli(config: CustomerCliConfig, argv: readonly string[], partialIo?: Partial<CliIo>, hooks?: CustomerCliHooks): Promise<number>;
24
30
  /** 프로세스 진입점 (bin.js). 첫 SIGINT = 진행 중 요청 abort (exit 130), 두 번째 = 즉시 종료 */
25
- export declare function main(config: CustomerCliConfig): Promise<void>;
31
+ export declare function main(config: CustomerCliConfig, hooks?: CustomerCliHooks): Promise<void>;
@@ -6,7 +6,7 @@ import { EXIT, formatJson } from "../cli/output.js";
6
6
  import { mcpInstallText } from "../mcp/install.js";
7
7
  import { envPrefix } from "../remote/credentials.js";
8
8
  import { runRemote } from "./runner.js";
9
- export async function runCustomerCli(config, argv, partialIo = {}) {
9
+ export async function runCustomerCli(config, argv, partialIo = {}, hooks = {}) {
10
10
  const io = resolveIo(partialIo);
11
11
  const { bin } = config;
12
12
  const prefix = envPrefix(bin);
@@ -48,17 +48,18 @@ export async function runCustomerCli(config, argv, partialIo = {}) {
48
48
  version: config.version,
49
49
  ...(config.manifest ? { manifest: config.manifest, manifestHash: config.manifestHash } : {}),
50
50
  rootHelpFooter: builtinHelp(config, url),
51
+ ...(hooks.formatError ? { formatError: hooks.formatError } : {}),
51
52
  }, argv, io);
52
53
  }
53
54
  /** 프로세스 진입점 (bin.js). 첫 SIGINT = 진행 중 요청 abort (exit 130), 두 번째 = 즉시 종료 */
54
- export async function main(config) {
55
+ export async function main(config, hooks = {}) {
55
56
  const controller = new AbortController();
56
57
  process.on("SIGINT", () => {
57
58
  if (controller.signal.aborted)
58
59
  process.exit(130);
59
60
  controller.abort();
60
61
  });
61
- const code = await runCustomerCli(config, process.argv.slice(2), { signal: controller.signal });
62
+ const code = await runCustomerCli(config, process.argv.slice(2), { signal: controller.signal }, hooks);
62
63
  process.stdout.write("", () => process.stderr.write("", () => process.exit(code)));
63
64
  }
64
65
  function mcp(config, url, argv, io) {
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/client/main.ts"],"names":[],"mappings":"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","sourcesContent":["import { table } from \"../cli/help.ts\"\nimport { resolveIo, type CliIo } from \"../cli/io.ts\"\nimport { loginCommand } from \"../cli/login.ts\"\nimport { logoutCommand } from \"../cli/logout.ts\"\nimport { EXIT, formatJson } from \"../cli/output.ts\"\nimport { mcpInstallText } from \"../mcp/install.ts\"\nimport { envPrefix } from \"../remote/credentials.ts\"\nimport type { ClientManifest } from \"./manifest.ts\"\nimport { runRemote } from \"./runner.ts\"\n\n/**\n * 고객 CLI 런타임 (§5). mext build 가 이 모듈 + 설정 (CustomerCliConfig) 을 .mext/cli/bin.js 하나로 번들한다.\n * 번들 · 범용 형태가 같은 코드: manifest 가 있으면 help · 플래그 파싱을 내장 manifest 로, 없으면 서버 help 로.\n * 명령: <bin> help · <bin> <ns> <cmd> [flags] · login · logout · mcp install · --version. 원격 실행 = mext run --url 과 같은 runner\n */\n\n/** bin.js 에 인라인되는 설정 (mext build 가 생성) */\nexport interface CustomerCliConfig {\n bin: string\n /** 빌드 설정의 서버 MCP 엔드포인트. 실행 시 <BIN>_URL 이 있으면 그 값 */\n url: string\n /** --version · clientInfo.version */\n version: string\n packageName: string\n /** 빌드한 @obov/mext 버전 */\n mextVersion: string\n /** 빌드 시 manifestHash. 번들 형태 = 내장 manifest 의 해시 (서버 _meta[\"dev.mext/manifestHash\"] 와 비교) */\n manifestHash: string\n /** 번들 형태의 내장 manifest. 없음 = 범용 형태 */\n manifest?: ClientManifest\n}\n\nexport async function runCustomerCli(\n config: CustomerCliConfig,\n argv: readonly string[],\n partialIo: Partial<CliIo> = {},\n): Promise<number> {\n const io = resolveIo(partialIo)\n const { bin } = config\n const prefix = envPrefix(bin)\n const url = io.env[`${prefix}_URL`] || config.url\n if (!/^https?:\\/\\//i.test(url)) {\n io.stderr.write(`error: ${prefix}_URL must be an http(s) URL (got \"${url}\")\\n`)\n return EXIT.usage\n }\n const [first, ...rest] = argv\n switch (first) {\n case \"--version\":\n case \"-v\":\n io.stdout.write(`${config.version}\\n`)\n return EXIT.ok\n case \"login\":\n return loginCommand({ bin, url })(rest, io)\n case \"logout\":\n return logoutCommand({ bin, url })(rest, io)\n case \"mcp\":\n return mcp(config, url, rest, io)\n case \"__mext_inspect\":\n // 숨김: 지원 · 디버깅용 내장 설정 (토큰 없음)\n io.stdout.write(\n formatJson({\n bin,\n url,\n version: config.version,\n packageName: config.packageName,\n mextVersion: config.mextVersion,\n manifestHash: config.manifestHash,\n form: config.manifest ? \"bundled\" : \"universal\",\n }),\n )\n return EXIT.ok\n }\n return runRemote(\n {\n bin,\n url,\n prog: bin,\n login: `${bin} login`,\n version: config.version,\n ...(config.manifest ? { manifest: config.manifest, manifestHash: config.manifestHash } : {}),\n rootHelpFooter: builtinHelp(config, url),\n },\n argv,\n io,\n )\n}\n\n/** 프로세스 진입점 (bin.js). 첫 SIGINT = 진행 중 요청 abort (exit 130), 두 번째 = 즉시 종료 */\nexport async function main(config: CustomerCliConfig): Promise<void> {\n const controller = new AbortController()\n process.on(\"SIGINT\", () => {\n if (controller.signal.aborted) process.exit(130)\n controller.abort()\n })\n const code = await runCustomerCli(config, process.argv.slice(2), { signal: controller.signal })\n process.stdout.write(\"\", () => process.stderr.write(\"\", () => process.exit(code)))\n}\n\nfunction mcp(config: CustomerCliConfig, url: string, argv: readonly string[], io: CliIo): number {\n const [sub, ...rest] = argv\n const isHelp = (t: string) => t === \"--help\" || t === \"-h\"\n const usage = `Usage:\\n ${config.bin} mcp install Print MCP client registration commands (does not modify client config files)\\n`\n if (sub !== undefined && (isHelp(sub) || (sub === \"install\" && rest.some(isHelp)))) {\n io.stdout.write(usage)\n return EXIT.ok\n }\n if (sub === \"install\" && rest.length === 0) {\n io.stdout.write(`${mcpInstallText({ name: config.bin, url, stdioCommand: false, auth: true })}\\n`)\n return EXIT.ok\n }\n io.stderr.write(`error: ${sub === undefined ? \"missing subcommand\" : `unknown argument \"${argv.join(\" \")}\"`}\\n\\n${usage}`)\n return EXIT.usage\n}\n\n/** 사람용 루트 help 뒤의 내장 명령 · 환경변수 */\nfunction builtinHelp(config: CustomerCliConfig, url: string): string {\n const { bin } = config\n const prefix = envPrefix(bin)\n return [\n \"CLI commands:\",\n ...table([\n [\"login\", `Log in to ${url} (${bin} login --help)`],\n [\"logout\", \"Revoke and remove the stored token\"],\n [\"mcp install\", \"Print MCP client registration commands\"],\n [\"--version\", \"Print the version\"],\n ]).map((l) => ` ${l}`),\n \"\",\n \"Environment:\",\n ...table([\n [`${prefix}_TOKEN`, `API token (instead of ${bin} login)`],\n [`${prefix}_URL`, `Server URL (default: ${config.url})`],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n}\n"]}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/client/main.ts"],"names":[],"mappings":"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","sourcesContent":["import { table } from \"../cli/help.ts\"\nimport { resolveIo, type CliIo } from \"../cli/io.ts\"\nimport { loginCommand } from \"../cli/login.ts\"\nimport { logoutCommand } from \"../cli/logout.ts\"\nimport { EXIT, formatJson, type CliErrorFormatter } from \"../cli/output.ts\"\nimport { mcpInstallText } from \"../mcp/install.ts\"\nimport { envPrefix } from \"../remote/credentials.ts\"\nimport type { ClientManifest } from \"./manifest.ts\"\nimport { runRemote } from \"./runner.ts\"\n\n/**\n * 고객 CLI 런타임 (§5). mext build 가 이 모듈 + 설정 (CustomerCliConfig) 을 .mext/cli/bin.js 하나로 번들한다.\n * 번들 · 범용 형태가 같은 코드: manifest 가 있으면 help · 플래그 파싱을 내장 manifest 로, 없으면 서버 help 로.\n * 명령: <bin> help · <bin> <ns> <cmd> [flags] · login · logout · mcp install · --version. 원격 실행 = mext run --url 과 같은 runner\n */\n\n/** bin.js 에 인라인되는 설정 (mext build 가 생성) */\nexport interface CustomerCliConfig {\n bin: string\n /** 빌드 설정의 서버 MCP 엔드포인트. 실행 시 <BIN>_URL 이 있으면 그 값 */\n url: string\n /** --version · clientInfo.version */\n version: string\n packageName: string\n /** 빌드한 @obov/mext 버전 */\n mextVersion: string\n /** 빌드 시 manifestHash. 번들 형태 = 내장 manifest 의 해시 (서버 _meta[\"dev.mext/manifestHash\"] 와 비교) */\n manifestHash: string\n /** 번들 형태의 내장 manifest. 없음 = 범용 형태 */\n manifest?: ClientManifest\n}\n\n/** bin.js 에 번들되는 코드 (설정과 달리 JSON 이 아님) */\nexport interface CustomerCliHooks {\n /** cli.errors 모듈의 default export */\n formatError?: CliErrorFormatter\n}\n\nexport async function runCustomerCli(\n config: CustomerCliConfig,\n argv: readonly string[],\n partialIo: Partial<CliIo> = {},\n hooks: CustomerCliHooks = {},\n): Promise<number> {\n const io = resolveIo(partialIo)\n const { bin } = config\n const prefix = envPrefix(bin)\n const url = io.env[`${prefix}_URL`] || config.url\n if (!/^https?:\\/\\//i.test(url)) {\n io.stderr.write(`error: ${prefix}_URL must be an http(s) URL (got \"${url}\")\\n`)\n return EXIT.usage\n }\n const [first, ...rest] = argv\n switch (first) {\n case \"--version\":\n case \"-v\":\n io.stdout.write(`${config.version}\\n`)\n return EXIT.ok\n case \"login\":\n return loginCommand({ bin, url })(rest, io)\n case \"logout\":\n return logoutCommand({ bin, url })(rest, io)\n case \"mcp\":\n return mcp(config, url, rest, io)\n case \"__mext_inspect\":\n // 숨김: 지원 · 디버깅용 내장 설정 (토큰 없음)\n io.stdout.write(\n formatJson({\n bin,\n url,\n version: config.version,\n packageName: config.packageName,\n mextVersion: config.mextVersion,\n manifestHash: config.manifestHash,\n form: config.manifest ? \"bundled\" : \"universal\",\n }),\n )\n return EXIT.ok\n }\n return runRemote(\n {\n bin,\n url,\n prog: bin,\n login: `${bin} login`,\n version: config.version,\n ...(config.manifest ? { manifest: config.manifest, manifestHash: config.manifestHash } : {}),\n rootHelpFooter: builtinHelp(config, url),\n ...(hooks.formatError ? { formatError: hooks.formatError } : {}),\n },\n argv,\n io,\n )\n}\n\n/** 프로세스 진입점 (bin.js). 첫 SIGINT = 진행 중 요청 abort (exit 130), 두 번째 = 즉시 종료 */\nexport async function main(config: CustomerCliConfig, hooks: CustomerCliHooks = {}): Promise<void> {\n const controller = new AbortController()\n process.on(\"SIGINT\", () => {\n if (controller.signal.aborted) process.exit(130)\n controller.abort()\n })\n const code = await runCustomerCli(config, process.argv.slice(2), { signal: controller.signal }, hooks)\n process.stdout.write(\"\", () => process.stderr.write(\"\", () => process.exit(code)))\n}\n\nfunction mcp(config: CustomerCliConfig, url: string, argv: readonly string[], io: CliIo): number {\n const [sub, ...rest] = argv\n const isHelp = (t: string) => t === \"--help\" || t === \"-h\"\n const usage = `Usage:\\n ${config.bin} mcp install Print MCP client registration commands (does not modify client config files)\\n`\n if (sub !== undefined && (isHelp(sub) || (sub === \"install\" && rest.some(isHelp)))) {\n io.stdout.write(usage)\n return EXIT.ok\n }\n if (sub === \"install\" && rest.length === 0) {\n io.stdout.write(`${mcpInstallText({ name: config.bin, url, stdioCommand: false, auth: true })}\\n`)\n return EXIT.ok\n }\n io.stderr.write(`error: ${sub === undefined ? \"missing subcommand\" : `unknown argument \"${argv.join(\" \")}\"`}\\n\\n${usage}`)\n return EXIT.usage\n}\n\n/** 사람용 루트 help 뒤의 내장 명령 · 환경변수 */\nfunction builtinHelp(config: CustomerCliConfig, url: string): string {\n const { bin } = config\n const prefix = envPrefix(bin)\n return [\n \"CLI commands:\",\n ...table([\n [\"login\", `Log in to ${url} (${bin} login --help)`],\n [\"logout\", \"Revoke and remove the stored token\"],\n [\"mcp install\", \"Print MCP client registration commands\"],\n [\"--version\", \"Print the version\"],\n ]).map((l) => ` ${l}`),\n \"\",\n \"Environment:\",\n ...table([\n [`${prefix}_TOKEN`, `API token (instead of ${bin} login)`],\n [`${prefix}_URL`, `Server URL (default: ${config.url})`],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { CommandHelp, Help } from "../mcp/help.ts";
2
2
  import type { RegisteredCommand } from "../registry.ts";
3
3
  import { type CliIo } from "../cli/io.ts";
4
+ import { type CliErrorFormatter } from "../cli/output.ts";
4
5
  import { type ClientManifest } from "./manifest.ts";
5
6
  /**
6
7
  * 원격 실행 공용 runner: mext run --url (bin = mext) 과 고객 CLI (bin = cli.bin) 가 같은 코드 경로.
@@ -31,6 +32,8 @@ export interface RemoteRunOptions {
31
32
  manifestHash?: string;
32
33
  /** 사람용 루트 help 뒤에 붙일 텍스트 (고객 CLI 의 login · logout · mcp install 목록) */
33
34
  rootHelpFooter?: string;
35
+ /** 오류 출력 · exit code 커스텀 (cli.errors). 없음 = mext 기본 */
36
+ formatError?: CliErrorFormatter;
34
37
  }
35
38
  /** help 출처. 서버 (tools/list → dispatcher help 호출) · 내장 manifest · 스키마 캐시 (client/cache.ts Schema) */
36
39
  export interface HelpSource {
@@ -7,7 +7,7 @@ import { isExpired, OAuthFlowError, refreshCredentials } from "../remote/oauth.j
7
7
  import { cliSpecFromJsonSchema, coerceValue, defineCliSpec, parseArgs } from "../cli/args.js";
8
8
  import { cliPath, renderHelp } from "../cli/help.js";
9
9
  import { createLineReader, readAll } from "../cli/io.js";
10
- import { CliInterrupted, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult, } from "../cli/output.js";
10
+ import { CliInterrupted, emitError, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult, } from "../cli/output.js";
11
11
  import { MANIFEST_HASH_META_KEY } from "../mcp/constants.js";
12
12
  import { VERSION } from "../version.js";
13
13
  import { openSchema, schemaCacheDir, schemaCacheFile } from "./cache.js";
@@ -15,7 +15,13 @@ import { manifestHelp } from "./manifest.js";
15
15
  export async function runRemote(options, argv, io) {
16
16
  const { url, prog } = options;
17
17
  const tokenEnv = `${envPrefix(options.bin)}_TOKEN`;
18
- const ctx = { login: options.login, tokenEnv, auth: undefined, footer: options.rootHelpFooter };
18
+ const ctx = {
19
+ login: options.login,
20
+ tokenEnv,
21
+ auth: undefined,
22
+ footer: options.rootHelpFooter,
23
+ formatter: options.formatError,
24
+ };
19
25
  const lead = { json: false, yes: false, help: false };
20
26
  let i = 0;
21
27
  for (; i < argv.length; i++) {
@@ -103,9 +109,8 @@ export async function runRemote(options, argv, io) {
103
109
  const error = new MextError("UNKNOWN_COMMAND", id === "" ? "No command given." : `"${cliPath(id)}" is a namespace. Specify a command.`, { command: id, suggested: [], usage: id === "" ? "help" : `help ${id}` });
104
110
  if (json)
105
111
  return report(error, io, prog, true, ctx);
106
- io.stderr.write(formatError(error, prog));
107
- io.stderr.write(`\n${helpText(help, prog, ctx)}`);
108
- return EXIT.usage;
112
+ const text = `${formatError(error, prog)}\n${helpText(help, prog, ctx)}`;
113
+ return emitError(io, { body: errorBody(error), text, exitCode: EXIT.usage, json: false }, ctx.formatter);
109
114
  }
110
115
  const { command, entry } = planned;
111
116
  const yes = lead.yes || planned.yes;
@@ -445,17 +450,13 @@ function report(error, io, prog, json, ctx) {
445
450
  let e = error;
446
451
  if (e instanceof RemoteUnauthorizedError)
447
452
  e = unauthenticatedFrom(e, ctx);
448
- if (json) {
449
- io.stderr.write(formatJson(errorBody(e)));
450
- return exitCodeOf(e);
451
- }
452
- io.stderr.write(formatError(e, prog));
453
+ let text = formatError(e, prog);
453
454
  if (e instanceof MextError && e.code === "PERMISSION_DENIED" && ctx.auth === "none") {
454
455
  const reason = e.details.reason;
455
456
  if (reason === "anonymous")
456
- io.stderr.write(`Run "${ctx.login}" to log in.\n`);
457
+ text += `Run "${ctx.login}" to log in.\n`;
457
458
  }
458
- return exitCodeOf(e);
459
+ return emitError(io, { body: errorBody(e), text, exitCode: exitCodeOf(e), json }, ctx.formatter);
459
460
  }
460
461
  function errorBody(error) {
461
462
  if (error instanceof RemoteExecutionError)