@mcpeak/cli 0.10.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 +5 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/commands.cjs +1 -3
- package/dist/commands.d.cts +28 -6
- package/dist/commands.d.mts +28 -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-BbjJIX7j.cjs → test-command-B_faNSfA.cjs} +1385 -924
- package/dist/{test-command-CtQbeyhy.mjs → test-command-DEp9hrfH.mjs} +1380 -907
- package/package.json +6 -5
- package/dist/src-BzVS8KXQ.mjs +0 -459
- package/dist/src-CwhDFSWk.cjs +0 -482
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,217 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
|
|
1
|
+
import { a as nodeGenerateDependencies, c as GLOBAL_HELP, d as escapeTerminalText, f as version, i as runRepairCommand, l as commandDiscovery, n as runCli, o as nodeReviewIO, s as runGenerateCommand, t as parseTestCommand, u as commandHelp } from "./test-command-DEp9hrfH.mjs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
const COMMANDS = [
|
|
5
|
+
"test",
|
|
6
|
+
"generate",
|
|
7
|
+
"repair",
|
|
8
|
+
"record",
|
|
9
|
+
"mock"
|
|
10
|
+
];
|
|
11
|
+
const unavailableDependencies = {
|
|
12
|
+
readFile: async () => {
|
|
13
|
+
throw new Error("runtime dependencies unavailable");
|
|
14
|
+
},
|
|
15
|
+
validateSuite: () => {
|
|
16
|
+
throw new Error("runtime dependencies unavailable");
|
|
17
|
+
},
|
|
18
|
+
connect: async () => {
|
|
19
|
+
throw new Error("runtime dependencies unavailable");
|
|
20
|
+
},
|
|
21
|
+
startRunner: () => {
|
|
22
|
+
throw new Error("runtime dependencies unavailable");
|
|
23
|
+
},
|
|
24
|
+
finalize: async () => {
|
|
25
|
+
throw new Error("runtime dependencies unavailable");
|
|
26
|
+
},
|
|
27
|
+
renderReport: () => {
|
|
28
|
+
throw new Error("runtime dependencies unavailable");
|
|
29
|
+
},
|
|
30
|
+
renderJUnit: () => {
|
|
31
|
+
throw new Error("runtime dependencies unavailable");
|
|
32
|
+
},
|
|
33
|
+
writeFile: async () => {
|
|
34
|
+
throw new Error("runtime dependencies unavailable");
|
|
35
|
+
},
|
|
36
|
+
colorEnabled: false,
|
|
37
|
+
writeStdout: (text) => process.stdout.write(text),
|
|
38
|
+
writeStderr: (text) => process.stderr.write(text)
|
|
39
|
+
};
|
|
40
|
+
const unavailableRuntimeDependencies = {
|
|
41
|
+
...unavailableDependencies,
|
|
42
|
+
readFile
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* `repair` 실행 의존성. 함수로 빼 둔 이유는 **주입 자체를 테스트가 단언할 수 있게** 하기
|
|
46
|
+
* 위해서다. 분기 안에 리터럴로 두면 `reviewIO` 를 빠뜨려도 아무 테스트도 안 깨진다.
|
|
47
|
+
* 실제로 그렇게 한 번 빠뜨렸다(T10 보고서).
|
|
48
|
+
*
|
|
49
|
+
* 대화형 판정은 `generate` 와 같은 기준이다. 같은 `nodeReviewIO()` 를 쓰고, 그 구현이
|
|
50
|
+
* stdin·stdout 이 둘 다 TTY 일 때만 `interactive` 를 참으로 만든다.
|
|
51
|
+
*/
|
|
52
|
+
function nodeRepairDependencies(generate) {
|
|
53
|
+
return {
|
|
54
|
+
readFile: (path) => readFile(path, "utf8"),
|
|
55
|
+
writeStdout: (text) => void process.stdout.write(text),
|
|
56
|
+
writeStderr: (text) => void process.stderr.write(text),
|
|
57
|
+
reviewIO: nodeReviewIO(),
|
|
58
|
+
diagnosis: {
|
|
59
|
+
prepare: generate.prepareDiagnosisRequest,
|
|
60
|
+
dispatch: generate.dispatchDiagnosisRequest,
|
|
61
|
+
providers: {
|
|
62
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
63
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** `mcpeak help <이름>` 으로 볼 수 있는 명령. 위 두 갈래가 같은 목록을 봐야 한다. */
|
|
69
|
+
const HELP_TOPICS = [
|
|
70
|
+
"test",
|
|
71
|
+
"generate",
|
|
72
|
+
"repair"
|
|
73
|
+
];
|
|
74
|
+
const isHelpTopic = (value) => HELP_TOPICS.includes(value);
|
|
75
|
+
async function run(argv) {
|
|
76
|
+
if (argv.length === 0 || argv.length === 1 && [
|
|
77
|
+
"--help",
|
|
78
|
+
"-h",
|
|
79
|
+
"help"
|
|
80
|
+
].includes(argv[0] ?? "")) {
|
|
81
|
+
process.stdout.write(GLOBAL_HELP);
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
if (argv.length === 2) {
|
|
85
|
+
const command = argv[0] === "help" ? argv[1] : argv[1] === "--help" ? argv[0] : void 0;
|
|
86
|
+
if (command === "replay" || command === "verify") return runCli([command], unavailableDependencies);
|
|
87
|
+
if (isHelpTopic(command)) {
|
|
88
|
+
process.stdout.write(commandHelp(command));
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `help` 로 시작했는데 위에서 안 걸렸다면 **뒤에 붙은 인자가 틀린 것**이다.
|
|
94
|
+
*
|
|
95
|
+
* 여기서 안 막고 아래로 흘려보내면 `runCli` 가 `argv[0]` 인 `help` 를 명령으로 읽어
|
|
96
|
+
* "알 수 없는 CLI 명령 'help'입니다" 라고 한다. 사용자가 정확히 친 토큰을 틀렸다고
|
|
97
|
+
* 지목하는 셈이라, 무엇을 고쳐야 하는지가 화면에서 사라진다.
|
|
98
|
+
*/
|
|
99
|
+
if (argv[0] === "help") {
|
|
100
|
+
const target = argv[1] ?? "";
|
|
101
|
+
const extra = argv[2];
|
|
102
|
+
const message = isHelpTopic(target) && extra !== void 0 ? `\`help ${escapeTerminalText(target)}\` 뒤의 '${escapeTerminalText(extra)}' 는 받지 않습니다.` : `도움말이 없는 명령 '${escapeTerminalText(target)}'입니다.`;
|
|
103
|
+
process.stderr.write(`오류 [CLI_USAGE]: ${message}\n해결: ${commandDiscovery}\n`);
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
if (argv.length === 1 && argv[0] === "--version") {
|
|
107
|
+
process.stdout.write(`mcpeak ${version}\n`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
if (argv[0] === "generate") {
|
|
111
|
+
let core;
|
|
112
|
+
let runner;
|
|
113
|
+
let generate;
|
|
114
|
+
try {
|
|
115
|
+
[core, runner, generate] = await Promise.all([
|
|
116
|
+
import("@mcpeak/core"),
|
|
117
|
+
import("@mcpeak/runner"),
|
|
118
|
+
import("@mcpeak/generate")
|
|
119
|
+
]);
|
|
120
|
+
} catch {
|
|
121
|
+
return runGenerateCommand(argv, {
|
|
122
|
+
...nodeGenerateDependencies(),
|
|
123
|
+
connect: unavailableDependencies.connect,
|
|
124
|
+
createBaselineSuite: unavailableDependencies.validateSuite,
|
|
125
|
+
createAuthoringSession: unavailableDependencies.validateSuite,
|
|
126
|
+
finalizeAuthoringDraft: unavailableDependencies.validateSuite,
|
|
127
|
+
getAuthoringExecutionSuite: unavailableDependencies.validateSuite,
|
|
128
|
+
validateSuite: unavailableDependencies.validateSuite
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return runGenerateCommand(argv, {
|
|
132
|
+
...nodeGenerateDependencies(),
|
|
133
|
+
connect: core.connectStdio,
|
|
134
|
+
connectHttp: core.connectHttp,
|
|
135
|
+
readEnv: (name) => process.env[name],
|
|
136
|
+
createBaselineSuite: generate.createBaselineSuite,
|
|
137
|
+
createAuthoringSession: generate.createAuthoringSession,
|
|
138
|
+
finalizeAuthoringDraft: generate.finalizeAuthoringDraft,
|
|
139
|
+
getAuthoringExecutionSuite: generate.getAuthoringExecutionSuite,
|
|
140
|
+
validateSuite: runner.validateMcpSuite,
|
|
141
|
+
reviewIO: nodeReviewIO(),
|
|
142
|
+
providers: {
|
|
143
|
+
codex: (model) => generate.createCodexAuthoringProvider({ model }),
|
|
144
|
+
claude: (model) => generate.createClaudeAuthoringProvider({ model })
|
|
145
|
+
},
|
|
146
|
+
prepareAuthoringRequest: generate.prepareAuthoringRequest,
|
|
147
|
+
dispatchAuthoringRequest: generate.dispatchAuthoringRequest,
|
|
148
|
+
createAuthoringDiff: generate.createAuthoringDiff,
|
|
149
|
+
applyAuthoringChanges: generate.applyAuthoringChanges,
|
|
150
|
+
reviewLocalAuthoringCandidate: generate.reviewLocalAuthoringCandidate,
|
|
151
|
+
computeCoverage: generate.computeCoverage,
|
|
152
|
+
preparePreFillRequest: generate.preparePreFillRequest,
|
|
153
|
+
previewPreFillRequest: generate.previewPreFillRequest,
|
|
154
|
+
dispatchPreFillRequest: generate.dispatchPreFillRequest,
|
|
155
|
+
preFillProviders: {
|
|
156
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
157
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
158
|
+
},
|
|
159
|
+
prepareRejectionDiagnosisRequests: generate.prepareRejectionDiagnosisRequests,
|
|
160
|
+
dispatchRejectionDiagnosis: generate.dispatchRejectionDiagnosis,
|
|
161
|
+
rejectionProviders: {
|
|
162
|
+
codex: (model) => generate.createCodexProvider({ model }),
|
|
163
|
+
claude: (model) => generate.createClaudeProvider({ model })
|
|
164
|
+
},
|
|
165
|
+
GenerateTestsError: generate.GenerateTestsError
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (argv[0] === "repair") {
|
|
169
|
+
/**
|
|
170
|
+
* `generate` 분기와 같은 모양으로 동적 import 한다. `test` 경로는 이 분기를 지나지 않으므로
|
|
171
|
+
* 여전히 `core` 와 `runner` 만 로드한다. 계획서 §8 위험표 첫 줄.
|
|
172
|
+
*/
|
|
173
|
+
let generate;
|
|
174
|
+
try {
|
|
175
|
+
generate = await import("@mcpeak/generate");
|
|
176
|
+
} catch {
|
|
177
|
+
process.stderr.write("오류 [REPAIR_RUNTIME_UNAVAILABLE]: 진단에 필요한 @mcpeak/generate 를 로드하지 못했습니다.\n해결: 의존성을 설치한 뒤 다시 실행하세요.\n");
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
const dependencies = nodeRepairDependencies(generate);
|
|
181
|
+
try {
|
|
182
|
+
return await runRepairCommand(argv, dependencies);
|
|
183
|
+
} finally {
|
|
184
|
+
dependencies.reviewIO?.close?.();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (argv[0] !== "test") return runCli(argv, unavailableDependencies);
|
|
188
|
+
try {
|
|
189
|
+
if (!parseTestCommand(argv.slice(1)).suitePath.toLowerCase().endsWith(".json")) return runCli(argv, unavailableDependencies);
|
|
190
|
+
} catch {
|
|
191
|
+
return runCli(argv, unavailableDependencies);
|
|
192
|
+
}
|
|
193
|
+
let core;
|
|
194
|
+
let runner;
|
|
195
|
+
try {
|
|
196
|
+
[core, runner] = await Promise.all([import("@mcpeak/core"), import("@mcpeak/runner")]);
|
|
197
|
+
} catch {
|
|
198
|
+
return runCli(argv, unavailableRuntimeDependencies);
|
|
199
|
+
}
|
|
200
|
+
return runCli(argv, {
|
|
201
|
+
readFile,
|
|
202
|
+
validateSuite: runner.validateMcpSuite,
|
|
203
|
+
connect: core.connectStdio,
|
|
204
|
+
connectHttp: core.connectHttp,
|
|
205
|
+
readEnv: (name) => process.env[name],
|
|
206
|
+
startRunner: runner.runSuite,
|
|
207
|
+
finalize: runner.finalizeRunnerExecution,
|
|
208
|
+
renderReport: runner.renderReport,
|
|
209
|
+
renderJUnit: runner.renderJUnit,
|
|
210
|
+
writeFile: (path, text) => writeFile(path, text, "utf8"),
|
|
211
|
+
colorEnabled: process.stdout.isTTY === true && process.env.NO_COLOR === void 0,
|
|
212
|
+
writeStdout: (text) => process.stdout.write(text),
|
|
213
|
+
writeStderr: (text) => process.stderr.write(text)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { COMMANDS, nodeRepairDependencies, run };
|
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Cassette } from "@mcpeak/record";
|
|
3
|
-
import { McpStdioConnection, ToolDef } from "@mcpeak/core";
|
|
1
|
+
import { McpHttpConnection, McpStdioConnection, ToolDef } from "@mcpeak/core";
|
|
4
2
|
import { AuthoringExecutionSnapshot, AuthoringSessionView, BaselineGenerationResult, PreFillProvider } from "@mcpeak/generate";
|
|
3
|
+
import { SuiteValidationResult, TestSuiteSpec } from "@mcpeak/runner";
|
|
4
|
+
//#region src/connect-target.d.ts
|
|
5
|
+
/** 파서가 확정한 연결 대상. */
|
|
6
|
+
type ConnectTarget = {
|
|
7
|
+
readonly transport: "stdio";
|
|
8
|
+
readonly command: string;
|
|
9
|
+
readonly args: readonly string[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly transport: "http";
|
|
12
|
+
readonly url: string;
|
|
13
|
+
/**
|
|
14
|
+
* 헤더 이름 → 그 값을 담은 **환경변수 이름**. 값 자체는 여기에도 argv 에도 없다.
|
|
15
|
+
*
|
|
16
|
+
* 평문 `--header 'Authorization: Bearer …'` 를 두지 않은 이유가 이 타입이다. 토큰을
|
|
17
|
+
* argv 에 실으면 `ps` 목록과 셸 히스토리에 그대로 남고, 그건 우리가 만든 노출이다.
|
|
18
|
+
*/
|
|
19
|
+
readonly headerEnv: Readonly<Record<string, string>>;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
5
22
|
//#region src/generate-command.d.ts
|
|
6
23
|
interface GenerateCommandInput {
|
|
7
24
|
readonly suiteId: string;
|
|
8
25
|
readonly name: string;
|
|
9
26
|
readonly outPath: string;
|
|
10
|
-
|
|
11
|
-
|
|
27
|
+
/**
|
|
28
|
+
* 명세를 뽑을 대상. `--command`/`--arg` 면 stdio, `--url` 이면 Streamable HTTP 다(#137).
|
|
29
|
+
* `command` · `args` 두 필드를 이 하나로 바꾼 근거는 `TestCommandInput.target` 과 같다.
|
|
30
|
+
*/
|
|
31
|
+
readonly target: ConnectTarget;
|
|
12
32
|
readonly baselineOnly: boolean;
|
|
13
33
|
readonly provider?: "codex" | "claude";
|
|
14
34
|
readonly model?: string;
|
|
15
35
|
/** 승인 전 시험 실행 여부. 기본은 실행이고 `--no-dry-run` 이 끈다. 설계 문서 §4.3. */
|
|
16
36
|
readonly dryRun: boolean;
|
|
17
|
-
/** `--cassette` 경로. 없으면 서버를 직접 부른다. */
|
|
18
|
-
readonly cassettePath?: string;
|
|
19
|
-
/** `--record`. 카세트 파일이 있어도 새로 녹화한다. */
|
|
20
|
-
readonly forceRecord: boolean;
|
|
21
37
|
/** `--force`. `--out` 에 파일이 있으면 지우고 새로 쓴다. 설계 문서 §5. */
|
|
22
38
|
readonly force: boolean;
|
|
23
39
|
/** `--reset-cmd`. 시험 실행 직전 1회 실행한다. */
|
|
@@ -30,6 +46,16 @@ interface GenerateCommandDependencies {
|
|
|
30
46
|
command: string;
|
|
31
47
|
args: readonly string[];
|
|
32
48
|
}): Promise<McpStdioConnection>;
|
|
49
|
+
/**
|
|
50
|
+
* 원격(Streamable HTTP) 대상용 연결. `core.connectHttp` 다. 선택 사항으로 두는 근거는
|
|
51
|
+
* `TestCommandDependencies.connectHttp` 와 같다(#137).
|
|
52
|
+
*/
|
|
53
|
+
connectHttp?(options: {
|
|
54
|
+
url: string;
|
|
55
|
+
headers?: Readonly<Record<string, string>>;
|
|
56
|
+
}): Promise<McpHttpConnection>;
|
|
57
|
+
/** `--header-env` 가 가리키는 환경변수를 읽는다. CLI 가 `process` 를 직접 읽지 않기 위한 주입점. */
|
|
58
|
+
readEnv?(name: string): string | undefined;
|
|
33
59
|
createBaselineSuite(tools: readonly ToolDef[], options: {
|
|
34
60
|
suiteId: string;
|
|
35
61
|
suiteName: string;
|
|
@@ -100,15 +126,6 @@ interface GenerateCommandDependencies {
|
|
|
100
126
|
* 동일성도 보장된다. 위 6개 필드가 같은 방식이다.
|
|
101
127
|
*/
|
|
102
128
|
GenerateTestsError?: typeof import("@mcpeak/generate").GenerateTestsError;
|
|
103
|
-
/**
|
|
104
|
-
* 카세트 파일 입출력. 주입점을 여기 하나만 두는 이유는 시험 실행 경로에서 파일시스템을
|
|
105
|
-
* 만지는 곳이 이것뿐이기 때문이다. 나머지(`runDryRun`·`reviewDryRun`)는 주입한 client 와
|
|
106
|
-
* io 만 쓰므로 테스트가 실제 구현을 그대로 돌린다.
|
|
107
|
-
*/
|
|
108
|
-
cassetteIo?: {
|
|
109
|
-
load(path: string): Promise<Cassette | null>;
|
|
110
|
-
save(path: string, cassette: Cassette): Promise<void>;
|
|
111
|
-
};
|
|
112
129
|
}
|
|
113
130
|
interface ReviewIO {
|
|
114
131
|
input(message: string): Promise<string>;
|
|
@@ -158,29 +175,4 @@ declare function parseRepairCommand(argv: readonly string[]): RepairCommandInput
|
|
|
158
175
|
*/
|
|
159
176
|
declare function runRepairCommand(argv: readonly string[], deps: RepairCommandDependencies): Promise<number>;
|
|
160
177
|
//#endregion
|
|
161
|
-
|
|
162
|
-
interface ReplayCommandInput {
|
|
163
|
-
readonly suitePath: string;
|
|
164
|
-
readonly cassettePath: string;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* `connect` 가 없다. replay 가 서버를 띄우지 않는다는 것이 주석이 아니라 타입에 드러난다.
|
|
168
|
-
* ADR-0028.
|
|
169
|
-
*/
|
|
170
|
-
interface ReplayCommandDependencies {
|
|
171
|
-
readFile(path: string): Promise<Uint8Array>;
|
|
172
|
-
validateSuite(input: unknown): SuiteValidationResult;
|
|
173
|
-
loadCassette(path: string): Promise<Cassette | null>;
|
|
174
|
-
startRunner(options: RunSuiteOptions): RunnerExecution;
|
|
175
|
-
finalize(options: FinalizeRunnerExecutionOptions): Promise<RunnerReport>;
|
|
176
|
-
renderReport(report: RunnerReport, options?: {
|
|
177
|
-
color?: boolean;
|
|
178
|
-
}): string;
|
|
179
|
-
colorEnabled: boolean;
|
|
180
|
-
writeStdout(text: string): void;
|
|
181
|
-
writeStderr(text: string): void;
|
|
182
|
-
}
|
|
183
|
-
declare function parseReplayCommand(argv: readonly string[]): ReplayCommandInput;
|
|
184
|
-
declare function runReplayCommand(argv: readonly string[], dependencies: ReplayCommandDependencies): Promise<number>;
|
|
185
|
-
//#endregion
|
|
186
|
-
export { RepairCommandDependencies as a, runRepairCommand as c, ReviewIO as d, nodeGenerateDependencies as f, runReplayCommand as i, GenerateCommandDependencies as l, runGenerateCommand as m, ReplayCommandInput as n, RepairCommandInput as o, nodeReviewIO as p, parseReplayCommand as r, parseRepairCommand as s, ReplayCommandDependencies as t, GenerateCommandInput as u };
|
|
178
|
+
export { GenerateCommandDependencies as a, nodeGenerateDependencies as c, ConnectTarget as d, runRepairCommand as i, nodeReviewIO as l, RepairCommandInput as n, GenerateCommandInput as o, parseRepairCommand as r, ReviewIO as s, RepairCommandDependencies as t, runGenerateCommand as u };
|
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SuiteValidationResult, TestSuiteSpec } from "@mcpeak/runner";
|
|
2
|
+
import { McpHttpConnection, McpStdioConnection, ToolDef } from "@mcpeak/core";
|
|
2
3
|
import { AuthoringExecutionSnapshot, AuthoringSessionView, BaselineGenerationResult, PreFillProvider } from "@mcpeak/generate";
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
//#region src/connect-target.d.ts
|
|
5
|
+
/** 파서가 확정한 연결 대상. */
|
|
6
|
+
type ConnectTarget = {
|
|
7
|
+
readonly transport: "stdio";
|
|
8
|
+
readonly command: string;
|
|
9
|
+
readonly args: readonly string[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly transport: "http";
|
|
12
|
+
readonly url: string;
|
|
13
|
+
/**
|
|
14
|
+
* 헤더 이름 → 그 값을 담은 **환경변수 이름**. 값 자체는 여기에도 argv 에도 없다.
|
|
15
|
+
*
|
|
16
|
+
* 평문 `--header 'Authorization: Bearer …'` 를 두지 않은 이유가 이 타입이다. 토큰을
|
|
17
|
+
* argv 에 실으면 `ps` 목록과 셸 히스토리에 그대로 남고, 그건 우리가 만든 노출이다.
|
|
18
|
+
*/
|
|
19
|
+
readonly headerEnv: Readonly<Record<string, string>>;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
5
22
|
//#region src/generate-command.d.ts
|
|
6
23
|
interface GenerateCommandInput {
|
|
7
24
|
readonly suiteId: string;
|
|
8
25
|
readonly name: string;
|
|
9
26
|
readonly outPath: string;
|
|
10
|
-
|
|
11
|
-
|
|
27
|
+
/**
|
|
28
|
+
* 명세를 뽑을 대상. `--command`/`--arg` 면 stdio, `--url` 이면 Streamable HTTP 다(#137).
|
|
29
|
+
* `command` · `args` 두 필드를 이 하나로 바꾼 근거는 `TestCommandInput.target` 과 같다.
|
|
30
|
+
*/
|
|
31
|
+
readonly target: ConnectTarget;
|
|
12
32
|
readonly baselineOnly: boolean;
|
|
13
33
|
readonly provider?: "codex" | "claude";
|
|
14
34
|
readonly model?: string;
|
|
15
35
|
/** 승인 전 시험 실행 여부. 기본은 실행이고 `--no-dry-run` 이 끈다. 설계 문서 §4.3. */
|
|
16
36
|
readonly dryRun: boolean;
|
|
17
|
-
/** `--cassette` 경로. 없으면 서버를 직접 부른다. */
|
|
18
|
-
readonly cassettePath?: string;
|
|
19
|
-
/** `--record`. 카세트 파일이 있어도 새로 녹화한다. */
|
|
20
|
-
readonly forceRecord: boolean;
|
|
21
37
|
/** `--force`. `--out` 에 파일이 있으면 지우고 새로 쓴다. 설계 문서 §5. */
|
|
22
38
|
readonly force: boolean;
|
|
23
39
|
/** `--reset-cmd`. 시험 실행 직전 1회 실행한다. */
|
|
@@ -30,6 +46,16 @@ interface GenerateCommandDependencies {
|
|
|
30
46
|
command: string;
|
|
31
47
|
args: readonly string[];
|
|
32
48
|
}): Promise<McpStdioConnection>;
|
|
49
|
+
/**
|
|
50
|
+
* 원격(Streamable HTTP) 대상용 연결. `core.connectHttp` 다. 선택 사항으로 두는 근거는
|
|
51
|
+
* `TestCommandDependencies.connectHttp` 와 같다(#137).
|
|
52
|
+
*/
|
|
53
|
+
connectHttp?(options: {
|
|
54
|
+
url: string;
|
|
55
|
+
headers?: Readonly<Record<string, string>>;
|
|
56
|
+
}): Promise<McpHttpConnection>;
|
|
57
|
+
/** `--header-env` 가 가리키는 환경변수를 읽는다. CLI 가 `process` 를 직접 읽지 않기 위한 주입점. */
|
|
58
|
+
readEnv?(name: string): string | undefined;
|
|
33
59
|
createBaselineSuite(tools: readonly ToolDef[], options: {
|
|
34
60
|
suiteId: string;
|
|
35
61
|
suiteName: string;
|
|
@@ -100,15 +126,6 @@ interface GenerateCommandDependencies {
|
|
|
100
126
|
* 동일성도 보장된다. 위 6개 필드가 같은 방식이다.
|
|
101
127
|
*/
|
|
102
128
|
GenerateTestsError?: typeof import("@mcpeak/generate").GenerateTestsError;
|
|
103
|
-
/**
|
|
104
|
-
* 카세트 파일 입출력. 주입점을 여기 하나만 두는 이유는 시험 실행 경로에서 파일시스템을
|
|
105
|
-
* 만지는 곳이 이것뿐이기 때문이다. 나머지(`runDryRun`·`reviewDryRun`)는 주입한 client 와
|
|
106
|
-
* io 만 쓰므로 테스트가 실제 구현을 그대로 돌린다.
|
|
107
|
-
*/
|
|
108
|
-
cassetteIo?: {
|
|
109
|
-
load(path: string): Promise<Cassette | null>;
|
|
110
|
-
save(path: string, cassette: Cassette): Promise<void>;
|
|
111
|
-
};
|
|
112
129
|
}
|
|
113
130
|
interface ReviewIO {
|
|
114
131
|
input(message: string): Promise<string>;
|
|
@@ -158,29 +175,4 @@ declare function parseRepairCommand(argv: readonly string[]): RepairCommandInput
|
|
|
158
175
|
*/
|
|
159
176
|
declare function runRepairCommand(argv: readonly string[], deps: RepairCommandDependencies): Promise<number>;
|
|
160
177
|
//#endregion
|
|
161
|
-
|
|
162
|
-
interface ReplayCommandInput {
|
|
163
|
-
readonly suitePath: string;
|
|
164
|
-
readonly cassettePath: string;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* `connect` 가 없다. replay 가 서버를 띄우지 않는다는 것이 주석이 아니라 타입에 드러난다.
|
|
168
|
-
* ADR-0028.
|
|
169
|
-
*/
|
|
170
|
-
interface ReplayCommandDependencies {
|
|
171
|
-
readFile(path: string): Promise<Uint8Array>;
|
|
172
|
-
validateSuite(input: unknown): SuiteValidationResult;
|
|
173
|
-
loadCassette(path: string): Promise<Cassette | null>;
|
|
174
|
-
startRunner(options: RunSuiteOptions): RunnerExecution;
|
|
175
|
-
finalize(options: FinalizeRunnerExecutionOptions): Promise<RunnerReport>;
|
|
176
|
-
renderReport(report: RunnerReport, options?: {
|
|
177
|
-
color?: boolean;
|
|
178
|
-
}): string;
|
|
179
|
-
colorEnabled: boolean;
|
|
180
|
-
writeStdout(text: string): void;
|
|
181
|
-
writeStderr(text: string): void;
|
|
182
|
-
}
|
|
183
|
-
declare function parseReplayCommand(argv: readonly string[]): ReplayCommandInput;
|
|
184
|
-
declare function runReplayCommand(argv: readonly string[], dependencies: ReplayCommandDependencies): Promise<number>;
|
|
185
|
-
//#endregion
|
|
186
|
-
export { RepairCommandDependencies as a, runRepairCommand as c, ReviewIO as d, nodeGenerateDependencies as f, runReplayCommand as i, GenerateCommandDependencies as l, runGenerateCommand as m, ReplayCommandInput as n, RepairCommandInput as o, nodeReviewIO as p, parseReplayCommand as r, parseRepairCommand as s, ReplayCommandDependencies as t, GenerateCommandInput as u };
|
|
178
|
+
export { GenerateCommandDependencies as a, nodeGenerateDependencies as c, ConnectTarget as d, runRepairCommand as i, nodeReviewIO as l, RepairCommandInput as n, GenerateCommandInput as o, parseRepairCommand as r, ReviewIO as s, RepairCommandDependencies as t, runGenerateCommand as u };
|