@mcpeak/cli 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +12 -2
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/commands.cjs +1 -3
- package/dist/commands.d.cts +44 -6
- package/dist/commands.d.mts +44 -6
- package/dist/commands.mjs +2 -2
- package/dist/index.cjs +219 -5
- package/dist/index.d.cts +3 -14
- package/dist/index.d.mts +3 -14
- package/dist/index.mjs +217 -2
- package/dist/{replay-command-BAtSzf8R.d.mts → repair-command-D3zL3G5W.d.cts} +36 -44
- package/dist/{replay-command-V0ZSJPiD.d.cts → repair-command-DQh2h4tQ.d.mts} +36 -44
- package/dist/{test-command-BZ_94UmI.cjs → test-command-B_faNSfA.cjs} +1583 -873
- package/dist/{test-command-CsJfojut.mjs → test-command-DEp9hrfH.mjs} +1591 -869
- package/package.json +14 -10
- package/dist/src-D1NW6ckv.mjs +0 -459
- package/dist/src-Df8147pD.cjs +0 -482
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ mcpeak test packages/cli/tests/fixtures/weather-suite.json \
|
|
|
22
22
|
--arg examples/weather-server/server.mjs
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
문법은 `mcpeak test <suite.json> --command <executable> [--arg <value> ...] [--json] [--junit <path>] [--stderr-lines <N>]`입니다. 위 예시의 command와 arg는 `node examples/weather-server/server.mjs`로 실행됩니다. `--arg`는 반복할 수 있고, 하이픈으로 시작하는 값은 `--arg=-m`, 빈 값은 `--arg=`로 전달합니다.
|
|
25
|
+
문법은 `mcpeak test <suite.json> --command <executable> [--arg <value> ...] [--json] [--junit <path>] [--stderr-lines <N>] [--record-session <path> | --session <path>]`입니다. 위 예시의 command와 arg는 `node examples/weather-server/server.mjs`로 실행됩니다. `--arg`는 반복할 수 있고, 하이픈으로 시작하는 값은 `--arg=-m`, 빈 값은 `--arg=`로 전달합니다.
|
|
26
26
|
|
|
27
27
|
stdout에는 보고서만 나갑니다. 기본은 사람이 읽는 보고서이고, `--json`을 주면 `RunnerReport` JSON이 나갑니다. CLI 오류와 서버 프로세스 진단은 stderr로만 나가므로 `--json > report.json`이 깨지지 않습니다. 모든 테스트가 통과하면 종료 코드 0을, failed 또는 aborted report와 입력, 연결, 종료 오류에는 1을 반환합니다.
|
|
28
28
|
|
|
@@ -40,6 +40,11 @@ XML을 만드는 단계에서 실패하면 `CLI_INTERNAL_ERROR`와 함께 종료
|
|
|
40
40
|
|
|
41
41
|
`RunnerReport`에는 시간 정보가 없어 모든 `time` 속성은 `0`입니다. "0초 걸렸다"가 아니라 "시간 정보를 갖고 있지 않다"는 뜻입니다. 근거는 [ADR-0016](../../docs/adr/0016-junit-time-속성-고정.md), 플래그 설계는 [ADR-0019](../../docs/adr/0019-junit-리포터-cli-노출-방식.md)에 있습니다.
|
|
42
42
|
|
|
43
|
+
`--record-session <path>`는 서버가 `globalThis.fetch`로 호출한 외부 HTTP 응답을 녹화하고,
|
|
44
|
+
`--session <path>`는 그 녹화본을 재생합니다. 서버는 재생 때도 실제로 실행됩니다. `node:http`와
|
|
45
|
+
`node:https` 계층 호출은 범위 밖이라 실제 네트워크로 나갈 수 있으며, CLI는 녹화 0건이나 재생
|
|
46
|
+
0건처럼 범위 밖 호출이 의심되는 실행을 stderr 알림으로 드러냅니다.
|
|
47
|
+
|
|
43
48
|
`--stderr-lines <N>`은 실패했거나 서버가 비정상 종료·중단했을 때, 진단 내용이 있으면 stderr에 붙는 서버 프로세스 진단 블록의 stderr 표시 줄 수입니다. 기본값은 20이고, `0`을 주면 블록을 완전히 끕니다(그때 출력 바이트는 이 기능이 없던 때와 같습니다). 블록에는 종료 코드와 시그널, 서버가 남긴 stderr의 마지막 N줄이 담기며 잘린 사실은 헤더에 적힙니다. 서버가 정상 종료했고 stderr도 비어 있으면 보여줄 근거가 없으므로 블록을 쓰지 않습니다.
|
|
44
49
|
|
|
45
50
|
### 승인 지문 대조
|
|
@@ -56,11 +61,16 @@ XML을 만드는 단계에서 실패하면 `CLI_INTERNAL_ERROR`와 함께 종료
|
|
|
56
61
|
|
|
57
62
|
전부 통과인데 불일치일 때만 예외를 두는 이유는 승인받지 않은 명세로 초록불이 뜬 상태이기 때문입니다. 그 사실은 실패보다 오히려 조용히 지나가기 쉽습니다.
|
|
58
63
|
|
|
64
|
+
현재 명세에서 계산한 지문과 저장된 `approval.fingerprint`가 불일치하면 전부 통과했는지 실패가
|
|
65
|
+
있는지에 맞춰 상황을 구분해 알립니다. 지문 두 개만으로는 구체적인 변경 내용을 복원할 수 없으므로
|
|
66
|
+
버전 관리에서 명세를 비교하도록 안내하고, 의도한 변경이면 같은 설정의 `mcpeak generate`에
|
|
67
|
+
`--force`를 붙여 다시 생성·승인하도록 안내합니다.
|
|
68
|
+
|
|
59
69
|
명세 줄은 보고서 본문 뒤에 빈 줄 하나를 두고 stdout으로 나갑니다. `--json`에는 위 억제 규칙을 적용하지 않고 `spec` 키를 항상 넣습니다(`approval`은 `matched` · `mismatched` · `absent` 중 하나, `fingerprint`는 실행 시점 계산값, `approvedFingerprint`는 파일에 적힌 값이며 `absent`면 키가 없습니다). 기계가 읽는 출력에서 키가 조건부로 사라지면 소비자가 분기를 하나 더 써야 합니다. 기존 보고서 키는 그대로이므로 기존 소비자는 깨지지 않습니다.
|
|
60
70
|
|
|
61
71
|
지문이 없는 명세도 정상입니다. 손으로 쓴 명세와 이 기능 이전에 만든 명세가 여기에 해당하며 실패로 취급하지 않습니다.
|
|
62
72
|
|
|
63
|
-
현재는 UTF-8 JSON 단일 명세와 stdio 서버만 지원합니다. shell 문법, 여러 명세, TypeScript 모듈 명세는 지원하지 않습니다. `record`, `
|
|
73
|
+
현재는 UTF-8 JSON 단일 명세와 stdio 서버만 지원합니다. shell 문법, 여러 명세, TypeScript 모듈 명세는 지원하지 않습니다. `record`, `mock` 명령은 아직 구현되지 않았습니다.
|
|
64
74
|
|
|
65
75
|
## generate
|
|
66
76
|
|
package/dist/cli.cjs
CHANGED
package/dist/cli.mjs
CHANGED
package/dist/commands.cjs
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_test_command = require("./test-command-
|
|
2
|
+
const require_test_command = require("./test-command-B_faNSfA.cjs");
|
|
3
3
|
exports.nodeGenerateDependencies = require_test_command.nodeGenerateDependencies;
|
|
4
4
|
exports.nodeReviewIO = require_test_command.nodeReviewIO;
|
|
5
5
|
exports.parseRepairCommand = require_test_command.parseRepairCommand;
|
|
6
|
-
exports.parseReplayCommand = require_test_command.parseReplayCommand;
|
|
7
6
|
exports.parseTestCommand = require_test_command.parseTestCommand;
|
|
8
7
|
exports.runCli = require_test_command.runCli;
|
|
9
8
|
exports.runGenerateCommand = require_test_command.runGenerateCommand;
|
|
10
9
|
exports.runRepairCommand = require_test_command.runRepairCommand;
|
|
11
|
-
exports.runReplayCommand = require_test_command.runReplayCommand;
|
package/dist/commands.d.cts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { McpStdioConnection } from "@mcpeak/core";
|
|
1
|
+
import { a as GenerateCommandDependencies, c as nodeGenerateDependencies, d as ConnectTarget, i as runRepairCommand, l as nodeReviewIO, n as RepairCommandInput, o as GenerateCommandInput, r as parseRepairCommand, s as ReviewIO, t as RepairCommandDependencies, u as runGenerateCommand } from "./repair-command-D3zL3G5W.cjs";
|
|
2
|
+
import { McpHttpConnection, McpStdioConnection } from "@mcpeak/core";
|
|
3
3
|
import { CheckDeterminismOptions, DeterminismResult, FinalizeRunnerExecutionOptions, InputContractOptions, RunSuiteOptions, RunnerExecution, RunnerReport, SpecFindingsResult, SuiteValidationIssue, SuiteValidationResult, TestSuiteSpec } from "@mcpeak/runner";
|
|
4
|
+
import "@mcpeak/record/external";
|
|
4
5
|
//#region src/test-command.d.ts
|
|
5
6
|
interface TestCommandInput {
|
|
6
7
|
readonly suitePath: string;
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 붙을 대상. `--command`/`--arg` 면 stdio, `--url` 이면 Streamable HTTP 다(#137).
|
|
10
|
+
*
|
|
11
|
+
* `command` · `args` 두 필드를 이 하나로 바꿨다. 두 transport 를 나란히 두면 "url 이
|
|
12
|
+
* 있으면 command 는 무시" 같은 규칙이 읽는 자리마다 되풀이되고, 그 규칙이 한 군데서만
|
|
13
|
+
* 어긋나도 사용자가 지정하지 않은 곳에 붙는다.
|
|
14
|
+
*/
|
|
15
|
+
readonly target: ConnectTarget;
|
|
9
16
|
readonly json: boolean;
|
|
10
17
|
/** `--junit` 로 받은 XML 출력 경로. 지정하지 않으면 undefined 이고 XML 을 만들지 않는다. */
|
|
11
18
|
readonly junitPath: string | undefined;
|
|
@@ -15,9 +22,13 @@ interface TestCommandInput {
|
|
|
15
22
|
readonly determinism: boolean;
|
|
16
23
|
/** `--reset-cmd` 로 받은 초기화 명령. 각 회차 전에 1번씩 실행한다. */
|
|
17
24
|
readonly resetCmd: string | undefined;
|
|
25
|
+
/** `--session`. External 세션을 재생한다. */
|
|
26
|
+
readonly sessionPath: string | undefined;
|
|
27
|
+
/** `--record-session`. External 세션을 녹화한다. */
|
|
28
|
+
readonly recordSessionPath: string | undefined;
|
|
18
29
|
readonly stderrLines: number;
|
|
19
30
|
}
|
|
20
|
-
type CliErrorCode = "CLI_USAGE" | "COMMAND_NOT_IMPLEMENTED" | "SUITE_FORMAT_UNSUPPORTED" | "SUITE_READ_FAILED" | "SUITE_ENCODING_INVALID" | "SUITE_JSON_INVALID" | "SUITE_VALIDATION_FAILED" | "MCP_CONNECTION_FAILED" | "RUNNER_EXECUTION_FAILED" | "RUNNER_FINALIZATION_FAILED" | "JUNIT_WRITE_FAILED" | "REPAIR_BUNDLE_WRITE_FAILED" | "RESET_COMMAND_FAILED" | "CLI_INTERNAL_ERROR";
|
|
31
|
+
type CliErrorCode = "CLI_USAGE" | "COMMAND_NOT_IMPLEMENTED" | "SUITE_FORMAT_UNSUPPORTED" | "SUITE_READ_FAILED" | "SUITE_ENCODING_INVALID" | "SUITE_JSON_INVALID" | "SUITE_VALIDATION_FAILED" | "MCP_CONNECTION_FAILED" | "RUNNER_EXECUTION_FAILED" | "RUNNER_FINALIZATION_FAILED" | "RUNNER_PAYLOAD_LIMIT_EXCEEDED" | "JUNIT_WRITE_FAILED" | "REPAIR_BUNDLE_WRITE_FAILED" | "RESET_COMMAND_FAILED" | "EXTERNAL_SESSION_FAILED" | "CLI_INTERNAL_ERROR";
|
|
21
32
|
interface CliFailure {
|
|
22
33
|
readonly code: CliErrorCode;
|
|
23
34
|
readonly message: string;
|
|
@@ -31,7 +42,25 @@ interface TestCommandDependencies {
|
|
|
31
42
|
connect(options: {
|
|
32
43
|
command: string;
|
|
33
44
|
args: readonly string[];
|
|
45
|
+
/** External 배선이 만든 자식 환경 변수. Bootstrap 주입이 여기 실린다. */
|
|
46
|
+
env?: Readonly<Record<string, string>>;
|
|
34
47
|
}): Promise<McpStdioConnection>;
|
|
48
|
+
/**
|
|
49
|
+
* 원격(Streamable HTTP) 대상용 연결. `core.connectHttp` 다.
|
|
50
|
+
*
|
|
51
|
+
* **선택 사항으로 둔다.** 위 `checkInputContract` 와 같은 이유다 — 진입점의 "런타임 의존성
|
|
52
|
+
* 없음" 경로와 대시보드처럼 stdio 만 배선한 호출자가 이 필드를 채울 수 없다. 없으면
|
|
53
|
+
* `--url` 이 조용히 stdio 로 떨어지지 않고 무엇이 없는지 말하고 멈춘다(#137).
|
|
54
|
+
*/
|
|
55
|
+
connectHttp?(options: {
|
|
56
|
+
url: string;
|
|
57
|
+
headers?: Readonly<Record<string, string>>;
|
|
58
|
+
}): Promise<McpHttpConnection>;
|
|
59
|
+
/**
|
|
60
|
+
* `--header-env` 가 가리키는 환경변수를 읽는다. 이것이 없으면 `--header-env` 를 쓸 수 없다.
|
|
61
|
+
* CLI 코드가 `process` 를 직접 읽지 않기 위한 주입점이다(ADR-0013).
|
|
62
|
+
*/
|
|
63
|
+
readEnv?(name: string): string | undefined;
|
|
35
64
|
startRunner(options: RunSuiteOptions): RunnerExecution;
|
|
36
65
|
finalize(options: FinalizeRunnerExecutionOptions): Promise<RunnerReport>;
|
|
37
66
|
renderReport(report: RunnerReport, options?: {
|
|
@@ -62,6 +91,15 @@ interface TestCommandDependencies {
|
|
|
62
91
|
writeStderr(text: string): void;
|
|
63
92
|
}
|
|
64
93
|
declare function parseTestCommand(argv: readonly string[]): TestCommandInput;
|
|
94
|
+
/**
|
|
95
|
+
* `test` 실행에 External Record/Replay 수명주기를 씌운다.
|
|
96
|
+
*
|
|
97
|
+
* Coordinator 를 **먼저 열고 마지막에 닫는다**(ADR-0052). 그 사이에 들어가는 것이 기존 실행
|
|
98
|
+
* 전부라, 성공·실패·예외 어느 경로로 빠져나가도 `finish` 가 불려야 한다. 안 불리면 SQLite
|
|
99
|
+
* 파일 핸들이 남고 Record 세션이 `running` 인 채로 남아 다음 실행이 이어 쓸 수 없다.
|
|
100
|
+
*
|
|
101
|
+
* 세션 옵션이 없으면 배선을 아예 만들지 않는다 — 기존 실행 경로가 한 글자도 달라지지 않는다.
|
|
102
|
+
*/
|
|
65
103
|
declare function runCli(argv: readonly string[], dependencies: TestCommandDependencies): Promise<number>;
|
|
66
104
|
//#endregion
|
|
67
|
-
export { type CliErrorCode, type CliFailure, type GenerateCommandDependencies, type GenerateCommandInput, type RepairCommandDependencies, type RepairCommandInput, type
|
|
105
|
+
export { type CliErrorCode, type CliFailure, type GenerateCommandDependencies, type GenerateCommandInput, type RepairCommandDependencies, type RepairCommandInput, type ReviewIO, type TestCommandDependencies, type TestCommandInput, nodeGenerateDependencies, nodeReviewIO, parseRepairCommand, parseTestCommand, runCli, runGenerateCommand, runRepairCommand };
|
package/dist/commands.d.mts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as GenerateCommandDependencies, c as nodeGenerateDependencies, d as ConnectTarget, i as runRepairCommand, l as nodeReviewIO, n as RepairCommandInput, o as GenerateCommandInput, r as parseRepairCommand, s as ReviewIO, t as RepairCommandDependencies, u as runGenerateCommand } from "./repair-command-DQh2h4tQ.mjs";
|
|
2
2
|
import { CheckDeterminismOptions, DeterminismResult, FinalizeRunnerExecutionOptions, InputContractOptions, RunSuiteOptions, RunnerExecution, RunnerReport, SpecFindingsResult, SuiteValidationIssue, SuiteValidationResult, TestSuiteSpec } from "@mcpeak/runner";
|
|
3
|
-
import { McpStdioConnection } from "@mcpeak/core";
|
|
3
|
+
import { McpHttpConnection, McpStdioConnection } from "@mcpeak/core";
|
|
4
|
+
import "@mcpeak/record/external";
|
|
4
5
|
//#region src/test-command.d.ts
|
|
5
6
|
interface TestCommandInput {
|
|
6
7
|
readonly suitePath: string;
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 붙을 대상. `--command`/`--arg` 면 stdio, `--url` 이면 Streamable HTTP 다(#137).
|
|
10
|
+
*
|
|
11
|
+
* `command` · `args` 두 필드를 이 하나로 바꿨다. 두 transport 를 나란히 두면 "url 이
|
|
12
|
+
* 있으면 command 는 무시" 같은 규칙이 읽는 자리마다 되풀이되고, 그 규칙이 한 군데서만
|
|
13
|
+
* 어긋나도 사용자가 지정하지 않은 곳에 붙는다.
|
|
14
|
+
*/
|
|
15
|
+
readonly target: ConnectTarget;
|
|
9
16
|
readonly json: boolean;
|
|
10
17
|
/** `--junit` 로 받은 XML 출력 경로. 지정하지 않으면 undefined 이고 XML 을 만들지 않는다. */
|
|
11
18
|
readonly junitPath: string | undefined;
|
|
@@ -15,9 +22,13 @@ interface TestCommandInput {
|
|
|
15
22
|
readonly determinism: boolean;
|
|
16
23
|
/** `--reset-cmd` 로 받은 초기화 명령. 각 회차 전에 1번씩 실행한다. */
|
|
17
24
|
readonly resetCmd: string | undefined;
|
|
25
|
+
/** `--session`. External 세션을 재생한다. */
|
|
26
|
+
readonly sessionPath: string | undefined;
|
|
27
|
+
/** `--record-session`. External 세션을 녹화한다. */
|
|
28
|
+
readonly recordSessionPath: string | undefined;
|
|
18
29
|
readonly stderrLines: number;
|
|
19
30
|
}
|
|
20
|
-
type CliErrorCode = "CLI_USAGE" | "COMMAND_NOT_IMPLEMENTED" | "SUITE_FORMAT_UNSUPPORTED" | "SUITE_READ_FAILED" | "SUITE_ENCODING_INVALID" | "SUITE_JSON_INVALID" | "SUITE_VALIDATION_FAILED" | "MCP_CONNECTION_FAILED" | "RUNNER_EXECUTION_FAILED" | "RUNNER_FINALIZATION_FAILED" | "JUNIT_WRITE_FAILED" | "REPAIR_BUNDLE_WRITE_FAILED" | "RESET_COMMAND_FAILED" | "CLI_INTERNAL_ERROR";
|
|
31
|
+
type CliErrorCode = "CLI_USAGE" | "COMMAND_NOT_IMPLEMENTED" | "SUITE_FORMAT_UNSUPPORTED" | "SUITE_READ_FAILED" | "SUITE_ENCODING_INVALID" | "SUITE_JSON_INVALID" | "SUITE_VALIDATION_FAILED" | "MCP_CONNECTION_FAILED" | "RUNNER_EXECUTION_FAILED" | "RUNNER_FINALIZATION_FAILED" | "RUNNER_PAYLOAD_LIMIT_EXCEEDED" | "JUNIT_WRITE_FAILED" | "REPAIR_BUNDLE_WRITE_FAILED" | "RESET_COMMAND_FAILED" | "EXTERNAL_SESSION_FAILED" | "CLI_INTERNAL_ERROR";
|
|
21
32
|
interface CliFailure {
|
|
22
33
|
readonly code: CliErrorCode;
|
|
23
34
|
readonly message: string;
|
|
@@ -31,7 +42,25 @@ interface TestCommandDependencies {
|
|
|
31
42
|
connect(options: {
|
|
32
43
|
command: string;
|
|
33
44
|
args: readonly string[];
|
|
45
|
+
/** External 배선이 만든 자식 환경 변수. Bootstrap 주입이 여기 실린다. */
|
|
46
|
+
env?: Readonly<Record<string, string>>;
|
|
34
47
|
}): Promise<McpStdioConnection>;
|
|
48
|
+
/**
|
|
49
|
+
* 원격(Streamable HTTP) 대상용 연결. `core.connectHttp` 다.
|
|
50
|
+
*
|
|
51
|
+
* **선택 사항으로 둔다.** 위 `checkInputContract` 와 같은 이유다 — 진입점의 "런타임 의존성
|
|
52
|
+
* 없음" 경로와 대시보드처럼 stdio 만 배선한 호출자가 이 필드를 채울 수 없다. 없으면
|
|
53
|
+
* `--url` 이 조용히 stdio 로 떨어지지 않고 무엇이 없는지 말하고 멈춘다(#137).
|
|
54
|
+
*/
|
|
55
|
+
connectHttp?(options: {
|
|
56
|
+
url: string;
|
|
57
|
+
headers?: Readonly<Record<string, string>>;
|
|
58
|
+
}): Promise<McpHttpConnection>;
|
|
59
|
+
/**
|
|
60
|
+
* `--header-env` 가 가리키는 환경변수를 읽는다. 이것이 없으면 `--header-env` 를 쓸 수 없다.
|
|
61
|
+
* CLI 코드가 `process` 를 직접 읽지 않기 위한 주입점이다(ADR-0013).
|
|
62
|
+
*/
|
|
63
|
+
readEnv?(name: string): string | undefined;
|
|
35
64
|
startRunner(options: RunSuiteOptions): RunnerExecution;
|
|
36
65
|
finalize(options: FinalizeRunnerExecutionOptions): Promise<RunnerReport>;
|
|
37
66
|
renderReport(report: RunnerReport, options?: {
|
|
@@ -62,6 +91,15 @@ interface TestCommandDependencies {
|
|
|
62
91
|
writeStderr(text: string): void;
|
|
63
92
|
}
|
|
64
93
|
declare function parseTestCommand(argv: readonly string[]): TestCommandInput;
|
|
94
|
+
/**
|
|
95
|
+
* `test` 실행에 External Record/Replay 수명주기를 씌운다.
|
|
96
|
+
*
|
|
97
|
+
* Coordinator 를 **먼저 열고 마지막에 닫는다**(ADR-0052). 그 사이에 들어가는 것이 기존 실행
|
|
98
|
+
* 전부라, 성공·실패·예외 어느 경로로 빠져나가도 `finish` 가 불려야 한다. 안 불리면 SQLite
|
|
99
|
+
* 파일 핸들이 남고 Record 세션이 `running` 인 채로 남아 다음 실행이 이어 쓸 수 없다.
|
|
100
|
+
*
|
|
101
|
+
* 세션 옵션이 없으면 배선을 아예 만들지 않는다 — 기존 실행 경로가 한 글자도 달라지지 않는다.
|
|
102
|
+
*/
|
|
65
103
|
declare function runCli(argv: readonly string[], dependencies: TestCommandDependencies): Promise<number>;
|
|
66
104
|
//#endregion
|
|
67
|
-
export { type CliErrorCode, type CliFailure, type GenerateCommandDependencies, type GenerateCommandInput, type RepairCommandDependencies, type RepairCommandInput, type
|
|
105
|
+
export { type CliErrorCode, type CliFailure, type GenerateCommandDependencies, type GenerateCommandInput, type RepairCommandDependencies, type RepairCommandInput, type ReviewIO, type TestCommandDependencies, type TestCommandInput, nodeGenerateDependencies, nodeReviewIO, parseRepairCommand, parseTestCommand, runCli, runGenerateCommand, runRepairCommand };
|
package/dist/commands.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { nodeGenerateDependencies, nodeReviewIO, parseRepairCommand,
|
|
1
|
+
import { a as nodeGenerateDependencies, i as runRepairCommand, n as runCli, o as nodeReviewIO, r as parseRepairCommand, s as runGenerateCommand, t as parseTestCommand } from "./test-command-DEp9hrfH.mjs";
|
|
2
|
+
export { nodeGenerateDependencies, nodeReviewIO, parseRepairCommand, parseTestCommand, runCli, runGenerateCommand, runRepairCommand };
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,220 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const require_test_command = require("./test-command-B_faNSfA.cjs");
|
|
3
|
+
let node_fs_promises = require("node:fs/promises");
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
const COMMANDS = [
|
|
6
|
+
"test",
|
|
7
|
+
"generate",
|
|
8
|
+
"repair",
|
|
9
|
+
"record",
|
|
10
|
+
"mock"
|
|
11
|
+
];
|
|
12
|
+
const unavailableDependencies = {
|
|
13
|
+
readFile: async () => {
|
|
14
|
+
throw new Error("runtime dependencies unavailable");
|
|
15
|
+
},
|
|
16
|
+
validateSuite: () => {
|
|
17
|
+
throw new Error("runtime dependencies unavailable");
|
|
18
|
+
},
|
|
19
|
+
connect: async () => {
|
|
20
|
+
throw new Error("runtime dependencies unavailable");
|
|
21
|
+
},
|
|
22
|
+
startRunner: () => {
|
|
23
|
+
throw new Error("runtime dependencies unavailable");
|
|
24
|
+
},
|
|
25
|
+
finalize: async () => {
|
|
26
|
+
throw new Error("runtime dependencies unavailable");
|
|
27
|
+
},
|
|
28
|
+
renderReport: () => {
|
|
29
|
+
throw new Error("runtime dependencies unavailable");
|
|
30
|
+
},
|
|
31
|
+
renderJUnit: () => {
|
|
32
|
+
throw new Error("runtime dependencies unavailable");
|
|
33
|
+
},
|
|
34
|
+
writeFile: async () => {
|
|
35
|
+
throw new Error("runtime dependencies unavailable");
|
|
36
|
+
},
|
|
37
|
+
colorEnabled: false,
|
|
38
|
+
writeStdout: (text) => process.stdout.write(text),
|
|
39
|
+
writeStderr: (text) => process.stderr.write(text)
|
|
40
|
+
};
|
|
41
|
+
const unavailableRuntimeDependencies = {
|
|
42
|
+
...unavailableDependencies,
|
|
43
|
+
readFile: node_fs_promises.readFile
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* `repair` 실행 의존성. 함수로 빼 둔 이유는 **주입 자체를 테스트가 단언할 수 있게** 하기
|
|
47
|
+
* 위해서다. 분기 안에 리터럴로 두면 `reviewIO` 를 빠뜨려도 아무 테스트도 안 깨진다.
|
|
48
|
+
* 실제로 그렇게 한 번 빠뜨렸다(T10 보고서).
|
|
49
|
+
*
|
|
50
|
+
* 대화형 판정은 `generate` 와 같은 기준이다. 같은 `nodeReviewIO()` 를 쓰고, 그 구현이
|
|
51
|
+
* stdin·stdout 이 둘 다 TTY 일 때만 `interactive` 를 참으로 만든다.
|
|
52
|
+
*/
|
|
53
|
+
function nodeRepairDependencies(generate) {
|
|
54
|
+
return {
|
|
55
|
+
readFile: (path) => (0, node_fs_promises.readFile)(path, "utf8"),
|
|
56
|
+
writeStdout: (text) => void process.stdout.write(text),
|
|
57
|
+
writeStderr: (text) => void process.stderr.write(text),
|
|
58
|
+
reviewIO: require_test_command.nodeReviewIO(),
|
|
59
|
+
diagnosis: {
|
|
60
|
+
prepare: generate.prepareDiagnosisRequest,
|
|
61
|
+
dispatch: generate.dispatchDiagnosisRequest,
|
|
62
|
+
providers: {
|
|
63
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
64
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** `mcpeak help <이름>` 으로 볼 수 있는 명령. 위 두 갈래가 같은 목록을 봐야 한다. */
|
|
70
|
+
const HELP_TOPICS = [
|
|
71
|
+
"test",
|
|
72
|
+
"generate",
|
|
73
|
+
"repair"
|
|
74
|
+
];
|
|
75
|
+
const isHelpTopic = (value) => HELP_TOPICS.includes(value);
|
|
76
|
+
async function run(argv) {
|
|
77
|
+
if (argv.length === 0 || argv.length === 1 && [
|
|
78
|
+
"--help",
|
|
79
|
+
"-h",
|
|
80
|
+
"help"
|
|
81
|
+
].includes(argv[0] ?? "")) {
|
|
82
|
+
process.stdout.write(require_test_command.GLOBAL_HELP);
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
if (argv.length === 2) {
|
|
86
|
+
const command = argv[0] === "help" ? argv[1] : argv[1] === "--help" ? argv[0] : void 0;
|
|
87
|
+
if (command === "replay" || command === "verify") return require_test_command.runCli([command], unavailableDependencies);
|
|
88
|
+
if (isHelpTopic(command)) {
|
|
89
|
+
process.stdout.write(require_test_command.commandHelp(command));
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `help` 로 시작했는데 위에서 안 걸렸다면 **뒤에 붙은 인자가 틀린 것**이다.
|
|
95
|
+
*
|
|
96
|
+
* 여기서 안 막고 아래로 흘려보내면 `runCli` 가 `argv[0]` 인 `help` 를 명령으로 읽어
|
|
97
|
+
* "알 수 없는 CLI 명령 'help'입니다" 라고 한다. 사용자가 정확히 친 토큰을 틀렸다고
|
|
98
|
+
* 지목하는 셈이라, 무엇을 고쳐야 하는지가 화면에서 사라진다.
|
|
99
|
+
*/
|
|
100
|
+
if (argv[0] === "help") {
|
|
101
|
+
const target = argv[1] ?? "";
|
|
102
|
+
const extra = argv[2];
|
|
103
|
+
const message = isHelpTopic(target) && extra !== void 0 ? `\`help ${require_test_command.escapeTerminalText(target)}\` 뒤의 '${require_test_command.escapeTerminalText(extra)}' 는 받지 않습니다.` : `도움말이 없는 명령 '${require_test_command.escapeTerminalText(target)}'입니다.`;
|
|
104
|
+
process.stderr.write(`오류 [CLI_USAGE]: ${message}\n해결: ${require_test_command.commandDiscovery}\n`);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
if (argv.length === 1 && argv[0] === "--version") {
|
|
108
|
+
process.stdout.write(`mcpeak ${require_test_command.version}\n`);
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
if (argv[0] === "generate") {
|
|
112
|
+
let core;
|
|
113
|
+
let runner;
|
|
114
|
+
let generate;
|
|
115
|
+
try {
|
|
116
|
+
[core, runner, generate] = await Promise.all([
|
|
117
|
+
import("@mcpeak/core"),
|
|
118
|
+
import("@mcpeak/runner"),
|
|
119
|
+
import("@mcpeak/generate")
|
|
120
|
+
]);
|
|
121
|
+
} catch {
|
|
122
|
+
return require_test_command.runGenerateCommand(argv, {
|
|
123
|
+
...require_test_command.nodeGenerateDependencies(),
|
|
124
|
+
connect: unavailableDependencies.connect,
|
|
125
|
+
createBaselineSuite: unavailableDependencies.validateSuite,
|
|
126
|
+
createAuthoringSession: unavailableDependencies.validateSuite,
|
|
127
|
+
finalizeAuthoringDraft: unavailableDependencies.validateSuite,
|
|
128
|
+
getAuthoringExecutionSuite: unavailableDependencies.validateSuite,
|
|
129
|
+
validateSuite: unavailableDependencies.validateSuite
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return require_test_command.runGenerateCommand(argv, {
|
|
133
|
+
...require_test_command.nodeGenerateDependencies(),
|
|
134
|
+
connect: core.connectStdio,
|
|
135
|
+
connectHttp: core.connectHttp,
|
|
136
|
+
readEnv: (name) => process.env[name],
|
|
137
|
+
createBaselineSuite: generate.createBaselineSuite,
|
|
138
|
+
createAuthoringSession: generate.createAuthoringSession,
|
|
139
|
+
finalizeAuthoringDraft: generate.finalizeAuthoringDraft,
|
|
140
|
+
getAuthoringExecutionSuite: generate.getAuthoringExecutionSuite,
|
|
141
|
+
validateSuite: runner.validateMcpSuite,
|
|
142
|
+
reviewIO: require_test_command.nodeReviewIO(),
|
|
143
|
+
providers: {
|
|
144
|
+
codex: (model) => generate.createCodexAuthoringProvider({ model }),
|
|
145
|
+
claude: (model) => generate.createClaudeAuthoringProvider({ model })
|
|
146
|
+
},
|
|
147
|
+
prepareAuthoringRequest: generate.prepareAuthoringRequest,
|
|
148
|
+
dispatchAuthoringRequest: generate.dispatchAuthoringRequest,
|
|
149
|
+
createAuthoringDiff: generate.createAuthoringDiff,
|
|
150
|
+
applyAuthoringChanges: generate.applyAuthoringChanges,
|
|
151
|
+
reviewLocalAuthoringCandidate: generate.reviewLocalAuthoringCandidate,
|
|
152
|
+
computeCoverage: generate.computeCoverage,
|
|
153
|
+
preparePreFillRequest: generate.preparePreFillRequest,
|
|
154
|
+
previewPreFillRequest: generate.previewPreFillRequest,
|
|
155
|
+
dispatchPreFillRequest: generate.dispatchPreFillRequest,
|
|
156
|
+
preFillProviders: {
|
|
157
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
158
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
159
|
+
},
|
|
160
|
+
prepareRejectionDiagnosisRequests: generate.prepareRejectionDiagnosisRequests,
|
|
161
|
+
dispatchRejectionDiagnosis: generate.dispatchRejectionDiagnosis,
|
|
162
|
+
rejectionProviders: {
|
|
163
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
164
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
165
|
+
},
|
|
166
|
+
GenerateTestsError: generate.GenerateTestsError
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (argv[0] === "repair") {
|
|
170
|
+
/**
|
|
171
|
+
* `generate` 분기와 같은 모양으로 동적 import 한다. `test` 경로는 이 분기를 지나지 않으므로
|
|
172
|
+
* 여전히 `core` 와 `runner` 만 로드한다. 계획서 §8 위험표 첫 줄.
|
|
173
|
+
*/
|
|
174
|
+
let generate;
|
|
175
|
+
try {
|
|
176
|
+
generate = await import("@mcpeak/generate");
|
|
177
|
+
} catch {
|
|
178
|
+
process.stderr.write("오류 [REPAIR_RUNTIME_UNAVAILABLE]: 진단에 필요한 @mcpeak/generate 를 로드하지 못했습니다.\n해결: 의존성을 설치한 뒤 다시 실행하세요.\n");
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
const dependencies = nodeRepairDependencies(generate);
|
|
182
|
+
try {
|
|
183
|
+
return await require_test_command.runRepairCommand(argv, dependencies);
|
|
184
|
+
} finally {
|
|
185
|
+
dependencies.reviewIO?.close?.();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (argv[0] !== "test") return require_test_command.runCli(argv, unavailableDependencies);
|
|
189
|
+
try {
|
|
190
|
+
if (!require_test_command.parseTestCommand(argv.slice(1)).suitePath.toLowerCase().endsWith(".json")) return require_test_command.runCli(argv, unavailableDependencies);
|
|
191
|
+
} catch {
|
|
192
|
+
return require_test_command.runCli(argv, unavailableDependencies);
|
|
193
|
+
}
|
|
194
|
+
let core;
|
|
195
|
+
let runner;
|
|
196
|
+
try {
|
|
197
|
+
[core, runner] = await Promise.all([import("@mcpeak/core"), import("@mcpeak/runner")]);
|
|
198
|
+
} catch {
|
|
199
|
+
return require_test_command.runCli(argv, unavailableRuntimeDependencies);
|
|
200
|
+
}
|
|
201
|
+
return require_test_command.runCli(argv, {
|
|
202
|
+
readFile: node_fs_promises.readFile,
|
|
203
|
+
validateSuite: runner.validateMcpSuite,
|
|
204
|
+
connect: core.connectStdio,
|
|
205
|
+
connectHttp: core.connectHttp,
|
|
206
|
+
readEnv: (name) => process.env[name],
|
|
207
|
+
startRunner: runner.runSuite,
|
|
208
|
+
finalize: runner.finalizeRunnerExecution,
|
|
209
|
+
renderReport: runner.renderReport,
|
|
210
|
+
renderJUnit: runner.renderJUnit,
|
|
211
|
+
writeFile: (path, text) => (0, node_fs_promises.writeFile)(path, text, "utf8"),
|
|
212
|
+
colorEnabled: process.stdout.isTTY === true && process.env.NO_COLOR === void 0,
|
|
213
|
+
writeStdout: (text) => process.stdout.write(text),
|
|
214
|
+
writeStderr: (text) => process.stderr.write(text)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
exports.COMMANDS = COMMANDS;
|
|
219
|
+
exports.nodeRepairDependencies = nodeRepairDependencies;
|
|
220
|
+
exports.run = run;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as RepairCommandDependencies } from "./repair-command-D3zL3G5W.cjs";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
type Command = (argv: string[]) => Promise<number>;
|
|
4
|
-
declare const COMMANDS: readonly ["test", "generate", "repair", "record", "
|
|
4
|
+
declare const COMMANDS: readonly ["test", "generate", "repair", "record", "mock"];
|
|
5
5
|
/**
|
|
6
6
|
* `repair` 실행 의존성. 함수로 빼 둔 이유는 **주입 자체를 테스트가 단언할 수 있게** 하기
|
|
7
7
|
* 위해서다. 분기 안에 리터럴로 두면 `reviewIO` 를 빠뜨려도 아무 테스트도 안 깨진다.
|
|
@@ -11,17 +11,6 @@ declare const COMMANDS: readonly ["test", "generate", "repair", "record", "repla
|
|
|
11
11
|
* stdin·stdout 이 둘 다 TTY 일 때만 `interactive` 를 참으로 만든다.
|
|
12
12
|
*/
|
|
13
13
|
declare function nodeRepairDependencies(generate: typeof import("@mcpeak/generate")): RepairCommandDependencies;
|
|
14
|
-
/**
|
|
15
|
-
* replay 는 `connect` 를 쓰지 않는다. 대신 카세트 로더가 필요하다. 런타임 의존성을 못 불러도
|
|
16
|
-
* 사용 오류는 정상적으로 내야 하므로, 실제로 쓰이기 전에 끝나는 경로를 위해 자리만 채운다.
|
|
17
|
-
*
|
|
18
|
-
* **테스트가 배선을 직접 단언할 수 있도록 내보낸다.** 여기서 평범한 Error 로 되돌아가면
|
|
19
|
-
* 사용자는 "이슈를 보고하세요" 를 보게 되는데, 모듈 모킹으로는 이 경로를 재현할 수 없다 —
|
|
20
|
-
* `record` 와 `runner` 가 `generate-command` 를 통해 정적 import 체인에 있어서, 그것을
|
|
21
|
-
* 모킹하면 `index.ts` 로드 자체가 깨진다. `nodeRepairDependencies` 를 함수로 뺀 것과 같은
|
|
22
|
-
* 이유다(주입을 빠뜨려도 아무 테스트가 안 깨지는 상황을 막는다).
|
|
23
|
-
*/
|
|
24
|
-
declare const unavailableReplayDependencies: ReplayCommandDependencies;
|
|
25
14
|
declare function run(argv: string[]): Promise<number>;
|
|
26
15
|
//#endregion
|
|
27
|
-
export { COMMANDS, Command, nodeRepairDependencies, run
|
|
16
|
+
export { COMMANDS, Command, nodeRepairDependencies, run };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as RepairCommandDependencies } from "./repair-command-DQh2h4tQ.mjs";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
type Command = (argv: string[]) => Promise<number>;
|
|
4
|
-
declare const COMMANDS: readonly ["test", "generate", "repair", "record", "
|
|
4
|
+
declare const COMMANDS: readonly ["test", "generate", "repair", "record", "mock"];
|
|
5
5
|
/**
|
|
6
6
|
* `repair` 실행 의존성. 함수로 빼 둔 이유는 **주입 자체를 테스트가 단언할 수 있게** 하기
|
|
7
7
|
* 위해서다. 분기 안에 리터럴로 두면 `reviewIO` 를 빠뜨려도 아무 테스트도 안 깨진다.
|
|
@@ -11,17 +11,6 @@ declare const COMMANDS: readonly ["test", "generate", "repair", "record", "repla
|
|
|
11
11
|
* stdin·stdout 이 둘 다 TTY 일 때만 `interactive` 를 참으로 만든다.
|
|
12
12
|
*/
|
|
13
13
|
declare function nodeRepairDependencies(generate: typeof import("@mcpeak/generate")): RepairCommandDependencies;
|
|
14
|
-
/**
|
|
15
|
-
* replay 는 `connect` 를 쓰지 않는다. 대신 카세트 로더가 필요하다. 런타임 의존성을 못 불러도
|
|
16
|
-
* 사용 오류는 정상적으로 내야 하므로, 실제로 쓰이기 전에 끝나는 경로를 위해 자리만 채운다.
|
|
17
|
-
*
|
|
18
|
-
* **테스트가 배선을 직접 단언할 수 있도록 내보낸다.** 여기서 평범한 Error 로 되돌아가면
|
|
19
|
-
* 사용자는 "이슈를 보고하세요" 를 보게 되는데, 모듈 모킹으로는 이 경로를 재현할 수 없다 —
|
|
20
|
-
* `record` 와 `runner` 가 `generate-command` 를 통해 정적 import 체인에 있어서, 그것을
|
|
21
|
-
* 모킹하면 `index.ts` 로드 자체가 깨진다. `nodeRepairDependencies` 를 함수로 뺀 것과 같은
|
|
22
|
-
* 이유다(주입을 빠뜨려도 아무 테스트가 안 깨지는 상황을 막는다).
|
|
23
|
-
*/
|
|
24
|
-
declare const unavailableReplayDependencies: ReplayCommandDependencies;
|
|
25
14
|
declare function run(argv: string[]): Promise<number>;
|
|
26
15
|
//#endregion
|
|
27
|
-
export { COMMANDS, Command, nodeRepairDependencies, run
|
|
16
|
+
export { COMMANDS, Command, nodeRepairDependencies, run };
|