@skyramp/mcp 0.4.4 → 0.5.0-rc.10
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/build/entitlements/entitlementGate.d.ts +8 -0
- package/build/entitlements/entitlementGate.js +57 -0
- package/build/entitlements/runCodes.d.ts +18 -0
- package/build/entitlements/runCodes.js +16 -0
- package/build/index.js +32 -6
- package/build/playwright/PlaywrightTraceService.d.ts +17 -0
- package/build/playwright/PlaywrightTraceService.js +29 -1
- package/build/playwright/dataSafetyConfig.d.ts +74 -0
- package/build/playwright/dataSafetyConfig.js +109 -0
- package/build/playwright/essentialTools.d.ts +1 -0
- package/build/playwright/essentialTools.js +44 -0
- package/build/playwright/registerPlaywrightTools.d.ts +36 -1
- package/build/playwright/registerPlaywrightTools.js +87 -43
- package/build/playwright/safeResolveDataSafety.d.ts +24 -0
- package/build/playwright/safeResolveDataSafety.js +33 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +46 -177
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/test-maintenance/driftAnalysisShared.js +1 -1
- package/build/services/FleetExecutionService.d.ts +87 -16
- package/build/services/FleetExecutionService.js +487 -48
- package/build/services/FleetRunRegistry.d.ts +49 -0
- package/build/services/FleetRunRegistry.js +178 -0
- package/build/services/containerEnv.js +8 -2
- package/build/services/fleetLimitConfirmation.d.ts +19 -0
- package/build/services/fleetLimitConfirmation.js +126 -0
- package/build/tool-phases.d.ts +14 -8
- package/build/tool-phases.js +88 -43
- package/build/tools/code-refactor/assertion-state.js +3 -1
- package/build/tools/code-refactor/codeReuseTool.js +2 -1
- package/build/tools/code-refactor/utils-verify-gates.js +1 -1
- package/build/tools/executeFleetTool.d.ts +6 -4
- package/build/tools/executeFleetTool.js +67 -23
- package/build/tools/fixErrorTool.js +2 -1
- package/build/tools/fleetLifecycleTools.d.ts +20 -0
- package/build/tools/fleetLifecycleTools.js +124 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +38 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.js +7 -1
- package/build/tools/generate-tests/generateUILoadRestTool.d.ts +19 -1
- package/build/tools/generate-tests/generateUILoadRestTool.js +316 -50
- package/build/tools/submitReportTool.js +4 -122
- package/build/tools/test-management/registerTestPlanTool.js +22 -6
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +11 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +111 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +40 -4
- package/build/types/AssertionOutcome.d.ts +3 -0
- package/build/types/TestbotReport.d.ts +0 -37
- package/build/types/TestbotReport.js +0 -22
- package/build/types/index.d.ts +1 -1
- package/build/types/index.js +1 -1
- package/build/utils/assertion-verify/created-add-guard.d.ts +12 -0
- package/build/utils/assertion-verify/created-add-guard.js +173 -0
- package/build/utils/assertion-verify/index.d.ts +1 -0
- package/build/utils/assertion-verify/index.js +1 -0
- package/build/utils/assertion-verify/verify.d.ts +7 -3
- package/build/utils/assertion-verify/verify.js +9 -3
- package/build/utils/featureFlags.js +6 -3
- package/build/utils/repositorySlug.d.ts +14 -0
- package/build/utils/repositorySlug.js +46 -1
- package/build/utils/telemetry.d.ts +2 -0
- package/build/utils/telemetry.js +12 -0
- package/build/utils/utils-verify/action-key.d.ts +59 -2
- package/build/utils/utils-verify/action-key.js +162 -6
- package/build/utils/utils-verify/action-sites.d.ts +7 -0
- package/build/utils/utils-verify/action-sites.js +277 -35
- package/build/utils/utils-verify/call-sites.d.ts +8 -2
- package/build/utils/utils-verify/language-spec.js +1 -1
- package/build/utils/utils-verify/parse.d.ts +4 -0
- package/build/utils/utils-verify/parse.js +2 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +63 -121
- package/build/workspace/workspace.js +45 -48
- package/build/workspace/workspaceVerifier.d.ts +22 -0
- package/build/workspace/workspaceVerifier.js +65 -0
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +7 -6
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +5 -4
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +12 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetyGuard.js +822 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetySession.js +372 -0
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +101 -8
- package/node_modules/playwright/lib/mcp/skyramp/finishTestSetupTool.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/index.js +16 -0
- package/node_modules/playwright/lib/mcp/skyramp/recordingPhase.js +65 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +1 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +598 -127
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +85 -31
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +132 -5
- package/node_modules/playwright/node_modules/playwright-core/lib/cli/program.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/client/browserContext.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/protocol/validator.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/browserContext.js +17 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harRecorder.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harTracer.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/clickExpandedState.js +75 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +85 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bou8kKzE.js → codeMirrorModule-BuhAdckO.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-COAHN430.js +809 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.0MJW0uG7.js → index.7AU8h0PZ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.GLqsx5cI.js → uiMode.Dl7smBJn.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/cli/program.ts +5 -0
- package/node_modules/playwright/node_modules/playwright-core/src/client/browserContext.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/client/types.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/protocol/validator.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/browserContext.ts +25 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harRecorder.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harTracer.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/clickExpandedState.ts +82 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +137 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -4
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/prompts/testbot-task1.md +1 -1
- package/plugin/skills/enhance-assertions/reference/integration.md +1 -0
- package/plugin/skills/setup-testbot-target-sut/SKILL.md +60 -0
- package/plugin/skills/setup-testbot-target-sut/reference/ci-workflow.md +26 -0
- package/plugin/skills/setup-testbot-target-sut/reference/deployed-environment.md +105 -0
- package/plugin/skills/setup-testbot-target-sut/reference/existing-tests.md +14 -0
- package/plugin/skills/setup-testbot-target-sut/reference/troubleshooting.md +20 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +0 -178
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +0 -311
- package/build/prompts/sut-setup/shared.d.ts +0 -62
- package/build/prompts/sut-setup/shared.js +0 -196
- package/build/resources/sutSetupResource.d.ts +0 -2
- package/build/resources/sutSetupResource.js +0 -63
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DTp2-8Si.js +0 -809
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type FleetExecutionOptions, type FleetExecutionResult } from "./FleetExecutionService.js";
|
|
2
|
+
/** Process-local jobs: callers must poll the same MCP session that started a run. */
|
|
3
|
+
export declare class FleetRunRegistry {
|
|
4
|
+
private readonly execute;
|
|
5
|
+
private readonly runs;
|
|
6
|
+
constructor(execute?: (options: FleetExecutionOptions) => Promise<FleetExecutionResult>);
|
|
7
|
+
start(options: FleetExecutionOptions): string;
|
|
8
|
+
/** Start a run and wait for it, under the same per-directory guard; `signal` cancels it. */
|
|
9
|
+
run(options: FleetExecutionOptions, signal?: AbortSignal): Promise<FleetExecutionResult>;
|
|
10
|
+
/** Interrupt a running fleet; it gets a grace to cancel its workers before it is killed. */
|
|
11
|
+
cancel(id: string): {
|
|
12
|
+
runId: string;
|
|
13
|
+
done: boolean;
|
|
14
|
+
events: {
|
|
15
|
+
sequence: number;
|
|
16
|
+
event: Record<string, unknown>;
|
|
17
|
+
}[];
|
|
18
|
+
next: number;
|
|
19
|
+
more: boolean;
|
|
20
|
+
truncated: boolean;
|
|
21
|
+
counters: Record<string, unknown>;
|
|
22
|
+
cancelled: true | undefined;
|
|
23
|
+
elapsedMs: number;
|
|
24
|
+
runStartedAt: string | undefined;
|
|
25
|
+
error: string | undefined;
|
|
26
|
+
};
|
|
27
|
+
/** Interrupt every running fleet and wait, bounded, for them to end. For shutdown. */
|
|
28
|
+
cancelAll(waitMs?: number): Promise<void>;
|
|
29
|
+
private output;
|
|
30
|
+
private get;
|
|
31
|
+
status(id: string, since?: number): {
|
|
32
|
+
runId: string;
|
|
33
|
+
done: boolean;
|
|
34
|
+
events: {
|
|
35
|
+
sequence: number;
|
|
36
|
+
event: Record<string, unknown>;
|
|
37
|
+
}[];
|
|
38
|
+
next: number;
|
|
39
|
+
more: boolean;
|
|
40
|
+
truncated: boolean;
|
|
41
|
+
counters: Record<string, unknown>;
|
|
42
|
+
cancelled: true | undefined;
|
|
43
|
+
elapsedMs: number;
|
|
44
|
+
runStartedAt: string | undefined;
|
|
45
|
+
error: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
result(id: string, release?: boolean): FleetExecutionResult;
|
|
48
|
+
}
|
|
49
|
+
export declare const fleetRuns: FleetRunRegistry;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { executeFleet, } from "./FleetExecutionService.js";
|
|
4
|
+
const MAX_RETAINED_RUNS = 16;
|
|
5
|
+
/** How long shutdown waits for interrupted runs to cancel their workers and end. */
|
|
6
|
+
const SHUTDOWN_WAIT_MS = 30_000;
|
|
7
|
+
const RESULT_RETENTION_MS = 60 * 60 * 1000;
|
|
8
|
+
const MAX_EVENTS = 10000;
|
|
9
|
+
/** Events one status call returns; the rest follow from its next cursor. */
|
|
10
|
+
const MAX_PAGE_EVENTS = 200;
|
|
11
|
+
/** Process-local jobs: callers must poll the same MCP session that started a run. */
|
|
12
|
+
export class FleetRunRegistry {
|
|
13
|
+
execute;
|
|
14
|
+
runs = new Map();
|
|
15
|
+
constructor(execute = executeFleet) {
|
|
16
|
+
this.execute = execute;
|
|
17
|
+
}
|
|
18
|
+
start(options) {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
for (const [id, run] of this.runs) {
|
|
21
|
+
if (run.ended && now - run.ended > RESULT_RETENTION_MS)
|
|
22
|
+
this.runs.delete(id);
|
|
23
|
+
}
|
|
24
|
+
const directory = path.resolve(options.directory);
|
|
25
|
+
for (const run of this.runs.values()) {
|
|
26
|
+
if (!run.ended && run.directory === directory)
|
|
27
|
+
throw new Error("a fleet is already running from this directory");
|
|
28
|
+
}
|
|
29
|
+
if (this.runs.size >= MAX_RETAINED_RUNS) {
|
|
30
|
+
// Finished runs make room, oldest first; only running ones are never dropped.
|
|
31
|
+
const finished = [...this.runs.values()]
|
|
32
|
+
.filter((r) => r.ended !== undefined)
|
|
33
|
+
.sort((a, b) => a.ended - b.ended);
|
|
34
|
+
for (const run of finished.slice(0, this.runs.size - MAX_RETAINED_RUNS + 1))
|
|
35
|
+
this.runs.delete(run.id);
|
|
36
|
+
if (this.runs.size >= MAX_RETAINED_RUNS)
|
|
37
|
+
throw new Error(`${MAX_RETAINED_RUNS} fleets are already running; wait for one to finish or cancel one`);
|
|
38
|
+
}
|
|
39
|
+
const id = randomUUID();
|
|
40
|
+
const abort = new AbortController();
|
|
41
|
+
const run = {
|
|
42
|
+
id,
|
|
43
|
+
directory,
|
|
44
|
+
started: now,
|
|
45
|
+
pending: "",
|
|
46
|
+
next: 0,
|
|
47
|
+
events: [],
|
|
48
|
+
counters: {},
|
|
49
|
+
abort,
|
|
50
|
+
finished: Promise.resolve(),
|
|
51
|
+
};
|
|
52
|
+
this.runs.set(id, run);
|
|
53
|
+
// Store rejection as data; starting a job must never produce an unhandled promise.
|
|
54
|
+
run.finished = Promise.resolve()
|
|
55
|
+
.then(() => this.execute({
|
|
56
|
+
...options,
|
|
57
|
+
onOutput: (chunk) => this.output(run, chunk),
|
|
58
|
+
signal: abort.signal,
|
|
59
|
+
}))
|
|
60
|
+
.then((result) => {
|
|
61
|
+
run.result = result;
|
|
62
|
+
}, (error) => {
|
|
63
|
+
run.error = error instanceof Error ? error.message : String(error);
|
|
64
|
+
})
|
|
65
|
+
.finally(() => {
|
|
66
|
+
if (run.pending)
|
|
67
|
+
this.output(run, "\n");
|
|
68
|
+
run.ended = Date.now();
|
|
69
|
+
});
|
|
70
|
+
return id;
|
|
71
|
+
}
|
|
72
|
+
/** Start a run and wait for it, under the same per-directory guard; `signal` cancels it. */
|
|
73
|
+
async run(options, signal) {
|
|
74
|
+
const id = this.start(options);
|
|
75
|
+
const cancel = () => this.cancel(id);
|
|
76
|
+
if (signal?.aborted)
|
|
77
|
+
cancel();
|
|
78
|
+
else
|
|
79
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
80
|
+
try {
|
|
81
|
+
await this.get(id).finished;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
signal?.removeEventListener("abort", cancel);
|
|
85
|
+
}
|
|
86
|
+
return this.result(id, true);
|
|
87
|
+
}
|
|
88
|
+
/** Interrupt a running fleet; it gets a grace to cancel its workers before it is killed. */
|
|
89
|
+
cancel(id) {
|
|
90
|
+
const run = this.get(id);
|
|
91
|
+
if (run.ended === undefined)
|
|
92
|
+
run.abort.abort();
|
|
93
|
+
return this.status(id);
|
|
94
|
+
}
|
|
95
|
+
/** Interrupt every running fleet and wait, bounded, for them to end. For shutdown. */
|
|
96
|
+
async cancelAll(waitMs = SHUTDOWN_WAIT_MS) {
|
|
97
|
+
const running = [...this.runs.values()].filter((r) => !r.ended);
|
|
98
|
+
for (const run of running)
|
|
99
|
+
run.abort.abort();
|
|
100
|
+
let timer;
|
|
101
|
+
await Promise.race([
|
|
102
|
+
Promise.all(running.map((r) => r.finished)),
|
|
103
|
+
new Promise((resolve) => (timer = setTimeout(resolve, waitMs))),
|
|
104
|
+
]);
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
}
|
|
107
|
+
output(run, chunk) {
|
|
108
|
+
run.pending += chunk;
|
|
109
|
+
const lines = run.pending.split(/\r?\n/);
|
|
110
|
+
run.pending = lines.pop() ?? "";
|
|
111
|
+
// A malformed unterminated log line must not grow without bound.
|
|
112
|
+
if (run.pending.length > 1024 * 1024)
|
|
113
|
+
run.pending = run.pending.slice(-1024 * 1024);
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
const match = line.match(/SKYRAMP_(?:LIMIT|PROGRESS) (\{.*\})$/);
|
|
116
|
+
if (!match)
|
|
117
|
+
continue;
|
|
118
|
+
try {
|
|
119
|
+
const event = JSON.parse(match[1]);
|
|
120
|
+
if (typeof event.runStartedAt === "string" &&
|
|
121
|
+
Number.isFinite(Date.parse(event.runStartedAt)) &&
|
|
122
|
+
(!run.runStartedAt ||
|
|
123
|
+
Date.parse(event.runStartedAt) < Date.parse(run.runStartedAt))) {
|
|
124
|
+
run.runStartedAt = event.runStartedAt;
|
|
125
|
+
}
|
|
126
|
+
if (event.stats && typeof event.host === "string")
|
|
127
|
+
run.counters[event.host] = event.stats;
|
|
128
|
+
if (event.phase !== "limit")
|
|
129
|
+
continue;
|
|
130
|
+
run.events.push({ sequence: run.next++, event });
|
|
131
|
+
if (run.events.length > MAX_EVENTS)
|
|
132
|
+
run.events.shift();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* Unrelated or truncated output is not a limit event. */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
get(id) {
|
|
140
|
+
const run = this.runs.get(id);
|
|
141
|
+
if (!run)
|
|
142
|
+
throw new Error("unknown fleet runId (use the same MCP session)");
|
|
143
|
+
return run;
|
|
144
|
+
}
|
|
145
|
+
status(id, since = 0) {
|
|
146
|
+
const run = this.get(id);
|
|
147
|
+
if (!Number.isSafeInteger(since) || since < 0 || since > run.next)
|
|
148
|
+
throw new Error("invalid event cursor");
|
|
149
|
+
const first = run.events[0]?.sequence ?? run.next;
|
|
150
|
+
const pending = run.events.filter((e) => e.sequence >= since);
|
|
151
|
+
const events = pending.slice(0, MAX_PAGE_EVENTS);
|
|
152
|
+
const more = events.length < pending.length;
|
|
153
|
+
return {
|
|
154
|
+
runId: id,
|
|
155
|
+
done: run.ended !== undefined,
|
|
156
|
+
events,
|
|
157
|
+
next: more ? events[events.length - 1].sequence + 1 : run.next,
|
|
158
|
+
more,
|
|
159
|
+
truncated: since < first,
|
|
160
|
+
counters: run.counters,
|
|
161
|
+
cancelled: run.abort.signal.aborted || undefined,
|
|
162
|
+
elapsedMs: (run.ended ?? Date.now()) - run.started,
|
|
163
|
+
runStartedAt: run.runStartedAt,
|
|
164
|
+
error: run.error,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
result(id, release = false) {
|
|
168
|
+
const run = this.get(id);
|
|
169
|
+
if (!run.ended)
|
|
170
|
+
throw new Error("fleet is still running; poll skyramp_fleet_status");
|
|
171
|
+
if (release)
|
|
172
|
+
this.runs.delete(id);
|
|
173
|
+
if (run.error)
|
|
174
|
+
throw new Error(run.error);
|
|
175
|
+
return run.result;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export const fleetRuns = new FleetRunRegistry();
|
|
@@ -36,8 +36,14 @@ const PASSTHROUGH_RESERVED_EXACT = new Set([
|
|
|
36
36
|
const PASSTHROUGH_RESERVED_PREFIXES = ["SKYRAMP_"];
|
|
37
37
|
// CI identity and secret-shaped names. Testbot puts the workflow GitHub token
|
|
38
38
|
// in the MCP process env, and a declaration would send it to the container and
|
|
39
|
-
// into a query string, where the SUT access log records it.
|
|
40
|
-
|
|
39
|
+
// into a query string, where the SUT access log records it. The Azure DevOps
|
|
40
|
+
// names are the pipeline's token and the `az devops` CLI's.
|
|
41
|
+
const PASSTHROUGH_CREDENTIAL_EXACT = new Set([
|
|
42
|
+
"GITHUB_TOKEN",
|
|
43
|
+
"GH_TOKEN",
|
|
44
|
+
"SYSTEM_ACCESSTOKEN",
|
|
45
|
+
"AZURE_DEVOPS_EXT_PAT",
|
|
46
|
+
]);
|
|
41
47
|
const PASSTHROUGH_CREDENTIAL_PREFIXES = ["ACTIONS_", "AWS_"];
|
|
42
48
|
const PASSTHROUGH_CREDENTIAL_SUBSTRINGS = ["_SECRET", "_PRIVATE_KEY"];
|
|
43
49
|
/** Why a name is refused, or null when it is allowed. The two reasons need
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The limit lines a fleet file will run, read from the file itself so a confirmation
|
|
3
|
+
* covers what actually executes. Fails closed on anything but one list of string literals.
|
|
4
|
+
*/
|
|
5
|
+
export declare function readFleetFileLimits(fleetFile: string): string[];
|
|
6
|
+
/** Ties a confirmation to one fleet file and the exact set of limits it runs. */
|
|
7
|
+
export declare function limitConfirmationToken(fleetFile: string, limits: string[]): string;
|
|
8
|
+
/**
|
|
9
|
+
* D10: a fleet that carries limits runs only with the token the generation returned
|
|
10
|
+
* beside the echoed limits. The token is recomputed from the fleet file, so an edited
|
|
11
|
+
* limit, another fleet or a guessed value does not pass. It binds the run to the echoed
|
|
12
|
+
* limits; it does not prove the user confirmed them, which is the agent's job. A fleet
|
|
13
|
+
* with no limits (legacy thresholds included) runs as before.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertFleetLimitsConfirmed(args: {
|
|
16
|
+
directory: string;
|
|
17
|
+
entryClass?: string;
|
|
18
|
+
confirmationToken?: string;
|
|
19
|
+
}): void;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { findEntryClass, fleetFileName, fleetLanguage, } from "./FleetExecutionService.js";
|
|
5
|
+
/**
|
|
6
|
+
* Where each language's generated fleet file opens its limit list: Java's builder
|
|
7
|
+
* `.limits(List.of(`, Python's `limits=[` and TypeScript/JavaScript's `limits: [`.
|
|
8
|
+
*/
|
|
9
|
+
const LIMITS_OPENER = {
|
|
10
|
+
java: /\.\s*limits\s*\(\s*List\s*\.\s*of\s*\(/,
|
|
11
|
+
python: /\blimits\s*=\s*\[/,
|
|
12
|
+
typescript: /\blimits\s*:\s*\[/,
|
|
13
|
+
javascript: /\blimits\s*:\s*\[/,
|
|
14
|
+
};
|
|
15
|
+
/** One double-quoted literal; the generator writes JSON-compatible escapes in every language. */
|
|
16
|
+
const STRING_LITERAL = /\s*("(?:[^"\\]|\\.)*")\s*,?/y;
|
|
17
|
+
/** Any mention of limits: a variable, a quoted key, a second list or a comment. */
|
|
18
|
+
const LIMITS_WORD = /\blimits\b/g;
|
|
19
|
+
/**
|
|
20
|
+
* What actually closes the limit list in each language: Java's `List.of(` and `.limits(`
|
|
21
|
+
* must both close, a bare array literal just needs its `]`.
|
|
22
|
+
*/
|
|
23
|
+
const LIMITS_CLOSER = {
|
|
24
|
+
java: /^\s*\)\s*\)/,
|
|
25
|
+
python: /^\s*\]/,
|
|
26
|
+
typescript: /^\s*\]/,
|
|
27
|
+
javascript: /^\s*\]/,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* What may follow the closed list without silently extending its value, e.g. Python's
|
|
31
|
+
* `["safe"] + [...]` or TypeScript's `["safe"].concat([...])`. Java allows a `.` because
|
|
32
|
+
* by this point the list itself is already closed, so a `.` only continues the
|
|
33
|
+
* surrounding builder chain (`.limits(List.of(...)).timeout(5)`).
|
|
34
|
+
*/
|
|
35
|
+
const LIMITS_TERMINATOR = {
|
|
36
|
+
java: /^(?:$|[.,;)}])/,
|
|
37
|
+
python: /^(?:$|[,;)}])/,
|
|
38
|
+
typescript: /^(?:$|[,;)}])/,
|
|
39
|
+
javascript: /^(?:$|[,;)}])/,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The limit lines a fleet file will run, read from the file itself so a confirmation
|
|
43
|
+
* covers what actually executes. Fails closed on anything but one list of string literals.
|
|
44
|
+
*/
|
|
45
|
+
export function readFleetFileLimits(fleetFile) {
|
|
46
|
+
const source = fs.readFileSync(fleetFile, "utf8");
|
|
47
|
+
const unreadable = new Error(`${path.basename(fleetFile)} sets limits that are not one list of plain string ` +
|
|
48
|
+
"literals; regenerate it so the limits can be confirmed");
|
|
49
|
+
const mentions = source.match(LIMITS_WORD)?.length ?? 0;
|
|
50
|
+
const language = fleetLanguage(path.basename(fleetFile));
|
|
51
|
+
const opener = LIMITS_OPENER[language].exec(source);
|
|
52
|
+
if (!opener) {
|
|
53
|
+
if (mentions > 0)
|
|
54
|
+
throw unreadable;
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
if (mentions > 1)
|
|
58
|
+
throw unreadable;
|
|
59
|
+
const limits = [];
|
|
60
|
+
// A failed sticky match resets lastIndex, so the position is kept here.
|
|
61
|
+
let at = opener.index + opener[0].length;
|
|
62
|
+
for (;;) {
|
|
63
|
+
STRING_LITERAL.lastIndex = at;
|
|
64
|
+
const m = STRING_LITERAL.exec(source);
|
|
65
|
+
if (!m)
|
|
66
|
+
break;
|
|
67
|
+
try {
|
|
68
|
+
limits.push(JSON.parse(m[1]));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw unreadable;
|
|
72
|
+
}
|
|
73
|
+
at = STRING_LITERAL.lastIndex;
|
|
74
|
+
}
|
|
75
|
+
const closer = LIMITS_CLOSER[language].exec(source.slice(at));
|
|
76
|
+
if (!closer)
|
|
77
|
+
throw unreadable;
|
|
78
|
+
const afterClose = source.slice(at + closer[0].length).trimStart();
|
|
79
|
+
if (!LIMITS_TERMINATOR[language].test(afterClose))
|
|
80
|
+
throw unreadable;
|
|
81
|
+
return limits;
|
|
82
|
+
}
|
|
83
|
+
/** Ties a confirmation to one fleet file and the exact set of limits it runs. */
|
|
84
|
+
export function limitConfirmationToken(fleetFile, limits) {
|
|
85
|
+
let file = path.resolve(fleetFile);
|
|
86
|
+
try {
|
|
87
|
+
file = fs.realpathSync(file);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// A file that does not exist yet cannot be confirmed anyway.
|
|
91
|
+
}
|
|
92
|
+
const set = [...new Set(limits.map((l) => l.trim()))].sort();
|
|
93
|
+
return createHash("sha256")
|
|
94
|
+
.update(file + "\0" + set.join("\n"))
|
|
95
|
+
.digest("hex")
|
|
96
|
+
.slice(0, 16);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* D10: a fleet that carries limits runs only with the token the generation returned
|
|
100
|
+
* beside the echoed limits. The token is recomputed from the fleet file, so an edited
|
|
101
|
+
* limit, another fleet or a guessed value does not pass. It binds the run to the echoed
|
|
102
|
+
* limits; it does not prove the user confirmed them, which is the agent's job. A fleet
|
|
103
|
+
* with no limits (legacy thresholds included) runs as before.
|
|
104
|
+
*/
|
|
105
|
+
export function assertFleetLimitsConfirmed(args) {
|
|
106
|
+
const directory = path.resolve(args.directory);
|
|
107
|
+
if (!fs.existsSync(directory))
|
|
108
|
+
return; // the run itself reports the missing directory
|
|
109
|
+
const fleetFile = path.join(directory, fleetFileName(findEntryClass(directory, args.entryClass)));
|
|
110
|
+
if (!fs.existsSync(fleetFile))
|
|
111
|
+
return;
|
|
112
|
+
const limits = readFleetFileLimits(fleetFile);
|
|
113
|
+
if (limits.length === 0)
|
|
114
|
+
return;
|
|
115
|
+
const expected = limitConfirmationToken(fleetFile, limits);
|
|
116
|
+
if (args.confirmationToken === expected)
|
|
117
|
+
return;
|
|
118
|
+
throw new Error((args.confirmationToken
|
|
119
|
+
? "confirmationToken does not match the limits in "
|
|
120
|
+
: "no confirmationToken was passed for the limits in ") +
|
|
121
|
+
`${path.basename(fleetFile)}:\n` +
|
|
122
|
+
limits.map((l) => `- ${l}`).join("\n") +
|
|
123
|
+
"\nThe user confirms limits through the echo of skyramp_ui_load_test_generation, " +
|
|
124
|
+
"which returns the confirmationToken. Echo these limits, wait for the user's " +
|
|
125
|
+
"confirmation, and pass that token; regenerate the fleet if you do not hold it.");
|
|
126
|
+
}
|
package/build/tool-phases.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* One entry per Skyramp MCP tool: its testbot progress phase, if it has one,
|
|
3
|
+
* and the entitlement code it needs when the run has a SKYRAMP_RUN_TOKEN.
|
|
4
|
+
* Every registered tool must have an entry; tool-phase-coverage.test.ts fails
|
|
5
|
+
* when one is missing.
|
|
3
6
|
*
|
|
4
|
-
* The testbot progress UI reads
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Tools not in this map must be listed in TOOLS_WITHOUT_PHASE.
|
|
7
|
+
* The testbot progress UI reads TOOL_PHASE_MAP at runtime (through the
|
|
8
|
+
* `@skyramp/mcp/tool-phases` package export) to know which tool calls
|
|
9
|
+
* correspond to which progress steps.
|
|
9
10
|
*
|
|
10
11
|
* Phases: analyzing, generating, executing, maintaining, reporting
|
|
11
12
|
*
|
|
@@ -29,10 +30,15 @@ export type PhaseMapping = string | {
|
|
|
29
30
|
before: string;
|
|
30
31
|
after: string;
|
|
31
32
|
};
|
|
33
|
+
export type EntitlementCode = "analyze" | "recommend" | "generate" | "execute";
|
|
34
|
+
export declare const NOT_GATED = "not gated";
|
|
35
|
+
export type ToolEntry = {
|
|
36
|
+
phase?: PhaseMapping;
|
|
37
|
+
entitlement: EntitlementCode | typeof NOT_GATED;
|
|
38
|
+
};
|
|
39
|
+
export declare const TOOLS: Readonly<Record<string, ToolEntry>>;
|
|
32
40
|
export declare const TOOL_PHASE_MAP: Record<string, PhaseMapping>;
|
|
33
41
|
/** Tools whose phase depends on context — listed here for consumer discovery. */
|
|
34
42
|
export declare const CONTEXT_DEPENDENT_TOOLS: Set<string>;
|
|
35
43
|
/** All tools that belong to the "generating" phase — used as the boundary. */
|
|
36
44
|
export declare const GENERATING_TOOLS: Set<string>;
|
|
37
|
-
/** Tools that intentionally have no progress phase (infrastructure/utility). */
|
|
38
|
-
export declare const TOOLS_WITHOUT_PHASE: Set<string>;
|
package/build/tool-phases.js
CHANGED
|
@@ -1,25 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import { ESSENTIAL_TOOLS } from "./playwright/essentialTools.js";
|
|
2
|
+
export const NOT_GATED = "not gated";
|
|
3
|
+
const EXECUTION_PHASE = { before: "maintaining", after: "executing" };
|
|
4
|
+
export const TOOLS = {
|
|
5
|
+
skyramp_analyze_changes: { phase: "analyzing", entitlement: "analyze" },
|
|
6
|
+
skyramp_analyze_test_health: {
|
|
7
|
+
phase: "maintaining",
|
|
8
|
+
entitlement: "analyze",
|
|
9
|
+
},
|
|
10
|
+
// A prompt, not a tool; listed for its phase.
|
|
11
|
+
skyramp_recommend_tests: { phase: "analyzing", entitlement: "recommend" },
|
|
12
|
+
skyramp_register_test_plan: { phase: "analyzing", entitlement: "recommend" },
|
|
13
|
+
skyramp_resolve_screen: { entitlement: "recommend" },
|
|
14
|
+
skyramp_smoke_test_generation: {
|
|
15
|
+
phase: "generating",
|
|
16
|
+
entitlement: "generate",
|
|
17
|
+
},
|
|
18
|
+
skyramp_fuzz_test_generation: {
|
|
19
|
+
phase: "generating",
|
|
20
|
+
entitlement: "generate",
|
|
21
|
+
},
|
|
22
|
+
skyramp_contract_test_generation: {
|
|
23
|
+
phase: "generating",
|
|
24
|
+
entitlement: "generate",
|
|
25
|
+
},
|
|
26
|
+
skyramp_load_test_generation: {
|
|
27
|
+
phase: "generating",
|
|
28
|
+
entitlement: "generate",
|
|
29
|
+
},
|
|
30
|
+
skyramp_integration_test_generation: {
|
|
31
|
+
phase: "generating",
|
|
32
|
+
entitlement: "generate",
|
|
33
|
+
},
|
|
34
|
+
skyramp_e2e_test_generation: { phase: "generating", entitlement: "generate" },
|
|
35
|
+
skyramp_ui_test_generation: { phase: "generating", entitlement: "generate" },
|
|
36
|
+
skyramp_ui_load_test_generation: {
|
|
37
|
+
phase: "generating",
|
|
38
|
+
entitlement: "generate",
|
|
39
|
+
},
|
|
40
|
+
skyramp_batch_scenario_test_generation: {
|
|
41
|
+
phase: "generating",
|
|
42
|
+
entitlement: "generate",
|
|
43
|
+
},
|
|
44
|
+
skyramp_mock_generation: { phase: "generating", entitlement: "generate" },
|
|
45
|
+
skyramp_batch_mock_generation: {
|
|
46
|
+
phase: "generating",
|
|
47
|
+
entitlement: "generate",
|
|
48
|
+
},
|
|
49
|
+
skyramp_generate_enriched_integration_test: {
|
|
50
|
+
phase: "generating",
|
|
51
|
+
entitlement: "generate",
|
|
52
|
+
},
|
|
53
|
+
skyramp_enrich_test_with_mocks: {
|
|
54
|
+
phase: "generating",
|
|
55
|
+
entitlement: "generate",
|
|
56
|
+
},
|
|
57
|
+
skyramp_reuse_code: { entitlement: "generate" },
|
|
58
|
+
skyramp_modularization: { entitlement: "generate" },
|
|
59
|
+
skyramp_enhance_assertions: { entitlement: "generate" },
|
|
60
|
+
skyramp_enhance_assertions_snapshot: { entitlement: "generate" },
|
|
61
|
+
skyramp_verify_enhance_assertions: { entitlement: "generate" },
|
|
62
|
+
skyramp_align_assertions: { entitlement: "generate" },
|
|
63
|
+
skyramp_verify_test_dependencies: { entitlement: "generate" },
|
|
64
|
+
skyramp_fix_errors: { entitlement: "generate" },
|
|
65
|
+
skyramp_actions: { entitlement: "generate" },
|
|
66
|
+
skyramp_one_click_tool: { entitlement: "generate" },
|
|
67
|
+
skyramp_preflight_mock_check: { entitlement: "generate" },
|
|
68
|
+
skyramp_setup_local_dev_worker: { entitlement: "generate" },
|
|
69
|
+
skyramp_query_proxy_mocks: { entitlement: "generate" },
|
|
70
|
+
...Object.fromEntries([...ESSENTIAL_TOOLS].map((name) => [
|
|
71
|
+
name,
|
|
72
|
+
{ entitlement: "generate" },
|
|
73
|
+
])),
|
|
74
|
+
skyramp_execute_test: { phase: EXECUTION_PHASE, entitlement: "execute" },
|
|
75
|
+
skyramp_execute_fleet: { phase: EXECUTION_PHASE, entitlement: "execute" },
|
|
76
|
+
skyramp_start_fleet: { phase: EXECUTION_PHASE, entitlement: "execute" },
|
|
77
|
+
skyramp_fleet_status: { phase: EXECUTION_PHASE, entitlement: "execute" },
|
|
78
|
+
skyramp_fleet_result: { phase: "reporting", entitlement: "execute" },
|
|
79
|
+
skyramp_fleet_cancel: { phase: EXECUTION_PHASE, entitlement: "execute" },
|
|
80
|
+
skyramp_kpi_catalog: { entitlement: NOT_GATED },
|
|
81
|
+
skyramp_start_trace_collection: { entitlement: "execute" },
|
|
82
|
+
skyramp_stop_trace_collection: { entitlement: "execute" },
|
|
83
|
+
skyramp_submit_report: { phase: "reporting", entitlement: NOT_GATED },
|
|
84
|
+
skyramp_login: { entitlement: NOT_GATED },
|
|
85
|
+
skyramp_logout: { entitlement: NOT_GATED },
|
|
86
|
+
skyramp_init_scan: { entitlement: NOT_GATED },
|
|
87
|
+
skyramp_init_workspace: { entitlement: NOT_GATED },
|
|
22
88
|
};
|
|
89
|
+
export const TOOL_PHASE_MAP = Object.fromEntries(Object.entries(TOOLS).flatMap(([name, entry]) => entry.phase === undefined ? [] : [[name, entry.phase]]));
|
|
23
90
|
/** Tools whose phase depends on context — listed here for consumer discovery. */
|
|
24
91
|
export const CONTEXT_DEPENDENT_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
25
92
|
.filter((entry) => typeof entry[1] === "object")
|
|
@@ -28,25 +95,3 @@ export const CONTEXT_DEPENDENT_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
|
28
95
|
export const GENERATING_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
29
96
|
.filter((entry) => typeof entry[1] === "string" && entry[1] === "generating")
|
|
30
97
|
.map((entry) => entry[0]));
|
|
31
|
-
/** Tools that intentionally have no progress phase (infrastructure/utility). */
|
|
32
|
-
export const TOOLS_WITHOUT_PHASE = new Set([
|
|
33
|
-
"skyramp_login",
|
|
34
|
-
"skyramp_logout",
|
|
35
|
-
"skyramp_init_scan",
|
|
36
|
-
"skyramp_init_workspace",
|
|
37
|
-
"skyramp_one_click_tool",
|
|
38
|
-
"skyramp_setup_local_dev_worker",
|
|
39
|
-
"skyramp_actions",
|
|
40
|
-
"skyramp_start_trace_collection",
|
|
41
|
-
"skyramp_stop_trace_collection",
|
|
42
|
-
"skyramp_fix_errors",
|
|
43
|
-
"skyramp_modularization",
|
|
44
|
-
"skyramp_reuse_code",
|
|
45
|
-
"skyramp_enhance_assertions",
|
|
46
|
-
"skyramp_enhance_assertions_snapshot",
|
|
47
|
-
"skyramp_verify_enhance_assertions",
|
|
48
|
-
"skyramp_align_assertions",
|
|
49
|
-
"skyramp_preflight_mock_check",
|
|
50
|
-
"skyramp_query_proxy_mocks",
|
|
51
|
-
"skyramp_verify_test_dependencies",
|
|
52
|
-
]);
|
|
@@ -3,7 +3,7 @@ import { realpathSync } from "fs";
|
|
|
3
3
|
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
4
4
|
import { logger } from "../../utils/logger.js";
|
|
5
5
|
import { resolveRunStatePath, StateManager, } from "../../utils/AnalysisStateManager.js";
|
|
6
|
-
import { computeAssertionMetrics, detectAssertionLanguage, formatAssertionVerifyReport, sha256Of, verifyAssertionEnhancement, } from "../../utils/assertion-verify/index.js";
|
|
6
|
+
import { collectCreatedAddLines, computeAssertionMetrics, detectAssertionLanguage, formatAssertionVerifyReport, sha256Of, verifyAssertionEnhancement, } from "../../utils/assertion-verify/index.js";
|
|
7
7
|
/**
|
|
8
8
|
* Persistence for the assertion-enhancement verification loop, keyed by the
|
|
9
9
|
* spec's CANONICAL absolute path in `UnifiedAnalysisState.assertionOutcomes`.
|
|
@@ -157,6 +157,7 @@ export async function recordAssertionBaseline(testFile, testType, enhanceType, e
|
|
|
157
157
|
baselineCount: metrics?.count,
|
|
158
158
|
baselineStrength: metrics?.strengthScore,
|
|
159
159
|
baselineFingerprints: metrics?.fingerprints,
|
|
160
|
+
baselineCreatedAddLines: collectCreatedAddLines(content),
|
|
160
161
|
issuedAt: new Date().toISOString(),
|
|
161
162
|
}), explicitStateFile, data);
|
|
162
163
|
// A failed record write must not strand an orphan copy nobody references.
|
|
@@ -203,6 +204,7 @@ export function baselineFrom(record, testFile) {
|
|
|
203
204
|
count: record.baselineCount,
|
|
204
205
|
strengthScore: record.baselineStrength,
|
|
205
206
|
fingerprints: record.baselineFingerprints,
|
|
207
|
+
createdAddLines: record.baselineCreatedAddLines,
|
|
206
208
|
baselineFilePath: record.baselineFilePath,
|
|
207
209
|
};
|
|
208
210
|
}
|
|
@@ -469,8 +469,9 @@ export function registerCodeReuseTool(server) {
|
|
|
469
469
|
return errorResult;
|
|
470
470
|
}
|
|
471
471
|
finally {
|
|
472
|
+
// No prompt: it carries the user's test code, and PushToolEvent
|
|
473
|
+
// forwards every field verbatim.
|
|
472
474
|
AnalyticsService.pushMCPToolEvent(TOOL_NAME, errorResult, {
|
|
473
|
-
prompt: params.prompt,
|
|
474
475
|
testFile: params.testFile,
|
|
475
476
|
language: params.language,
|
|
476
477
|
framework: params.framework,
|
|
@@ -248,7 +248,7 @@ function inlineCallSitesAdvisory(r) {
|
|
|
248
248
|
"",
|
|
249
249
|
"",
|
|
250
250
|
`ADVISORY — ${n} ${family.inlineSite.noun}${n === 1 ? "" : "s"} in other Skyramp-generated tests that a shared helper already ${family.inlineSite.already}:`,
|
|
251
|
-
...r.inlineCallSites.map((c) => `- ${show(c.file, r)}:${lines(c)}${c.localHelper ? ` (in local helper ${c.localHelper})` : ""} · ${what(c)} · ${labelOf(c.utilsFile)}.${c.helper}`),
|
|
251
|
+
...r.inlineCallSites.map((c) => `- ${show(c.file, r)}:${lines(c)}${c.localHelper ? ` (in local helper ${c.localHelper})` : ""} · ${what(c)} · ${labelOf(c.utilsFile)}.${c.helper}${c.kind === "sequence" && c.call ? ` · replace lines ${lines(c)} (the generated bindings in them included) with \`${c.call}\`` : ""}`),
|
|
252
252
|
`${family.inlineSite.instruction} ${inlineSiteTail(family.inlineSite.localCopy)}`,
|
|
253
253
|
].join("\n");
|
|
254
254
|
}
|
|
@@ -3,9 +3,11 @@ import { McpServer } from "@modelcontextprotocol/server";
|
|
|
3
3
|
export declare const fleetExecutionInputSchema: {
|
|
4
4
|
directory: z.ZodString;
|
|
5
5
|
entryClass: z.ZodOptional<z.ZodString>;
|
|
6
|
-
certsDir: z.ZodString
|
|
6
|
+
certsDir: z.ZodOptional<z.ZodString>;
|
|
7
|
+
localDocker: z.ZodOptional<z.ZodBoolean>;
|
|
7
8
|
credentials: z.ZodOptional<z.ZodString>;
|
|
8
9
|
libraryJar: z.ZodOptional<z.ZodString>;
|
|
10
|
+
libraryPackage: z.ZodOptional<z.ZodString>;
|
|
9
11
|
executorImage: z.ZodOptional<z.ZodString>;
|
|
10
12
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
11
13
|
linux: "linux";
|
|
@@ -13,6 +15,7 @@ export declare const fleetExecutionInputSchema: {
|
|
|
13
15
|
}>>;
|
|
14
16
|
timeoutSec: z.ZodOptional<z.ZodNumber>;
|
|
15
17
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
18
|
+
confirmationToken: z.ZodOptional<z.ZodString>;
|
|
16
19
|
};
|
|
17
20
|
/**
|
|
18
21
|
* Runs a generated fleet test and reports what happened.
|
|
@@ -21,8 +24,7 @@ export declare const fleetExecutionInputSchema: {
|
|
|
21
24
|
* executor container every other generated test runs in compiles the trio and starts the
|
|
22
25
|
* fleet manager, and the caller gets the run's own report plus its result as data.
|
|
23
26
|
*
|
|
24
|
-
* The worker is deployed by the test, not by this tool
|
|
25
|
-
*
|
|
26
|
-
* address the fleet resolves for it.
|
|
27
|
+
* The worker is deployed by the test, not by this tool: on a remote Docker engine over mTLS,
|
|
28
|
+
* or, for a host that names no engine, on the local daemon through its socket.
|
|
27
29
|
*/
|
|
28
30
|
export declare function registerExecuteFleetTool(server: McpServer): void;
|