@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,33 @@
1
+ import { type Surface } from "./caller.ts";
2
+ /**
3
+ * 승인 (defineCommand needsApproval · requiresConfirmation, spec §4 승인).
4
+ * 승인이 필요한 호출을 누가 사람에게 확인받는지 = 표면마다 한 곳:
5
+ * ask mext 가 ctx.ask confirm 으로 묻는다 (CLI 프롬프트 · --yes, MCP = MRTR). 기본
6
+ * client 클라이언트가 자기 화면에서 확인받고 요청에 확인 표시를 싣는다. mext 는 묻지 않는다
7
+ */
8
+ export type ApprovalMode = "ask" | "client";
9
+ /** mext.config.ts 의 approval */
10
+ export interface ApprovalConfig {
11
+ /**
12
+ * 표면별 확인 방식. 키 = 논리 표면 (ctx.caller.surface 와 같은 값) 또는 "*" (적지 않은 표면 전부). 적지 않은 표면 = "ask".
13
+ * client = 요청 _meta["dev.mext/approval"] = { confirmed: true } + 서버가 정한 행위자가 human 일 때만 통과
14
+ */
15
+ surfaces?: {
16
+ readonly [surface: string]: ApprovalMode | undefined;
17
+ };
18
+ }
19
+ /** 감사 이벤트의 승인 신호 (AuditEvent.approval). 누가 · 어느 표면 = 같은 이벤트의 actor · principal · surface */
20
+ export interface ApprovalSignal {
21
+ /** 이번 호출에 승인이 필요했는지 (needsApproval 조건식 결과. 예외 · boolean 아닌 값 = true) */
22
+ required: boolean;
23
+ /** required: 그 표면의 확인 방식 */
24
+ method?: ApprovalMode;
25
+ /** required: 확인됨. ask = ctx.ask 승인 (프롬프트 · --yes · MRTR accept), client = 요청의 확인 표시를 인정 */
26
+ confirmed?: boolean;
27
+ }
28
+ /** 표면의 확인 방식: surfaces[surface] → surfaces["*"] → "ask" */
29
+ export declare function approvalModeOf(config: ApprovalConfig | undefined, surface: Surface): ApprovalMode;
30
+ /** 요청 _meta 의 확인 표시 값인지: { confirmed: true }. 다른 키는 무시 (예약) */
31
+ export declare function isApprovalMark(value: unknown): boolean;
32
+ /** mext.config.ts 의 approval 형식 문제. 없으면 undefined */
33
+ export declare function approvalConfigProblem(value: unknown): string | undefined;
@@ -0,0 +1,36 @@
1
+ import { ANY_SURFACE } from "./caller.js";
2
+ import { isSegment } from "./ids.js";
3
+ /** 표면의 확인 방식: surfaces[surface] → surfaces["*"] → "ask" */
4
+ export function approvalModeOf(config, surface) {
5
+ const surfaces = config?.surfaces;
6
+ if (!surfaces)
7
+ return "ask";
8
+ const own = Object.hasOwn(surfaces, surface) ? surfaces[surface] : undefined;
9
+ return own ?? (Object.hasOwn(surfaces, ANY_SURFACE) ? surfaces[ANY_SURFACE] : undefined) ?? "ask";
10
+ }
11
+ /** 요청 _meta 의 확인 표시 값인지: { confirmed: true }. 다른 키는 무시 (예약) */
12
+ export function isApprovalMark(value) {
13
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value.confirmed === true;
14
+ }
15
+ /** mext.config.ts 의 approval 형식 문제. 없으면 undefined */
16
+ export function approvalConfigProblem(value) {
17
+ if (typeof value !== "object" || value === null || Array.isArray(value))
18
+ return "approval must be an object ({ surfaces })";
19
+ const a = value;
20
+ const unknown = Object.keys(a).filter((k) => k !== "surfaces");
21
+ if (unknown.length > 0)
22
+ return `unknown approval key${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}`;
23
+ if (a.surfaces === undefined)
24
+ return undefined;
25
+ if (typeof a.surfaces !== "object" || a.surfaces === null || Array.isArray(a.surfaces)) {
26
+ return 'approval.surfaces must be an object (e.g. { web: "client" })';
27
+ }
28
+ for (const [surface, mode] of Object.entries(a.surfaces)) {
29
+ if (surface !== ANY_SURFACE && !isSegment(surface))
30
+ return `approval.surfaces key "${surface}" must be a surface name (kebab-case) or "*"`;
31
+ if (mode !== "ask" && mode !== "client")
32
+ return `approval.surfaces.${surface} must be "ask" or "client"`;
33
+ }
34
+ return undefined;
35
+ }
36
+ //# sourceMappingURL=approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAgB,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA6BpC,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,MAAkC,EAAE,OAAgB;IACjF,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAA;IACjC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC5E,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,CAAA;AACnG,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAK,KAAiC,CAAC,SAAS,KAAK,IAAI,CAAA;AACtI,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,2CAA2C,CAAA;IAC3H,MAAM,CAAC,GAAG,KAAgC,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,CAAA;IAC9D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,uBAAuB,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;IAC9G,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC9C,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvF,OAAO,8DAA8D,CAAA;IACvE,CAAC;IACD,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,IAAI,OAAO,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,OAAO,0BAA0B,OAAO,8CAA8C,CAAA;QAC1I,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,qBAAqB,OAAO,4BAA4B,CAAA;IAC1G,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC","sourcesContent":["import { ANY_SURFACE, type Surface } from \"./caller.ts\"\nimport { isSegment } from \"./ids.ts\"\n\n/**\n * 승인 (defineCommand needsApproval · requiresConfirmation, spec §4 승인).\n * 승인이 필요한 호출을 누가 사람에게 확인받는지 = 표면마다 한 곳:\n * ask mext 가 ctx.ask confirm 으로 묻는다 (CLI 프롬프트 · --yes, MCP = MRTR). 기본\n * client 클라이언트가 자기 화면에서 확인받고 요청에 확인 표시를 싣는다. mext 는 묻지 않는다\n */\nexport type ApprovalMode = \"ask\" | \"client\"\n\n/** mext.config.ts 의 approval */\nexport interface ApprovalConfig {\n /**\n * 표면별 확인 방식. 키 = 논리 표면 (ctx.caller.surface 와 같은 값) 또는 \"*\" (적지 않은 표면 전부). 적지 않은 표면 = \"ask\".\n * client = 요청 _meta[\"dev.mext/approval\"] = { confirmed: true } + 서버가 정한 행위자가 human 일 때만 통과\n */\n surfaces?: { readonly [surface: string]: ApprovalMode | undefined }\n}\n\n/** 감사 이벤트의 승인 신호 (AuditEvent.approval). 누가 · 어느 표면 = 같은 이벤트의 actor · principal · surface */\nexport interface ApprovalSignal {\n /** 이번 호출에 승인이 필요했는지 (needsApproval 조건식 결과. 예외 · boolean 아닌 값 = true) */\n required: boolean\n /** required: 그 표면의 확인 방식 */\n method?: ApprovalMode\n /** required: 확인됨. ask = ctx.ask 승인 (프롬프트 · --yes · MRTR accept), client = 요청의 확인 표시를 인정 */\n confirmed?: boolean\n}\n\n/** 표면의 확인 방식: surfaces[surface] → surfaces[\"*\"] → \"ask\" */\nexport function approvalModeOf(config: ApprovalConfig | undefined, surface: Surface): ApprovalMode {\n const surfaces = config?.surfaces\n if (!surfaces) return \"ask\"\n const own = Object.hasOwn(surfaces, surface) ? surfaces[surface] : undefined\n return own ?? (Object.hasOwn(surfaces, ANY_SURFACE) ? surfaces[ANY_SURFACE] : undefined) ?? \"ask\"\n}\n\n/** 요청 _meta 의 확인 표시 값인지: { confirmed: true }. 다른 키는 무시 (예약) */\nexport function isApprovalMark(value: unknown): boolean {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) && (value as { confirmed?: unknown }).confirmed === true\n}\n\n/** mext.config.ts 의 approval 형식 문제. 없으면 undefined */\nexport function approvalConfigProblem(value: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return \"approval must be an object ({ surfaces })\"\n const a = value as Record<string, unknown>\n const unknown = Object.keys(a).filter((k) => k !== \"surfaces\")\n if (unknown.length > 0) return `unknown approval key${unknown.length === 1 ? \"\" : \"s\"}: ${unknown.join(\", \")}`\n if (a.surfaces === undefined) return undefined\n if (typeof a.surfaces !== \"object\" || a.surfaces === null || Array.isArray(a.surfaces)) {\n return 'approval.surfaces must be an object (e.g. { web: \"client\" })'\n }\n for (const [surface, mode] of Object.entries(a.surfaces)) {\n if (surface !== ANY_SURFACE && !isSegment(surface)) return `approval.surfaces key \"${surface}\" must be a surface name (kebab-case) or \"*\"`\n if (mode !== \"ask\" && mode !== \"client\") return `approval.surfaces.${surface} must be \"ask\" or \"client\"`\n }\n return undefined\n}\n"]}
@@ -0,0 +1,123 @@
1
+ import type { ApprovalSignal } from "./approval.ts";
2
+ import { type ReportedClient, type Surface } from "./caller.ts";
3
+ import type { Command } from "./command.ts";
4
+ import type { TraceIds, Transport } from "./instrumentation.ts";
5
+ import type { Actor, Principal } from "./principal.ts";
6
+ import type { RegisteredCommand } from "./registry.ts";
7
+ /**
8
+ * 감사 (spec §11.1). invoke 경계에서 호출 1건 = AuditEvent 1건을 config.audit.sink 로 넘긴다.
9
+ * mext 는 저장소에 의존하지 않는다. sink 미설정 = startAudit 가 undefined (이벤트 · 측정 없음).
10
+ * span (§11) 과 달리 표본이 아니다: 대상 명령어의 모든 호출을 sink 에 넘긴다
11
+ */
12
+ /** mext.config.ts 의 audit */
13
+ export interface AuditConfig {
14
+ /** 이벤트 1건 전달. 응답 전에 await 한다. 예외 = onerror (응답 불변) */
15
+ sink: (event: AuditEvent) => unknown;
16
+ /** readOnly 명령어도 기록. 기본 false = 변경 명령어만 */
17
+ includeReadOnly?: boolean;
18
+ /**
19
+ * expose (§4) 로 숨긴 명령어를 그 표면에서 호출하려 한 시도도 기록 (outcome "hidden", 입력 없음). 기본 false.
20
+ * 호출자가 받는 응답은 기록 여부와 무관하게 없는 명령어와 같다. 실제로 없는 ID 는 기록하지 않는다
21
+ */
22
+ includeHidden?: boolean;
23
+ /** false = 입력을 기록하지 않는다. 기본 true (명령어 audit.input 으로 가림) */
24
+ input?: boolean;
25
+ /** sink · 명령어 audit 함수 (target · summary · input) 의 예외 보고. 기본 stderr 1줄 */
26
+ onerror?: (error: unknown) => void;
27
+ }
28
+ /** hidden = expose 로 숨긴 명령어의 호출 시도 (includeHidden). 실행 · 권한 판정 전이라 denied 와 구분한다 */
29
+ export type AuditOutcome = "success" | "error" | "denied" | "hidden";
30
+ /** 대상. type · id 외 값 (버전 해시 등) 을 자유롭게 싣는다 */
31
+ export interface AuditTarget {
32
+ type: string;
33
+ id?: string;
34
+ [key: string]: unknown;
35
+ }
36
+ export interface AuditEvent {
37
+ /** 정식 명령어 ID (별칭으로 호출해도) */
38
+ command: string;
39
+ readOnly: boolean;
40
+ /** 시작 시각 (ISO 8601) */
41
+ at: string;
42
+ /** 종료까지 (ms). task = 분리 실행 종료까지. hidden = 0 */
43
+ durationMs: number;
44
+ /** ctx.caller.surface 와 같은 값: 서버가 정한 논리 표면 */
45
+ surface: Surface;
46
+ /** 핸들러를 실행한 어댑터 */
47
+ transport: Transport;
48
+ /** 서버 (auth · localPrincipal) 가 정한 행위자. 없음 = 미확정 */
49
+ actor?: Actor;
50
+ /** 자가 신고 clientInfo. 검증되지 않은 값 */
51
+ client?: ReportedClient;
52
+ /** scopes · claims · 토큰은 넣지 않는다 */
53
+ principal: {
54
+ id: string;
55
+ kind: Principal["kind"];
56
+ };
57
+ outcome: AuditOutcome;
58
+ /** error · denied 의 code (MextError · CommandError code · EXECUTION_ERROR · INTERRUPTED) · message */
59
+ error?: {
60
+ code: string;
61
+ message: string;
62
+ };
63
+ /**
64
+ * 승인 신호 (§4 승인). 승인 대상 명령어 (needsApproval · requiresConfirmation) 의 승인 판정까지 간 호출에만 있다.
65
+ * 누가 · 어느 표면 = 이 이벤트의 actor · principal · surface
66
+ */
67
+ approval?: ApprovalSignal;
68
+ /** 검증된 입력 (명령어 audit.input 으로 가린 값). 입력 검증 전 거부 · hidden · 기록을 끈 경우 = 없음 */
69
+ input?: unknown;
70
+ target?: AuditTarget;
71
+ summary?: string;
72
+ traceId?: string;
73
+ spanId?: string;
74
+ /** 분리 실행 (tasks 확장) 의 taskId */
75
+ taskId?: string;
76
+ }
77
+ /** 취소 (ctx.signal abort 후 예외) 의 error.code */
78
+ export declare const AUDIT_INTERRUPTED = "INTERRUPTED";
79
+ /** startAudit 의 입력 (InvokeOptions 의 부분) */
80
+ export interface AuditContext {
81
+ audit?: AuditConfig;
82
+ principal: Principal;
83
+ transport?: Transport;
84
+ client?: ReportedClient;
85
+ /** W3C trace context carrier. span 이 없을 때 traceparent 의 traceId */
86
+ traceContext?: Record<string, unknown>;
87
+ }
88
+ /** 호출 1건의 기록. finish · skip 중 먼저 온 것만 적용 (이벤트는 많아야 1건) */
89
+ export interface AuditTrail {
90
+ /** 입력 검증 통과. 명령어 audit 옵션 (target · summary) 을 쓸 수 있고, 가린 입력을 이 시점에 계산 */
91
+ validated(command: Command, input: Record<string, unknown>): void;
92
+ /** span 의 traceId · spanId (observeCommand 가 넘긴다) */
93
+ trace(ids: TraceIds | undefined): void;
94
+ /** 분리 실행 (task) */
95
+ task(taskId: string | undefined): void;
96
+ /** 승인 판정 · 확인 결과 (마지막 값이 이벤트에 실린다) */
97
+ approval(signal: ApprovalSignal): void;
98
+ /** 기록하지 않고 끝 (입력 검증 실패 · MRTR 입력 대기 라운드) */
99
+ skip(): void;
100
+ /** 종료 → 이벤트 전달 (sink await). aborted = ctx.signal 취소 여부. 거부하지 않는다 */
101
+ finish(result: {
102
+ value: unknown;
103
+ } | {
104
+ error: unknown;
105
+ }, aborted: boolean): Promise<void>;
106
+ }
107
+ /**
108
+ * 호출 1건의 감사 시작. sink 없음 · 대상 아님 (readOnly 인데 includeReadOnly 아님) = undefined.
109
+ * entry.command 의 메타데이터만 쓴다 (lazy Registry = 모듈 import 전에도 호출 가능)
110
+ */
111
+ export declare function startAudit(entry: RegisteredCommand, context: AuditContext): AuditTrail | undefined;
112
+ /**
113
+ * expose 로 숨긴 명령어의 호출 시도 1건 (config.audit.includeHidden, spec §11.1). entry = 숨긴 명령어 (호출자가 판정해서 넘긴다).
114
+ * 입력 · 오류 · target · summary 없음 (검증 전 입력에는 가림 규칙을 적용할 수 없다). readOnly 명령어도 기록한다.
115
+ * sink 호출 = 다음 macrotask (응답 처리가 먼저 진행된다). 반환 = 전달 완료 (거부하지 않는다):
116
+ * 응답 시간으로 숨긴 명령어가 드러나지 않게 MCP 어댑터는 기다리지 않고, 로컬 CLI 는 종료 전에 await 한다.
117
+ * includeHidden 이 아니면 undefined (이벤트를 만들지 않는다)
118
+ */
119
+ export declare function auditHidden(entry: RegisteredCommand, context: AuditContext): Promise<void> | undefined;
120
+ /** defineCommand 의 audit 형식 문제. 없으면 undefined */
121
+ export declare function commandAuditProblem(value: unknown): string | undefined;
122
+ /** mext.config.ts 의 audit 형식 문제. 없으면 undefined */
123
+ export declare function auditConfigProblem(value: unknown): string | undefined;
package/dist/audit.js ADDED
@@ -0,0 +1,221 @@
1
+ import { callerOf } from "./caller.js";
2
+ import { isDeclaredError, isMextError } from "./errors.js";
3
+ /** 취소 (ctx.signal abort 후 예외) 의 error.code */
4
+ export const AUDIT_INTERRUPTED = "INTERRUPTED";
5
+ /**
6
+ * 호출 1건의 감사 시작. sink 없음 · 대상 아님 (readOnly 인데 includeReadOnly 아님) = undefined.
7
+ * entry.command 의 메타데이터만 쓴다 (lazy Registry = 모듈 import 전에도 호출 가능)
8
+ */
9
+ export function startAudit(entry, context) {
10
+ const config = context.audit;
11
+ if (!config)
12
+ return undefined;
13
+ const readOnly = entry.command.readOnly;
14
+ if (readOnly && config.includeReadOnly !== true)
15
+ return undefined;
16
+ const at = new Date().toISOString();
17
+ const started = performance.now();
18
+ let done = false;
19
+ let command;
20
+ let input;
21
+ let validatedInput = {};
22
+ let trace;
23
+ let taskId;
24
+ let approval;
25
+ return {
26
+ validated(loaded, value) {
27
+ command = loaded;
28
+ validatedInput = value;
29
+ const option = loaded.audit?.input;
30
+ if (config.input === false || option === false)
31
+ return;
32
+ if (typeof option !== "function")
33
+ input = { value };
34
+ // input 함수 예외 = 입력 없이 기록 (가리지 않은 값으로 대체하지 않는다)
35
+ else
36
+ guarded(config, () => void (input = { value: option(value) }));
37
+ },
38
+ trace(ids) {
39
+ trace = ids;
40
+ },
41
+ task(id) {
42
+ taskId = id;
43
+ },
44
+ approval(signal) {
45
+ approval = signal;
46
+ },
47
+ skip() {
48
+ done = true;
49
+ },
50
+ async finish(result, aborted) {
51
+ if (done)
52
+ return;
53
+ done = true;
54
+ const value = "value" in result ? result.value : undefined;
55
+ const event = {
56
+ command: entry.id,
57
+ readOnly,
58
+ at,
59
+ durationMs: Math.max(0, Math.round(performance.now() - started)),
60
+ ...callerFields(context),
61
+ ..."error" in result ? failure(result.error, aborted) : { outcome: "success" },
62
+ ...(approval ? { approval } : {}),
63
+ ...(input ? { input: input.value } : {}),
64
+ };
65
+ const options = command?.audit;
66
+ if (options?.target) {
67
+ guarded(config, () => {
68
+ const target = options.target(validatedInput, value);
69
+ if (isTarget(target))
70
+ event.target = target;
71
+ });
72
+ }
73
+ if (options?.summary !== undefined) {
74
+ guarded(config, () => {
75
+ const summary = typeof options.summary === "function" ? options.summary(validatedInput, value) : options.summary;
76
+ if (typeof summary === "string")
77
+ event.summary = summary;
78
+ });
79
+ }
80
+ const ids = trace ?? traceparentIds(context.traceContext);
81
+ if (ids?.traceId !== undefined)
82
+ event.traceId = ids.traceId;
83
+ if (ids?.spanId !== undefined)
84
+ event.spanId = ids.spanId;
85
+ if (taskId !== undefined)
86
+ event.taskId = taskId;
87
+ await deliver(config, event);
88
+ },
89
+ };
90
+ }
91
+ /** 이벤트의 호출자 필드 (ctx.caller 와 같은 계산) */
92
+ function callerFields(context) {
93
+ const caller = callerOf(context.principal, context.transport ?? "local", context.client);
94
+ return {
95
+ surface: caller.surface,
96
+ transport: caller.transport,
97
+ ...(caller.actor !== undefined ? { actor: caller.actor } : {}),
98
+ ...(caller.client !== undefined ? { client: caller.client } : {}),
99
+ principal: { id: context.principal.id, kind: context.principal.kind },
100
+ };
101
+ }
102
+ /**
103
+ * expose 로 숨긴 명령어의 호출 시도 1건 (config.audit.includeHidden, spec §11.1). entry = 숨긴 명령어 (호출자가 판정해서 넘긴다).
104
+ * 입력 · 오류 · target · summary 없음 (검증 전 입력에는 가림 규칙을 적용할 수 없다). readOnly 명령어도 기록한다.
105
+ * sink 호출 = 다음 macrotask (응답 처리가 먼저 진행된다). 반환 = 전달 완료 (거부하지 않는다):
106
+ * 응답 시간으로 숨긴 명령어가 드러나지 않게 MCP 어댑터는 기다리지 않고, 로컬 CLI 는 종료 전에 await 한다.
107
+ * includeHidden 이 아니면 undefined (이벤트를 만들지 않는다)
108
+ */
109
+ export function auditHidden(entry, context) {
110
+ const config = context.audit;
111
+ if (config?.includeHidden !== true)
112
+ return undefined;
113
+ const event = {
114
+ command: entry.id,
115
+ readOnly: entry.command.readOnly,
116
+ at: new Date().toISOString(),
117
+ durationMs: 0,
118
+ ...callerFields(context),
119
+ outcome: "hidden",
120
+ };
121
+ const ids = traceparentIds(context.traceContext);
122
+ if (ids?.traceId !== undefined)
123
+ event.traceId = ids.traceId;
124
+ return new Promise((resolve) => setTimeout(() => resolve(deliver(config, event)), 0));
125
+ }
126
+ /**
127
+ * sink 전달. sink 예외 정책 (spec §11.1 sink 실패, 결정): onerror 보고 후 삼킨다 = 응답 불변 (onCommandError 와 같은 원칙).
128
+ * 감사는 실행 후 기록이라 여기서 실패해도 실행은 이미 끝났다. 정책은 이 함수 한 곳에 있다
129
+ */
130
+ async function deliver(config, event) {
131
+ try {
132
+ await config.sink(event);
133
+ }
134
+ catch (error) {
135
+ report(config, error);
136
+ }
137
+ }
138
+ /** 명령어 audit 함수 (target · summary · input) 실행. 예외 = onerror, 그 필드 없이 계속 */
139
+ function guarded(config, fn) {
140
+ try {
141
+ fn();
142
+ }
143
+ catch (error) {
144
+ report(config, error);
145
+ }
146
+ }
147
+ function report(config, error) {
148
+ try {
149
+ ;
150
+ (config.onerror ?? defaultOnError)(error);
151
+ }
152
+ catch {
153
+ // 보고는 응답을 바꾸지 않는다
154
+ }
155
+ }
156
+ function defaultOnError(error) {
157
+ const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
158
+ process.stderr.write(`mext: audit threw: ${message}\n`);
159
+ }
160
+ /**
161
+ * 예외 → outcome · error. 거부 = PERMISSION_DENIED · INPUT_DECLINED · APPROVAL_REQUIRED. 그 외 = error:
162
+ * 취소 후 예외 = INTERRUPTED, MextError · CommandError = 그 code, 나머지 = EXECUTION_ERROR (응답과 같은 code · message)
163
+ */
164
+ function failure(error, aborted) {
165
+ if (isMextError(error, "PERMISSION_DENIED") || isMextError(error, "INPUT_DECLINED") || isMextError(error, "APPROVAL_REQUIRED")) {
166
+ return { outcome: "denied", error: { code: error.code, message: error.message } };
167
+ }
168
+ if (aborted)
169
+ return { outcome: "error", error: { code: AUDIT_INTERRUPTED, message: "Cancelled before completion." } };
170
+ if (isDeclaredError(error))
171
+ return { outcome: "error", error: { code: error.code, message: error.message } };
172
+ return { outcome: "error", error: { code: "EXECUTION_ERROR", message: error instanceof Error ? error.message : String(error) } };
173
+ }
174
+ function isTarget(value) {
175
+ return typeof value === "object" && value !== null && typeof value.type === "string";
176
+ }
177
+ /** traceparent ("00-<traceId 32 hex>-<parent 16 hex>-<flags>") 의 traceId. 호출 쪽 span 이라 spanId 는 쓰지 않는다 */
178
+ function traceparentIds(carrier) {
179
+ const value = carrier?.traceparent;
180
+ const match = typeof value === "string" ? /^[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}/.exec(value) : null;
181
+ return match && !/^0+$/.test(match[1]) ? { traceId: match[1] } : undefined;
182
+ }
183
+ /** defineCommand 의 audit 형식 문제. 없으면 undefined */
184
+ export function commandAuditProblem(value) {
185
+ if (typeof value !== "object" || value === null || Array.isArray(value))
186
+ return "audit must be an object ({ target, summary, input })";
187
+ const a = value;
188
+ const unknown = Object.keys(a).filter((k) => k !== "target" && k !== "summary" && k !== "input");
189
+ if (unknown.length > 0)
190
+ return `unknown audit key${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}`;
191
+ if (a.target !== undefined && typeof a.target !== "function")
192
+ return "audit.target must be a function";
193
+ if (a.summary !== undefined && typeof a.summary !== "string" && typeof a.summary !== "function") {
194
+ return "audit.summary must be a string or a function";
195
+ }
196
+ if (a.input !== undefined && typeof a.input !== "boolean" && typeof a.input !== "function") {
197
+ return "audit.input must be false or a function";
198
+ }
199
+ return undefined;
200
+ }
201
+ /** mext.config.ts 의 audit 형식 문제. 없으면 undefined */
202
+ export function auditConfigProblem(value) {
203
+ if (typeof value !== "object" || value === null || Array.isArray(value))
204
+ return "audit must be an object ({ sink })";
205
+ const a = value;
206
+ const unknown = Object.keys(a).filter((k) => !["sink", "includeReadOnly", "includeHidden", "input", "onerror"].includes(k));
207
+ if (unknown.length > 0)
208
+ return `unknown audit key${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}`;
209
+ if (typeof a.sink !== "function")
210
+ return "audit.sink must be a function";
211
+ if (a.includeReadOnly !== undefined && typeof a.includeReadOnly !== "boolean")
212
+ return "audit.includeReadOnly must be a boolean";
213
+ if (a.includeHidden !== undefined && typeof a.includeHidden !== "boolean")
214
+ return "audit.includeHidden must be a boolean";
215
+ if (a.input !== undefined && typeof a.input !== "boolean")
216
+ return "audit.input must be a boolean";
217
+ if (a.onerror !== undefined && typeof a.onerror !== "function")
218
+ return "audit.onerror must be a function";
219
+ return undefined;
220
+ }
221
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAqC,MAAM,aAAa,CAAA;AAEzE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0E1D,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAA;AA4B9C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAwB,EAAE,OAAqB;IACxE,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAA;IACvC,IAAI,QAAQ,IAAI,MAAM,CAAC,eAAe,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACnC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IACjC,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,IAAI,OAA4B,CAAA;IAChC,IAAI,KAAqC,CAAA;IACzC,IAAI,cAAc,GAA4B,EAAE,CAAA;IAChD,IAAI,KAA2B,CAAA;IAC/B,IAAI,MAA0B,CAAA;IAC9B,IAAI,QAAoC,CAAA;IACxC,OAAO;QACL,SAAS,CAAC,MAAM,EAAE,KAAK;YACrB,OAAO,GAAG,MAAM,CAAA;YAChB,cAAc,GAAG,KAAK,CAAA;YACtB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAA;YAClC,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK;gBAAE,OAAM;YACtD,IAAI,OAAO,MAAM,KAAK,UAAU;gBAAE,KAAK,GAAG,EAAE,KAAK,EAAE,CAAA;YACnD,+CAA+C;;gBAC1C,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QACrE,CAAC;QACD,KAAK,CAAC,GAAG;YACP,KAAK,GAAG,GAAG,CAAA;QACb,CAAC;QACD,IAAI,CAAC,EAAE;YACL,MAAM,GAAG,EAAE,CAAA;QACb,CAAC;QACD,QAAQ,CAAC,MAAM;YACb,QAAQ,GAAG,MAAM,CAAA;QACnB,CAAC;QACD,IAAI;YACF,IAAI,GAAG,IAAI,CAAA;QACb,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO;YAC1B,IAAI,IAAI;gBAAE,OAAM;YAChB,IAAI,GAAG,IAAI,CAAA;YACX,MAAM,KAAK,GAAG,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;YAC1D,MAAM,KAAK,GAAe;gBACxB,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,QAAQ;gBACR,EAAE;gBACF,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;gBAChE,GAAG,YAAY,CAAC,OAAO,CAAC;gBACxB,GAAG,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAkB,EAAE;gBACvF,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzC,CAAA;YACD,MAAM,OAAO,GAAG,OAAO,EAAE,KAAK,CAAA;YAC9B,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;oBACnB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAO,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;oBACrD,IAAI,QAAQ,CAAC,MAAM,CAAC;wBAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;gBAC7C,CAAC,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,OAAO,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;gBACnC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;oBACnB,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAA;oBAChH,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAA;gBAC1D,CAAC,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;YACzD,IAAI,GAAG,EAAE,OAAO,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;YAC3D,IAAI,GAAG,EAAE,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;YACxD,IAAI,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;YAC/C,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC9B,CAAC;KACF,CAAA;AACH,CAAC;AAED,uCAAuC;AACvC,SAAS,YAAY,CAAC,OAAqB;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACxF,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,SAAS,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE;KACtE,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAwB,EAAE,OAAqB;IACzE,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,IAAI,MAAM,EAAE,aAAa,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACpD,MAAM,KAAK,GAAe;QACxB,OAAO,EAAE,KAAK,CAAC,EAAE;QACjB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;QAChC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,UAAU,EAAE,CAAC;QACb,GAAG,YAAY,CAAC,OAAO,CAAC;QACxB,OAAO,EAAE,QAAQ;KAClB,CAAA;IACD,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAChD,IAAI,GAAG,EAAE,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;IAC3D,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AAC7F,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,OAAO,CAAC,MAAmB,EAAE,KAAiB;IAC3D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACvB,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,SAAS,OAAO,CAAC,MAAmB,EAAE,EAAc;IAClD,IAAI,CAAC;QACH,EAAE,EAAE,CAAA;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACvB,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,MAAmB,EAAE,KAAc;IACjD,IAAI,CAAC;QACH,CAAC;QAAA,CAAC,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,CAAC,KAAK,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACvF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,OAAO,IAAI,CAAC,CAAA;AACzD,CAAC;AAED;;;GAGG;AACH,SAAS,OAAO,CAAC,KAAc,EAAE,OAAgB;IAC/C,IAAI,WAAW,CAAC,KAAK,EAAE,mBAAmB,CAAC,IAAI,WAAW,CAAC,KAAK,EAAE,gBAAgB,CAAC,IAAI,WAAW,CAAC,KAAK,EAAE,mBAAmB,CAAC,EAAE,CAAC;QAC/H,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAA;IACnF,CAAC;IACD,IAAI,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,CAAA;IACrH,IAAI,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAA;IAC5G,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;AAClI,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA4B,CAAC,IAAI,KAAK,QAAQ,CAAA;AAC9G,CAAC;AAED,0GAA0G;AAC1G,SAAS,cAAc,CAAC,OAA4C;IAClE,MAAM,KAAK,GAAG,OAAO,EAAE,WAAW,CAAA;IAClC,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,sDAAsD,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACnH,OAAO,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9E,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,sDAAsD,CAAA;IACtI,MAAM,CAAC,GAAG,KAAgC,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,OAAO,CAAC,CAAA;IAChG,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,oBAAoB,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;IAC3G,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,iCAAiC,CAAA;IACtG,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QAChG,OAAO,8CAA8C,CAAA;IACvD,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QAC3F,OAAO,yCAAyC,CAAA;IAClD,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,oCAAoC,CAAA;IACpH,MAAM,CAAC,GAAG,KAAgC,CAAA;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3H,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,oBAAoB,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;IAC3G,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,+BAA+B,CAAA;IACxE,IAAI,CAAC,CAAC,eAAe,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,eAAe,KAAK,SAAS;QAAE,OAAO,yCAAyC,CAAA;IAC/H,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,aAAa,KAAK,SAAS;QAAE,OAAO,uCAAuC,CAAA;IACzH,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,+BAA+B,CAAA;IACjG,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,kCAAkC,CAAA;IACzG,OAAO,SAAS,CAAA;AAClB,CAAC","sourcesContent":["import type { ApprovalSignal } from \"./approval.ts\"\nimport { callerOf, type ReportedClient, type Surface } from \"./caller.ts\"\nimport type { Command } from \"./command.ts\"\nimport { isDeclaredError, isMextError } from \"./errors.ts\"\nimport type { TraceIds, Transport } from \"./instrumentation.ts\"\nimport type { Actor, Principal } from \"./principal.ts\"\nimport type { RegisteredCommand } from \"./registry.ts\"\n\n/**\n * 감사 (spec §11.1). invoke 경계에서 호출 1건 = AuditEvent 1건을 config.audit.sink 로 넘긴다.\n * mext 는 저장소에 의존하지 않는다. sink 미설정 = startAudit 가 undefined (이벤트 · 측정 없음).\n * span (§11) 과 달리 표본이 아니다: 대상 명령어의 모든 호출을 sink 에 넘긴다\n */\n\n/** mext.config.ts 의 audit */\nexport interface AuditConfig {\n /** 이벤트 1건 전달. 응답 전에 await 한다. 예외 = onerror (응답 불변) */\n sink: (event: AuditEvent) => unknown\n /** readOnly 명령어도 기록. 기본 false = 변경 명령어만 */\n includeReadOnly?: boolean\n /**\n * expose (§4) 로 숨긴 명령어를 그 표면에서 호출하려 한 시도도 기록 (outcome \"hidden\", 입력 없음). 기본 false.\n * 호출자가 받는 응답은 기록 여부와 무관하게 없는 명령어와 같다. 실제로 없는 ID 는 기록하지 않는다\n */\n includeHidden?: boolean\n /** false = 입력을 기록하지 않는다. 기본 true (명령어 audit.input 으로 가림) */\n input?: boolean\n /** sink · 명령어 audit 함수 (target · summary · input) 의 예외 보고. 기본 stderr 1줄 */\n onerror?: (error: unknown) => void\n}\n\n/** hidden = expose 로 숨긴 명령어의 호출 시도 (includeHidden). 실행 · 권한 판정 전이라 denied 와 구분한다 */\nexport type AuditOutcome = \"success\" | \"error\" | \"denied\" | \"hidden\"\n\n/** 대상. type · id 외 값 (버전 해시 등) 을 자유롭게 싣는다 */\nexport interface AuditTarget {\n type: string\n id?: string\n [key: string]: unknown\n}\n\nexport interface AuditEvent {\n /** 정식 명령어 ID (별칭으로 호출해도) */\n command: string\n readOnly: boolean\n /** 시작 시각 (ISO 8601) */\n at: string\n /** 종료까지 (ms). task = 분리 실행 종료까지. hidden = 0 */\n durationMs: number\n /** ctx.caller.surface 와 같은 값: 서버가 정한 논리 표면 */\n surface: Surface\n /** 핸들러를 실행한 어댑터 */\n transport: Transport\n /** 서버 (auth · localPrincipal) 가 정한 행위자. 없음 = 미확정 */\n actor?: Actor\n /** 자가 신고 clientInfo. 검증되지 않은 값 */\n client?: ReportedClient\n /** scopes · claims · 토큰은 넣지 않는다 */\n principal: { id: string; kind: Principal[\"kind\"] }\n outcome: AuditOutcome\n /** error · denied 의 code (MextError · CommandError code · EXECUTION_ERROR · INTERRUPTED) · message */\n error?: { code: string; message: string }\n /**\n * 승인 신호 (§4 승인). 승인 대상 명령어 (needsApproval · requiresConfirmation) 의 승인 판정까지 간 호출에만 있다.\n * 누가 · 어느 표면 = 이 이벤트의 actor · principal · surface\n */\n approval?: ApprovalSignal\n /** 검증된 입력 (명령어 audit.input 으로 가린 값). 입력 검증 전 거부 · hidden · 기록을 끈 경우 = 없음 */\n input?: unknown\n target?: AuditTarget\n summary?: string\n traceId?: string\n spanId?: string\n /** 분리 실행 (tasks 확장) 의 taskId */\n taskId?: string\n}\n\n/** 취소 (ctx.signal abort 후 예외) 의 error.code */\nexport const AUDIT_INTERRUPTED = \"INTERRUPTED\"\n\n/** startAudit 의 입력 (InvokeOptions 의 부분) */\nexport interface AuditContext {\n audit?: AuditConfig\n principal: Principal\n transport?: Transport\n client?: ReportedClient\n /** W3C trace context carrier. span 이 없을 때 traceparent 의 traceId */\n traceContext?: Record<string, unknown>\n}\n\n/** 호출 1건의 기록. finish · skip 중 먼저 온 것만 적용 (이벤트는 많아야 1건) */\nexport interface AuditTrail {\n /** 입력 검증 통과. 명령어 audit 옵션 (target · summary) 을 쓸 수 있고, 가린 입력을 이 시점에 계산 */\n validated(command: Command, input: Record<string, unknown>): void\n /** span 의 traceId · spanId (observeCommand 가 넘긴다) */\n trace(ids: TraceIds | undefined): void\n /** 분리 실행 (task) */\n task(taskId: string | undefined): void\n /** 승인 판정 · 확인 결과 (마지막 값이 이벤트에 실린다) */\n approval(signal: ApprovalSignal): void\n /** 기록하지 않고 끝 (입력 검증 실패 · MRTR 입력 대기 라운드) */\n skip(): void\n /** 종료 → 이벤트 전달 (sink await). aborted = ctx.signal 취소 여부. 거부하지 않는다 */\n finish(result: { value: unknown } | { error: unknown }, aborted: boolean): Promise<void>\n}\n\n/**\n * 호출 1건의 감사 시작. sink 없음 · 대상 아님 (readOnly 인데 includeReadOnly 아님) = undefined.\n * entry.command 의 메타데이터만 쓴다 (lazy Registry = 모듈 import 전에도 호출 가능)\n */\nexport function startAudit(entry: RegisteredCommand, context: AuditContext): AuditTrail | undefined {\n const config = context.audit\n if (!config) return undefined\n const readOnly = entry.command.readOnly\n if (readOnly && config.includeReadOnly !== true) return undefined\n const at = new Date().toISOString()\n const started = performance.now()\n let done = false\n let command: Command | undefined\n let input: { value: unknown } | undefined\n let validatedInput: Record<string, unknown> = {}\n let trace: TraceIds | undefined\n let taskId: string | undefined\n let approval: ApprovalSignal | undefined\n return {\n validated(loaded, value) {\n command = loaded\n validatedInput = value\n const option = loaded.audit?.input\n if (config.input === false || option === false) return\n if (typeof option !== \"function\") input = { value }\n // input 함수 예외 = 입력 없이 기록 (가리지 않은 값으로 대체하지 않는다)\n else guarded(config, () => void (input = { value: option(value) }))\n },\n trace(ids) {\n trace = ids\n },\n task(id) {\n taskId = id\n },\n approval(signal) {\n approval = signal\n },\n skip() {\n done = true\n },\n async finish(result, aborted) {\n if (done) return\n done = true\n const value = \"value\" in result ? result.value : undefined\n const event: AuditEvent = {\n command: entry.id,\n readOnly,\n at,\n durationMs: Math.max(0, Math.round(performance.now() - started)),\n ...callerFields(context),\n ...\"error\" in result ? failure(result.error, aborted) : { outcome: \"success\" as const },\n ...(approval ? { approval } : {}),\n ...(input ? { input: input.value } : {}),\n }\n const options = command?.audit\n if (options?.target) {\n guarded(config, () => {\n const target = options.target!(validatedInput, value)\n if (isTarget(target)) event.target = target\n })\n }\n if (options?.summary !== undefined) {\n guarded(config, () => {\n const summary = typeof options.summary === \"function\" ? options.summary(validatedInput, value) : options.summary\n if (typeof summary === \"string\") event.summary = summary\n })\n }\n const ids = trace ?? traceparentIds(context.traceContext)\n if (ids?.traceId !== undefined) event.traceId = ids.traceId\n if (ids?.spanId !== undefined) event.spanId = ids.spanId\n if (taskId !== undefined) event.taskId = taskId\n await deliver(config, event)\n },\n }\n}\n\n/** 이벤트의 호출자 필드 (ctx.caller 와 같은 계산) */\nfunction callerFields(context: AuditContext): Pick<AuditEvent, \"surface\" | \"transport\" | \"actor\" | \"client\" | \"principal\"> {\n const caller = callerOf(context.principal, context.transport ?? \"local\", context.client)\n return {\n surface: caller.surface,\n transport: caller.transport,\n ...(caller.actor !== undefined ? { actor: caller.actor } : {}),\n ...(caller.client !== undefined ? { client: caller.client } : {}),\n principal: { id: context.principal.id, kind: context.principal.kind },\n }\n}\n\n/**\n * expose 로 숨긴 명령어의 호출 시도 1건 (config.audit.includeHidden, spec §11.1). entry = 숨긴 명령어 (호출자가 판정해서 넘긴다).\n * 입력 · 오류 · target · summary 없음 (검증 전 입력에는 가림 규칙을 적용할 수 없다). readOnly 명령어도 기록한다.\n * sink 호출 = 다음 macrotask (응답 처리가 먼저 진행된다). 반환 = 전달 완료 (거부하지 않는다):\n * 응답 시간으로 숨긴 명령어가 드러나지 않게 MCP 어댑터는 기다리지 않고, 로컬 CLI 는 종료 전에 await 한다.\n * includeHidden 이 아니면 undefined (이벤트를 만들지 않는다)\n */\nexport function auditHidden(entry: RegisteredCommand, context: AuditContext): Promise<void> | undefined {\n const config = context.audit\n if (config?.includeHidden !== true) return undefined\n const event: AuditEvent = {\n command: entry.id,\n readOnly: entry.command.readOnly,\n at: new Date().toISOString(),\n durationMs: 0,\n ...callerFields(context),\n outcome: \"hidden\",\n }\n const ids = traceparentIds(context.traceContext)\n if (ids?.traceId !== undefined) event.traceId = ids.traceId\n return new Promise<void>((resolve) => setTimeout(() => resolve(deliver(config, event)), 0))\n}\n\n/**\n * sink 전달. sink 예외 정책 (spec §11.1 sink 실패, 결정): onerror 보고 후 삼킨다 = 응답 불변 (onCommandError 와 같은 원칙).\n * 감사는 실행 후 기록이라 여기서 실패해도 실행은 이미 끝났다. 정책은 이 함수 한 곳에 있다\n */\nasync function deliver(config: AuditConfig, event: AuditEvent): Promise<void> {\n try {\n await config.sink(event)\n } catch (error) {\n report(config, error)\n }\n}\n\n/** 명령어 audit 함수 (target · summary · input) 실행. 예외 = onerror, 그 필드 없이 계속 */\nfunction guarded(config: AuditConfig, fn: () => void): void {\n try {\n fn()\n } catch (error) {\n report(config, error)\n }\n}\n\nfunction report(config: AuditConfig, error: unknown): void {\n try {\n ;(config.onerror ?? defaultOnError)(error)\n } catch {\n // 보고는 응답을 바꾸지 않는다\n }\n}\n\nfunction defaultOnError(error: unknown): void {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error)\n process.stderr.write(`mext: audit threw: ${message}\\n`)\n}\n\n/**\n * 예외 → outcome · error. 거부 = PERMISSION_DENIED · INPUT_DECLINED · APPROVAL_REQUIRED. 그 외 = error:\n * 취소 후 예외 = INTERRUPTED, MextError · CommandError = 그 code, 나머지 = EXECUTION_ERROR (응답과 같은 code · message)\n */\nfunction failure(error: unknown, aborted: boolean): Pick<AuditEvent, \"outcome\" | \"error\"> {\n if (isMextError(error, \"PERMISSION_DENIED\") || isMextError(error, \"INPUT_DECLINED\") || isMextError(error, \"APPROVAL_REQUIRED\")) {\n return { outcome: \"denied\", error: { code: error.code, message: error.message } }\n }\n if (aborted) return { outcome: \"error\", error: { code: AUDIT_INTERRUPTED, message: \"Cancelled before completion.\" } }\n if (isDeclaredError(error)) return { outcome: \"error\", error: { code: error.code, message: error.message } }\n return { outcome: \"error\", error: { code: \"EXECUTION_ERROR\", message: error instanceof Error ? error.message : String(error) } }\n}\n\nfunction isTarget(value: unknown): value is AuditTarget {\n return typeof value === \"object\" && value !== null && typeof (value as { type?: unknown }).type === \"string\"\n}\n\n/** traceparent (\"00-<traceId 32 hex>-<parent 16 hex>-<flags>\") 의 traceId. 호출 쪽 span 이라 spanId 는 쓰지 않는다 */\nfunction traceparentIds(carrier: Record<string, unknown> | undefined): TraceIds | undefined {\n const value = carrier?.traceparent\n const match = typeof value === \"string\" ? /^[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}/.exec(value) : null\n return match && !/^0+$/.test(match[1]!) ? { traceId: match[1]! } : undefined\n}\n\n/** defineCommand 의 audit 형식 문제. 없으면 undefined */\nexport function commandAuditProblem(value: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return \"audit must be an object ({ target, summary, input })\"\n const a = value as Record<string, unknown>\n const unknown = Object.keys(a).filter((k) => k !== \"target\" && k !== \"summary\" && k !== \"input\")\n if (unknown.length > 0) return `unknown audit key${unknown.length === 1 ? \"\" : \"s\"}: ${unknown.join(\", \")}`\n if (a.target !== undefined && typeof a.target !== \"function\") return \"audit.target must be a function\"\n if (a.summary !== undefined && typeof a.summary !== \"string\" && typeof a.summary !== \"function\") {\n return \"audit.summary must be a string or a function\"\n }\n if (a.input !== undefined && typeof a.input !== \"boolean\" && typeof a.input !== \"function\") {\n return \"audit.input must be false or a function\"\n }\n return undefined\n}\n\n/** mext.config.ts 의 audit 형식 문제. 없으면 undefined */\nexport function auditConfigProblem(value: unknown): string | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return \"audit must be an object ({ sink })\"\n const a = value as Record<string, unknown>\n const unknown = Object.keys(a).filter((k) => ![\"sink\", \"includeReadOnly\", \"includeHidden\", \"input\", \"onerror\"].includes(k))\n if (unknown.length > 0) return `unknown audit key${unknown.length === 1 ? \"\" : \"s\"}: ${unknown.join(\", \")}`\n if (typeof a.sink !== \"function\") return \"audit.sink must be a function\"\n if (a.includeReadOnly !== undefined && typeof a.includeReadOnly !== \"boolean\") return \"audit.includeReadOnly must be a boolean\"\n if (a.includeHidden !== undefined && typeof a.includeHidden !== \"boolean\") return \"audit.includeHidden must be a boolean\"\n if (a.input !== undefined && typeof a.input !== \"boolean\") return \"audit.input must be a boolean\"\n if (a.onerror !== undefined && typeof a.onerror !== \"function\") return \"audit.onerror must be a function\"\n return undefined\n}\n"]}
@@ -34,6 +34,7 @@ export interface BuildCustomerCliOptions {
34
34
  /** .mext/ 절대 경로 */
35
35
  outDir: string;
36
36
  cli: CliConfig;
37
+ /** 고객 CLI 에 내장할 manifest · manifestHash. expose 사용 시 = mcp 표면에서 보이는 명령어 기준 (§4) */
37
38
  manifest: BuildManifest;
38
39
  }
39
40
  export declare function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild>;
@@ -1 +1 @@
1
- {"version":3,"file":"build-cli.js","sourceRoot":"","sources":["../src/build-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type { RolldownModule, RolldownOutputItem } from \"./build.ts\"\nimport { clientManifest } from \"./client/manifest.ts\"\nimport type { CustomerCliConfig } from \"./client/main.ts\"\nimport { CLI_BUILTIN_COMMANDS, LOCAL_BIN_BUILTIN_COMMANDS, type CliConfig } from \"./config.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport type { BuildManifest } from \"./manifest.ts\"\nimport type { Registry } from \"./registry.ts\"\nimport { BUN_ENGINE } from \"./runtime.ts\"\nimport { envPrefix } from \"./remote/credentials.ts\"\n\n/**\n * 고객 CLI 패키지 (.mext/cli/, §5). mext build 가 config.cli 가 있을 때 호출.\n * bin.js = client/main.ts (고객 CLI 런타임) + 인라인 설정 (CustomerCliConfig) 의 rolldown 단일 번들. 외부 = Node 내장만\n * (의존성 전부 번들 → package.json dependencies 없음). 런타임은 Zod · MCP 서버 SDK · 핸들러 · 스캔 · 빌드 코드를 import 하지 않는다\n */\n\n/** .mext/ 아래 고객 CLI 폴더 */\nexport const CLI_DIR = \"cli\"\nexport const CLI_BIN_FILE = \"bin.js\"\n/** 고객 CLI package.json engines.node (번들 target node22, 타입 제거 불필요) */\nexport const CLI_NODE_ENGINE = \">=22\"\n\n/** 고객 CLI 런타임 엔트리 (src = .ts, dist = .js: 이 파일과 같은 확장자) */\nconst RUNTIME_ENTRY = fileURLToPath(new URL(`./client/main${import.meta.url.slice(import.meta.url.lastIndexOf(\".\"))}`, import.meta.url))\nconst ENTRY_ID = \"\\0mext-customer-cli\"\n\nexport interface CustomerCliBuild {\n /** 절대 경로 (.mext/cli) */\n dir: string\n bin: string\n packageName: string\n version: string\n form: \"bundled\" | \"universal\"\n /** .mext/cli 기준 생성 파일 (정렬) */\n files: string[]\n /** bin.js 바이트 */\n size: number\n}\n\n/** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp, 로컬 bin = help · mcp) 과 겹치면 RESERVED_ID */\nexport function customerCliIssues(cli: CliConfig | undefined, registry: Registry): BuildIssue[] {\n if (!cli) return []\n const builtins = cli.local === true ? LOCAL_BIN_BUILTIN_COMMANDS : CLI_BUILTIN_COMMANDS\n // 루트 별칭은 Registry 가 금지하므로 루트 명령어 · 네임스페이스만 본다\n const root = registry.namespaces.get(\"\")\n const ids = new Set([...(root?.commands ?? []), ...(root?.namespaces ?? [])])\n return [...ids]\n .filter((id) => builtins.includes(id))\n .sort()\n .map((id) => {\n const file = registry.commands.get(id)?.file\n return {\n code: \"RESERVED_ID\",\n message: `\"${id}\" is a built-in command of the customer CLI (${cli.bin} ${id}). Rename it or remove cli from mext.config.ts`,\n id,\n ...(file !== undefined ? { file } : {}),\n }\n })\n}\n\nexport interface BuildCustomerCliOptions {\n rolldown: RolldownModule\n /** 도입 프로젝트 루트 (package.json version) */\n cwd: string\n /** .mext/ 절대 경로 */\n outDir: string\n cli: CliConfig\n manifest: BuildManifest\n}\n\nexport async function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild> {\n const { cli, manifest } = options\n if (cli.url === undefined) throw new Error(\"mext build: customer CLI requires cli.url\")\n const url = cli.url\n const dir = join(options.outDir, CLI_DIR)\n mkdirSync(dir, { recursive: true })\n const version = cli.version ?? projectVersion(options.cwd) ?? \"0.0.0\"\n const packageName = cli.packageName ?? cli.bin\n const bundled = cli.manifest !== false\n const config: CustomerCliConfig = {\n bin: cli.bin,\n url,\n version,\n packageName,\n mextVersion: manifest.mextVersion,\n manifestHash: manifest.manifestHash,\n ...(bundled ? { manifest: clientManifest(manifest) } : {}),\n }\n const errorsFile = cli.errors === undefined ? undefined : resolve(options.cwd, cli.errors)\n if (errorsFile !== undefined && !existsSync(errorsFile)) {\n throw new MextBuildError([{ code: \"INVALID_CONFIG\", message: `cli.errors file not found: ${cli.errors}` }])\n }\n const entry = [\n `import { main } from ${JSON.stringify(RUNTIME_ENTRY)}`,\n ...(errorsFile !== undefined ? [`import formatError from ${JSON.stringify(errorsFile)}`] : []),\n \"\",\n errorsFile !== undefined\n ? `await main(${JSON.stringify(config)}, { formatError })`\n : `await main(${JSON.stringify(config)})`,\n \"\",\n ].join(\"\\n\")\n\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await options.rolldown.build({\n cwd: options.cwd,\n input: { bin: ENTRY_ID },\n platform: \"node\",\n external: (id: string): boolean => id.startsWith(\"node:\") || isBuiltin(id),\n plugins: [\n {\n name: \"mext-customer-cli\",\n resolveId: (id: string) => (id === ENTRY_ID ? ENTRY_ID : null),\n load: (id: string) => (id === ENTRY_ID ? entry : null),\n },\n ],\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n output: {\n dir,\n format: \"esm\",\n entryFileNames: CLI_BIN_FILE,\n banner: \"#!/usr/bin/env node\",\n codeSplitting: false,\n sourcemap: false,\n },\n }))\n } catch (error) {\n throw new MextBuildError([\n { code: \"BUNDLE_FAILED\", message: `customer CLI: ${error instanceof Error ? error.message : String(error)}` },\n ])\n }\n const emitted = output.map((o) => o.fileName)\n if (emitted.length !== 1 || emitted[0] !== CLI_BIN_FILE) {\n throw new Error(`mext build: customer CLI bundle must be a single ${CLI_BIN_FILE} (got ${emitted.join(\", \")})`)\n }\n const binPath = join(dir, CLI_BIN_FILE)\n chmodSync(binPath, 0o755)\n\n const pkg = {\n name: packageName,\n version,\n description: `Command-line client for ${url}`,\n type: \"module\",\n bin: { [cli.bin]: `./${CLI_BIN_FILE}` },\n files: [CLI_BIN_FILE],\n engines: { node: CLI_NODE_ENGINE, bun: BUN_ENGINE },\n }\n writeFileSync(join(dir, \"package.json\"), `${JSON.stringify(pkg, null, 2)}\\n`)\n writeFileSync(join(dir, \"README.md\"), readme(cli.bin, url, packageName))\n\n return {\n dir,\n bin: cli.bin,\n packageName,\n version,\n form: bundled ? \"bundled\" : \"universal\",\n files: [CLI_BIN_FILE, \"README.md\", \"package.json\"],\n size: statSync(binPath).size,\n }\n}\n\nexport function projectVersion(cwd: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as { version?: unknown }\n return typeof pkg.version === \"string\" && pkg.version !== \"\" ? pkg.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction readme(bin: string, url: string, packageName: string): string {\n const prefix = envPrefix(bin)\n return [\n `# ${packageName}`,\n \"\",\n `Command-line client for ${url}. Generated by mext build.`,\n \"\",\n \"```sh\",\n `npm install -g ${packageName}`,\n `${bin} login # log in (browser, device code, or --with-token)`,\n `${bin} help # commands`,\n `${bin} <namespace> <command> --help`,\n `${bin} mcp install # MCP client registration commands`,\n `${bin} logout`,\n \"```\",\n \"\",\n \"Environment:\",\n \"\",\n `- \\`${prefix}_TOKEN\\`: API token (used instead of the stored login)`,\n `- \\`${prefix}_URL\\`: server URL (default: ${url})`,\n `- \\`${prefix}_NO_KEYRING=1\\`: store the token in \\`~/.config/${bin}/credentials.json\\` instead of the OS keychain`,\n `- \\`${prefix}_CACHE_DIR\\`: server schema cache folder (default: \\`\\${XDG_CACHE_HOME:-~/.cache}/${bin}\\`)`,\n \"\",\n \"Exit codes: 0 success · 1 error · 2 invalid arguments or unknown command · 77 authentication or permission · 130 interrupted.\",\n \"\",\n ].join(\"\\n\")\n}\n"]}
1
+ {"version":3,"file":"build-cli.js","sourceRoot":"","sources":["../src/build-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\"\nimport { isBuiltin } from \"node:module\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport type { RolldownModule, RolldownOutputItem } from \"./build.ts\"\nimport { clientManifest } from \"./client/manifest.ts\"\nimport type { CustomerCliConfig } from \"./client/main.ts\"\nimport { CLI_BUILTIN_COMMANDS, LOCAL_BIN_BUILTIN_COMMANDS, type CliConfig } from \"./config.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport type { BuildManifest } from \"./manifest.ts\"\nimport type { Registry } from \"./registry.ts\"\nimport { BUN_ENGINE } from \"./runtime.ts\"\nimport { envPrefix } from \"./remote/credentials.ts\"\n\n/**\n * 고객 CLI 패키지 (.mext/cli/, §5). mext build 가 config.cli 가 있을 때 호출.\n * bin.js = client/main.ts (고객 CLI 런타임) + 인라인 설정 (CustomerCliConfig) 의 rolldown 단일 번들. 외부 = Node 내장만\n * (의존성 전부 번들 → package.json dependencies 없음). 런타임은 Zod · MCP 서버 SDK · 핸들러 · 스캔 · 빌드 코드를 import 하지 않는다\n */\n\n/** .mext/ 아래 고객 CLI 폴더 */\nexport const CLI_DIR = \"cli\"\nexport const CLI_BIN_FILE = \"bin.js\"\n/** 고객 CLI package.json engines.node (번들 target node22, 타입 제거 불필요) */\nexport const CLI_NODE_ENGINE = \">=22\"\n\n/** 고객 CLI 런타임 엔트리 (src = .ts, dist = .js: 이 파일과 같은 확장자) */\nconst RUNTIME_ENTRY = fileURLToPath(new URL(`./client/main${import.meta.url.slice(import.meta.url.lastIndexOf(\".\"))}`, import.meta.url))\nconst ENTRY_ID = \"\\0mext-customer-cli\"\n\nexport interface CustomerCliBuild {\n /** 절대 경로 (.mext/cli) */\n dir: string\n bin: string\n packageName: string\n version: string\n form: \"bundled\" | \"universal\"\n /** .mext/cli 기준 생성 파일 (정렬) */\n files: string[]\n /** bin.js 바이트 */\n size: number\n}\n\n/** cli 설정이 있을 때 루트 세그먼트가 고객 CLI 내장 명령 (login · logout · mcp, 로컬 bin = help · mcp) 과 겹치면 RESERVED_ID */\nexport function customerCliIssues(cli: CliConfig | undefined, registry: Registry): BuildIssue[] {\n if (!cli) return []\n const builtins = cli.local === true ? LOCAL_BIN_BUILTIN_COMMANDS : CLI_BUILTIN_COMMANDS\n // 루트 별칭은 Registry 가 금지하므로 루트 명령어 · 네임스페이스만 본다\n const root = registry.namespaces.get(\"\")\n const ids = new Set([...(root?.commands ?? []), ...(root?.namespaces ?? [])])\n return [...ids]\n .filter((id) => builtins.includes(id))\n .sort()\n .map((id) => {\n const file = registry.commands.get(id)?.file\n return {\n code: \"RESERVED_ID\",\n message: `\"${id}\" is a built-in command of the customer CLI (${cli.bin} ${id}). Rename it or remove cli from mext.config.ts`,\n id,\n ...(file !== undefined ? { file } : {}),\n }\n })\n}\n\nexport interface BuildCustomerCliOptions {\n rolldown: RolldownModule\n /** 도입 프로젝트 루트 (package.json version) */\n cwd: string\n /** .mext/ 절대 경로 */\n outDir: string\n cli: CliConfig\n /** 고객 CLI 에 내장할 manifest · manifestHash. expose 사용 시 = mcp 표면에서 보이는 명령어 기준 (§4) */\n manifest: BuildManifest\n}\n\nexport async function buildCustomerCli(options: BuildCustomerCliOptions): Promise<CustomerCliBuild> {\n const { cli, manifest } = options\n if (cli.url === undefined) throw new Error(\"mext build: customer CLI requires cli.url\")\n const url = cli.url\n const dir = join(options.outDir, CLI_DIR)\n mkdirSync(dir, { recursive: true })\n const version = cli.version ?? projectVersion(options.cwd) ?? \"0.0.0\"\n const packageName = cli.packageName ?? cli.bin\n const bundled = cli.manifest !== false\n const config: CustomerCliConfig = {\n bin: cli.bin,\n url,\n version,\n packageName,\n mextVersion: manifest.mextVersion,\n manifestHash: manifest.manifestHash,\n ...(bundled ? { manifest: clientManifest(manifest) } : {}),\n }\n const errorsFile = cli.errors === undefined ? undefined : resolve(options.cwd, cli.errors)\n if (errorsFile !== undefined && !existsSync(errorsFile)) {\n throw new MextBuildError([{ code: \"INVALID_CONFIG\", message: `cli.errors file not found: ${cli.errors}` }])\n }\n const entry = [\n `import { main } from ${JSON.stringify(RUNTIME_ENTRY)}`,\n ...(errorsFile !== undefined ? [`import formatError from ${JSON.stringify(errorsFile)}`] : []),\n \"\",\n errorsFile !== undefined\n ? `await main(${JSON.stringify(config)}, { formatError })`\n : `await main(${JSON.stringify(config)})`,\n \"\",\n ].join(\"\\n\")\n\n let output: readonly RolldownOutputItem[]\n try {\n ;({ output } = await options.rolldown.build({\n cwd: options.cwd,\n input: { bin: ENTRY_ID },\n platform: \"node\",\n external: (id: string): boolean => id.startsWith(\"node:\") || isBuiltin(id),\n plugins: [\n {\n name: \"mext-customer-cli\",\n resolveId: (id: string) => (id === ENTRY_ID ? ENTRY_ID : null),\n load: (id: string) => (id === ENTRY_ID ? entry : null),\n },\n ],\n transform: { target: \"node22\" },\n logLevel: \"warn\",\n output: {\n dir,\n format: \"esm\",\n entryFileNames: CLI_BIN_FILE,\n banner: \"#!/usr/bin/env node\",\n codeSplitting: false,\n sourcemap: false,\n },\n }))\n } catch (error) {\n throw new MextBuildError([\n { code: \"BUNDLE_FAILED\", message: `customer CLI: ${error instanceof Error ? error.message : String(error)}` },\n ])\n }\n const emitted = output.map((o) => o.fileName)\n if (emitted.length !== 1 || emitted[0] !== CLI_BIN_FILE) {\n throw new Error(`mext build: customer CLI bundle must be a single ${CLI_BIN_FILE} (got ${emitted.join(\", \")})`)\n }\n const binPath = join(dir, CLI_BIN_FILE)\n chmodSync(binPath, 0o755)\n\n const pkg = {\n name: packageName,\n version,\n description: `Command-line client for ${url}`,\n type: \"module\",\n bin: { [cli.bin]: `./${CLI_BIN_FILE}` },\n files: [CLI_BIN_FILE],\n engines: { node: CLI_NODE_ENGINE, bun: BUN_ENGINE },\n }\n writeFileSync(join(dir, \"package.json\"), `${JSON.stringify(pkg, null, 2)}\\n`)\n writeFileSync(join(dir, \"README.md\"), readme(cli.bin, url, packageName))\n\n return {\n dir,\n bin: cli.bin,\n packageName,\n version,\n form: bundled ? \"bundled\" : \"universal\",\n files: [CLI_BIN_FILE, \"README.md\", \"package.json\"],\n size: statSync(binPath).size,\n }\n}\n\nexport function projectVersion(cwd: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(cwd, \"package.json\"), \"utf8\")) as { version?: unknown }\n return typeof pkg.version === \"string\" && pkg.version !== \"\" ? pkg.version : undefined\n } catch {\n return undefined\n }\n}\n\nfunction readme(bin: string, url: string, packageName: string): string {\n const prefix = envPrefix(bin)\n return [\n `# ${packageName}`,\n \"\",\n `Command-line client for ${url}. Generated by mext build.`,\n \"\",\n \"```sh\",\n `npm install -g ${packageName}`,\n `${bin} login # log in (browser, device code, or --with-token)`,\n `${bin} help # commands`,\n `${bin} <namespace> <command> --help`,\n `${bin} mcp install # MCP client registration commands`,\n `${bin} logout`,\n \"```\",\n \"\",\n \"Environment:\",\n \"\",\n `- \\`${prefix}_TOKEN\\`: API token (used instead of the stored login)`,\n `- \\`${prefix}_URL\\`: server URL (default: ${url})`,\n `- \\`${prefix}_NO_KEYRING=1\\`: store the token in \\`~/.config/${bin}/credentials.json\\` instead of the OS keychain`,\n `- \\`${prefix}_CACHE_DIR\\`: server schema cache folder (default: \\`\\${XDG_CACHE_HOME:-~/.cache}/${bin}\\`)`,\n \"\",\n \"Exit codes: 0 success · 1 error · 2 invalid arguments or unknown command · 77 authentication or permission · 130 interrupted.\",\n \"\",\n ].join(\"\\n\")\n}\n"]}
package/dist/build.d.ts CHANGED
@@ -53,6 +53,8 @@ export interface BuildResult {
53
53
  bin: string;
54
54
  version: string;
55
55
  };
56
+ /** 명령어 타입 생성 파일 (config.types 가 있을 때, specs/client.md §4). 절대 경로 */
57
+ types?: string;
56
58
  }
57
59
  /** rolldown 을 찾을 수 없음. message 에 설치 명령 */
58
60
  export declare class RolldownUnavailableError extends Error {