isthmus-cli 0.5.0 → 0.7.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 (146) hide show
  1. package/README.ko.md +170 -45
  2. package/README.md +178 -36
  3. package/Skills/isthmus/SKILL.md +104 -5
  4. package/compatibility.json +10 -0
  5. package/dist/cli/check-command.d.ts +2 -2
  6. package/dist/cli/check-command.js +22 -9
  7. package/dist/cli/check-command.js.map +1 -1
  8. package/dist/cli/command-support.d.ts +20 -2
  9. package/dist/cli/command-support.js +38 -9
  10. package/dist/cli/command-support.js.map +1 -1
  11. package/dist/cli/diff-command.js +4 -3
  12. package/dist/cli/diff-command.js.map +1 -1
  13. package/dist/cli/doctor-command.d.ts +11 -0
  14. package/dist/cli/doctor-command.js +128 -0
  15. package/dist/cli/doctor-command.js.map +1 -0
  16. package/dist/cli/extract-js-command.d.ts +33 -0
  17. package/dist/cli/extract-js-command.js +233 -0
  18. package/dist/cli/extract-js-command.js.map +1 -0
  19. package/dist/cli/graph-command.js +13 -6
  20. package/dist/cli/graph-command.js.map +1 -1
  21. package/dist/cli/impact-command.d.ts +5 -0
  22. package/dist/cli/impact-command.js +111 -0
  23. package/dist/cli/impact-command.js.map +1 -0
  24. package/dist/cli/init-command.d.ts +11 -0
  25. package/dist/cli/init-command.js +115 -0
  26. package/dist/cli/init-command.js.map +1 -0
  27. package/dist/cli/main.js +67 -1
  28. package/dist/cli/main.js.map +1 -1
  29. package/dist/cli/mcp-server.d.ts +16 -0
  30. package/dist/cli/mcp-server.js +534 -0
  31. package/dist/cli/mcp-server.js.map +1 -0
  32. package/dist/cli/preflight-command.d.ts +5 -0
  33. package/dist/cli/preflight-command.js +83 -0
  34. package/dist/cli/preflight-command.js.map +1 -0
  35. package/dist/cli/query-command.js +13 -6
  36. package/dist/cli/query-command.js.map +1 -1
  37. package/dist/cli/retentions-command.js +14 -7
  38. package/dist/cli/retentions-command.js.map +1 -1
  39. package/dist/cli/runtime-command.d.ts +5 -0
  40. package/dist/cli/runtime-command.js +42 -0
  41. package/dist/cli/runtime-command.js.map +1 -0
  42. package/dist/cli/runtime-json-reader.d.ts +12 -0
  43. package/dist/cli/runtime-json-reader.js +37 -0
  44. package/dist/cli/runtime-json-reader.js.map +1 -0
  45. package/dist/cli/serve-command.d.ts +13 -0
  46. package/dist/cli/serve-command.js +43 -0
  47. package/dist/cli/serve-command.js.map +1 -0
  48. package/dist/exchange/capture-config.d.ts +18 -0
  49. package/dist/exchange/capture-config.js +100 -0
  50. package/dist/exchange/capture-config.js.map +1 -0
  51. package/dist/exchange/impact-selection.d.ts +13 -0
  52. package/dist/exchange/impact-selection.js +35 -0
  53. package/dist/exchange/impact-selection.js.map +1 -0
  54. package/dist/exchange/kartograph-impact.d.ts +4 -0
  55. package/dist/exchange/kartograph-impact.js +169 -0
  56. package/dist/exchange/kartograph-impact.js.map +1 -0
  57. package/dist/exchange/messages.d.ts +36 -0
  58. package/dist/exchange/messages.js +96 -0
  59. package/dist/exchange/messages.js.map +1 -0
  60. package/dist/exchange/parse.d.ts +69 -0
  61. package/dist/exchange/parse.js +127 -20
  62. package/dist/exchange/parse.js.map +1 -1
  63. package/dist/exchange/preflight-context.d.ts +71 -0
  64. package/dist/exchange/preflight-context.js +317 -0
  65. package/dist/exchange/preflight-context.js.map +1 -0
  66. package/dist/exchange/producer-impact.d.ts +17 -0
  67. package/dist/exchange/producer-impact.js +226 -0
  68. package/dist/exchange/producer-impact.js.map +1 -0
  69. package/dist/exchange/runtime.d.ts +69 -0
  70. package/dist/exchange/runtime.js +160 -0
  71. package/dist/exchange/runtime.js.map +1 -0
  72. package/dist/extract/js-document.d.ts +22 -0
  73. package/dist/extract/js-document.js +230 -0
  74. package/dist/extract/js-document.js.map +1 -0
  75. package/dist/extract/js-scan.d.ts +93 -0
  76. package/dist/extract/js-scan.js +1205 -0
  77. package/dist/extract/js-scan.js.map +1 -0
  78. package/dist/extract/lexer.d.ts +26 -0
  79. package/dist/extract/lexer.js +316 -0
  80. package/dist/extract/lexer.js.map +1 -0
  81. package/dist/join/join.d.ts +66 -1
  82. package/dist/join/join.js +171 -1
  83. package/dist/join/join.js.map +1 -1
  84. package/dist/join/message-address.d.ts +7 -0
  85. package/dist/join/message-address.js +37 -0
  86. package/dist/join/message-address.js.map +1 -0
  87. package/dist/join/messages.d.ts +26 -0
  88. package/dist/join/messages.js +87 -0
  89. package/dist/join/messages.js.map +1 -0
  90. package/dist/report/check-report.d.ts +20 -3
  91. package/dist/report/check-report.js +240 -8
  92. package/dist/report/check-report.js.map +1 -1
  93. package/dist/report/codequality.d.ts +45 -0
  94. package/dist/report/codequality.js +74 -0
  95. package/dist/report/codequality.js.map +1 -0
  96. package/dist/report/diff.d.ts +25 -3
  97. package/dist/report/diff.js +91 -10
  98. package/dist/report/diff.js.map +1 -1
  99. package/dist/report/graph.d.ts +10 -4
  100. package/dist/report/graph.js +60 -6
  101. package/dist/report/graph.js.map +1 -1
  102. package/dist/report/impact.d.ts +62 -0
  103. package/dist/report/impact.js +168 -0
  104. package/dist/report/impact.js.map +1 -0
  105. package/dist/report/preflight-runtime.d.ts +42 -0
  106. package/dist/report/preflight-runtime.js +193 -0
  107. package/dist/report/preflight-runtime.js.map +1 -0
  108. package/dist/report/preflight-view.d.ts +146 -0
  109. package/dist/report/preflight-view.js +216 -0
  110. package/dist/report/preflight-view.js.map +1 -0
  111. package/dist/report/preflight.d.ts +104 -0
  112. package/dist/report/preflight.js +331 -0
  113. package/dist/report/preflight.js.map +1 -0
  114. package/dist/report/query.d.ts +11 -4
  115. package/dist/report/query.js +84 -17
  116. package/dist/report/query.js.map +1 -1
  117. package/dist/report/retentions.d.ts +17 -5
  118. package/dist/report/retentions.js +85 -8
  119. package/dist/report/retentions.js.map +1 -1
  120. package/dist/report/rules.d.ts +16 -0
  121. package/dist/report/rules.js +30 -0
  122. package/dist/report/rules.js.map +1 -0
  123. package/dist/report/runtime-impact.d.ts +38 -0
  124. package/dist/report/runtime-impact.js +94 -0
  125. package/dist/report/runtime-impact.js.map +1 -0
  126. package/dist/report/runtime.d.ts +54 -0
  127. package/dist/report/runtime.js +137 -0
  128. package/dist/report/runtime.js.map +1 -0
  129. package/dist/report/sarif.d.ts +2 -8
  130. package/dist/report/sarif.js +2 -10
  131. package/dist/report/sarif.js.map +1 -1
  132. package/dist/report/sorted-json.d.ts +1 -1
  133. package/dist/report/sorted-json.js +2 -2
  134. package/dist/report/sorted-json.js.map +1 -1
  135. package/docs/BRIDGE-EVENTS.md +87 -0
  136. package/docs/BRIDGE-MESSAGES.md +117 -0
  137. package/docs/GRAPH-EXCHANGE.md +382 -0
  138. package/docs/IMPACT.md +96 -0
  139. package/docs/MCP.md +62 -0
  140. package/docs/PREFLIGHT.md +311 -0
  141. package/docs/RUNTIME.md +172 -0
  142. package/docs/TOOLCHAIN.md +101 -0
  143. package/package.json +14 -2
  144. package/scripts/build-preflight-toolchain.mjs +186 -0
  145. package/scripts/capture-preflight.mjs +380 -0
  146. package/scripts/run-child.mjs +17 -0
@@ -0,0 +1,83 @@
1
+ import { isSafeNonEmptyString } from "../exchange/parse.js";
2
+ import { parsePreflightContext, PreflightValidationError } from "../exchange/preflight-context.js";
3
+ import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from "../exchange/runtime.js";
4
+ import { MAX_DOCUMENTS_PER_JOIN } from "../join/join.js";
5
+ import { createPreflightReport, hasPreflightBlockers, PreflightGraphError } from "../report/preflight.js";
6
+ import { attachPreflightRuntime } from "../report/preflight-runtime.js";
7
+ import { createPreflightExplanation, createPreflightSummary } from "../report/preflight-view.js";
8
+ import { encodeSortedJson } from "../report/sorted-json.js";
9
+ import { inputFailure, inputFailureResult, internalError, isJsonParseFailure, MAX_TOTAL_INPUT_TEXT_LENGTH } from "./command-support.js";
10
+ import { parseCommandArguments } from "./parse-arguments.js";
11
+ import { RuntimeInputError, RuntimeJsonReader } from "./runtime-json-reader.js";
12
+ /** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */
13
+ export async function runPreflightCommand(arguments_, readTextFile) {
14
+ const parsed = parseCommandArguments(arguments_.slice(1), ['--revision', '--expectations', '--limit', '--explain'], ['--strict', '--compact', '--summary']);
15
+ const revision = parsed?.valueFlags.get('--revision');
16
+ const expectedPath = parsed?.valueFlags.get('--expectations');
17
+ const summaryRequested = parsed?.booleanFlags.has('--summary') ?? false;
18
+ const explainSelector = parsed?.valueFlags.get('--explain');
19
+ const limitText = parsed?.valueFlags.get('--limit');
20
+ const limit = limitText === undefined ? undefined : Number(limitText);
21
+ if (parsed === undefined || parsed.positionals.length < 1 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN + 1 ||
22
+ (expectedPath === undefined && parsed.positionals.length !== 1) ||
23
+ (revision !== undefined && !isSafeNonEmptyString(revision)) ||
24
+ (summaryRequested && explainSelector !== undefined) || (!summaryRequested && limitText !== undefined) ||
25
+ (limitText !== undefined && (!Number.isSafeInteger(limit) || limit < 1 || limit > 100))) {
26
+ return { standardOutput: '', standardError: `${preflightUsage}\n`, exitCode: 64 };
27
+ }
28
+ let text;
29
+ try {
30
+ text = await readTextFile(parsed.positionals[0]);
31
+ }
32
+ catch {
33
+ return inputFailure('Unable to read preflight context; check that the file exists and is readable.\n');
34
+ }
35
+ if (text.length > MAX_TOTAL_INPUT_TEXT_LENGTH)
36
+ return inputFailure('Preflight context exceeds the input size limit.\n');
37
+ let value;
38
+ try {
39
+ value = JSON.parse(text);
40
+ }
41
+ catch (error) {
42
+ return isJsonParseFailure(error) ? inputFailure('Preflight context is not valid JSON.\n') : internalError();
43
+ }
44
+ try {
45
+ const context = parsePreflightContext(value);
46
+ let report = createPreflightReport(revision === undefined || revision === context.revision ? context : {
47
+ ...context, limitations: [...context.limitations, 'stale-preflight-context: capture revision differs from the requested revision'],
48
+ });
49
+ if (expectedPath !== undefined) {
50
+ const reader = new RuntimeJsonReader(readTextFile, text.length);
51
+ const expectations = parseRuntimeExpectations(await reader.read(expectedPath));
52
+ const documents = [];
53
+ for (const path of parsed.positionals.slice(1))
54
+ documents.push(parseBridgeRuntime(await reader.read(path)));
55
+ report = attachPreflightRuntime(context, report, expectations, documents);
56
+ }
57
+ const blocked = parsed.booleanFlags.has('--strict') && hasPreflightBlockers(report);
58
+ const view = summaryRequested
59
+ ? createPreflightSummary(report, limit ?? 20)
60
+ : explainSelector === undefined ? report : createPreflightExplanation(report, explainSelector);
61
+ const viewStatus = explainSelector === undefined ? 'found' : view.status;
62
+ const statusHint = explainSelector === undefined || viewStatus === 'found' ? ''
63
+ : viewStatus === 'ambiguous'
64
+ ? 'The requested preflight selector matches multiple subjects; repeat with an exact subject key or producer id.\n'
65
+ : 'No preflight subject matches the requested selector; query an exact subject key, producer id, or qualifiedName.\n';
66
+ const exitCode = viewStatus === 'found' ? blocked ? 1 : 0 : 64;
67
+ return { standardOutput: encodeSortedJson(view, parsed.booleanFlags.has('--compact')),
68
+ standardError: viewStatus === 'found' && blocked
69
+ ? 'Cross-language preflight requires review: errors, evidence gaps, or unobserved selections remain.\n'
70
+ : statusHint, exitCode };
71
+ }
72
+ catch (error) {
73
+ if (error instanceof PreflightValidationError || error instanceof PreflightGraphError ||
74
+ error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {
75
+ return inputFailure(`Preflight context violates its contract: ${error.message}\n`);
76
+ }
77
+ return inputFailureResult(error) ?? internalError();
78
+ }
79
+ }
80
+ /** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */
81
+ export const preflightUsage = 'Usage: isthmus preflight <context.json> [<runtime.json> ...] '
82
+ + '[--expectations <checks.json>] [--revision <revision>] [--summary [--limit <1..100>] | --explain <selector>] [--strict] [--compact]';
83
+ //# sourceMappingURL=preflight-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-command.js","sourceRoot":"","sources":["../../src/cli/preflight-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { isSafeNonEmptyString } from '../exchange/parse.ts';\nimport { parsePreflightContext, PreflightValidationError } from '../exchange/preflight-context.ts';\nimport { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from '../exchange/runtime.ts';\nimport { MAX_DOCUMENTS_PER_JOIN } from '../join/join.ts';\nimport { createPreflightReport, hasPreflightBlockers, PreflightGraphError } from '../report/preflight.ts';\nimport { attachPreflightRuntime } from '../report/preflight-runtime.ts';\nimport { createPreflightExplanation, createPreflightSummary } from '../report/preflight-view.ts';\nimport { encodeSortedJson } from '../report/sorted-json.ts';\nimport { inputFailure, inputFailureResult, internalError, isJsonParseFailure, MAX_TOTAL_INPUT_TEXT_LENGTH } from './command-support.ts';\nimport type { CommandResult, ReadTextFile } from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\nimport { RuntimeInputError, RuntimeJsonReader } from './runtime-json-reader.ts';\n\n/** 같은 실행 문맥의 언어별 영향 결과를 연결해 사람이거나 AI인 소비자에게 전달한다. */\nexport async function runPreflightCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), ['--revision', '--expectations', '--limit', '--explain'], ['--strict', '--compact', '--summary']);\n const revision = parsed?.valueFlags.get('--revision');\n const expectedPath = parsed?.valueFlags.get('--expectations');\n const summaryRequested = parsed?.booleanFlags.has('--summary') ?? false;\n const explainSelector = parsed?.valueFlags.get('--explain');\n const limitText = parsed?.valueFlags.get('--limit');\n const limit = limitText === undefined ? undefined : Number(limitText);\n if (parsed === undefined || parsed.positionals.length < 1 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN + 1 ||\n (expectedPath === undefined && parsed.positionals.length !== 1) ||\n (revision !== undefined && !isSafeNonEmptyString(revision)) ||\n (summaryRequested && explainSelector !== undefined) || (!summaryRequested && limitText !== undefined) ||\n (limitText !== undefined && (!Number.isSafeInteger(limit) || limit! < 1 || limit! > 100))) {\n return { standardOutput: '', standardError: `${preflightUsage}\\n`, exitCode: 64 };\n }\n let text: string;\n try { text = await readTextFile(parsed.positionals[0]!); }\n catch { return inputFailure('Unable to read preflight context; check that the file exists and is readable.\\n'); }\n if (text.length > MAX_TOTAL_INPUT_TEXT_LENGTH) return inputFailure('Preflight context exceeds the input size limit.\\n');\n let value: unknown;\n try { value = JSON.parse(text); }\n catch (error) {\n return isJsonParseFailure(error) ? inputFailure('Preflight context is not valid JSON.\\n') : internalError();\n }\n try {\n const context = parsePreflightContext(value);\n let report = createPreflightReport(revision === undefined || revision === context.revision ? context : {\n ...context, limitations: [...context.limitations, 'stale-preflight-context: capture revision differs from the requested revision'],\n });\n if (expectedPath !== undefined) {\n const reader = new RuntimeJsonReader(readTextFile, text.length);\n const expectations = parseRuntimeExpectations(await reader.read(expectedPath));\n const documents = [];\n for (const path of parsed.positionals.slice(1)) documents.push(parseBridgeRuntime(await reader.read(path)));\n report = attachPreflightRuntime(context, report, expectations, documents);\n }\n const blocked = parsed.booleanFlags.has('--strict') && hasPreflightBlockers(report);\n const view = summaryRequested\n ? createPreflightSummary(report, limit ?? 20)\n : explainSelector === undefined ? report : createPreflightExplanation(report, explainSelector);\n const viewStatus = explainSelector === undefined ? 'found' : view.status;\n const statusHint = explainSelector === undefined || viewStatus === 'found' ? ''\n : viewStatus === 'ambiguous'\n ? 'The requested preflight selector matches multiple subjects; repeat with an exact subject key or producer id.\\n'\n : 'No preflight subject matches the requested selector; query an exact subject key, producer id, or qualifiedName.\\n';\n const exitCode = viewStatus === 'found' ? blocked ? 1 : 0 : 64;\n return { standardOutput: encodeSortedJson(view, parsed.booleanFlags.has('--compact')),\n standardError: viewStatus === 'found' && blocked\n ? 'Cross-language preflight requires review: errors, evidence gaps, or unobserved selections remain.\\n'\n : statusHint, exitCode };\n } catch (error) {\n if (error instanceof PreflightValidationError || error instanceof PreflightGraphError ||\n error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {\n return inputFailure(`Preflight context violates its contract: ${error.message}\\n`);\n }\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/** 입력 생성은 producer workflow가 맡으며 CLI는 JSON만 읽는다. */\nexport const preflightUsage = 'Usage: isthmus preflight <context.json> [<runtime.json> ...] '\n + '[--expectations <checks.json>] [--revision <revision>] [--summary [--limit <1..100>] | --explain <selector>] [--strict] [--compact]';\n"]}
@@ -1,6 +1,7 @@
1
- import { isBridgeJoinDeferred, joinBridgeDocuments, MAX_DOCUMENTS_PER_JOIN, } from "../join/join.js";
1
+ import { emptyBridgeJoinResult, isBridgeJoinDeferred, joinBridgeDocuments, MAX_DOCUMENTS_PER_JOIN, } from "../join/join.js";
2
+ import { joinMessageBridges } from "../join/messages.js";
2
3
  import { createBridgeQuery, encodeBridgeQuery } from "../report/query.js";
3
- import { bridgeJoinDeferredError, inputFailureResult, internalError, readBridgeDocuments, } from "./command-support.js";
4
+ import { bridgeJoinDeferredError, inputFailureResult, internalError, readBridgeInputs, } from "./command-support.js";
4
5
  import { parseCommandArguments } from "./parse-arguments.js";
5
6
  /** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */
6
7
  export async function runQueryCommand(arguments_, readTextFile) {
@@ -16,12 +17,18 @@ export async function runQueryCommand(arguments_, readTextFile) {
16
17
  return queryUsageError();
17
18
  }
18
19
  try {
19
- const documents = await readBridgeDocuments(inputPaths, readTextFile);
20
- const joined = joinBridgeDocuments(documents);
20
+ const { bridges, messages } = await readBridgeInputs(inputPaths, readTextFile);
21
+ const project = bridges[0]?.project ?? messages[0].project;
22
+ const joined = bridges.length > 0
23
+ ? joinBridgeDocuments(bridges)
24
+ : emptyBridgeJoinResult();
21
25
  if (isBridgeJoinDeferred(joined)) {
22
- return bridgeJoinDeferredError(joined.observedFacts, documents.length);
26
+ return bridgeJoinDeferredError(joined.observedFacts, bridges.length);
23
27
  }
24
- const query = createBridgeQuery(joined, requested);
28
+ const messageJoin = messages.length > 0
29
+ ? joinMessageBridges(messages, project)
30
+ : undefined;
31
+ const query = createBridgeQuery(joined, requested, messageJoin);
25
32
  return {
26
33
  standardOutput: encodeBridgeQuery(query),
27
34
  standardError: queryStatusHint(query),
@@ -1 +1 @@
1
- {"version":3,"file":"query-command.js","sourceRoot":"","sources":["../../src/cli/query-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAA6B,EAC7B,YAA0B;IAE1B,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,eAAe,EAAE,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/C,IACE,SAAS,KAAK,SAAS;QACvB,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAC7B,UAAU,CAAC,MAAM,GAAG,CAAC;QACrB,UAAU,CAAC,MAAM,GAAG,sBAAsB,EAC1C,CAAC;QACD,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IACD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,OAAO,uBAAuB,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACnD,OAAO;YACL,cAAc,EAAE,iBAAiB,CAAC,KAAK,CAAC;YACxC,aAAa,EAAE,eAAe,CAAC,KAAK,CAAC;YACrC,QAAQ,EAAE,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SAC5C,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,kBAAkB,CAAC,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;IACtD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,KAA0B;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QACjC,OAAO,8BAA8B,KAAK,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,gBAAgB;cAC9E,0DAA0D,CAAC;IACjE,CAAC;IACD,OAAO,0DAA0D;UAC7D,4DAA4D,CAAC;AACnE,CAAC;AAED,qCAAqC;AACrC,SAAS,eAAe;IACtB,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,UAAU,IAAI;QAChC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,UAAU,GACrB,+DAA+D;MAC7D,+BAA+B,CAAC","sourcesContent":["import {\n isBridgeJoinDeferred,\n joinBridgeDocuments,\n MAX_DOCUMENTS_PER_JOIN,\n} from '../join/join.ts';\nimport { createBridgeQuery, encodeBridgeQuery } from '../report/query.ts';\nimport type { BridgeQueryDocument } from '../report/query.ts';\nimport {\n bridgeJoinDeferredError,\n inputFailureResult,\n internalError,\n readBridgeDocuments,\n type CommandResult,\n type ReadTextFile,\n} from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\n\n/** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */\nexport async function runQueryCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), [], []);\n if (parsed === undefined) return queryUsageError();\n const requested = parsed.positionals[0];\n const inputPaths = parsed.positionals.slice(1);\n if (\n requested === undefined ||\n requested.trim().length === 0 ||\n inputPaths.length < 2 ||\n inputPaths.length > MAX_DOCUMENTS_PER_JOIN\n ) {\n return queryUsageError();\n }\n try {\n const documents = await readBridgeDocuments(inputPaths, readTextFile);\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) {\n return bridgeJoinDeferredError(joined.observedFacts, documents.length);\n }\n const query = createBridgeQuery(joined, requested);\n return {\n standardOutput: encodeBridgeQuery(query),\n standardError: queryStatusHint(query),\n exitCode: query.status === 'found' ? 0 : 64,\n };\n } catch (error) {\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/**\n * 찾지 못한 질의에 원인을 한 줄로 알린다.\n *\n * 문서 자체는 stdout JSON이 이미 다 담고 있으므로, 사람이 종료 코드만 봐도\n * \"인수가 잘못됐는지\"와 \"이름이 없는지\"를 구분할 수 있게 하는 보조 문구다.\n * 정적 문자열과 숫자만 보간한다.\n */\nfunction queryStatusHint(query: BridgeQueryDocument): string {\n if (query.status === 'found') return '';\n if (query.status === 'ambiguous') {\n return `The requested name matches ${query.candidates?.length ?? 0} bridge keys; `\n + 'repeat the query with a qualifiedName from candidates.\\n';\n }\n return 'No bridge channel or method matches the requested name; '\n + 'query a channel or method name that the inputs observed.\\n';\n}\n\n/** 잘못된 query 호출을 사용법과 코드 64로 바꾼다. */\nfunction queryUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${queryUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** query 명령의 한 줄 사용법이다. */\nexport const queryUsage =\n 'Usage: isthmus query <channel-or-method> <bridge-facts.json> '\n + '<bridge-facts.json> [more...]';\n"]}
1
+ {"version":3,"file":"query-command.js","sourceRoot":"","sources":["../../src/cli/query-command.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n emptyBridgeJoinResult,\n isBridgeJoinDeferred,\n joinBridgeDocuments,\n MAX_DOCUMENTS_PER_JOIN,\n} from '../join/join.ts';\nimport { joinMessageBridges } from '../join/messages.ts';\nimport { createBridgeQuery, encodeBridgeQuery } from '../report/query.ts';\nimport type { BridgeQueryDocument } from '../report/query.ts';\nimport {\n bridgeJoinDeferredError,\n inputFailureResult,\n internalError,\n readBridgeInputs,\n type CommandResult,\n type ReadTextFile,\n} from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\n\n/** query 인자를 실행해 bridge 질의 JSON과 종료 코드를 반환한다. */\nexport async function runQueryCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), [], []);\n if (parsed === undefined) return queryUsageError();\n const requested = parsed.positionals[0];\n const inputPaths = parsed.positionals.slice(1);\n if (\n requested === undefined ||\n requested.trim().length === 0 ||\n inputPaths.length < 2 ||\n inputPaths.length > MAX_DOCUMENTS_PER_JOIN\n ) {\n return queryUsageError();\n }\n try {\n const { bridges, messages } = await readBridgeInputs(inputPaths, readTextFile);\n const project = bridges[0]?.project ?? messages[0]!.project;\n const joined = bridges.length > 0\n ? joinBridgeDocuments(bridges)\n : emptyBridgeJoinResult();\n if (isBridgeJoinDeferred(joined)) {\n return bridgeJoinDeferredError(joined.observedFacts, bridges.length);\n }\n const messageJoin = messages.length > 0\n ? joinMessageBridges(messages, project)\n : undefined;\n const query = createBridgeQuery(joined, requested, messageJoin);\n return {\n standardOutput: encodeBridgeQuery(query),\n standardError: queryStatusHint(query),\n exitCode: query.status === 'found' ? 0 : 64,\n };\n } catch (error) {\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/**\n * 찾지 못한 질의에 원인을 한 줄로 알린다.\n *\n * 문서 자체는 stdout JSON이 이미 다 담고 있으므로, 사람이 종료 코드만 봐도\n * \"인수가 잘못됐는지\"와 \"이름이 없는지\"를 구분할 수 있게 하는 보조 문구다.\n * 정적 문자열과 숫자만 보간한다.\n */\nfunction queryStatusHint(query: BridgeQueryDocument): string {\n if (query.status === 'found') return '';\n if (query.status === 'ambiguous') {\n return `The requested name matches ${query.candidates?.length ?? 0} bridge keys; `\n + 'repeat the query with a qualifiedName from candidates.\\n';\n }\n return 'No bridge channel or method matches the requested name; '\n + 'query a channel or method name that the inputs observed.\\n';\n}\n\n/** 잘못된 query 호출을 사용법과 코드 64로 바꾼다. */\nfunction queryUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${queryUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** query 명령의 한 줄 사용법이다. */\nexport const queryUsage =\n 'Usage: isthmus query <channel-or-method> <bridge-facts.json> '\n + '<bridge-facts.json> [more...]';\n"]}
@@ -1,6 +1,7 @@
1
- import { isBridgeJoinDeferred, joinBridgeDocuments, MAX_DOCUMENTS_PER_JOIN, } from "../join/join.js";
1
+ import { emptyBridgeJoinResult, isBridgeJoinDeferred, joinBridgeDocuments, MAX_DOCUMENTS_PER_JOIN, } from "../join/join.js";
2
+ import { joinMessageBridges } from "../join/messages.js";
2
3
  import { createCartographRetentionsDocument, encodeCartographRetentionsDocument, RetentionValidationError, validateCartographRetentionInputs, } from "../report/retentions.js";
3
- import { bridgeJoinDeferredError, inputFailureResult, internalError, readBridgeDocuments, } from "./command-support.js";
4
+ import { bridgeJoinDeferredError, inputFailureResult, internalError, readBridgeInputs, } from "./command-support.js";
4
5
  import { parseCommandArguments } from "./parse-arguments.js";
5
6
  /** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */
6
7
  export async function runRetentionsCommand(arguments_, readTextFile, now, producerVersion) {
@@ -8,13 +9,19 @@ export async function runRetentionsCommand(arguments_, readTextFile, now, produc
8
9
  if (inputPaths === undefined)
9
10
  return retentionUsageError();
10
11
  try {
11
- const documents = await readBridgeDocuments(inputPaths, readTextFile);
12
- const joined = joinBridgeDocuments(documents);
12
+ const { bridges, messages } = await readBridgeInputs(inputPaths, readTextFile);
13
+ const project = bridges[0]?.project ?? messages[0].project;
14
+ const joined = bridges.length > 0
15
+ ? joinBridgeDocuments(bridges)
16
+ : emptyBridgeJoinResult();
13
17
  if (isBridgeJoinDeferred(joined)) {
14
- return bridgeJoinDeferredError(joined.observedFacts, documents.length);
18
+ return bridgeJoinDeferredError(joined.observedFacts, bridges.length);
15
19
  }
16
- validateCartographRetentionInputs(documents);
17
- const retentions = createCartographRetentionsDocument(joined, now().toISOString(), producerVersion);
20
+ validateCartographRetentionInputs(bridges, messages);
21
+ const messageJoin = messages.length > 0
22
+ ? joinMessageBridges(messages, project)
23
+ : undefined;
24
+ const retentions = createCartographRetentionsDocument(joined, now().toISOString(), producerVersion, messageJoin);
18
25
  return {
19
26
  standardOutput: encodeCartographRetentionsDocument(retentions),
20
27
  standardError: '',
@@ -1 +1 @@
1
- {"version":3,"file":"retentions-command.js","sourceRoot":"","sources":["../../src/cli/retentions-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kCAAkC,EAClC,kCAAkC,EAClC,wBAAwB,EACxB,iCAAiC,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,GAIpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAI7D,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA6B,EAC7B,YAA0B,EAC1B,GAAU,EACV,eAAuB;IAEvB,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,mBAAmB,EAAE,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,OAAO,uBAAuB,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QACzE,CAAC;QACD,iCAAiC,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,kCAAkC,CACnD,MAAM,EACN,GAAG,EAAE,CAAC,WAAW,EAAE,EACnB,eAAe,CAChB,CAAC;QACF,OAAO;YACL,cAAc,EAAE,kCAAkC,CAAC,UAAU,CAAC;YAC9D,aAAa,EAAE,EAAE;YACjB,QAAQ,EAAE,CAAC;SACZ,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;YAC9C,OAAO;gBACL,cAAc,EAAE,EAAE;gBAClB,aAAa,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI;gBACnC,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;QACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;IACtD,CAAC;AACH,CAAC;AAED,uCAAuC;AACvC,SAAS,mBAAmB,CAAC,UAA6B;IACxD,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IACrD,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IACzE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IACtE,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,sBAAsB;QAC9D,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC;AAED,0CAA0C;AAC1C,SAAS,mBAAmB;IAC1B,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,GAAG,cAAc,IAAI;QACpC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,gCAAgC;AAChC,MAAM,CAAC,MAAM,cAAc,GACzB,oEAAoE;MAClE,4BAA4B,CAAC","sourcesContent":["import {\n isBridgeJoinDeferred,\n joinBridgeDocuments,\n MAX_DOCUMENTS_PER_JOIN,\n} from '../join/join.ts';\nimport {\n createCartographRetentionsDocument,\n encodeCartographRetentionsDocument,\n RetentionValidationError,\n validateCartographRetentionInputs,\n} from '../report/retentions.ts';\nimport {\n bridgeJoinDeferredError,\n inputFailureResult,\n internalError,\n readBridgeDocuments,\n type Clock,\n type CommandResult,\n type ReadTextFile,\n} from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\n\nexport type { Clock };\n\n/** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */\nexport async function runRetentionsCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n now: Clock,\n producerVersion: string,\n): Promise<CommandResult> {\n const inputPaths = retentionInputPaths(arguments_);\n if (inputPaths === undefined) return retentionUsageError();\n try {\n const documents = await readBridgeDocuments(inputPaths, readTextFile);\n const joined = joinBridgeDocuments(documents);\n if (isBridgeJoinDeferred(joined)) {\n return bridgeJoinDeferredError(joined.observedFacts, documents.length);\n }\n validateCartographRetentionInputs(documents);\n const retentions = createCartographRetentionsDocument(\n joined,\n now().toISOString(),\n producerVersion,\n );\n return {\n standardOutput: encodeCartographRetentionsDocument(retentions),\n standardError: '',\n exitCode: 0,\n };\n } catch (error) {\n if (error instanceof RetentionValidationError) {\n return {\n standardOutput: '',\n standardError: `${error.message}\\n`,\n exitCode: 2,\n };\n }\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/** 지원 대상과 최소 입력 수를 검증해 파일 경로만 돌려준다. */\nfunction retentionInputPaths(arguments_: readonly string[]): string[] | undefined {\n if (arguments_[0] !== 'retentions') return undefined;\n const parsed = parseCommandArguments(arguments_.slice(1), ['--for'], []);\n if (parsed === undefined) return undefined;\n if (parsed.valueFlags.get('--for') !== 'cartograph') return undefined;\n const paths = [...parsed.positionals];\n return paths.length < 2 || paths.length > MAX_DOCUMENTS_PER_JOIN\n ? undefined\n : paths;\n}\n\n/** 잘못된 retentions 호출을 사용법과 코드 64로 바꾼다. */\nfunction retentionUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${retentionUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** retentions 명령의 한 줄 사용법이다. */\nexport const retentionUsage =\n 'Usage: isthmus retentions <bridge-facts.json> <bridge-facts.json> '\n + '[more...] --for cartograph';\n"]}
1
+ {"version":3,"file":"retentions-command.js","sourceRoot":"","sources":["../../src/cli/retentions-command.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n emptyBridgeJoinResult,\n isBridgeJoinDeferred,\n joinBridgeDocuments,\n MAX_DOCUMENTS_PER_JOIN,\n} from '../join/join.ts';\nimport { joinMessageBridges } from '../join/messages.ts';\nimport {\n createCartographRetentionsDocument,\n encodeCartographRetentionsDocument,\n RetentionValidationError,\n validateCartographRetentionInputs,\n} from '../report/retentions.ts';\nimport {\n bridgeJoinDeferredError,\n inputFailureResult,\n internalError,\n readBridgeInputs,\n type Clock,\n type CommandResult,\n type ReadTextFile,\n} from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\n\nexport type { Clock };\n\n/** retentions 인자를 실행해 cartograph용 보존 문서를 반환한다. */\nexport async function runRetentionsCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n now: Clock,\n producerVersion: string,\n): Promise<CommandResult> {\n const inputPaths = retentionInputPaths(arguments_);\n if (inputPaths === undefined) return retentionUsageError();\n try {\n const { bridges, messages } = await readBridgeInputs(inputPaths, readTextFile);\n const project = bridges[0]?.project ?? messages[0]!.project;\n const joined = bridges.length > 0\n ? joinBridgeDocuments(bridges)\n : emptyBridgeJoinResult();\n if (isBridgeJoinDeferred(joined)) {\n return bridgeJoinDeferredError(joined.observedFacts, bridges.length);\n }\n validateCartographRetentionInputs(bridges, messages);\n const messageJoin = messages.length > 0\n ? joinMessageBridges(messages, project)\n : undefined;\n const retentions = createCartographRetentionsDocument(\n joined,\n now().toISOString(),\n producerVersion,\n messageJoin,\n );\n return {\n standardOutput: encodeCartographRetentionsDocument(retentions),\n standardError: '',\n exitCode: 0,\n };\n } catch (error) {\n if (error instanceof RetentionValidationError) {\n return {\n standardOutput: '',\n standardError: `${error.message}\\n`,\n exitCode: 2,\n };\n }\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/** 지원 대상과 최소 입력 수를 검증해 파일 경로만 돌려준다. */\nfunction retentionInputPaths(arguments_: readonly string[]): string[] | undefined {\n if (arguments_[0] !== 'retentions') return undefined;\n const parsed = parseCommandArguments(arguments_.slice(1), ['--for'], []);\n if (parsed === undefined) return undefined;\n if (parsed.valueFlags.get('--for') !== 'cartograph') return undefined;\n const paths = [...parsed.positionals];\n return paths.length < 2 || paths.length > MAX_DOCUMENTS_PER_JOIN\n ? undefined\n : paths;\n}\n\n/** 잘못된 retentions 호출을 사용법과 코드 64로 바꾼다. */\nfunction retentionUsageError(): CommandResult {\n return {\n standardOutput: '',\n standardError: `${retentionUsage}\\n`,\n exitCode: 64,\n };\n}\n\n/** retentions 명령의 한 줄 사용법이다. */\nexport const retentionUsage =\n 'Usage: isthmus retentions <bridge-facts.json> <bridge-facts.json> '\n + '[more...] --for cartograph';\n"]}
@@ -0,0 +1,5 @@
1
+ import type { CommandResult, ReadTextFile } from './command-support.ts';
2
+ /** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */
3
+ export declare function runRuntimeCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
+ /** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */
5
+ export declare const runtimeUsage: string;
@@ -0,0 +1,42 @@
1
+ import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from "../exchange/runtime.js";
2
+ import { MAX_DOCUMENTS_PER_JOIN } from "../join/join.js";
3
+ import { verifyRuntimeEvidence } from "../report/runtime.js";
4
+ import { encodeSortedJson } from "../report/sorted-json.js";
5
+ import { inputFailure, internalError } from "./command-support.js";
6
+ import { parseCommandArguments } from "./parse-arguments.js";
7
+ import { RuntimeInputError, RuntimeJsonReader } from "./runtime-json-reader.js";
8
+ /** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */
9
+ export async function runRuntimeCommand(arguments_, readTextFile) {
10
+ const parsed = parseCommandArguments(arguments_.slice(1), ['--expectations'], ['--strict', '--compact']);
11
+ const expectedPath = parsed?.valueFlags.get('--expectations');
12
+ if (parsed === undefined || expectedPath === undefined || parsed.positionals.length === 0 ||
13
+ parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {
14
+ return { standardOutput: '', standardError: `${runtimeUsage}\n`, exitCode: 64 };
15
+ }
16
+ try {
17
+ const reader = new RuntimeJsonReader(readTextFile);
18
+ const expectations = parseRuntimeExpectations(await reader.read(expectedPath));
19
+ const documents = [];
20
+ for (const path of parsed.positionals)
21
+ documents.push(parseBridgeRuntime(await reader.read(path)));
22
+ const report = verifyRuntimeEvidence(expectations, documents);
23
+ const failed = parsed.booleanFlags.has('--strict') && report.status !== 'passed';
24
+ return {
25
+ standardOutput: encodeSortedJson(report, parsed.booleanFlags.has('--compact')),
26
+ standardError: failed
27
+ ? 'Runtime verification requires review: failed calls, missing scenarios, stale runs, or incomplete evidence remain.\n'
28
+ : '',
29
+ exitCode: failed ? 1 : 0,
30
+ };
31
+ }
32
+ catch (error) {
33
+ if (error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {
34
+ return inputFailure(`${error.message}\n`);
35
+ }
36
+ return internalError();
37
+ }
38
+ }
39
+ /** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */
40
+ export const runtimeUsage = 'Usage: isthmus verify-runtime --expectations <expectations.json> '
41
+ + '<runtime.json> [more...] [--strict] [--compact]';
42
+ //# sourceMappingURL=runtime-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-command.js","sourceRoot":"","sources":["../../src/cli/runtime-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC9G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAA6B,EAAE,YAA0B;IAEzD,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IACzG,MAAM,YAAY,GAAG,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9D,IAAI,MAAM,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QACvF,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACrD,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,YAAY,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,wBAAwB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC/E,MAAM,SAAS,GAAG,EAAE,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW;YAAE,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnG,MAAM,MAAM,GAAG,qBAAqB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC;QACjF,OAAO;YACL,cAAc,EAAE,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC9E,aAAa,EAAE,MAAM;gBACnB,CAAC,CAAC,qHAAqH;gBACvH,CAAC,CAAC,EAAE;YACN,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACzB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,sBAAsB,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YAClF,OAAO,YAAY,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,aAAa,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,qCAAqC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,mEAAmE;MAC3F,iDAAiD,CAAC","sourcesContent":["import { parseBridgeRuntime, parseRuntimeExpectations, RuntimeValidationError } from '../exchange/runtime.ts';\nimport { MAX_DOCUMENTS_PER_JOIN } from '../join/join.ts';\nimport { verifyRuntimeEvidence } from '../report/runtime.ts';\nimport { encodeSortedJson } from '../report/sorted-json.ts';\nimport { inputFailure, internalError } from './command-support.ts';\nimport type { CommandResult, ReadTextFile } from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\nimport { RuntimeInputError, RuntimeJsonReader } from './runtime-json-reader.ts';\n\n/** 시나리오별 실행 기대를 런타임 관찰 JSON과 대조하는 CLI다. */\nexport async function runRuntimeCommand(\n arguments_: readonly string[], readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1), ['--expectations'], ['--strict', '--compact']);\n const expectedPath = parsed?.valueFlags.get('--expectations');\n if (parsed === undefined || expectedPath === undefined || parsed.positionals.length === 0 ||\n parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {\n return { standardOutput: '', standardError: `${runtimeUsage}\\n`, exitCode: 64 };\n }\n try {\n const reader = new RuntimeJsonReader(readTextFile);\n const expectations = parseRuntimeExpectations(await reader.read(expectedPath));\n const documents = [];\n for (const path of parsed.positionals) documents.push(parseBridgeRuntime(await reader.read(path)));\n const report = verifyRuntimeEvidence(expectations, documents);\n const failed = parsed.booleanFlags.has('--strict') && report.status !== 'passed';\n return {\n standardOutput: encodeSortedJson(report, parsed.booleanFlags.has('--compact')),\n standardError: failed\n ? 'Runtime verification requires review: failed calls, missing scenarios, stale runs, or incomplete evidence remain.\\n'\n : '',\n exitCode: failed ? 1 : 0,\n };\n } catch (error) {\n if (error instanceof RuntimeValidationError || error instanceof RuntimeInputError) {\n return inputFailure(`${error.message}\\n`);\n }\n return internalError();\n }\n}\n\n/** 런타임 기대 파일과 하나 이상의 실행 로그가 필요하다. */\nexport const runtimeUsage = 'Usage: isthmus verify-runtime --expectations <expectations.json> '\n + '<runtime.json> [more...] [--strict] [--compact]';\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ReadTextFile } from './command-support.ts';
2
+ /** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */
3
+ export declare class RuntimeJsonReader {
4
+ private readonly readTextFile;
5
+ private totalLength;
6
+ private position;
7
+ constructor(readTextFile: ReadTextFile, initialLength?: number);
8
+ read(path: string): Promise<unknown>;
9
+ }
10
+ /** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */
11
+ export declare class RuntimeInputError extends Error {
12
+ }
@@ -0,0 +1,37 @@
1
+ import { isJsonParseFailure, MAX_INPUT_TEXT_LENGTH, MAX_TOTAL_INPUT_TEXT_LENGTH } from "./command-support.js";
2
+ /** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */
3
+ export class RuntimeJsonReader {
4
+ readTextFile;
5
+ totalLength;
6
+ position = 0;
7
+ constructor(readTextFile, initialLength = 0) {
8
+ this.readTextFile = readTextFile;
9
+ this.totalLength = initialLength;
10
+ }
11
+ async read(path) {
12
+ this.position++;
13
+ let text;
14
+ try {
15
+ text = await this.readTextFile(path);
16
+ }
17
+ catch {
18
+ throw new RuntimeInputError(`Unable to read runtime input ${this.position}; check the file exists and is readable.`);
19
+ }
20
+ this.totalLength += text.length;
21
+ if (text.length > MAX_INPUT_TEXT_LENGTH || this.totalLength > MAX_TOTAL_INPUT_TEXT_LENGTH) {
22
+ throw new RuntimeInputError(`Runtime input ${this.position} exceeds the input size limits.`);
23
+ }
24
+ try {
25
+ return JSON.parse(text);
26
+ }
27
+ catch (error) {
28
+ if (isJsonParseFailure(error))
29
+ throw new RuntimeInputError(`Runtime input ${this.position} is not valid JSON.`);
30
+ throw error;
31
+ }
32
+ }
33
+ }
34
+ /** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */
35
+ export class RuntimeInputError extends Error {
36
+ }
37
+ //# sourceMappingURL=runtime-json-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-json-reader.js","sourceRoot":"","sources":["../../src/cli/runtime-json-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAG9G,sDAAsD;AACtD,MAAM,OAAO,iBAAiB;IACX,YAAY,CAAe;IACpC,WAAW,CAAS;IACpB,QAAQ,GAAG,CAAC,CAAC;IAErB,YAAY,YAA0B,EAAE,aAAa,GAAG,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY;QACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YAAC,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAC7C,MAAM,CAAC;YAAC,MAAM,IAAI,iBAAiB,CAAC,gCAAgC,IAAI,CAAC,QAAQ,0CAA0C,CAAC,CAAC;QAAC,CAAC;QAC/H,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,IAAI,IAAI,CAAC,WAAW,GAAG,2BAA2B,EAAE,CAAC;YAC1F,MAAM,IAAI,iBAAiB,CAAC,iBAAiB,IAAI,CAAC,QAAQ,iCAAiC,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,CAAC;YAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAChC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,kBAAkB,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,iBAAiB,CAAC,iBAAiB,IAAI,CAAC,QAAQ,qBAAqB,CAAC,CAAC;YAChH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,2CAA2C;AAC3C,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG","sourcesContent":["import { isJsonParseFailure, MAX_INPUT_TEXT_LENGTH, MAX_TOTAL_INPUT_TEXT_LENGTH } from './command-support.ts';\nimport type { ReadTextFile } from './command-support.ts';\n\n/** 기대·실행 JSON의 예산을 공유하며 이미 읽은 정적 context 크기도 포함한다. */\nexport class RuntimeJsonReader {\n private readonly readTextFile: ReadTextFile;\n private totalLength: number;\n private position = 0;\n\n constructor(readTextFile: ReadTextFile, initialLength = 0) {\n this.readTextFile = readTextFile;\n this.totalLength = initialLength;\n }\n\n async read(path: string): Promise<unknown> {\n this.position++;\n let text: string;\n try { text = await this.readTextFile(path); }\n catch { throw new RuntimeInputError(`Unable to read runtime input ${this.position}; check the file exists and is readable.`); }\n this.totalLength += text.length;\n if (text.length > MAX_INPUT_TEXT_LENGTH || this.totalLength > MAX_TOTAL_INPUT_TEXT_LENGTH) {\n throw new RuntimeInputError(`Runtime input ${this.position} exceeds the input size limits.`);\n }\n try { return JSON.parse(text); }\n catch (error) {\n if (isJsonParseFailure(error)) throw new RuntimeInputError(`Runtime input ${this.position} is not valid JSON.`);\n throw error;\n }\n }\n}\n\n/** runtime 입력 I/O·JSON 오류를 내부 결함과 구분한다. */\nexport class RuntimeInputError extends Error {}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { CommandResult, ReadTextFile, WriteTextFile } from './command-support.ts';
2
+ export declare const serveUsage = "Usage: isthmus serve";
3
+ /** 세션이 읽는 NDJSON 입력과 응답을 쓰는 싱크다. 테스트는 메모리로 주입한다. */
4
+ export interface ServeIo {
5
+ readonly lines: AsyncIterable<string>;
6
+ readonly writeLine: (line: string) => void;
7
+ }
8
+ /**
9
+ * stdin의 한 줄씩 들어오는 JSON-RPC 메시지를 MCP 세션에 넘기고
10
+ * 응답을 stdout에 쓴다. stdin이 닫히면 0으로 끝난다 — 세션은
11
+ * 상태를 쌓지 않으므로 종료 시 잃는 것이 없다.
12
+ */
13
+ export declare function runServeCommand(arguments_: readonly string[], readTextFile: ReadTextFile, writeTextFile?: WriteTextFile, producerVersion?: string, io?: ServeIo): Promise<CommandResult>;
@@ -0,0 +1,43 @@
1
+ import { createInterface } from 'node:readline';
2
+ import { createMcpSession } from "./mcp-server.js";
3
+ export const serveUsage = 'Usage: isthmus serve';
4
+ /**
5
+ * stdin의 한 줄씩 들어오는 JSON-RPC 메시지를 MCP 세션에 넘기고
6
+ * 응답을 stdout에 쓴다. stdin이 닫히면 0으로 끝난다 — 세션은
7
+ * 상태를 쌓지 않으므로 종료 시 잃는 것이 없다.
8
+ */
9
+ export async function runServeCommand(arguments_, readTextFile, writeTextFile, producerVersion, io) {
10
+ if (arguments_[0] !== 'serve' || arguments_.length !== 1) {
11
+ return {
12
+ standardOutput: '',
13
+ standardError: `${serveUsage}\n`,
14
+ exitCode: 64,
15
+ };
16
+ }
17
+ const session = createMcpSession({
18
+ readTextFile,
19
+ writeTextFile,
20
+ producerVersion,
21
+ });
22
+ const { lines, writeLine } = io ?? stdioLines();
23
+ for await (const line of lines) {
24
+ if (line.trim().length === 0)
25
+ continue;
26
+ const response = await session.handleLine(line);
27
+ if (response !== undefined) {
28
+ writeLine(response);
29
+ }
30
+ }
31
+ return { standardOutput: '', standardError: '', exitCode: 0 };
32
+ }
33
+ /** stdin을 줄 단위로 읽고 stdout에 한 줄씩 쓰는 기본 IO다. */
34
+ function stdioLines() {
35
+ return {
36
+ lines: createInterface({
37
+ input: process.stdin,
38
+ crlfDelay: Infinity,
39
+ }),
40
+ writeLine: (line) => process.stdout.write(`${line}\n`),
41
+ };
42
+ }
43
+ //# sourceMappingURL=serve-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-command.js","sourceRoot":"","sources":["../../src/cli/serve-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAOnD,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAQjD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAA6B,EAC7B,YAA0B,EAC1B,aAA6B,EAC7B,eAAwB,EACxB,EAAY;IAEZ,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO;YACL,cAAc,EAAE,EAAE;YAClB,aAAa,EAAE,GAAG,UAAU,IAAI;YAChC,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,CAAC;QAC/B,YAAY;QACZ,aAAa;QACb,eAAe;KAChB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,UAAU,EAAE,CAAC;IAChD,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACvC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAChE,CAAC;AAED,8CAA8C;AAC9C,SAAS,UAAU;IACjB,OAAO;QACL,KAAK,EAAE,eAAe,CAAC;YACrB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,QAAQ;SACpB,CAAC;QACF,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC;KACvD,CAAC;AACJ,CAAC","sourcesContent":["import { createInterface } from 'node:readline';\n\nimport { createMcpSession } from './mcp-server.ts';\nimport type {\n CommandResult,\n ReadTextFile,\n WriteTextFile,\n} from './command-support.ts';\n\nexport const serveUsage = 'Usage: isthmus serve';\n\n/** 세션이 읽는 NDJSON 입력과 응답을 쓰는 싱크다. 테스트는 메모리로 주입한다. */\nexport interface ServeIo {\n readonly lines: AsyncIterable<string>;\n readonly writeLine: (line: string) => void;\n}\n\n/**\n * stdin의 한 줄씩 들어오는 JSON-RPC 메시지를 MCP 세션에 넘기고\n * 응답을 stdout에 쓴다. stdin이 닫히면 0으로 끝난다 — 세션은\n * 상태를 쌓지 않으므로 종료 시 잃는 것이 없다.\n */\nexport async function runServeCommand(\n arguments_: readonly string[],\n readTextFile: ReadTextFile,\n writeTextFile?: WriteTextFile,\n producerVersion?: string,\n io?: ServeIo,\n): Promise<CommandResult> {\n if (arguments_[0] !== 'serve' || arguments_.length !== 1) {\n return {\n standardOutput: '',\n standardError: `${serveUsage}\\n`,\n exitCode: 64,\n };\n }\n const session = createMcpSession({\n readTextFile,\n writeTextFile,\n producerVersion,\n });\n const { lines, writeLine } = io ?? stdioLines();\n for await (const line of lines) {\n if (line.trim().length === 0) continue;\n const response = await session.handleLine(line);\n if (response !== undefined) {\n writeLine(response);\n }\n }\n return { standardOutput: '', standardError: '', exitCode: 0 };\n}\n\n/** stdin을 줄 단위로 읽고 stdout에 한 줄씩 쓰는 기본 IO다. */\nfunction stdioLines(): ServeIo {\n return {\n lines: createInterface({\n input: process.stdin,\n crlfDelay: Infinity,\n }),\n writeLine: (line) => process.stdout.write(`${line}\\n`),\n };\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * capture workflow 설정이 계약을 어겼음을 나타낸다.
3
+ *
4
+ * 이 설정은 제품이 실행하는 명령 목록이 아니라 `scripts/capture-preflight.mjs`가
5
+ * 실행할 명령 목록이다. 제품은 JSON으로 검증만 하고 자매 도구를 실행하지 않는다.
6
+ */
7
+ export declare class CaptureConfigValidationError extends Error {
8
+ /** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */
9
+ constructor(message: string);
10
+ }
11
+ /**
12
+ * capture workflow 설정을 검증한다.
13
+ *
14
+ * `scripts/capture-preflight.mjs`가 실행 전에 적용하는 규칙과 같은 정본이다.
15
+ * 규칙을 여기에 두어 `isthmus doctor`와 수집기가 같은 판정을 내리게 한다.
16
+ * `project`는 이미 realpath로 정규화된 조인 루트다.
17
+ */
18
+ export declare function validateCaptureConfig(config: unknown, project: string): void;
@@ -0,0 +1,100 @@
1
+ import { isAbsolute, relative, resolve } from 'node:path';
2
+ import { isJsonObject, isProjectRelativePath, isSafeNonEmptyString } from "./parse.js";
3
+ /**
4
+ * capture workflow 설정이 계약을 어겼음을 나타낸다.
5
+ *
6
+ * 이 설정은 제품이 실행하는 명령 목록이 아니라 `scripts/capture-preflight.mjs`가
7
+ * 실행할 명령 목록이다. 제품은 JSON으로 검증만 하고 자매 도구를 실행하지 않는다.
8
+ */
9
+ export class CaptureConfigValidationError extends Error {
10
+ /** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'CaptureConfigValidationError';
14
+ }
15
+ }
16
+ /** 명령 배열은 비어 있지 않은 안전한 문자열 토큰의 비어 있지 않은 배열이다. */
17
+ function isCommand(value) {
18
+ return Array.isArray(value) && value.length > 0 && value.every(isSafeNonEmptyString);
19
+ }
20
+ /**
21
+ * capture workflow 설정을 검증한다.
22
+ *
23
+ * `scripts/capture-preflight.mjs`가 실행 전에 적용하는 규칙과 같은 정본이다.
24
+ * 규칙을 여기에 두어 `isthmus doctor`와 수집기가 같은 판정을 내리게 한다.
25
+ * `project`는 이미 realpath로 정규화된 조인 루트다.
26
+ */
27
+ export function validateCaptureConfig(config, project) {
28
+ if (!isJsonObject(config))
29
+ fail('Capture config must be a JSON object.');
30
+ if (!isSafeNonEmptyString(config.project))
31
+ fail('Capture config requires a project path.');
32
+ const command = isCommand;
33
+ if (!command(config.dartograph) ||
34
+ (config.cartograph === undefined && config.kartograph === undefined) ||
35
+ (config.cartograph !== undefined && !command(config.cartograph)) ||
36
+ (config.kartograph !== undefined && !command(config.kartograph)) ||
37
+ !Array.isArray(config.prepare) || config.prepare.length === 0 ||
38
+ !config.prepare.every(command)) {
39
+ fail('Configure producer commands and a native index preparation command.');
40
+ }
41
+ if (config.messages !== undefined && config.messages !== true &&
42
+ (config.messages === null || typeof config.messages !== 'object' ||
43
+ Array.isArray(config.messages) || Object.entries(config.messages).some(([name, value]) => !['dartograph', 'cartograph', 'kartograph'].includes(name) ||
44
+ config[name] === undefined || !command(value)))) {
45
+ fail('Invalid message producer configuration.');
46
+ }
47
+ if (config.events !== undefined && config.events !== true &&
48
+ (config.events === null || typeof config.events !== 'object' ||
49
+ Array.isArray(config.events) || Object.entries(config.events).some(([name, value]) => !['dartograph', 'cartograph', 'kartograph'].includes(name) ||
50
+ config[name] === undefined || !command(value)))) {
51
+ fail('Invalid event producer configuration.');
52
+ }
53
+ // Kotlin 소스 읽기는 스냅이 없어도 동작한다. 스냅샷은 impact 분석에만
54
+ // 필요하므로 selection.kotlin이 있을 때만 요구한다.
55
+ const selection = config.selection;
56
+ const kotlinSelection = typeof selection === 'object' && selection !== null &&
57
+ !Array.isArray(selection) && selection.kotlin !== undefined;
58
+ if (kotlinSelection && config.kartograph === undefined) {
59
+ fail('A Kotlin selection requires its Kartograph producer.');
60
+ }
61
+ if (kotlinSelection && !isSafeNonEmptyString(config.kartographSnapshot)) {
62
+ fail('Configure a Kartograph snapshot produced by the preparation command.');
63
+ }
64
+ if (config.kartograph === undefined && config.kartographSnapshot !== undefined) {
65
+ fail('A Kartograph snapshot requires its producer.');
66
+ }
67
+ if (!Array.isArray(config.inputs) || config.inputs.length === 0 ||
68
+ !config.inputs.every(isProjectRelativePath) ||
69
+ !Array.isArray(config.toolInputs) || config.toolInputs.length === 0 ||
70
+ !config.toolInputs.every(isSafeNonEmptyString)) {
71
+ fail('Declare source/config inputs and producer implementation files for fingerprinting.');
72
+ }
73
+ const explicit = config.selection !== undefined;
74
+ const since = config.since !== undefined;
75
+ if (explicit === since ||
76
+ (explicit && (config.selection === null || typeof config.selection !== 'object' ||
77
+ Array.isArray(config.selection))) ||
78
+ (since && !isSafeNonEmptyString(config.since)) ||
79
+ !isSafeNonEmptyString(config.output) || !isSafeNonEmptyString(config.cache)) {
80
+ fail('Configure exactly one selection or since revision, plus output and cache paths.');
81
+ }
82
+ for (const output of [config.output, `${config.output}.sources.json`, config.cache]) {
83
+ const target = resolve(project, output);
84
+ for (const input of [...config.inputs, ...config.toolInputs,
85
+ ...(config.kartographSnapshot === undefined ? [] : [config.kartographSnapshot])]) {
86
+ const part = relative(resolve(project, input), target);
87
+ if (part === '' || (!part.startsWith('..') && !isAbsolute(part))) {
88
+ fail('Keep output and cache outside fingerprint input trees.');
89
+ }
90
+ }
91
+ }
92
+ if (resolve(project, config.output) === resolve(project, config.cache)) {
93
+ fail('Output and cache paths must differ.');
94
+ }
95
+ }
96
+ /** 입력 값을 포함하지 않는 검증 오류를 던진다. */
97
+ function fail(message) {
98
+ throw new CaptureConfigValidationError(message);
99
+ }
100
+ //# sourceMappingURL=capture-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-config.js","sourceRoot":"","sources":["../../src/exchange/capture-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve } from 'node:path';\n\nimport { isJsonObject, isProjectRelativePath, isSafeNonEmptyString } from './parse.ts';\n\n/**\n * capture workflow 설정이 계약을 어겼음을 나타낸다.\n *\n * 이 설정은 제품이 실행하는 명령 목록이 아니라 `scripts/capture-preflight.mjs`가\n * 실행할 명령 목록이다. 제품은 JSON으로 검증만 하고 자매 도구를 실행하지 않는다.\n */\nexport class CaptureConfigValidationError extends Error {\n /** 입력 내용을 노출하지 않는 안전한 메시지를 보존한다. */\n constructor(message: string) {\n super(message);\n this.name = 'CaptureConfigValidationError';\n }\n}\n\n/** 명령 배열은 비어 있지 않은 안전한 문자열 토큰의 비어 있지 않은 배열이다. */\nfunction isCommand(value: unknown): value is readonly string[] {\n return Array.isArray(value) && value.length > 0 && value.every(isSafeNonEmptyString);\n}\n\n/**\n * capture workflow 설정을 검증한다.\n *\n * `scripts/capture-preflight.mjs`가 실행 전에 적용하는 규칙과 같은 정본이다.\n * 규칙을 여기에 두어 `isthmus doctor`와 수집기가 같은 판정을 내리게 한다.\n * `project`는 이미 realpath로 정규화된 조인 루트다.\n */\nexport function validateCaptureConfig(config: unknown, project: string): void {\n if (!isJsonObject(config)) fail('Capture config must be a JSON object.');\n if (!isSafeNonEmptyString(config.project)) fail('Capture config requires a project path.');\n const command = isCommand;\n if (!command(config.dartograph) ||\n (config.cartograph === undefined && config.kartograph === undefined) ||\n (config.cartograph !== undefined && !command(config.cartograph)) ||\n (config.kartograph !== undefined && !command(config.kartograph)) ||\n !Array.isArray(config.prepare) || config.prepare.length === 0 ||\n !config.prepare.every(command)) {\n fail('Configure producer commands and a native index preparation command.');\n }\n if (config.messages !== undefined && config.messages !== true &&\n (config.messages === null || typeof config.messages !== 'object' ||\n Array.isArray(config.messages) || Object.entries(config.messages).some(([name, value]) =>\n !['dartograph', 'cartograph', 'kartograph'].includes(name) ||\n config[name] === undefined || !command(value)))) {\n fail('Invalid message producer configuration.');\n }\n if (config.events !== undefined && config.events !== true &&\n (config.events === null || typeof config.events !== 'object' ||\n Array.isArray(config.events) || Object.entries(config.events).some(([name, value]) =>\n !['dartograph', 'cartograph', 'kartograph'].includes(name) ||\n config[name] === undefined || !command(value)))) {\n fail('Invalid event producer configuration.');\n }\n // Kotlin 소스 읽기는 스냅이 없어도 동작한다. 스냅샷은 impact 분석에만\n // 필요하므로 selection.kotlin이 있을 때만 요구한다.\n const selection = config.selection as Record<string, unknown> | undefined;\n const kotlinSelection = typeof selection === 'object' && selection !== null &&\n !Array.isArray(selection) && selection.kotlin !== undefined;\n if (kotlinSelection && config.kartograph === undefined) {\n fail('A Kotlin selection requires its Kartograph producer.');\n }\n if (kotlinSelection && !isSafeNonEmptyString(config.kartographSnapshot)) {\n fail('Configure a Kartograph snapshot produced by the preparation command.');\n }\n if (config.kartograph === undefined && config.kartographSnapshot !== undefined) {\n fail('A Kartograph snapshot requires its producer.');\n }\n if (!Array.isArray(config.inputs) || config.inputs.length === 0 ||\n !config.inputs.every(isProjectRelativePath) ||\n !Array.isArray(config.toolInputs) || config.toolInputs.length === 0 ||\n !config.toolInputs.every(isSafeNonEmptyString)) {\n fail('Declare source/config inputs and producer implementation files for fingerprinting.');\n }\n const explicit = config.selection !== undefined;\n const since = config.since !== undefined;\n if (explicit === since ||\n (explicit && (config.selection === null || typeof config.selection !== 'object' ||\n Array.isArray(config.selection))) ||\n (since && !isSafeNonEmptyString(config.since)) ||\n !isSafeNonEmptyString(config.output) || !isSafeNonEmptyString(config.cache)) {\n fail('Configure exactly one selection or since revision, plus output and cache paths.');\n }\n for (const output of [config.output, `${config.output}.sources.json`, config.cache]) {\n const target = resolve(project, output as string);\n for (const input of [...(config.inputs as string[]), ...(config.toolInputs as string[]),\n ...(config.kartographSnapshot === undefined ? [] : [config.kartographSnapshot as string])]) {\n const part = relative(resolve(project, input), target);\n if (part === '' || (!part.startsWith('..') && !isAbsolute(part))) {\n fail('Keep output and cache outside fingerprint input trees.');\n }\n }\n }\n if (resolve(project, config.output as string) === resolve(project, config.cache as string)) {\n fail('Output and cache paths must differ.');\n }\n}\n\n/** 입력 값을 포함하지 않는 검증 오류를 던진다. */\nfunction fail(message: string): never {\n throw new CaptureConfigValidationError(message);\n}"]}
@@ -0,0 +1,13 @@
1
+ /** 변경 대상의 프로젝트 상대 파일과 정확한 심볼 이름/USR이다. 두 목록은 합집합이다. */
2
+ export interface ImpactSelection {
3
+ readonly files: readonly string[];
4
+ readonly symbols: readonly string[];
5
+ }
6
+ /** 변경 목록이 큰 입력을 통한 선택 집합 증폭을 일으키지 않도록 제한한다. */
7
+ export declare const MAX_IMPACT_SELECTORS = 10000;
8
+ /** 변경 선택 계약 실패를 소스 값이 없는 메시지로 구분한다. */
9
+ export declare class ImpactSelectionValidationError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /** 외부 변경 목록을 검증하고 위치·심볼 문자열을 손대지 않은 채 중복 제거한다. */
13
+ export declare function parseImpactSelection(input: unknown): ImpactSelection;