@obov/mext 0.0.0-stage → 0.1.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.
Files changed (155) hide show
  1. package/dist/build-cli.d.ts +39 -0
  2. package/dist/build-cli.js +160 -0
  3. package/dist/build-cli.js.map +1 -0
  4. package/dist/build.d.ts +65 -0
  5. package/dist/build.js +252 -0
  6. package/dist/build.js.map +1 -0
  7. package/dist/cli/args.d.ts +107 -0
  8. package/dist/cli/args.js +621 -0
  9. package/dist/cli/args.js.map +1 -0
  10. package/dist/cli/bin.d.ts +2 -0
  11. package/dist/cli/bin.js +13 -0
  12. package/dist/cli/bin.js.map +1 -0
  13. package/dist/cli/build.d.ts +11 -0
  14. package/dist/cli/build.js +87 -0
  15. package/dist/cli/build.js.map +1 -0
  16. package/dist/cli/dev-lock.d.ts +23 -0
  17. package/dist/cli/dev-lock.js +68 -0
  18. package/dist/cli/dev-lock.js.map +1 -0
  19. package/dist/cli/dev-server.d.ts +20 -0
  20. package/dist/cli/dev-server.js +74 -0
  21. package/dist/cli/dev-server.js.map +1 -0
  22. package/dist/cli/dev.d.ts +21 -0
  23. package/dist/cli/dev.js +564 -0
  24. package/dist/cli/dev.js.map +1 -0
  25. package/dist/cli/help.d.ts +31 -0
  26. package/dist/cli/help.js +238 -0
  27. package/dist/cli/help.js.map +1 -0
  28. package/dist/cli/io.d.ts +26 -0
  29. package/dist/cli/io.js +74 -0
  30. package/dist/cli/io.js.map +1 -0
  31. package/dist/cli/login.d.ts +32 -0
  32. package/dist/cli/login.js +284 -0
  33. package/dist/cli/login.js.map +1 -0
  34. package/dist/cli/logout.d.ts +9 -0
  35. package/dist/cli/logout.js +105 -0
  36. package/dist/cli/logout.js.map +1 -0
  37. package/dist/cli/main.d.ts +19 -0
  38. package/dist/cli/main.js +77 -0
  39. package/dist/cli/main.js.map +1 -0
  40. package/dist/cli/mcp.d.ts +16 -0
  41. package/dist/cli/mcp.js +92 -0
  42. package/dist/cli/mcp.js.map +1 -0
  43. package/dist/cli/output.d.ts +34 -0
  44. package/dist/cli/output.js +172 -0
  45. package/dist/cli/output.js.map +1 -0
  46. package/dist/cli/remote.d.ts +17 -0
  47. package/dist/cli/remote.js +35 -0
  48. package/dist/cli/remote.js.map +1 -0
  49. package/dist/cli/run.d.ts +14 -0
  50. package/dist/cli/run.js +218 -0
  51. package/dist/cli/run.js.map +1 -0
  52. package/dist/cli/start.d.ts +8 -0
  53. package/dist/cli/start.js +37 -0
  54. package/dist/cli/start.js.map +1 -0
  55. package/dist/client/cache.d.ts +104 -0
  56. package/dist/client/cache.js +208 -0
  57. package/dist/client/cache.js.map +1 -0
  58. package/dist/client/main.d.ts +25 -0
  59. package/dist/client/main.js +100 -0
  60. package/dist/client/main.js.map +1 -0
  61. package/dist/client/manifest.d.ts +19 -0
  62. package/dist/client/manifest.js +68 -0
  63. package/dist/client/manifest.js.map +1 -0
  64. package/dist/client/runner.d.ts +49 -0
  65. package/dist/client/runner.js +562 -0
  66. package/dist/client/runner.js.map +1 -0
  67. package/dist/command.d.ts +75 -0
  68. package/dist/command.js +44 -0
  69. package/dist/command.js.map +1 -0
  70. package/dist/config.d.ts +53 -0
  71. package/dist/config.js +122 -0
  72. package/dist/config.js.map +1 -0
  73. package/dist/errors.d.ts +74 -0
  74. package/dist/errors.js +38 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/ids.d.ts +6 -0
  77. package/dist/ids.js +12 -0
  78. package/dist/ids.js.map +1 -0
  79. package/dist/index.d.ts +45 -0
  80. package/dist/index.js +27 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/instrumentation.d.ts +97 -0
  83. package/dist/instrumentation.js +165 -0
  84. package/dist/instrumentation.js.map +1 -0
  85. package/dist/invoke.d.ts +64 -0
  86. package/dist/invoke.js +152 -0
  87. package/dist/invoke.js.map +1 -0
  88. package/dist/lookup.d.ts +9 -0
  89. package/dist/lookup.js +27 -0
  90. package/dist/lookup.js.map +1 -0
  91. package/dist/manifest.d.ts +68 -0
  92. package/dist/manifest.js +77 -0
  93. package/dist/manifest.js.map +1 -0
  94. package/dist/mcp/constants.d.ts +9 -0
  95. package/dist/mcp/constants.js +10 -0
  96. package/dist/mcp/constants.js.map +1 -0
  97. package/dist/mcp/dispatch.d.ts +66 -0
  98. package/dist/mcp/dispatch.js +249 -0
  99. package/dist/mcp/dispatch.js.map +1 -0
  100. package/dist/mcp/help.d.ts +57 -0
  101. package/dist/mcp/help.js +136 -0
  102. package/dist/mcp/help.js.map +1 -0
  103. package/dist/mcp/http.d.ts +92 -0
  104. package/dist/mcp/http.js +366 -0
  105. package/dist/mcp/http.js.map +1 -0
  106. package/dist/mcp/index.d.ts +15 -0
  107. package/dist/mcp/index.js +9 -0
  108. package/dist/mcp/index.js.map +1 -0
  109. package/dist/mcp/install.d.ts +17 -0
  110. package/dist/mcp/install.js +36 -0
  111. package/dist/mcp/install.js.map +1 -0
  112. package/dist/mcp/mrtr.d.ts +82 -0
  113. package/dist/mcp/mrtr.js +130 -0
  114. package/dist/mcp/mrtr.js.map +1 -0
  115. package/dist/mcp/server.d.ts +58 -0
  116. package/dist/mcp/server.js +204 -0
  117. package/dist/mcp/server.js.map +1 -0
  118. package/dist/mcp/stdio.d.ts +57 -0
  119. package/dist/mcp/stdio.js +79 -0
  120. package/dist/mcp/stdio.js.map +1 -0
  121. package/dist/principal.d.ts +16 -0
  122. package/dist/principal.js +24 -0
  123. package/dist/principal.js.map +1 -0
  124. package/dist/registry.d.ts +90 -0
  125. package/dist/registry.js +169 -0
  126. package/dist/registry.js.map +1 -0
  127. package/dist/remote/browser.d.ts +15 -0
  128. package/dist/remote/browser.js +47 -0
  129. package/dist/remote/browser.js.map +1 -0
  130. package/dist/remote/client.d.ts +113 -0
  131. package/dist/remote/client.js +312 -0
  132. package/dist/remote/client.js.map +1 -0
  133. package/dist/remote/credentials.d.ts +48 -0
  134. package/dist/remote/credentials.js +196 -0
  135. package/dist/remote/credentials.js.map +1 -0
  136. package/dist/remote/index.d.ts +8 -0
  137. package/dist/remote/index.js +5 -0
  138. package/dist/remote/index.js.map +1 -0
  139. package/dist/remote/oauth.d.ts +186 -0
  140. package/dist/remote/oauth.js +507 -0
  141. package/dist/remote/oauth.js.map +1 -0
  142. package/dist/scan.d.ts +34 -0
  143. package/dist/scan.js +155 -0
  144. package/dist/scan.js.map +1 -0
  145. package/dist/start.d.ts +63 -0
  146. package/dist/start.js +267 -0
  147. package/dist/start.js.map +1 -0
  148. package/dist/suggest.d.ts +7 -0
  149. package/dist/suggest.js +45 -0
  150. package/dist/suggest.js.map +1 -0
  151. package/dist/version.d.ts +4 -0
  152. package/dist/version.js +13 -0
  153. package/dist/version.js.map +1 -0
  154. package/package.json +49 -4
  155. package/README.md +0 -3
@@ -0,0 +1,92 @@
1
+ import { loadConfig } from "../config.js";
2
+ import { DEFAULT_HOST, DEFAULT_PORT, MCP_PATH } from "../mcp/constants.js";
3
+ import { mcpInstallText } from "../mcp/install.js";
4
+ import { table } from "./help.js";
5
+ import { EXIT, exitCodeOf, formatError } from "./output.js";
6
+ export const MCP_PROG = "mext mcp";
7
+ /**
8
+ * mext mcp install [--name] [--url] MCP 클라이언트 등록 안내 출력 (설정 파일은 쓰지 않음)
9
+ * 서버 형태 (mext mcp · mext mcp --stdio) 는 제거: mext dev · mext dev --stdio (운영 = mext start)
10
+ */
11
+ export async function mcpCommand(argv, io) {
12
+ const parsed = parseMcpArgs(argv);
13
+ if (typeof parsed === "string") {
14
+ io.stderr.write(`error: ${parsed}\nRun "${MCP_PROG} --help" for usage.\n`);
15
+ return EXIT.usage;
16
+ }
17
+ if (parsed.help) {
18
+ io.stdout.write(mcpHelp());
19
+ return EXIT.ok;
20
+ }
21
+ return install(parsed, io);
22
+ }
23
+ /** 반환 문자열 = 사용법 오류 메시지 */
24
+ export function parseMcpArgs(argv) {
25
+ const [first, ...rest] = argv;
26
+ if (first === "--help" || first === "-h")
27
+ return { help: true };
28
+ if (first !== "install") {
29
+ if (argv.includes("--stdio"))
30
+ return `"${MCP_PROG} --stdio" was removed. Use "mext dev --stdio"`;
31
+ if (first === undefined || first.startsWith("--")) {
32
+ return `the "${MCP_PROG}" server was removed. Use "mext dev" (development) or "mext start" (production)`;
33
+ }
34
+ return `unknown subcommand "${first}"`;
35
+ }
36
+ const args = { help: false };
37
+ for (let i = 0; i < rest.length; i++) {
38
+ const tok = rest[i];
39
+ if (tok === "--help" || tok === "-h") {
40
+ args.help = true;
41
+ continue;
42
+ }
43
+ if (!tok.startsWith("--"))
44
+ return `unexpected argument "${tok}"`;
45
+ const eq = tok.indexOf("=");
46
+ const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq);
47
+ if (name !== "name" && name !== "url")
48
+ return `unknown flag --${name} for ${MCP_PROG} install`;
49
+ const value = eq < 0 ? rest[++i] : tok.slice(eq + 1);
50
+ if (value === undefined || value === "")
51
+ return `--${name} requires a value`;
52
+ if (name === "name")
53
+ args.name = value;
54
+ else
55
+ args.url = value;
56
+ }
57
+ return args;
58
+ }
59
+ async function install(args, io) {
60
+ let config;
61
+ try {
62
+ config = await loadConfig(io.cwd);
63
+ }
64
+ catch (error) {
65
+ io.stderr.write(formatError(error, MCP_PROG));
66
+ return exitCodeOf(error);
67
+ }
68
+ const host = config.server?.host ?? DEFAULT_HOST;
69
+ const port = config.server?.port ?? DEFAULT_PORT;
70
+ const url = args.url ?? `http://${host.includes(":") ? `[${host}]` : host}:${port}${MCP_PATH}`;
71
+ io.stdout.write(`${mcpInstallText({ name: args.name ?? "mext", url, auth: config.auth !== undefined })}\n`);
72
+ return EXIT.ok;
73
+ }
74
+ export function mcpHelp() {
75
+ const lines = [
76
+ "Print MCP client registration commands (does not modify client config files).",
77
+ "",
78
+ "Usage:",
79
+ ` ${MCP_PROG} install [--name <name>] [--url <url>]`,
80
+ "",
81
+ "Flags:",
82
+ ...table([
83
+ ["--name <name>", 'Server name to register (default "mext")'],
84
+ ["--url <url>", "Streamable HTTP endpoint (default from mext.config.ts server)"],
85
+ ["-h, --help", "Show help"],
86
+ ]).map((l) => ` ${l}`),
87
+ "",
88
+ 'The MCP server itself runs with "mext dev" (development; --stdio for stdio) or "mext start" (production).',
89
+ ];
90
+ return `${lines.join("\n")}\n`;
91
+ }
92
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/cli/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import { loadConfig, type MextConfig } from \"../config.ts\"\nimport { DEFAULT_HOST, DEFAULT_PORT, MCP_PATH } from \"../mcp/constants.ts\"\nimport { mcpInstallText } from \"../mcp/install.ts\"\nimport { table } from \"./help.ts\"\nimport type { CliIo } from \"./io.ts\"\nimport { EXIT, exitCodeOf, formatError } from \"./output.ts\"\n\nexport const MCP_PROG = \"mext mcp\"\n\n/**\n * mext mcp install [--name] [--url] MCP 클라이언트 등록 안내 출력 (설정 파일은 쓰지 않음)\n * 서버 형태 (mext mcp · mext mcp --stdio) 는 제거: mext dev · mext dev --stdio (운영 = mext start)\n */\nexport async function mcpCommand(argv: readonly string[], io: CliIo): Promise<number> {\n const parsed = parseMcpArgs(argv)\n if (typeof parsed === \"string\") {\n io.stderr.write(`error: ${parsed}\\nRun \"${MCP_PROG} --help\" for usage.\\n`)\n return EXIT.usage\n }\n if (parsed.help) {\n io.stdout.write(mcpHelp())\n return EXIT.ok\n }\n return install(parsed, io)\n}\n\ninterface McpArgs {\n help: boolean\n name?: string\n url?: string\n}\n\n/** 반환 문자열 = 사용법 오류 메시지 */\nexport function parseMcpArgs(argv: readonly string[]): McpArgs | string {\n const [first, ...rest] = argv\n if (first === \"--help\" || first === \"-h\") return { help: true }\n if (first !== \"install\") {\n if (argv.includes(\"--stdio\")) return `\"${MCP_PROG} --stdio\" was removed. Use \"mext dev --stdio\"`\n if (first === undefined || first.startsWith(\"--\")) {\n return `the \"${MCP_PROG}\" server was removed. Use \"mext dev\" (development) or \"mext start\" (production)`\n }\n return `unknown subcommand \"${first}\"`\n }\n const args: McpArgs = { help: false }\n for (let i = 0; i < rest.length; i++) {\n const tok = rest[i]!\n if (tok === \"--help\" || tok === \"-h\") {\n args.help = true\n continue\n }\n if (!tok.startsWith(\"--\")) return `unexpected argument \"${tok}\"`\n const eq = tok.indexOf(\"=\")\n const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq)\n if (name !== \"name\" && name !== \"url\") return `unknown flag --${name} for ${MCP_PROG} install`\n const value = eq < 0 ? rest[++i] : tok.slice(eq + 1)\n if (value === undefined || value === \"\") return `--${name} requires a value`\n if (name === \"name\") args.name = value\n else args.url = value\n }\n return args\n}\n\nasync function install(args: McpArgs, io: CliIo): Promise<number> {\n let config: MextConfig\n try {\n config = await loadConfig(io.cwd)\n } catch (error) {\n io.stderr.write(formatError(error, MCP_PROG))\n return exitCodeOf(error)\n }\n const host = config.server?.host ?? DEFAULT_HOST\n const port = config.server?.port ?? DEFAULT_PORT\n const url = args.url ?? `http://${host.includes(\":\") ? `[${host}]` : host}:${port}${MCP_PATH}`\n io.stdout.write(`${mcpInstallText({ name: args.name ?? \"mext\", url, auth: config.auth !== undefined })}\\n`)\n return EXIT.ok\n}\n\nexport function mcpHelp(): string {\n const lines = [\n \"Print MCP client registration commands (does not modify client config files).\",\n \"\",\n \"Usage:\",\n ` ${MCP_PROG} install [--name <name>] [--url <url>]`,\n \"\",\n \"Flags:\",\n ...table([\n [\"--name <name>\", 'Server name to register (default \"mext\")'],\n [\"--url <url>\", \"Streamable HTTP endpoint (default from mext.config.ts server)\"],\n [\"-h, --help\", \"Show help\"],\n ]).map((l) => ` ${l}`),\n \"\",\n 'The MCP server itself runs with \"mext dev\" (development; --stdio for stdio) or \"mext start\" (production).',\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import type { ProgressUpdate } from "../command.ts";
2
+ /** exit code (spec §5) */
3
+ export declare const EXIT: {
4
+ readonly ok: 0;
5
+ readonly error: 1;
6
+ readonly usage: 2;
7
+ readonly auth: 77;
8
+ readonly interrupted: 130;
9
+ };
10
+ /** 실행 중 SIGINT (ctx.signal abort) 를 나타내는 CLI 내부 오류 */
11
+ export declare class CliInterrupted extends Error {
12
+ constructor();
13
+ }
14
+ /**
15
+ * 오류 → exit code.
16
+ * INVALID_ARGUMENT · UNKNOWN_COMMAND · INPUT_UNAVAILABLE = 2 · UNAUTHENTICATED · PERMISSION_DENIED = 77 · SIGINT = 130 · 그 외 = 1
17
+ */
18
+ export declare function exitCodeOf(error: unknown): number;
19
+ /**
20
+ * --json 오류 본문. MextError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
21
+ * SIGINT = INTERRUPTED, 핸들러 예외 = EXECUTION_ERROR
22
+ */
23
+ export declare function errorJson(error: unknown): Record<string, unknown>;
24
+ /** 사람용 오류 텍스트 (끝 줄바꿈 포함) */
25
+ export declare function formatError(error: unknown, prog: string): string;
26
+ /**
27
+ * 사람용 결과 텍스트 (끝 줄바꿈 포함, undefined = 빈 문자열).
28
+ * 문자열 = 그대로 · 원시 값 = String · 객체 배열 = 표 · 원시 배열 = 줄마다 · 객체 = 키 값 정렬. 중첩 값은 JSON 한 줄
29
+ */
30
+ export declare function formatResult(value: unknown): string;
31
+ /** --json 결과 */
32
+ export declare function formatJson(value: unknown): string;
33
+ /** ctx.progress → stderr 한 줄 */
34
+ export declare function formatProgress(update: ProgressUpdate): string;
@@ -0,0 +1,172 @@
1
+ import { isMextError, MextBuildError, MextError } from "../errors.js";
2
+ import { toKebab } from "./args.js";
3
+ import { cliPath, table } from "./help.js";
4
+ /** exit code (spec §5) */
5
+ export const EXIT = {
6
+ ok: 0,
7
+ error: 1,
8
+ usage: 2,
9
+ auth: 77,
10
+ interrupted: 130,
11
+ };
12
+ /** 실행 중 SIGINT (ctx.signal abort) 를 나타내는 CLI 내부 오류 */
13
+ export class CliInterrupted extends Error {
14
+ constructor() {
15
+ super("Interrupted.");
16
+ this.name = "CliInterrupted";
17
+ }
18
+ }
19
+ /**
20
+ * 오류 → exit code.
21
+ * INVALID_ARGUMENT · UNKNOWN_COMMAND · INPUT_UNAVAILABLE = 2 · UNAUTHENTICATED · PERMISSION_DENIED = 77 · SIGINT = 130 · 그 외 = 1
22
+ */
23
+ export function exitCodeOf(error) {
24
+ if (error instanceof CliInterrupted)
25
+ return EXIT.interrupted;
26
+ if (error instanceof MextError) {
27
+ switch (error.code) {
28
+ case "INVALID_ARGUMENT":
29
+ case "UNKNOWN_COMMAND":
30
+ case "INPUT_UNAVAILABLE":
31
+ return EXIT.usage;
32
+ case "UNAUTHENTICATED":
33
+ case "PERMISSION_DENIED":
34
+ return EXIT.auth;
35
+ }
36
+ }
37
+ return EXIT.error;
38
+ }
39
+ /**
40
+ * --json 오류 본문. MextError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,
41
+ * SIGINT = INTERRUPTED, 핸들러 예외 = EXECUTION_ERROR
42
+ */
43
+ export function errorJson(error) {
44
+ if (error instanceof MextError)
45
+ return error.toJSON();
46
+ if (error instanceof MextBuildError)
47
+ return { code: "BUILD_ERROR", message: error.message, issues: error.issues };
48
+ if (error instanceof CliInterrupted)
49
+ return { code: "INTERRUPTED", message: error.message };
50
+ return { code: "EXECUTION_ERROR", message: error instanceof Error ? error.message : String(error) };
51
+ }
52
+ /** 사람용 오류 텍스트 (끝 줄바꿈 포함) */
53
+ export function formatError(error, prog) {
54
+ const lines = [];
55
+ if (isMextError(error, "INVALID_ARGUMENT")) {
56
+ const d = error.details;
57
+ lines.push(d.command === "" ? "error: invalid arguments" : `error: invalid arguments for ${cliPath(d.command)}`);
58
+ const rows = d.issues.map((i) => {
59
+ if (i.path === "")
60
+ return [i.message];
61
+ return [flagOf(i.path), d.missing_fields.includes(i.path) ? "required" : i.message];
62
+ });
63
+ lines.push(...table(rows.filter((r) => r.length === 2)).map((l) => ` ${l}`));
64
+ lines.push(...rows.filter((r) => r.length === 1).map((r) => ` ${r[0]}`));
65
+ if (d.command !== "")
66
+ lines.push(`Run "${prog} ${cliPath(d.command)} --help" for usage.`);
67
+ }
68
+ else if (isMextError(error, "UNKNOWN_COMMAND")) {
69
+ const d = error.details;
70
+ if (d.command === "")
71
+ lines.push("error: no command given");
72
+ else if (error.message.startsWith("Unknown command"))
73
+ lines.push(`error: unknown command "${cliPath(d.command)}"`);
74
+ else
75
+ lines.push(`error: ${error.message}`);
76
+ if (d.suggested.length > 0)
77
+ lines.push(`Did you mean: ${d.suggested.map((s) => `${prog} ${cliPath(s)}`).join(", ")}?`);
78
+ lines.push(`Run "${usageCommand(prog, d.usage)}" for available commands.`);
79
+ }
80
+ else if (isMextError(error, "PERMISSION_DENIED")) {
81
+ const d = error.details;
82
+ lines.push(`error: ${error.message}`);
83
+ lines.push(` required scopes: ${d.required_scopes.join(", ") || "(none)"}`);
84
+ lines.push(` held scopes: ${d.held_scopes.join(", ") || "(none)"}`);
85
+ }
86
+ else if (isMextError(error, "UNAUTHENTICATED")) {
87
+ lines.push(`error: ${error.message}`);
88
+ if (error.details.login !== undefined)
89
+ lines.push(`Run "${error.details.login}" to log in.`);
90
+ }
91
+ else if (isMextError(error, "INPUT_UNAVAILABLE")) {
92
+ lines.push(`error: ${error.message}`);
93
+ }
94
+ else if (error instanceof Error) {
95
+ lines.push(`error: ${error.message}`);
96
+ }
97
+ else {
98
+ lines.push(`error: ${String(error)}`);
99
+ }
100
+ return `${lines.join("\n")}\n`;
101
+ }
102
+ /** "help" → "mext run help" · "help users" → "mext run users --help" */
103
+ function usageCommand(prog, usage) {
104
+ const target = usage.replace(/^help\s*/, "");
105
+ return target === "" ? `${prog} help` : `${prog} ${cliPath(target)} --help`;
106
+ }
107
+ /** 입력 경로 → 플래그 표기 ("filter.minAge" → "--filter.min-age") */
108
+ function flagOf(path) {
109
+ return `--${path
110
+ .split(".")
111
+ .map((s) => (/^\d+$/.test(s) ? s : toKebab(s)))
112
+ .join(".")}`;
113
+ }
114
+ /**
115
+ * 사람용 결과 텍스트 (끝 줄바꿈 포함, undefined = 빈 문자열).
116
+ * 문자열 = 그대로 · 원시 값 = String · 객체 배열 = 표 · 원시 배열 = 줄마다 · 객체 = 키 값 정렬. 중첩 값은 JSON 한 줄
117
+ */
118
+ export function formatResult(value) {
119
+ if (value === undefined)
120
+ return "";
121
+ if (value === null)
122
+ return "null\n";
123
+ if (typeof value === "string")
124
+ return value.endsWith("\n") ? value : `${value}\n`;
125
+ if (Array.isArray(value)) {
126
+ if (value.length === 0)
127
+ return "(empty)\n";
128
+ if (value.every(isRecord)) {
129
+ const columns = [];
130
+ for (const row of value)
131
+ for (const k of Object.keys(row))
132
+ if (!columns.includes(k))
133
+ columns.push(k);
134
+ return lines(table([columns, ...value.map((row) => columns.map((c) => cell(row[c])))]));
135
+ }
136
+ return lines(value.map(cell));
137
+ }
138
+ if (isRecord(value)) {
139
+ const entries = Object.entries(value).filter(([, v]) => v !== undefined);
140
+ if (entries.length === 0)
141
+ return "(empty)\n";
142
+ return lines(table(entries.map(([k, v]) => [k, cell(v)])));
143
+ }
144
+ return `${cell(value)}\n`;
145
+ }
146
+ /** --json 결과 */
147
+ export function formatJson(value) {
148
+ return `${JSON.stringify(value === undefined ? null : value, null, 2)}\n`;
149
+ }
150
+ function isRecord(value) {
151
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
152
+ }
153
+ function cell(value) {
154
+ if (value === undefined || value === null)
155
+ return "";
156
+ if (typeof value === "string")
157
+ return value;
158
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
159
+ return String(value);
160
+ if (value instanceof Date)
161
+ return value.toISOString();
162
+ return JSON.stringify(value) ?? "";
163
+ }
164
+ function lines(list) {
165
+ return `${list.join("\n")}\n`;
166
+ }
167
+ /** ctx.progress → stderr 한 줄 */
168
+ export function formatProgress(update) {
169
+ const head = update.total !== undefined ? `[${update.progress}/${update.total}]` : `[${update.progress}]`;
170
+ return `${update.message ? `${head} ${update.message}` : head}\n`;
171
+ }
172
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ProgressUpdate } from \"../command.ts\"\nimport { isMextError, MextBuildError, MextError } from \"../errors.ts\"\nimport { toKebab } from \"./args.ts\"\nimport { cliPath, table } from \"./help.ts\"\n\n/** exit code (spec §5) */\nexport const EXIT = {\n ok: 0,\n error: 1,\n usage: 2,\n auth: 77,\n interrupted: 130,\n} as const\n\n/** 실행 중 SIGINT (ctx.signal abort) 를 나타내는 CLI 내부 오류 */\nexport class CliInterrupted extends Error {\n constructor() {\n super(\"Interrupted.\")\n this.name = \"CliInterrupted\"\n }\n}\n\n/**\n * 오류 → exit code.\n * INVALID_ARGUMENT · UNKNOWN_COMMAND · INPUT_UNAVAILABLE = 2 · UNAUTHENTICATED · PERMISSION_DENIED = 77 · SIGINT = 130 · 그 외 = 1\n */\nexport function exitCodeOf(error: unknown): number {\n if (error instanceof CliInterrupted) return EXIT.interrupted\n if (error instanceof MextError) {\n switch (error.code) {\n case \"INVALID_ARGUMENT\":\n case \"UNKNOWN_COMMAND\":\n case \"INPUT_UNAVAILABLE\":\n return EXIT.usage\n case \"UNAUTHENTICATED\":\n case \"PERMISSION_DENIED\":\n return EXIT.auth\n }\n }\n return EXIT.error\n}\n\n/**\n * --json 오류 본문. MextError = toJSON(), 빌드 오류 = BUILD_ERROR + issues,\n * SIGINT = INTERRUPTED, 핸들러 예외 = EXECUTION_ERROR\n */\nexport function errorJson(error: unknown): Record<string, unknown> {\n if (error instanceof MextError) return error.toJSON()\n if (error instanceof MextBuildError) return { code: \"BUILD_ERROR\", message: error.message, issues: error.issues }\n if (error instanceof CliInterrupted) return { code: \"INTERRUPTED\", message: error.message }\n return { code: \"EXECUTION_ERROR\", message: error instanceof Error ? error.message : String(error) }\n}\n\n/** 사람용 오류 텍스트 (끝 줄바꿈 포함) */\nexport function formatError(error: unknown, prog: string): string {\n const lines: string[] = []\n if (isMextError(error, \"INVALID_ARGUMENT\")) {\n const d = error.details\n lines.push(d.command === \"\" ? \"error: invalid arguments\" : `error: invalid arguments for ${cliPath(d.command)}`)\n const rows = d.issues.map((i) => {\n if (i.path === \"\") return [i.message]\n return [flagOf(i.path), d.missing_fields.includes(i.path) ? \"required\" : i.message]\n })\n lines.push(...table(rows.filter((r) => r.length === 2)).map((l) => ` ${l}`))\n lines.push(...rows.filter((r) => r.length === 1).map((r) => ` ${r[0]}`))\n if (d.command !== \"\") lines.push(`Run \"${prog} ${cliPath(d.command)} --help\" for usage.`)\n } else if (isMextError(error, \"UNKNOWN_COMMAND\")) {\n const d = error.details\n if (d.command === \"\") lines.push(\"error: no command given\")\n else if (error.message.startsWith(\"Unknown command\")) lines.push(`error: unknown command \"${cliPath(d.command)}\"`)\n else lines.push(`error: ${error.message}`)\n if (d.suggested.length > 0) lines.push(`Did you mean: ${d.suggested.map((s) => `${prog} ${cliPath(s)}`).join(\", \")}?`)\n lines.push(`Run \"${usageCommand(prog, d.usage)}\" for available commands.`)\n } else if (isMextError(error, \"PERMISSION_DENIED\")) {\n const d = error.details\n lines.push(`error: ${error.message}`)\n lines.push(` required scopes: ${d.required_scopes.join(\", \") || \"(none)\"}`)\n lines.push(` held scopes: ${d.held_scopes.join(\", \") || \"(none)\"}`)\n } else if (isMextError(error, \"UNAUTHENTICATED\")) {\n lines.push(`error: ${error.message}`)\n if (error.details.login !== undefined) lines.push(`Run \"${error.details.login}\" to log in.`)\n } else if (isMextError(error, \"INPUT_UNAVAILABLE\")) {\n lines.push(`error: ${error.message}`)\n } else if (error instanceof Error) {\n lines.push(`error: ${error.message}`)\n } else {\n lines.push(`error: ${String(error)}`)\n }\n return `${lines.join(\"\\n\")}\\n`\n}\n\n/** \"help\" → \"mext run help\" · \"help users\" → \"mext run users --help\" */\nfunction usageCommand(prog: string, usage: string): string {\n const target = usage.replace(/^help\\s*/, \"\")\n return target === \"\" ? `${prog} help` : `${prog} ${cliPath(target)} --help`\n}\n\n/** 입력 경로 → 플래그 표기 (\"filter.minAge\" → \"--filter.min-age\") */\nfunction flagOf(path: string): string {\n return `--${path\n .split(\".\")\n .map((s) => (/^\\d+$/.test(s) ? s : toKebab(s)))\n .join(\".\")}`\n}\n\n/**\n * 사람용 결과 텍스트 (끝 줄바꿈 포함, undefined = 빈 문자열).\n * 문자열 = 그대로 · 원시 값 = String · 객체 배열 = 표 · 원시 배열 = 줄마다 · 객체 = 키 값 정렬. 중첩 값은 JSON 한 줄\n */\nexport function formatResult(value: unknown): string {\n if (value === undefined) return \"\"\n if (value === null) return \"null\\n\"\n if (typeof value === \"string\") return value.endsWith(\"\\n\") ? value : `${value}\\n`\n if (Array.isArray(value)) {\n if (value.length === 0) return \"(empty)\\n\"\n if (value.every(isRecord)) {\n const columns: string[] = []\n for (const row of value) for (const k of Object.keys(row)) if (!columns.includes(k)) columns.push(k)\n return lines(table([columns, ...value.map((row) => columns.map((c) => cell(row[c])))]))\n }\n return lines(value.map(cell))\n }\n if (isRecord(value)) {\n const entries = Object.entries(value).filter(([, v]) => v !== undefined)\n if (entries.length === 0) return \"(empty)\\n\"\n return lines(table(entries.map(([k, v]) => [k, cell(v)])))\n }\n return `${cell(value)}\\n`\n}\n\n/** --json 결과 */\nexport function formatJson(value: unknown): string {\n return `${JSON.stringify(value === undefined ? null : value, null, 2)}\\n`\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof Date)\n}\n\nfunction cell(value: unknown): string {\n if (value === undefined || value === null) return \"\"\n if (typeof value === \"string\") return value\n if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") return String(value)\n if (value instanceof Date) return value.toISOString()\n return JSON.stringify(value) ?? \"\"\n}\n\nfunction lines(list: string[]): string {\n return `${list.join(\"\\n\")}\\n`\n}\n\n/** ctx.progress → stderr 한 줄 */\nexport function formatProgress(update: ProgressUpdate): string {\n const head = update.total !== undefined ? `[${update.progress}/${update.total}]` : `[${update.progress}]`\n return `${update.message ? `${head} ${update.message}` : head}\\n`\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { CliIo } from "./io.ts";
2
+ /**
3
+ * mext run --url <url> (또는 MEXT_URL) — 원격 실행. 고객 CLI 와 같은 runner (client/runner.ts, bin = mext, 서버 help).
4
+ * 토큰 = MEXT_TOKEN, 없으면 저장된 토큰 (mext login --url). 출력 · exit code 는 로컬 모드와 같다.
5
+ */
6
+ export interface RemoteTarget {
7
+ url: string;
8
+ /** --url 을 뺀 mext run argv */
9
+ argv: string[];
10
+ /** MEXT_URL 에서 왔는지 (help 의 prog 표기) */
11
+ fromEnv: boolean;
12
+ }
13
+ /** mext run argv 앞쪽 플래그의 --url <url> · --url=<url>, 없으면 MEXT_URL. 둘 다 없으면 undefined (로컬) */
14
+ export declare function remoteTarget(argv: readonly string[], env: Record<string, string | undefined>): RemoteTarget | undefined;
15
+ /** mext login 안내 문구 */
16
+ export declare function loginCommand(url: string): string;
17
+ export declare function runRemoteCommand(target: RemoteTarget, io: CliIo): Promise<number>;
@@ -0,0 +1,35 @@
1
+ import { runRemote } from "../client/runner.js";
2
+ import { VERSION } from "../version.js";
3
+ import { RUN_PROG } from "./run.js";
4
+ /** mext run argv 앞쪽 플래그의 --url <url> · --url=<url>, 없으면 MEXT_URL. 둘 다 없으면 undefined (로컬) */
5
+ export function remoteTarget(argv, env) {
6
+ for (let i = 0; i < argv.length; i++) {
7
+ const tok = argv[i];
8
+ if (!tok.startsWith("-") || tok === "-" || tok === "--")
9
+ break;
10
+ if (tok === "--url" || tok.startsWith("--url=")) {
11
+ const url = tok === "--url" ? argv[i + 1] : tok.slice("--url=".length);
12
+ const rest = [...argv.slice(0, i), ...argv.slice(i + (tok === "--url" ? 2 : 1))];
13
+ return { url: url ?? "", argv: rest, fromEnv: false };
14
+ }
15
+ }
16
+ const url = env.MEXT_URL;
17
+ if (url !== undefined && url !== "")
18
+ return { url, argv: [...argv], fromEnv: true };
19
+ return undefined;
20
+ }
21
+ /** mext login 안내 문구 */
22
+ export function loginCommand(url) {
23
+ return `mext login --url ${url}`;
24
+ }
25
+ export function runRemoteCommand(target, io) {
26
+ const { url } = target;
27
+ return runRemote({
28
+ bin: "mext",
29
+ url,
30
+ prog: target.fromEnv ? RUN_PROG : `${RUN_PROG} --url ${url}`,
31
+ login: loginCommand(url),
32
+ version: VERSION,
33
+ }, target.argv, io);
34
+ }
35
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/cli/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAenC,4FAA4F;AAC5F,MAAM,UAAU,YAAY,CAAC,IAAuB,EAAE,GAAuC;IAC3F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAA;QACpB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,IAAI;YAAE,MAAK;QAC9D,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChD,MAAM,GAAG,GAAG,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YACtE,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAChF,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;QACvD,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAA;IACxB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IACnF,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,oBAAoB,GAAG,EAAE,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAoB,EAAE,EAAS;IAC9D,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,CAAA;IACtB,OAAO,SAAS,CACd;QACE,GAAG,EAAE,MAAM;QACX,GAAG;QACH,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,UAAU,GAAG,EAAE;QAC5D,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC;QACxB,OAAO,EAAE,OAAO;KACjB,EACD,MAAM,CAAC,IAAI,EACX,EAAE,CACH,CAAA;AACH,CAAC","sourcesContent":["import { runRemote } from \"../client/runner.ts\"\nimport { VERSION } from \"../version.ts\"\nimport type { CliIo } from \"./io.ts\"\nimport { RUN_PROG } from \"./run.ts\"\n\n/**\n * mext run --url <url> (또는 MEXT_URL) — 원격 실행. 고객 CLI 와 같은 runner (client/runner.ts, bin = mext, 서버 help).\n * 토큰 = MEXT_TOKEN, 없으면 저장된 토큰 (mext login --url). 출력 · exit code 는 로컬 모드와 같다.\n */\n\nexport interface RemoteTarget {\n url: string\n /** --url 을 뺀 mext run argv */\n argv: string[]\n /** MEXT_URL 에서 왔는지 (help 의 prog 표기) */\n fromEnv: boolean\n}\n\n/** mext run argv 앞쪽 플래그의 --url <url> · --url=<url>, 없으면 MEXT_URL. 둘 다 없으면 undefined (로컬) */\nexport function remoteTarget(argv: readonly string[], env: Record<string, string | undefined>): RemoteTarget | undefined {\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!\n if (!tok.startsWith(\"-\") || tok === \"-\" || tok === \"--\") break\n if (tok === \"--url\" || tok.startsWith(\"--url=\")) {\n const url = tok === \"--url\" ? argv[i + 1] : tok.slice(\"--url=\".length)\n const rest = [...argv.slice(0, i), ...argv.slice(i + (tok === \"--url\" ? 2 : 1))]\n return { url: url ?? \"\", argv: rest, fromEnv: false }\n }\n }\n const url = env.MEXT_URL\n if (url !== undefined && url !== \"\") return { url, argv: [...argv], fromEnv: true }\n return undefined\n}\n\n/** mext login 안내 문구 */\nexport function loginCommand(url: string): string {\n return `mext login --url ${url}`\n}\n\nexport function runRemoteCommand(target: RemoteTarget, io: CliIo): Promise<number> {\n const { url } = target\n return runRemote(\n {\n bin: \"mext\",\n url,\n prog: target.fromEnv ? RUN_PROG : `${RUN_PROG} --url ${url}`,\n login: loginCommand(url),\n version: VERSION,\n },\n target.argv,\n io,\n )\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { type Registry } from "../registry.ts";
2
+ import { type CliIo } from "./io.ts";
3
+ export declare const RUN_PROG = "mext run";
4
+ /**
5
+ * mext run <ns> <cmd> [flags] — 로컬 실행 (ctx.principal.kind = local, mext.config.ts 의 localPrincipal 로 변경 가능).
6
+ * mext run help [target] · mext run [<ns> [<cmd>]] --help · --json · --yes.
7
+ * --url <url> (또는 MEXT_URL) = 원격 실행 (cli/remote.ts)
8
+ */
9
+ export declare function runCommand(argv: readonly string[], io: CliIo): Promise<number>;
10
+ /** 앞쪽 경로 토큰 → 가장 긴 네임스페이스 · 명령어 (별칭 포함) ID */
11
+ export declare function resolvePath(registry: Registry, tokens: readonly string[]): {
12
+ id: string;
13
+ consumed: number;
14
+ };
@@ -0,0 +1,218 @@
1
+ import { createApp, loadConfig, resolveCommandsDir, resolveLocalPrincipal } from "../config.js";
2
+ import { MextError } from "../errors.js";
3
+ import { isSegment } from "../ids.js";
4
+ import { loadProjectInstrumentation } from "../instrumentation.js";
5
+ import { authorize, findCommand, invoke } from "../invoke.js";
6
+ import { help } from "../mcp/help.js";
7
+ import { resolveCommand } from "../registry.js";
8
+ import { loadRegistry } from "../scan.js";
9
+ import { analyzeInput, assertCliConvertible, coerceValue, parseArgs } from "./args.js";
10
+ import { cliPath, renderHelp } from "./help.js";
11
+ import { createLineReader, readAll } from "./io.js";
12
+ import { CliInterrupted, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult } from "./output.js";
13
+ import { remoteTarget, runRemoteCommand } from "./remote.js";
14
+ export const RUN_PROG = "mext run";
15
+ /**
16
+ * mext run <ns> <cmd> [flags] — 로컬 실행 (ctx.principal.kind = local, mext.config.ts 의 localPrincipal 로 변경 가능).
17
+ * mext run help [target] · mext run [<ns> [<cmd>]] --help · --json · --yes.
18
+ * --url <url> (또는 MEXT_URL) = 원격 실행 (cli/remote.ts)
19
+ */
20
+ export async function runCommand(argv, io) {
21
+ const remote = remoteTarget(argv, io.env);
22
+ if (remote)
23
+ return runRemoteCommand(remote, io);
24
+ const prog = RUN_PROG;
25
+ const lead = { json: false, yes: false, help: false };
26
+ let i = 0;
27
+ for (; i < argv.length; i++) {
28
+ const tok = argv[i];
29
+ if (!tok.startsWith("-") || tok === "-")
30
+ break;
31
+ if (tok === "--json")
32
+ lead.json = true;
33
+ else if (tok === "--yes")
34
+ lead.yes = true;
35
+ else if (tok === "--help" || tok === "-h")
36
+ lead.help = true;
37
+ else {
38
+ return report(usageError(`unknown flag ${tok} before the command`), io, prog, argv.includes("--json"));
39
+ }
40
+ }
41
+ const rest = argv.slice(i);
42
+ const beforeDashDash = rest.indexOf("--") < 0 ? rest : rest.slice(0, rest.indexOf("--"));
43
+ const json = lead.json || beforeDashDash.includes("--json");
44
+ const lines = createLineReader(io);
45
+ try {
46
+ // instrumentation.ts register() = 설정 · 명령어 모듈 import 전 (프로세스당 1회)
47
+ const instrumentation = await loadProjectInstrumentation(io.cwd);
48
+ const config = await loadConfig(io.cwd);
49
+ const registry = await loadRegistry(resolveCommandsDir(io.cwd, config));
50
+ assertCliConvertible(registry);
51
+ // mext run help [target...]
52
+ if (rest[0] === "help") {
53
+ const targetTokens = rest.slice(1).filter((t) => !t.startsWith("-"));
54
+ const { id, consumed } = resolvePath(registry, targetTokens);
55
+ if (consumed < targetTokens.length)
56
+ throw unknownAt(registry, id, targetTokens.slice(consumed));
57
+ return printHelp(registry, id, io, prog, json, config.tools);
58
+ }
59
+ const { id, consumed } = resolvePath(registry, rest);
60
+ const entry = id === "" ? undefined : resolveCommand(registry, id);
61
+ const next = rest[consumed];
62
+ const acceptsPositional = entry !== undefined && entry.command.positional.length > 0;
63
+ if (registry.namespaces.has(id) && next !== undefined && !next.startsWith("-") && !acceptsPositional) {
64
+ throw unknownAt(registry, id, rest.slice(consumed));
65
+ }
66
+ if (entry === undefined && next !== undefined && !next.startsWith("-")) {
67
+ throw unknownAt(registry, id, rest.slice(consumed));
68
+ }
69
+ const help = lead.help || beforeDashDash.includes("--help") || beforeDashDash.includes("-h");
70
+ if (help)
71
+ return printHelp(registry, id, io, prog, json, config.tools);
72
+ if (!entry) {
73
+ // 네임스페이스 (루트 포함) 만 지정 = 명령어 없음
74
+ 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
+ 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;
80
+ }
81
+ const principal = await resolveLocalPrincipal(config);
82
+ authorize(entry, principal); // 권한 → 입력 검증 순서 (invoke 와 같음)
83
+ const parsed = await parseArgs(entry, rest.slice(consumed), { readStdin: () => readAll(io.stdin) });
84
+ const yes = lead.yes || parsed.yes;
85
+ const app = await createApp(config);
86
+ const controller = new AbortController();
87
+ const onAbort = () => controller.abort(io.signal?.reason);
88
+ if (io.signal?.aborted)
89
+ onAbort();
90
+ io.signal?.addEventListener("abort", onAbort, { once: true });
91
+ try {
92
+ const result = await invoke(registry, entry.id, parsed.input, {
93
+ principal,
94
+ signal: controller.signal,
95
+ progress: (update) => io.stderr.write(formatProgress(update)),
96
+ ask: askHandler(io, lines, yes, controller.signal),
97
+ app,
98
+ instrumentation,
99
+ transport: "local",
100
+ });
101
+ if (controller.signal.aborted)
102
+ throw new CliInterrupted();
103
+ io.stdout.write(json ? formatJson(result.value) : formatResult(result.value));
104
+ return EXIT.ok;
105
+ }
106
+ catch (error) {
107
+ if (controller.signal.aborted)
108
+ throw new CliInterrupted();
109
+ throw error;
110
+ }
111
+ finally {
112
+ io.signal?.removeEventListener("abort", onAbort);
113
+ }
114
+ }
115
+ catch (error) {
116
+ return report(error, io, prog, json);
117
+ }
118
+ finally {
119
+ lines.close();
120
+ }
121
+ }
122
+ /** 앞쪽 경로 토큰 → 가장 긴 네임스페이스 · 명령어 (별칭 포함) ID */
123
+ export function resolvePath(registry, tokens) {
124
+ let id = "";
125
+ let consumed = 0;
126
+ for (const tok of tokens) {
127
+ if (!isSegment(tok))
128
+ break;
129
+ const candidate = id === "" ? tok : `${id}.${tok}`;
130
+ if (!registry.namespaces.has(candidate) && !resolveCommand(registry, candidate))
131
+ break;
132
+ id = candidate;
133
+ consumed++;
134
+ }
135
+ return { id, consumed };
136
+ }
137
+ /** 해석되지 않은 경로 토큰 → UNKNOWN_COMMAND (suggested · usage) */
138
+ function unknownAt(registry, id, tail) {
139
+ const segs = [];
140
+ for (const t of tail) {
141
+ if (t.startsWith("-") || (segs.length > 0 && !isSegment(t)))
142
+ break;
143
+ segs.push(t);
144
+ if (!isSegment(t))
145
+ break;
146
+ }
147
+ const unknown = [id, ...segs].filter((s) => s !== "").join(".");
148
+ try {
149
+ findCommand(registry, unknown);
150
+ }
151
+ catch (error) {
152
+ if (error instanceof MextError)
153
+ return error;
154
+ throw error;
155
+ }
156
+ return new MextError("UNKNOWN_COMMAND", `Unknown command "${unknown}".`, { command: unknown, suggested: [], usage: "help" });
157
+ }
158
+ /** --help --json = MCP help 의 structuredContent 와 같은 데이터 (mext mcp 의 tools 노출 기준) */
159
+ function printHelp(registry, id, io, prog, json, tools) {
160
+ const toolFor = tools === "split" ? (e) => (e.command.readOnly ? "query" : "mutate") : undefined;
161
+ io.stdout.write(json ? formatJson(help(registry, id, toolFor ? { toolFor } : {})) : renderHelp(registry, id, { prog }));
162
+ return EXIT.ok;
163
+ }
164
+ function report(error, io, prog, json) {
165
+ io.stderr.write(json ? formatJson(errorJson(error)) : formatError(error, prog));
166
+ return exitCodeOf(error);
167
+ }
168
+ function usageError(message) {
169
+ return new MextError("INVALID_ARGUMENT", message, {
170
+ command: "",
171
+ missing_fields: [],
172
+ issues: [{ path: "", code: "unknown_flag", message }],
173
+ expected_schema: {},
174
+ examples: [],
175
+ });
176
+ }
177
+ /**
178
+ * ctx.ask. --yes = confirm 승인. TTY 없음 = 즉시 INPUT_UNAVAILABLE.
179
+ * TTY = stderr 프롬프트 + stdin 한 줄 (confirm = y/yes, input = 필드별 입력을 스키마 모양으로 변환)
180
+ */
181
+ function askHandler(io, lines, yes, signal) {
182
+ return async (request) => {
183
+ if (request.kind === "confirm" && yes)
184
+ return true;
185
+ if (!io.isTTY) {
186
+ const hint = request.kind === "confirm" ? " Pass --yes to approve." : " Run it in an interactive terminal.";
187
+ throw new MextError("INPUT_UNAVAILABLE", `${request.command} asked "${request.message}", but stdin is not a terminal.${hint}`, { command: request.command });
188
+ }
189
+ if (request.kind === "confirm") {
190
+ const answer = await lines.question(`${request.message} [y/N] `, signal);
191
+ if (signal.aborted)
192
+ throw new CliInterrupted();
193
+ return /^y(es)?$/i.test((answer ?? "").trim());
194
+ }
195
+ io.stderr.write(`${request.message}\n`);
196
+ const { spec } = analyzeInput(request.schema, request.requestedSchema);
197
+ const out = {};
198
+ for (const f of spec.fields) {
199
+ const answer = await lines.question(` ${f.key}${f.required ? "" : " (optional)"}: `, signal);
200
+ if (signal.aborted)
201
+ throw new CliInterrupted();
202
+ if (answer === undefined || answer === "")
203
+ continue;
204
+ if (f.kind.type === "array") {
205
+ const kind = f.kind;
206
+ out[f.key] = answer.split(",").map((s) => {
207
+ const r = coerceValue(kind, s.trim());
208
+ return r.ok ? r.value : s;
209
+ });
210
+ continue;
211
+ }
212
+ const r = coerceValue(f.kind, answer);
213
+ out[f.key] = r.ok ? r.value : answer;
214
+ }
215
+ return out;
216
+ };
217
+ }
218
+ //# sourceMappingURL=run.js.map