@obov/mext 0.5.0 → 0.6.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 (157) hide show
  1. package/dist/approval.d.ts +33 -0
  2. package/dist/approval.js +36 -0
  3. package/dist/approval.js.map +1 -0
  4. package/dist/audit.d.ts +123 -0
  5. package/dist/audit.js +221 -0
  6. package/dist/audit.js.map +1 -0
  7. package/dist/build-cli.d.ts +1 -0
  8. package/dist/build-cli.js.map +1 -1
  9. package/dist/build.d.ts +2 -0
  10. package/dist/build.js +30 -3
  11. package/dist/build.js.map +1 -1
  12. package/dist/caller.d.ts +53 -0
  13. package/dist/caller.js +51 -0
  14. package/dist/caller.js.map +1 -0
  15. package/dist/cli/build.js +4 -2
  16. package/dist/cli/build.js.map +1 -1
  17. package/dist/cli/dev-audit.d.ts +25 -0
  18. package/dist/cli/dev-audit.js +84 -0
  19. package/dist/cli/dev-audit.js.map +1 -0
  20. package/dist/cli/dev-lock.d.ts +3 -1
  21. package/dist/cli/dev-lock.js +12 -5
  22. package/dist/cli/dev-lock.js.map +1 -1
  23. package/dist/cli/dev-server.d.ts +9 -0
  24. package/dist/cli/dev-server.js +24 -1
  25. package/dist/cli/dev-server.js.map +1 -1
  26. package/dist/cli/dev-tap.d.ts +20 -0
  27. package/dist/cli/dev-tap.js +70 -0
  28. package/dist/cli/dev-tap.js.map +1 -0
  29. package/dist/cli/dev.js +16 -4
  30. package/dist/cli/dev.js.map +1 -1
  31. package/dist/cli/help.js +2 -0
  32. package/dist/cli/help.js.map +1 -1
  33. package/dist/cli/inspect.d.ts +7 -0
  34. package/dist/cli/inspect.js +699 -0
  35. package/dist/cli/inspect.js.map +1 -0
  36. package/dist/cli/main.js +8 -0
  37. package/dist/cli/main.js.map +1 -1
  38. package/dist/cli/output.d.ts +1 -1
  39. package/dist/cli/output.js +3 -2
  40. package/dist/cli/output.js.map +1 -1
  41. package/dist/cli/run.js +28 -4
  42. package/dist/cli/run.js.map +1 -1
  43. package/dist/cli/types.d.ts +9 -0
  44. package/dist/cli/types.js +77 -0
  45. package/dist/cli/types.js.map +1 -0
  46. package/dist/client/index.d.ts +10 -0
  47. package/dist/client/index.js +7 -0
  48. package/dist/client/index.js.map +1 -0
  49. package/dist/client/manifest.js +6 -2
  50. package/dist/client/manifest.js.map +1 -1
  51. package/dist/client/runner.js +37 -3
  52. package/dist/client/runner.js.map +1 -1
  53. package/dist/client/typed.d.ts +54 -0
  54. package/dist/client/typed.js +490 -0
  55. package/dist/client/typed.js.map +1 -0
  56. package/dist/client/types.d.ts +196 -0
  57. package/dist/client/types.js +2 -0
  58. package/dist/client/types.js.map +1 -0
  59. package/dist/command.d.ts +61 -5
  60. package/dist/command.js +42 -1
  61. package/dist/command.js.map +1 -1
  62. package/dist/config.d.ts +42 -10
  63. package/dist/config.js +32 -7
  64. package/dist/config.js.map +1 -1
  65. package/dist/errors.d.ts +21 -3
  66. package/dist/errors.js +13 -0
  67. package/dist/errors.js.map +1 -1
  68. package/dist/expose.d.ts +9 -0
  69. package/dist/expose.js +52 -0
  70. package/dist/expose.js.map +1 -0
  71. package/dist/index.d.ts +15 -6
  72. package/dist/index.js +7 -2
  73. package/dist/index.js.map +1 -1
  74. package/dist/inspect/agent.d.ts +104 -0
  75. package/dist/inspect/agent.js +125 -0
  76. package/dist/inspect/agent.js.map +1 -0
  77. package/dist/inspect/audit.d.ts +46 -0
  78. package/dist/inspect/audit.js +133 -0
  79. package/dist/inspect/audit.js.map +1 -0
  80. package/dist/inspect/compare.d.ts +12 -0
  81. package/dist/inspect/compare.js +31 -0
  82. package/dist/inspect/compare.js.map +1 -0
  83. package/dist/inspect/index.d.ts +17 -0
  84. package/dist/inspect/index.js +12 -0
  85. package/dist/inspect/index.js.map +1 -0
  86. package/dist/inspect/inspector.d.ts +168 -0
  87. package/dist/inspect/inspector.js +392 -0
  88. package/dist/inspect/inspector.js.map +1 -0
  89. package/dist/inspect/profiles.d.ts +95 -0
  90. package/dist/inspect/profiles.js +235 -0
  91. package/dist/inspect/profiles.js.map +1 -0
  92. package/dist/inspect/scenario.d.ts +126 -0
  93. package/dist/inspect/scenario.js +555 -0
  94. package/dist/inspect/scenario.js.map +1 -0
  95. package/dist/inspect/ui-server.d.ts +31 -0
  96. package/dist/inspect/ui-server.js +378 -0
  97. package/dist/inspect/ui-server.js.map +1 -0
  98. package/dist/inspect/ui.html +917 -0
  99. package/dist/instrumentation.d.ts +13 -2
  100. package/dist/instrumentation.js +11 -1
  101. package/dist/instrumentation.js.map +1 -1
  102. package/dist/invoke.d.ts +32 -5
  103. package/dist/invoke.js +150 -16
  104. package/dist/invoke.js.map +1 -1
  105. package/dist/manifest.d.ts +13 -2
  106. package/dist/manifest.js +5 -1
  107. package/dist/manifest.js.map +1 -1
  108. package/dist/mcp/constants.d.ts +5 -0
  109. package/dist/mcp/constants.js +5 -0
  110. package/dist/mcp/constants.js.map +1 -1
  111. package/dist/mcp/dispatch.d.ts +1 -1
  112. package/dist/mcp/dispatch.js +14 -3
  113. package/dist/mcp/dispatch.js.map +1 -1
  114. package/dist/mcp/help.d.ts +2 -0
  115. package/dist/mcp/help.js +6 -1
  116. package/dist/mcp/help.js.map +1 -1
  117. package/dist/mcp/http.d.ts +16 -2
  118. package/dist/mcp/http.js +76 -7
  119. package/dist/mcp/http.js.map +1 -1
  120. package/dist/mcp/index.d.ts +1 -1
  121. package/dist/mcp/index.js +1 -1
  122. package/dist/mcp/index.js.map +1 -1
  123. package/dist/mcp/server.d.ts +12 -1
  124. package/dist/mcp/server.js +63 -17
  125. package/dist/mcp/server.js.map +1 -1
  126. package/dist/mcp/stdio.d.ts +4 -0
  127. package/dist/mcp/stdio.js +3 -0
  128. package/dist/mcp/stdio.js.map +1 -1
  129. package/dist/mcp/tasks.d.ts +3 -1
  130. package/dist/mcp/tasks.js +6 -3
  131. package/dist/mcp/tasks.js.map +1 -1
  132. package/dist/principal.d.ts +22 -0
  133. package/dist/principal.js +29 -0
  134. package/dist/principal.js.map +1 -1
  135. package/dist/registry.d.ts +5 -0
  136. package/dist/registry.js +21 -0
  137. package/dist/registry.js.map +1 -1
  138. package/dist/remote/client.d.ts +2 -0
  139. package/dist/remote/client.js +3 -2
  140. package/dist/remote/client.js.map +1 -1
  141. package/dist/remote/oauth.d.ts +1 -1
  142. package/dist/remote/oauth.js +1 -1
  143. package/dist/remote/oauth.js.map +1 -1
  144. package/dist/start.js +6 -2
  145. package/dist/start.js.map +1 -1
  146. package/dist/suggest.js +1 -1
  147. package/dist/suggest.js.map +1 -1
  148. package/dist/typegen.d.ts +27 -0
  149. package/dist/typegen.js +62 -0
  150. package/dist/typegen.js.map +1 -0
  151. package/dist/version-constant.d.ts +5 -0
  152. package/dist/version-constant.js +6 -0
  153. package/dist/version-constant.js.map +1 -0
  154. package/dist/version.d.ts +1 -2
  155. package/dist/version.js +1 -2
  156. package/dist/version.js.map +1 -1
  157. package/package.json +12 -2
@@ -0,0 +1,77 @@
1
+ import { relative } from "node:path";
2
+ import { loadConfig, resolveCommandsDir } from "../config.js";
3
+ import { resolveTypesPath, TYPES_FILE, writeTypes } from "../typegen.js";
4
+ import { table } from "./help.js";
5
+ import { EXIT, exitCodeOf, formatError } from "./output.js";
6
+ export const TYPES_PROG = "mext types";
7
+ /**
8
+ * mext types [--out <path>] [--check] — 명령어 타입 생성 (specs/client.md §4).
9
+ * 위치 = --out > mext.config.ts types > ./mext.gen.ts. types 설정이 없어도 동작한다.
10
+ * --check = 쓰지 않고 비교, 다르거나 없으면 exit 1
11
+ */
12
+ export async function typesCommand(argv, io) {
13
+ let out;
14
+ let check = false;
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const tok = argv[i];
17
+ if (tok === "--help" || tok === "-h") {
18
+ io.stdout.write(typesHelp());
19
+ return EXIT.ok;
20
+ }
21
+ if (tok === "--check") {
22
+ check = true;
23
+ continue;
24
+ }
25
+ if (tok === "--out" || tok.startsWith("--out=")) {
26
+ const value = tok === "--out" ? argv[++i] : tok.slice("--out=".length);
27
+ if (value === undefined || value === "")
28
+ return usage(io, "--out requires a path");
29
+ out = value;
30
+ continue;
31
+ }
32
+ return usage(io, tok.startsWith("-") ? `unknown flag ${tok}` : `unexpected argument "${tok}"`);
33
+ }
34
+ try {
35
+ const config = await loadConfig(io.cwd);
36
+ const path = resolveTypesPath(io.cwd, config.types, out, TYPES_FILE);
37
+ const result = writeTypes(resolveCommandsDir(io.cwd, config), path, { check });
38
+ const shown = relative(io.cwd, result.path) || result.path;
39
+ const n = `${result.commands} command${result.commands === 1 ? "" : "s"}`;
40
+ if (check && result.changed) {
41
+ io.stderr.write(`error: ${shown} is out of date. Run "${TYPES_PROG}" and commit the result.\n`);
42
+ return EXIT.error;
43
+ }
44
+ io.stdout.write(check ? `${shown} is up to date (${n})\n` : `${result.changed ? "Wrote" : "Unchanged"} ${shown} (${n})\n`);
45
+ return EXIT.ok;
46
+ }
47
+ catch (error) {
48
+ io.stderr.write(formatError(error, TYPES_PROG));
49
+ return exitCodeOf(error);
50
+ }
51
+ }
52
+ function usage(io, message) {
53
+ io.stderr.write(`error: ${message}\nRun "${TYPES_PROG} --help" for usage.\n`);
54
+ return EXIT.usage;
55
+ }
56
+ export function typesHelp() {
57
+ const lines = [
58
+ 'Generate command types for the typed client (createClient<Commands>() from "@obov/mext/client").',
59
+ "",
60
+ "Usage:",
61
+ ` ${TYPES_PROG} [--out <path>] [--check]`,
62
+ "",
63
+ "The generated file maps each command ID to the types of its command module. Input and output types follow",
64
+ "the command definitions; regenerate only after adding, removing or renaming a command file.",
65
+ "",
66
+ "Flags:",
67
+ ...table([
68
+ ["--out <path>", `Output file (default: mext.config.ts types, or ./${TYPES_FILE})`],
69
+ ["--check", "Do not write; exit 1 when the file is missing or out of date (CI)"],
70
+ ["-h, --help", "Show help"],
71
+ ]).map((l) => ` ${l}`),
72
+ "",
73
+ 'mext dev and mext build regenerate the file when mext.config.ts sets types (true or a path).',
74
+ ];
75
+ return `${lines.join("\n")}\n`;
76
+ }
77
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"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","sourcesContent":["import { relative } from \"node:path\"\nimport { loadConfig, resolveCommandsDir } from \"../config.ts\"\nimport { resolveTypesPath, TYPES_FILE, writeTypes } from \"../typegen.ts\"\nimport { table } from \"./help.ts\"\nimport type { CliIo } from \"./io.ts\"\nimport { EXIT, exitCodeOf, formatError } from \"./output.ts\"\n\nexport const TYPES_PROG = \"mext types\"\n\n/**\n * mext types [--out <path>] [--check] — 명령어 타입 생성 (specs/client.md §4).\n * 위치 = --out > mext.config.ts types > ./mext.gen.ts. types 설정이 없어도 동작한다.\n * --check = 쓰지 않고 비교, 다르거나 없으면 exit 1\n */\nexport async function typesCommand(argv: readonly string[], io: CliIo): Promise<number> {\n let out: string | undefined\n let check = false\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!\n if (tok === \"--help\" || tok === \"-h\") {\n io.stdout.write(typesHelp())\n return EXIT.ok\n }\n if (tok === \"--check\") {\n check = true\n continue\n }\n if (tok === \"--out\" || tok.startsWith(\"--out=\")) {\n const value = tok === \"--out\" ? argv[++i] : tok.slice(\"--out=\".length)\n if (value === undefined || value === \"\") return usage(io, \"--out requires a path\")\n out = value\n continue\n }\n return usage(io, tok.startsWith(\"-\") ? `unknown flag ${tok}` : `unexpected argument \"${tok}\"`)\n }\n try {\n const config = await loadConfig(io.cwd)\n const path = resolveTypesPath(io.cwd, config.types, out, TYPES_FILE)!\n const result = writeTypes(resolveCommandsDir(io.cwd, config), path, { check })\n const shown = relative(io.cwd, result.path) || result.path\n const n = `${result.commands} command${result.commands === 1 ? \"\" : \"s\"}`\n if (check && result.changed) {\n io.stderr.write(`error: ${shown} is out of date. Run \"${TYPES_PROG}\" and commit the result.\\n`)\n return EXIT.error\n }\n io.stdout.write(check ? `${shown} is up to date (${n})\\n` : `${result.changed ? \"Wrote\" : \"Unchanged\"} ${shown} (${n})\\n`)\n return EXIT.ok\n } catch (error) {\n io.stderr.write(formatError(error, TYPES_PROG))\n return exitCodeOf(error)\n }\n}\n\nfunction usage(io: CliIo, message: string): number {\n io.stderr.write(`error: ${message}\\nRun \"${TYPES_PROG} --help\" for usage.\\n`)\n return EXIT.usage\n}\n\nexport function typesHelp(): string {\n const lines = [\n 'Generate command types for the typed client (createClient<Commands>() from \"@obov/mext/client\").',\n \"\",\n \"Usage:\",\n ` ${TYPES_PROG} [--out <path>] [--check]`,\n \"\",\n \"The generated file maps each command ID to the types of its command module. Input and output types follow\",\n \"the command definitions; regenerate only after adding, removing or renaming a command file.\",\n \"\",\n \"Flags:\",\n ...table([\n [\"--out <path>\", `Output file (default: mext.config.ts types, or ./${TYPES_FILE})`],\n [\"--check\", \"Do not write; exit 1 when the file is missing or out of date (CI)\"],\n [\"-h, --help\", \"Show help\"],\n ]).map((l) => ` ${l}`),\n \"\",\n 'mext dev and mext build regenerate the file when mext.config.ts sets types (true or a path).',\n ]\n return `${lines.join(\"\\n\")}\\n`\n}\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @obov/mext/client — 타입 client (specs/client.md). 브라우저 · Node.js · Bun.
3
+ * 이 entry 의 import 그래프에는 Node 내장 모듈 · Zod · MCP 서버 SDK · 핸들러 코드가 없다 (타입 import 는 지워진다).
4
+ * 같은 폴더의 main.ts · runner.ts · cache.ts (고객 CLI 런타임, Node 전용) 는 여기서 export 하지 않는다
5
+ */
6
+ export { APPROVAL_META_KEY, CallError, createClient, isCallError } from "./typed.ts";
7
+ export type { MextClient } from "./typed.ts";
8
+ export type { AnyCommands, CallArgs, CallOptions, ClientOptions, CommandInfo, CommandList, CommandMap, CommandShape, CommandSummary, CommandTypes, InputAnswer, InputOf, InputRequestInfo, JsonSchema, Jsonify, NamespaceSummary, OutputOf, ProgressUpdate, RequestOptions, SafeResult, ServerInfo, TaskHandle, TaskStatus, } from "./types.ts";
9
+ export type { ToolExposure } from "../mcp/dispatch.ts";
10
+ export type { Help } from "../mcp/help.ts";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @obov/mext/client — 타입 client (specs/client.md). 브라우저 · Node.js · Bun.
3
+ * 이 entry 의 import 그래프에는 Node 내장 모듈 · Zod · MCP 서버 SDK · 핸들러 코드가 없다 (타입 import 는 지워진다).
4
+ * 같은 폴더의 main.ts · runner.ts · cache.ts (고객 CLI 런타임, Node 전용) 는 여기서 export 하지 않는다
5
+ */
6
+ export { APPROVAL_META_KEY, CallError, createClient, isCallError } from "./typed.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA","sourcesContent":["/**\n * @obov/mext/client — 타입 client (specs/client.md). 브라우저 · Node.js · Bun.\n * 이 entry 의 import 그래프에는 Node 내장 모듈 · Zod · MCP 서버 SDK · 핸들러 코드가 없다 (타입 import 는 지워진다).\n * 같은 폴더의 main.ts · runner.ts · cache.ts (고객 CLI 런타임, Node 전용) 는 여기서 export 하지 않는다\n */\nexport { APPROVAL_META_KEY, CallError, createClient, isCallError } from \"./typed.ts\"\nexport type { MextClient } from \"./typed.ts\"\nexport type {\n AnyCommands,\n CallArgs,\n CallOptions,\n ClientOptions,\n CommandInfo,\n CommandList,\n CommandMap,\n CommandShape,\n CommandSummary,\n CommandTypes,\n InputAnswer,\n InputOf,\n InputRequestInfo,\n JsonSchema,\n Jsonify,\n NamespaceSummary,\n OutputOf,\n ProgressUpdate,\n RequestOptions,\n SafeResult,\n ServerInfo,\n TaskHandle,\n TaskStatus,\n} from \"./types.ts\"\nexport type { ToolExposure } from \"../mcp/dispatch.ts\"\nexport type { Help } from \"../mcp/help.ts\"\n"]}
@@ -8,7 +8,8 @@ export function clientManifest(manifest) {
8
8
  ...(manifest.tools !== undefined ? { tools: manifest.tools } : {}),
9
9
  namespaces: manifest.namespaces.map((n) => ({ ...n })),
10
10
  commands: manifest.commands.map((c) => {
11
- const { file: _file, module: _module, ...rest } = c;
11
+ // expose = 서버 판정용 (내장 manifest 는 이미 mcp 표면 기준으로 걸러진 목록)
12
+ const { file: _file, module: _module, expose: _expose, ...rest } = c;
12
13
  return rest;
13
14
  }),
14
15
  };
@@ -41,6 +42,8 @@ export function manifestFromTools(tools) {
41
42
  examples: Array.isArray(meta.examples) ? [...meta.examples] : [],
42
43
  inputSchema: tool.inputSchema ?? { type: "object" },
43
44
  };
45
+ if (meta.needsApproval === "conditional")
46
+ command.needsApproval = "conditional";
44
47
  if (isRecord(tool.outputSchema))
45
48
  command.outputSchema = tool.outputSchema;
46
49
  if (isRecord(meta.task) && typeof meta.task.ttlMs === "number")
@@ -97,6 +100,7 @@ function commandSummary(c) {
97
100
  readOnly: c.readOnly,
98
101
  destructive: c.destructive,
99
102
  requiresConfirmation: c.requiresConfirmation,
103
+ ...(c.needsApproval !== undefined ? { needsApproval: c.needsApproval } : {}),
100
104
  };
101
105
  }
102
106
  /** mcp/help.ts commandHelp 와 같은 errors 규칙 */
@@ -104,7 +108,7 @@ function commandHelp(c) {
104
108
  const errors = ["INVALID_ARGUMENT"];
105
109
  if (!c.readOnly || c.scopes.length > 0)
106
110
  errors.push("UNAUTHENTICATED", "PERMISSION_DENIED");
107
- if (c.requiresConfirmation)
111
+ if (c.requiresConfirmation || c.needsApproval !== undefined)
108
112
  errors.push("INPUT_UNAVAILABLE", "INPUT_DECLINED");
109
113
  return { kind: "command", ...c, errors };
110
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/client/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import { parentNamespace } from \"../ids.ts\"\nimport { COMMAND_META_KEY } from \"../mcp/constants.ts\"\nimport { isRecord, type RemoteTool } from \"../remote/client.ts\"\nimport { unknownCommand } from \"../lookup.ts\"\nimport type { Manifest, ManifestCommand, ManifestNamespace } from \"../manifest.ts\"\nimport type { ToolExposure } from \"../mcp/dispatch.ts\"\nimport type { CommandHelp, Help, HelpCommandSummary, HelpNamespaceSummary, NamespaceHelp, RootHelp } from \"../mcp/help.ts\"\n\n/**\n * 고객 CLI 번들 형태의 내장 manifest = 빌드 manifest 에서 서버 전용 항목 (file · module · 빌드 경로) 을 뺀 것.\n * help 결과는 서버 help (mcp/help.ts help()) 와 같은 모양 (split 노출의 tool 필드 제외)\n */\n\nexport type ClientManifestCommand = Omit<ManifestCommand, \"file\">\n\nexport interface ClientManifest {\n /** 서버 도구 노출 방식 (빌드 manifest tools). command = 명령어 ID 를 도구 이름으로 호출. 없음 = dispatcher (execute · split) */\n tools?: ToolExposure\n /** commands/_index.md */\n description?: string\n /** 루트 제외, ID 순 */\n namespaces: ManifestNamespace[]\n /** ID 순 */\n commands: ClientManifestCommand[]\n}\n\n/** 빌드 manifest → 내장 manifest (mext build 가 호출) */\nexport function clientManifest(\n manifest: Pick<Manifest, \"description\" | \"namespaces\" | \"commands\"> & { tools?: ToolExposure },\n): ClientManifest {\n const out: ClientManifest = {\n ...(manifest.tools !== undefined ? { tools: manifest.tools } : {}),\n namespaces: manifest.namespaces.map((n) => ({ ...n })),\n commands: manifest.commands.map((c) => {\n const { file: _file, module: _module, ...rest } = c as ManifestCommand & { module?: string }\n return rest\n }),\n }\n if (manifest.description !== undefined) out.description = manifest.description\n return out\n}\n\n/**\n * command 노출 서버의 tools/list → manifest (범용 형태 · stale 번들의 help · 플래그 파싱, §12.2).\n * 명령어 = 도구 (이름 = ID · description · inputSchema · outputSchema) + _meta[COMMAND_META_KEY] (별칭 · 안전성 · scopes ·\n * positional · examples · task, 없으면 annotations 의 readOnlyHint · destructiveHint). 네임스페이스 = ID 의 상위 경로 (설명 없음)\n */\nexport function manifestFromTools(tools: readonly RemoteTool[]): ClientManifest {\n const commands: ClientManifestCommand[] = []\n const namespaces = new Set<string>()\n for (const tool of tools) {\n const meta = isRecord(tool._meta) && isRecord(tool._meta[COMMAND_META_KEY]) ? tool._meta[COMMAND_META_KEY] : {}\n const hints = isRecord(tool.annotations) ? tool.annotations : {}\n const flag = (key: string, hint: unknown): boolean => (typeof meta[key] === \"boolean\" ? meta[key] : hint === true)\n const strings = (key: string): string[] =>\n Array.isArray(meta[key]) ? meta[key].filter((v): v is string => typeof v === \"string\") : []\n const command: ClientManifestCommand = {\n id: tool.name,\n description: tool.description ?? \"\",\n aliases: strings(\"aliases\"),\n readOnly: flag(\"readOnly\", hints.readOnlyHint),\n destructive: flag(\"destructive\", hints.readOnlyHint === true ? false : hints.destructiveHint),\n requiresConfirmation: flag(\"requiresConfirmation\", false),\n scopes: strings(\"scopes\"),\n positional: strings(\"positional\"),\n examples: Array.isArray(meta.examples) ? [...meta.examples] : [],\n inputSchema: tool.inputSchema ?? { type: \"object\" },\n }\n if (isRecord(tool.outputSchema)) command.outputSchema = tool.outputSchema\n if (isRecord(meta.task) && typeof meta.task.ttlMs === \"number\") command.task = { ttlMs: meta.task.ttlMs }\n commands.push(command)\n for (let ns = parentNamespace(tool.name); ns !== \"\"; ns = parentNamespace(ns)) namespaces.add(ns)\n }\n const byId = (a: { id: string }, b: { id: string }) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n return {\n tools: \"command\",\n namespaces: [...namespaces].map((id) => ({ id })).sort(byId),\n commands: commands.sort(byId),\n }\n}\n\n/** 내장 manifest 의 help (target \"\" = 루트 · 네임스페이스 · 명령어 ID · 별칭). 없는 target = UNKNOWN_COMMAND */\nexport function manifestHelp(manifest: ClientManifest, target: string): Help {\n const id = target.trim()\n const namespaces = new Map(manifest.namespaces.map((n) => [n.id, n]))\n const children = (ns: string) => ({\n namespaces: manifest.namespaces.filter((n) => parentNamespace(n.id) === ns).map(namespaceSummary),\n commands: manifest.commands.filter((c) => parentNamespace(c.id) === ns).map(commandSummary),\n })\n // 키 순서 = 서버 help (--help --json 출력이 같은 텍스트)\n if (id === \"\") {\n const out: RootHelp = { kind: \"root\", ...children(\"\") }\n if (manifest.description !== undefined) out.description = manifest.description\n return out\n }\n const ns = namespaces.get(id)\n if (ns) {\n const out: NamespaceHelp = { kind: \"namespace\", id, ...children(id) }\n if (ns.description !== undefined) out.description = ns.description\n const self = manifest.commands.find((c) => c.id === id)\n if (self) out.command = commandHelp(self)\n return out\n }\n const command = manifest.commands.find((c) => c.id === id) ?? manifest.commands.find((c) => c.aliases.includes(id))\n if (command) return commandHelp(command)\n throw unknownCommand(\n id,\n manifest.commands.flatMap((c): [string, string][] => [[c.id, c.id], ...c.aliases.map((a): [string, string] => [a, c.id])]),\n (n) => namespaces.has(n),\n )\n}\n\nfunction namespaceSummary(ns: ManifestNamespace): HelpNamespaceSummary {\n const first = ns.description?.split(\"\\n\").find((l) => l.trim() !== \"\")\n return first === undefined ? { id: ns.id } : { id: ns.id, description: first.replace(/^#+\\s*/, \"\").trim() }\n}\n\nfunction commandSummary(c: ClientManifestCommand): HelpCommandSummary {\n return {\n id: c.id,\n description: c.description,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n }\n}\n\n/** mcp/help.ts commandHelp 와 같은 errors 규칙 */\nfunction commandHelp(c: ClientManifestCommand): CommandHelp {\n const errors = [\"INVALID_ARGUMENT\"]\n if (!c.readOnly || c.scopes.length > 0) errors.push(\"UNAUTHENTICATED\", \"PERMISSION_DENIED\")\n if (c.requiresConfirmation) errors.push(\"INPUT_UNAVAILABLE\", \"INPUT_DECLINED\")\n return { kind: \"command\", ...c, errors }\n}\n"]}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/client/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import { parentNamespace } from \"../ids.ts\"\nimport { COMMAND_META_KEY } from \"../mcp/constants.ts\"\nimport { isRecord, type RemoteTool } from \"../remote/client.ts\"\nimport { unknownCommand } from \"../lookup.ts\"\nimport type { Manifest, ManifestCommand, ManifestNamespace } from \"../manifest.ts\"\nimport type { ToolExposure } from \"../mcp/dispatch.ts\"\nimport type { CommandHelp, Help, HelpCommandSummary, HelpNamespaceSummary, NamespaceHelp, RootHelp } from \"../mcp/help.ts\"\n\n/**\n * 고객 CLI 번들 형태의 내장 manifest = 빌드 manifest 에서 서버 전용 항목 (file · module · 빌드 경로) 을 뺀 것.\n * help 결과는 서버 help (mcp/help.ts help()) 와 같은 모양 (split 노출의 tool 필드 제외)\n */\n\nexport type ClientManifestCommand = Omit<ManifestCommand, \"file\">\n\nexport interface ClientManifest {\n /** 서버 도구 노출 방식 (빌드 manifest tools). command = 명령어 ID 를 도구 이름으로 호출. 없음 = dispatcher (execute · split) */\n tools?: ToolExposure\n /** commands/_index.md */\n description?: string\n /** 루트 제외, ID 순 */\n namespaces: ManifestNamespace[]\n /** ID 순 */\n commands: ClientManifestCommand[]\n}\n\n/** 빌드 manifest → 내장 manifest (mext build 가 호출) */\nexport function clientManifest(\n manifest: Pick<Manifest, \"description\" | \"namespaces\" | \"commands\"> & { tools?: ToolExposure },\n): ClientManifest {\n const out: ClientManifest = {\n ...(manifest.tools !== undefined ? { tools: manifest.tools } : {}),\n namespaces: manifest.namespaces.map((n) => ({ ...n })),\n commands: manifest.commands.map((c) => {\n // expose = 서버 판정용 (내장 manifest 는 이미 mcp 표면 기준으로 걸러진 목록)\n const { file: _file, module: _module, expose: _expose, ...rest } = c as ManifestCommand & { module?: string; expose?: unknown }\n return rest\n }),\n }\n if (manifest.description !== undefined) out.description = manifest.description\n return out\n}\n\n/**\n * command 노출 서버의 tools/list → manifest (범용 형태 · stale 번들의 help · 플래그 파싱, §12.2).\n * 명령어 = 도구 (이름 = ID · description · inputSchema · outputSchema) + _meta[COMMAND_META_KEY] (별칭 · 안전성 · scopes ·\n * positional · examples · task, 없으면 annotations 의 readOnlyHint · destructiveHint). 네임스페이스 = ID 의 상위 경로 (설명 없음)\n */\nexport function manifestFromTools(tools: readonly RemoteTool[]): ClientManifest {\n const commands: ClientManifestCommand[] = []\n const namespaces = new Set<string>()\n for (const tool of tools) {\n const meta = isRecord(tool._meta) && isRecord(tool._meta[COMMAND_META_KEY]) ? tool._meta[COMMAND_META_KEY] : {}\n const hints = isRecord(tool.annotations) ? tool.annotations : {}\n const flag = (key: string, hint: unknown): boolean => (typeof meta[key] === \"boolean\" ? meta[key] : hint === true)\n const strings = (key: string): string[] =>\n Array.isArray(meta[key]) ? meta[key].filter((v): v is string => typeof v === \"string\") : []\n const command: ClientManifestCommand = {\n id: tool.name,\n description: tool.description ?? \"\",\n aliases: strings(\"aliases\"),\n readOnly: flag(\"readOnly\", hints.readOnlyHint),\n destructive: flag(\"destructive\", hints.readOnlyHint === true ? false : hints.destructiveHint),\n requiresConfirmation: flag(\"requiresConfirmation\", false),\n scopes: strings(\"scopes\"),\n positional: strings(\"positional\"),\n examples: Array.isArray(meta.examples) ? [...meta.examples] : [],\n inputSchema: tool.inputSchema ?? { type: \"object\" },\n }\n if (meta.needsApproval === \"conditional\") command.needsApproval = \"conditional\"\n if (isRecord(tool.outputSchema)) command.outputSchema = tool.outputSchema\n if (isRecord(meta.task) && typeof meta.task.ttlMs === \"number\") command.task = { ttlMs: meta.task.ttlMs }\n commands.push(command)\n for (let ns = parentNamespace(tool.name); ns !== \"\"; ns = parentNamespace(ns)) namespaces.add(ns)\n }\n const byId = (a: { id: string }, b: { id: string }) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n return {\n tools: \"command\",\n namespaces: [...namespaces].map((id) => ({ id })).sort(byId),\n commands: commands.sort(byId),\n }\n}\n\n/** 내장 manifest 의 help (target \"\" = 루트 · 네임스페이스 · 명령어 ID · 별칭). 없는 target = UNKNOWN_COMMAND */\nexport function manifestHelp(manifest: ClientManifest, target: string): Help {\n const id = target.trim()\n const namespaces = new Map(manifest.namespaces.map((n) => [n.id, n]))\n const children = (ns: string) => ({\n namespaces: manifest.namespaces.filter((n) => parentNamespace(n.id) === ns).map(namespaceSummary),\n commands: manifest.commands.filter((c) => parentNamespace(c.id) === ns).map(commandSummary),\n })\n // 키 순서 = 서버 help (--help --json 출력이 같은 텍스트)\n if (id === \"\") {\n const out: RootHelp = { kind: \"root\", ...children(\"\") }\n if (manifest.description !== undefined) out.description = manifest.description\n return out\n }\n const ns = namespaces.get(id)\n if (ns) {\n const out: NamespaceHelp = { kind: \"namespace\", id, ...children(id) }\n if (ns.description !== undefined) out.description = ns.description\n const self = manifest.commands.find((c) => c.id === id)\n if (self) out.command = commandHelp(self)\n return out\n }\n const command = manifest.commands.find((c) => c.id === id) ?? manifest.commands.find((c) => c.aliases.includes(id))\n if (command) return commandHelp(command)\n throw unknownCommand(\n id,\n manifest.commands.flatMap((c): [string, string][] => [[c.id, c.id], ...c.aliases.map((a): [string, string] => [a, c.id])]),\n (n) => namespaces.has(n),\n )\n}\n\nfunction namespaceSummary(ns: ManifestNamespace): HelpNamespaceSummary {\n const first = ns.description?.split(\"\\n\").find((l) => l.trim() !== \"\")\n return first === undefined ? { id: ns.id } : { id: ns.id, description: first.replace(/^#+\\s*/, \"\").trim() }\n}\n\nfunction commandSummary(c: ClientManifestCommand): HelpCommandSummary {\n return {\n id: c.id,\n description: c.description,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n ...(c.needsApproval !== undefined ? { needsApproval: c.needsApproval } : {}),\n }\n}\n\n/** mcp/help.ts commandHelp 와 같은 errors 규칙 */\nfunction commandHelp(c: ClientManifestCommand): CommandHelp {\n const errors = [\"INVALID_ARGUMENT\"]\n if (!c.readOnly || c.scopes.length > 0) errors.push(\"UNAUTHENTICATED\", \"PERMISSION_DENIED\")\n if (c.requiresConfirmation || c.needsApproval !== undefined) errors.push(\"INPUT_UNAVAILABLE\", \"INPUT_DECLINED\")\n return { kind: \"command\", ...c, errors }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { COMMAND } from "../command.js";
2
- import { MextError } from "../errors.js";
2
+ import { inputDeclined, MextError } from "../errors.js";
3
3
  import { isSegment } from "../ids.js";
4
4
  import { createRemoteClient, isRecord, RemoteInputRequiredError, RemoteRpcError, RemoteTransportError, RemoteUnauthorizedError, } from "../remote/client.js";
5
5
  import { credentialStore, envPrefix } from "../remote/credentials.js";
@@ -9,7 +9,7 @@ import { cliPath, renderHelp } from "../cli/help.js";
9
9
  import { askUrl } from "../cli/ask-url.js";
10
10
  import { createLineReader, readAll } from "../cli/io.js";
11
11
  import { CliInterrupted, emitError, EXIT, errorJson, exitCodeOf, formatError, formatJson, formatProgress, formatResult, } from "../cli/output.js";
12
- import { MANIFEST_HASH_META_KEY, TOOLS_META_KEY } from "../mcp/constants.js";
12
+ import { APPROVAL_META_KEY, MANIFEST_HASH_META_KEY, TOOLS_META_KEY } from "../mcp/constants.js";
13
13
  import { VERSION } from "../version.js";
14
14
  import { openSchema, schemaCacheDir, schemaCacheFile } from "./cache.js";
15
15
  import { manifestFromTools, manifestHelp } from "./manifest.js";
@@ -122,8 +122,10 @@ export async function runRemote(options, argv, io) {
122
122
  const input = planned.input;
123
123
  const args = exposed ? input : { command: entry.id, args: input };
124
124
  const onInputRequired = inputHandler(io, lines, yes, json, entry.id, controller.signal);
125
- let result = await s.call((o) => s.client.callTool(tool, args, {
125
+ // 승인 (§4 승인 원격 CLI): --yes = 확인 표시 (client 방식 표면) + confirm 입력 요청 승인 (ask 방식, inputHandler)
126
+ const callOnce = (confirmed) => s.call((o) => s.client.callTool(tool, args, {
126
127
  ...o,
128
+ ...(confirmed ? { meta: { [APPROVAL_META_KEY]: { confirmed: true } } } : {}),
127
129
  progressToken: "mext-cli",
128
130
  onNotification: (method, params) => {
129
131
  if (method !== "notifications/progress" || typeof params.progress !== "number")
@@ -137,6 +139,16 @@ export async function runRemote(options, argv, io) {
137
139
  },
138
140
  onInputRequired,
139
141
  }));
142
+ let result = await callOnce(yes);
143
+ // 확인 표시 없이 client 방식 표면에 닿음 = TTY 면 로컬 ask 와 같은 프롬프트 → 승인 = 확인 표시를 실어 1회 다시 호출
144
+ if (!yes && io.isTTY && approvalReason(result) === "unconfirmed") {
145
+ const answer = await lines.question(`${confirmationPrompt(command, input)} [y/N] `, controller.signal);
146
+ if (controller.signal.aborted)
147
+ throw new CliInterrupted();
148
+ if (!/^y(es)?$/i.test((answer ?? "").trim()))
149
+ throw inputDeclined(entry.id, "confirm");
150
+ result = await callOnce(true);
151
+ }
140
152
  // tasks 확장 (§12.6): 종료까지 폴링 → completed result = 동기 결과와 같은 경로 (출력 · exit code)
141
153
  if (isTaskResult(result)) {
142
154
  result = await awaitTask(result, {
@@ -172,6 +184,18 @@ export async function runRemote(options, argv, io) {
172
184
  async function exposureOf(options, schema) {
173
185
  return options.manifest ? options.manifest.tools : schema.exposure();
174
186
  }
187
+ /** isError 결과가 APPROVAL_REQUIRED 면 그 reason (§4 승인) */
188
+ function approvalReason(result) {
189
+ if (result.isError !== true)
190
+ return undefined;
191
+ const sc = result.structuredContent;
192
+ const body = isRecord(sc) && isRecord(sc.error) ? sc.error : undefined;
193
+ return body?.code === "APPROVAL_REQUIRED" && typeof body.reason === "string" ? body.reason : undefined;
194
+ }
195
+ /** 승인 프롬프트 문구. 로컬 ask (invoke.ts confirmationMessage) 와 같은 형식, 입력 = CLI 가 보낸 값 (invoke.ts 는 Zod 를 import 해 번들에 넣지 않는다) */
196
+ function confirmationPrompt(command, input) {
197
+ return `Run ${command.id} with ${JSON.stringify(input)}?${command.destructive ? " This command is destructive." : ""}`;
198
+ }
175
199
  /** 서버 help 재조회 대상 로컬 오류 */
176
200
  function isLocalParseFailure(error) {
177
201
  return error instanceof MextError && (error.code === "UNKNOWN_COMMAND" || error.code === "INVALID_ARGUMENT");
@@ -387,11 +411,16 @@ function commandOf(meta) {
387
411
  examples: [...(meta.examples ?? [])],
388
412
  positional: [...(meta.positional ?? [])],
389
413
  aliases: [],
414
+ // 조건부 승인 (help 의 needsApproval: "conditional") = help 렌더러용 자리 표시 (호출하지 않는다)
415
+ ...(meta.needsApproval === "conditional" ? { needsApproval: remoteOnly } : {}),
390
416
  execute() {
391
417
  throw new Error("remote command");
392
418
  },
393
419
  };
394
420
  }
421
+ function remoteOnly() {
422
+ throw new Error("remote command");
423
+ }
395
424
  /** 원격 명령어 help → RegisteredCommand (CLI 필드 = inputSchema 분석) */
396
425
  export function entryFromHelp(help) {
397
426
  const entry = {
@@ -458,6 +487,7 @@ const MEXT_CODES = [
458
487
  "UNAUTHENTICATED",
459
488
  "PERMISSION_DENIED",
460
489
  "INPUT_UNAVAILABLE",
490
+ "APPROVAL_REQUIRED",
461
491
  ];
462
492
  /** 서버 핸들러 예외 등 mext 구조화 오류 외의 도구 오류. --json = 서버 오류 본문 그대로, exit 1 */
463
493
  export class RemoteExecutionError extends Error {
@@ -511,6 +541,10 @@ function report(error, io, prog, json, ctx) {
511
541
  if (reason === "anonymous")
512
542
  text += `Run "${ctx.login}" to log in.\n`;
513
543
  }
544
+ // 확인 표시 없이 거부 (TTY 없음): 로컬 ask 의 안내와 같은 문구
545
+ if (e instanceof MextError && e.code === "APPROVAL_REQUIRED" && e.details.reason === "unconfirmed") {
546
+ text += "Pass --yes to approve.\n";
547
+ }
514
548
  return emitError(io, { body: errorBody(e), text, exitCode: exitCodeOf(e), json }, ctx.formatter);
515
549
  }
516
550
  function errorBody(error) {