@skyramp/mcp 0.4.3 → 0.4.4-rc.2
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/index.js +239 -210
- package/build/playwright/PlaywrightTraceService.d.ts +1 -1
- package/build/playwright/blueprintDigestStore.d.ts +1 -1
- package/build/playwright/registerPlaywrightTools.d.ts +1 -1
- package/build/playwright/traceRecordingPrompt.d.ts +1 -1
- package/build/playwright/traceRecordingPrompt.js +4 -3
- package/build/prompts/local-dev/local-dev-prompts.d.ts +1 -1
- package/build/prompts/local-dev/local-dev-prompts.js +2 -2
- package/build/prompts/orderAssertionRule.d.ts +9 -0
- package/build/prompts/orderAssertionRule.js +9 -0
- package/build/prompts/startTraceCollectionPrompts.d.ts +1 -1
- package/build/prompts/startTraceCollectionPrompts.js +2 -3
- package/build/prompts/test-maintenance/driftAnalysisShared.js +2 -0
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +1 -1
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +2 -2
- package/build/prompts/testbot/testbot-prompts.d.ts +1 -1
- package/build/prompts/testbot/testbot-prompts.js +9 -9
- package/build/recommendation/answers.d.ts +3 -3
- package/build/recommendation/answers.js +3 -2
- package/build/recommendation/verifierContracts.d.ts +4 -4
- package/build/recommendation/verifierContracts.js +6 -5
- package/build/recommendation/verifiers/coverage.js +25 -25
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +3 -3
- package/build/resources/analysisResources.d.ts +1 -1
- package/build/resources/analysisResources.js +1 -1
- package/build/resources/progressResource.d.ts +1 -1
- package/build/resources/sutSetupResource.d.ts +1 -1
- package/build/resources/sutSetupResource.js +1 -1
- package/build/resources/testbotResource.d.ts +1 -1
- package/build/resources/testbotResource.js +1 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/FleetExecutionService.d.ts +130 -0
- package/build/services/FleetExecutionService.js +391 -0
- package/build/services/ModularizationService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +7 -0
- package/build/services/TestExecutionService.js +21 -0
- package/build/services/TestGenerationService.d.ts +1 -1
- package/build/tool-phases.js +2 -0
- package/build/tools/auth/loginTool.d.ts +1 -1
- package/build/tools/auth/loginTool.js +2 -2
- package/build/tools/auth/logoutTool.d.ts +1 -1
- package/build/tools/auth/logoutTool.js +2 -2
- package/build/tools/code-refactor/alignAssertionsTool.d.ts +1 -1
- package/build/tools/code-refactor/alignAssertionsTool.js +1 -1
- package/build/tools/code-refactor/assertionSkillTools.d.ts +1 -1
- package/build/tools/code-refactor/assertionSkillTools.js +2 -2
- package/build/tools/code-refactor/codeReuseTool.d.ts +1 -1
- package/build/tools/code-refactor/codeReuseTool.js +2 -2
- package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +1 -1
- package/build/tools/code-refactor/enhanceAssertionsTool.js +1 -1
- package/build/tools/code-refactor/modularizationTool.d.ts +1 -1
- package/build/tools/code-refactor/modularizationTool.js +2 -2
- package/build/tools/enrichTestWithMocksTool.d.ts +1 -1
- package/build/tools/enrichTestWithMocksTool.js +2 -2
- package/build/tools/executeFleetTool.d.ts +28 -0
- package/build/tools/executeFleetTool.js +107 -0
- package/build/tools/executeSkyrampTestTool.d.ts +8 -5
- package/build/tools/executeSkyrampTestTool.js +13 -5
- package/build/tools/fixErrorTool.d.ts +1 -1
- package/build/tools/fixErrorTool.js +1 -1
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +6 -57
- package/build/tools/generate-tests/batchMockGenerationTool.js +3 -3
- package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +9 -45
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +29 -11
- package/build/tools/generate-tests/generateContractRestTool.d.ts +1 -2
- package/build/tools/generate-tests/generateContractRestTool.js +1 -1
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.js +10 -2
- package/build/tools/generate-tests/generateFuzzRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateFuzzRestTool.js +1 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +1 -2
- package/build/tools/generate-tests/generateIntegrationRestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -2
- package/build/tools/generate-tests/generateLoadRestTool.js +2 -1
- package/build/tools/generate-tests/generateMockRestTool.d.ts +1 -2
- package/build/tools/generate-tests/generateMockRestTool.js +3 -3
- package/build/tools/generate-tests/generateSmokeRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateSmokeRestTool.js +1 -1
- package/build/tools/generate-tests/generateUILoadRestTool.d.ts +64 -0
- package/build/tools/generate-tests/generateUILoadRestTool.js +512 -0
- package/build/tools/generate-tests/generateUIRestTool.d.ts +8 -7
- package/build/tools/generate-tests/generateUIRestTool.js +10 -2
- package/build/tools/generate-tests/loadTestSchema.d.ts +15 -15
- package/build/tools/generate-tests/trace-reuse-guard.d.ts +1 -1
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +1 -2
- package/build/tools/generateEnrichedIntegrationTestTool.js +1 -1
- package/build/tools/localDevWorkerComposeTool.d.ts +1 -1
- package/build/tools/localDevWorkerComposeTool.js +2 -2
- package/build/tools/one-click/oneClickTool.d.ts +1 -1
- package/build/tools/one-click/oneClickTool.js +1 -1
- package/build/tools/preflightMockCheckTool.d.ts +1 -1
- package/build/tools/preflightMockCheckTool.js +3 -3
- package/build/tools/queryProxyMocksTool.d.ts +1 -1
- package/build/tools/queryProxyMocksTool.js +3 -3
- package/build/tools/submitReportTool.d.ts +111 -414
- package/build/tools/submitReportTool.js +88 -38
- package/build/tools/test-management/actionsTool.d.ts +1 -1
- package/build/tools/test-management/actionsTool.js +7 -7
- package/build/tools/test-management/analyzeChangesTool.d.ts +22 -118
- package/build/tools/test-management/analyzeChangesTool.js +4 -4
- package/build/tools/test-management/analyzeTestHealthTool.d.ts +1 -1
- package/build/tools/test-management/analyzeTestHealthTool.js +4 -4
- package/build/tools/test-management/registerTestPlanTool.d.ts +107 -733
- package/build/tools/test-management/registerTestPlanTool.js +12 -12
- package/build/tools/test-management/resolveScreenTool.d.ts +1 -1
- package/build/tools/test-management/resolveScreenTool.js +6 -2
- package/build/tools/trace/startTraceCollectionTool.d.ts +1 -1
- package/build/tools/trace/startTraceCollectionTool.js +4 -4
- package/build/tools/trace/stopTraceCollectionTool.d.ts +1 -1
- package/build/tools/trace/stopTraceCollectionTool.js +2 -2
- package/build/tools/verifyTestDependenciesTool.d.ts +1 -1
- package/build/tools/verifyTestDependenciesTool.js +1 -1
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +1 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +1 -1
- package/build/tools/workspace/initializeWorkspaceTool.d.ts +1 -1
- package/build/tools/workspace/initializeWorkspaceTool.js +1 -1
- package/build/types/RepositoryAnalysis.d.ts +164 -1170
- package/build/types/RepositoryAnalysis.js +12 -10
- package/build/types/StepMethod.d.ts +24 -1
- package/build/types/StepMethod.js +1 -1
- package/build/types/TestTypes.d.ts +41 -133
- package/build/types/TestTypes.js +25 -3
- package/build/types/TestbotReport.d.ts +4 -1
- package/build/utils/assertion-verify/metrics.js +12 -0
- package/build/utils/docker.d.ts +29 -7
- package/build/utils/docker.js +83 -27
- package/build/utils/findUp.d.ts +20 -0
- package/build/utils/findUp.js +59 -0
- package/build/utils/initAgent.d.ts +3 -3
- package/build/utils/initAgent.js +58 -26
- package/build/utils/language-helper.js +1 -1
- package/build/utils/progress.d.ts +3 -4
- package/build/utils/progress.js +4 -4
- package/build/utils/repoSdkVersion.d.ts +16 -0
- package/build/utils/repoSdkVersion.js +88 -0
- package/build/utils/testDependencyPolicy.d.ts +20 -0
- package/build/utils/testDependencyPolicy.js +64 -4
- package/build/utils/utils-verify/typecheck.js +3 -18
- package/build/utils/utils.d.ts +1 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +117 -538
- package/build/workspace/workspace.js +2 -2
- package/node_modules/playwright/lib/mcp/skyramp/assertOrder.js +66 -0
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +12 -3
- package/node_modules/playwright/lib/mcp/skyramp/common/liveReadError.js +40 -0
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +21 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +2 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +96 -16
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +10 -0
- 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/recorder/recorderRunner.js +22 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/elementOrder.js +57 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.uiPTFncY.js → index.0MJW0uG7.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +26 -1
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/elementOrder.ts +83 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
- package/node_modules/playwright/package.json +1 -1
- package/package.json +9 -6
- package/plugin/prompts/plan-tests.md +9 -17
- package/plugin/skills/enhance-assertions/reference/shared-rules.md +1 -1
- package/plugin/skills/enhance-assertions/reference/ui.md +2 -2
- package/plugin/skills/fix-test-import-errors/SKILL.md +1 -1
- package/plugin/skills/repair-staged-files/SKILL.md +17 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { logger } from "./logger.js";
|
|
4
|
+
/** Deeper than any real checkout; a bound so a walk can never run away. */
|
|
5
|
+
const MAX_LEVELS = 64;
|
|
6
|
+
/**
|
|
7
|
+
* The first `<dir>/<candidate>` that exists, checking `from` and then each
|
|
8
|
+
* ancestor, or undefined.
|
|
9
|
+
*
|
|
10
|
+
* With `stopAtRepoRoot`, only the directories up to the nearest one holding
|
|
11
|
+
* `.git` (a directory or a file, so worktrees count) are searched, and a `from`
|
|
12
|
+
* outside any checkout searches nothing: a parent directory is not the
|
|
13
|
+
* repository's to answer for. The walk never passes the filesystem root or
|
|
14
|
+
* MAX_LEVELS.
|
|
15
|
+
*/
|
|
16
|
+
export function findUp(from, candidates, { stopAtRepoRoot = false } = {}) {
|
|
17
|
+
const dirs = ancestors(from);
|
|
18
|
+
if (stopAtRepoRoot) {
|
|
19
|
+
const root = dirs.findIndex((dir) => fs.existsSync(path.join(dir, ".git")));
|
|
20
|
+
if (root === -1)
|
|
21
|
+
return undefined;
|
|
22
|
+
dirs.length = root + 1;
|
|
23
|
+
}
|
|
24
|
+
for (const dir of dirs) {
|
|
25
|
+
for (const candidate of candidates) {
|
|
26
|
+
const found = path.join(dir, candidate);
|
|
27
|
+
if (fs.existsSync(found))
|
|
28
|
+
return found;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/** `from` and each ancestor, nearest first, up to the filesystem root. */
|
|
34
|
+
function ancestors(from) {
|
|
35
|
+
const dirs = [path.resolve(from)];
|
|
36
|
+
while (dirs.length < MAX_LEVELS) {
|
|
37
|
+
const parent = path.dirname(dirs[dirs.length - 1]);
|
|
38
|
+
if (parent === dirs[dirs.length - 1])
|
|
39
|
+
break;
|
|
40
|
+
dirs.push(parent);
|
|
41
|
+
}
|
|
42
|
+
return dirs;
|
|
43
|
+
}
|
|
44
|
+
/** The package's manifest as Node would find it from `from`: the nearest
|
|
45
|
+
* `node_modules/<pkg>/package.json` walking UP from there. Node's own lookup
|
|
46
|
+
* never reads NODE_PATH for this, and neither does this one. A manifest that
|
|
47
|
+
* does not parse is logged and treated as absent. */
|
|
48
|
+
export function installedManifest(pkg, from, options) {
|
|
49
|
+
const manifest = findUp(from, [path.join("node_modules", pkg, "package.json")], options);
|
|
50
|
+
if (!manifest)
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(fs.readFileSync(manifest, "utf8"));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logger.warning(`Could not read ${manifest}`, { error: String(error) });
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -16,9 +16,9 @@ export declare function ensureSkyrampMdInstalled(): Promise<void>;
|
|
|
16
16
|
* Previously both images were always pulled regardless.
|
|
17
17
|
* - Parallel pulls: missing images are pulled concurrently via Promise.all, halving
|
|
18
18
|
* wall-clock time when both need to be downloaded.
|
|
19
|
-
* - Arch-aware: pull platform is detected from process.arch (linux/arm64 on
|
|
20
|
-
* linux/amd64 otherwise) with an automatic amd64 fallback if no arm64
|
|
21
|
-
*
|
|
19
|
+
* - Arch-aware: the worker's pull platform is detected from process.arch (linux/arm64 on
|
|
20
|
+
* Apple Silicon, linux/amd64 otherwise) with an automatic amd64 fallback if no arm64
|
|
21
|
+
* manifest exists. The executor is always pulled as amd64, the platform it runs as.
|
|
22
22
|
* - No FFI overhead: direct Dockerode calls replace the Go shared-library bridge,
|
|
23
23
|
* removing the cost of loading the .so and marshalling output through a C string.
|
|
24
24
|
* - Cleaner error propagation: structured errors from Dockerode replace parsing a
|
package/build/utils/initAgent.js
CHANGED
|
@@ -4,7 +4,8 @@ import path from "path";
|
|
|
4
4
|
import { logger } from "./logger.js";
|
|
5
5
|
import { SKYRAMP_MD_CONTENT } from "./skyrampMdContent.js";
|
|
6
6
|
import { EXECUTOR_DOCKER_IMAGE, WORKER_DOCKER_IMAGE } from "./versions.js";
|
|
7
|
-
import { dockerImageExistsLocally, pullDockerImage } from "./docker.js";
|
|
7
|
+
import { dockerDaemonOsType, dockerImageExistsLocally, getDockerPlatform, isPlatformUnavailableError, pullDockerImage, } from "./docker.js";
|
|
8
|
+
import { resolveExecutorImage } from "../services/FleetExecutionService.js";
|
|
8
9
|
// Global skill discovery paths (resolved relative to os.homedir()).
|
|
9
10
|
const SKILL_INSTALL_PATHS = [
|
|
10
11
|
{
|
|
@@ -83,9 +84,9 @@ export async function ensureSkyrampMdInstalled() {
|
|
|
83
84
|
* Previously both images were always pulled regardless.
|
|
84
85
|
* - Parallel pulls: missing images are pulled concurrently via Promise.all, halving
|
|
85
86
|
* wall-clock time when both need to be downloaded.
|
|
86
|
-
* - Arch-aware: pull platform is detected from process.arch (linux/arm64 on
|
|
87
|
-
* linux/amd64 otherwise) with an automatic amd64 fallback if no arm64
|
|
88
|
-
*
|
|
87
|
+
* - Arch-aware: the worker's pull platform is detected from process.arch (linux/arm64 on
|
|
88
|
+
* Apple Silicon, linux/amd64 otherwise) with an automatic amd64 fallback if no arm64
|
|
89
|
+
* manifest exists. The executor is always pulled as amd64, the platform it runs as.
|
|
89
90
|
* - No FFI overhead: direct Dockerode calls replace the Go shared-library bridge,
|
|
90
91
|
* removing the cost of loading the .so and marshalling output through a C string.
|
|
91
92
|
* - Cleaner error propagation: structured errors from Dockerode replace parsing a
|
|
@@ -99,33 +100,64 @@ export async function initCheck() {
|
|
|
99
100
|
await ensureSkyrampMdInstalled();
|
|
100
101
|
logger.info("Skyramp skill files installed/refreshed");
|
|
101
102
|
}
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
// Two executors can be in use: skyramp_execute_test always runs the released constant,
|
|
104
|
+
// and skyramp_execute_fleet runs SKYRAMP_EXECUTOR_IMAGE when it is set. Fetch both, or a
|
|
105
|
+
// normal test run pays for the pull the override displaced.
|
|
106
|
+
const fleetExecutor = resolveExecutorImage();
|
|
107
|
+
// Every image is pulled for the daemon's own OS: a released executor is one tag carrying
|
|
108
|
+
// both, and the tag names no platform. Both run paths start the executor as amd64, so it
|
|
109
|
+
// is pulled as amd64 on every host -- an arm64 copy would not be the one the run starts.
|
|
110
|
+
const daemonOs = await dockerDaemonOsType();
|
|
111
|
+
const executorPlatform = daemonOs === "windows" ? "windows/amd64" : "linux/amd64";
|
|
112
|
+
const workerPlatform = daemonOs === "windows" ? "windows/amd64" : getDockerPlatform();
|
|
113
|
+
const executors = [...new Set([EXECUTOR_DOCKER_IMAGE, fleetExecutor])];
|
|
114
|
+
// An executor image without this daemon's OS -- an older release, or a single-OS
|
|
115
|
+
// SKYRAMP_EXECUTOR_IMAGE -- has nothing to pull here. That is a fact about the image,
|
|
116
|
+
// not a broken environment, so its pull is optional.
|
|
117
|
+
const wanted = [
|
|
118
|
+
...executors.map((image) => ({
|
|
119
|
+
what: "executor",
|
|
120
|
+
image,
|
|
121
|
+
platform: executorPlatform,
|
|
122
|
+
optional: true,
|
|
123
|
+
})),
|
|
124
|
+
{
|
|
125
|
+
what: "worker",
|
|
126
|
+
image: WORKER_DOCKER_IMAGE,
|
|
127
|
+
platform: workerPlatform,
|
|
128
|
+
optional: false,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
// Presence is checked for all of them at once; only the pulls that follow are conditional.
|
|
132
|
+
const present = await Promise.all(wanted.map((w) => dockerImageExistsLocally(w.image)));
|
|
107
133
|
const pulls = [];
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
134
|
+
wanted.forEach(({ what, image, platform, optional }, i) => {
|
|
135
|
+
if (present[i])
|
|
136
|
+
return;
|
|
137
|
+
logger.info(`Pulling missing ${what} image`, { image, platform });
|
|
138
|
+
const pull = pullDockerImage(image, platform);
|
|
139
|
+
// Only an image with nothing for this platform is tolerated, and only when optional.
|
|
140
|
+
// Any other failure -- the network, a rate limit, a full disk -- fails init so it is
|
|
141
|
+
// retried, instead of being reported as a missing variant.
|
|
142
|
+
pulls.push(optional
|
|
143
|
+
? pull.catch((err) => {
|
|
144
|
+
if (!isPlatformUnavailableError(err))
|
|
145
|
+
throw err;
|
|
146
|
+
logger.warning("Executor image has no variant for this daemon's platform: fleet runs " +
|
|
147
|
+
"need an executor built for it (a release that carries it, or " +
|
|
148
|
+
"SKYRAMP_EXECUTOR_IMAGE)", { image, platform, error: err.message });
|
|
149
|
+
})
|
|
150
|
+
: pull);
|
|
151
|
+
});
|
|
152
|
+
if (pulls.length > 0) {
|
|
153
|
+
await Promise.all(pulls);
|
|
154
|
+
logger.info("Skyramp init completed");
|
|
118
155
|
}
|
|
119
|
-
|
|
156
|
+
else {
|
|
120
157
|
logger.info("Skyramp images already present locally, skipping pull", {
|
|
121
|
-
|
|
122
|
-
worker: WORKER_DOCKER_IMAGE,
|
|
158
|
+
images: wanted.map((w) => w.image),
|
|
123
159
|
});
|
|
124
160
|
}
|
|
125
|
-
else {
|
|
126
|
-
await Promise.all(pulls);
|
|
127
|
-
logger.info("Skyramp init completed");
|
|
128
|
-
}
|
|
129
161
|
}
|
|
130
162
|
let initInFlight = false;
|
|
131
163
|
let initCompleted = false;
|
|
@@ -36,7 +36,7 @@ npx playwright test <test-file>.spec.${extension} --reporter=list
|
|
|
36
36
|
* a mock is deployed rather than run as a test — it needs no SDK. */
|
|
37
37
|
const SDK_DECLARATION = {
|
|
38
38
|
python: "Declare `skyramp` in the same development manifest.",
|
|
39
|
-
javascript: `If @skyramp/skyramp is not already declared, add ${npmSdkSpec} as a devDependency. If it is declared,
|
|
39
|
+
javascript: `If @skyramp/skyramp is not already declared, add ${npmSdkSpec} as a devDependency. If it is declared, keep its version unless skyramp_execute_test refuses the run for an older SDK.`,
|
|
40
40
|
java: "Declare `dev.skyramp:skyramp-library` with test scope. An existing manifest resolves the version through its own dependency management; a manifest you created has none, so give a concrete version there.",
|
|
41
41
|
};
|
|
42
42
|
/** The SDK line for a language, or nothing when the language has none. */
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
1
|
+
import { ServerContext } from "@modelcontextprotocol/server";
|
|
3
2
|
/**
|
|
4
3
|
* Reports incremental progress for a long-running tool call. No-ops when the
|
|
5
4
|
* client did not supply a progress token.
|
|
6
5
|
*/
|
|
7
6
|
export type ProgressReporter = (progress: number, total: number, message: string) => Promise<void>;
|
|
8
7
|
/**
|
|
9
|
-
* Build a progress reporter bound to a tool handler's
|
|
8
|
+
* Build a progress reporter bound to a tool handler's context. Sends
|
|
10
9
|
* `notifications/progress` only when the client provided a `progressToken`,
|
|
11
10
|
* otherwise silently no-ops.
|
|
12
11
|
*
|
|
13
12
|
* Consolidates the identical closure that previously lived inline in multiple
|
|
14
13
|
* progress-emitting tools (analyzeChanges, startTraceCollection).
|
|
15
14
|
*/
|
|
16
|
-
export declare function makeProgressReporter(
|
|
15
|
+
export declare function makeProgressReporter(ctx: ServerContext): ProgressReporter;
|
package/build/utils/progress.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build a progress reporter bound to a tool handler's
|
|
2
|
+
* Build a progress reporter bound to a tool handler's context. Sends
|
|
3
3
|
* `notifications/progress` only when the client provided a `progressToken`,
|
|
4
4
|
* otherwise silently no-ops.
|
|
5
5
|
*
|
|
6
6
|
* Consolidates the identical closure that previously lived inline in multiple
|
|
7
7
|
* progress-emitting tools (analyzeChanges, startTraceCollection).
|
|
8
8
|
*/
|
|
9
|
-
export function makeProgressReporter(
|
|
9
|
+
export function makeProgressReporter(ctx) {
|
|
10
10
|
return async (progress, total, message) => {
|
|
11
|
-
const progressToken =
|
|
11
|
+
const progressToken = ctx.mcpReq._meta?.progressToken;
|
|
12
12
|
if (progressToken !== undefined) {
|
|
13
13
|
try {
|
|
14
14
|
const notification = {
|
|
15
15
|
method: "notifications/progress",
|
|
16
16
|
params: { progressToken, progress, total, message },
|
|
17
17
|
};
|
|
18
|
-
await
|
|
18
|
+
await ctx.mcpReq.notify(notification);
|
|
19
19
|
}
|
|
20
20
|
catch {
|
|
21
21
|
// Progress is best-effort; a disconnected client should not fail the tool.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SdkEcosystem } from "./testDependencyPolicy.js";
|
|
2
|
+
/**
|
|
3
|
+
* The Skyramp SDK version the repository that owns `testFile` runs, as far as it
|
|
4
|
+
* can be read without its toolchain: the installed npm package, else the exact
|
|
5
|
+
* pin in the nearest manifest. Undefined when neither says. Both lookups stop at
|
|
6
|
+
* the repository root.
|
|
7
|
+
*/
|
|
8
|
+
export declare function repoSkyrampSdkVersion(testFile: string, ecosystem: SdkEcosystem): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* SKYR-4548: the refusal for a host run whose repository uses an older Skyramp
|
|
11
|
+
* SDK than the one that generated the test, or undefined when the run may go
|
|
12
|
+
* ahead. Such a run fails at import, which reads as a broken test. No readable
|
|
13
|
+
* version means no refusal: a missing SDK is an import error that
|
|
14
|
+
* fix-test-import-errors owns.
|
|
15
|
+
*/
|
|
16
|
+
export declare function staleSdkRefusal(testFile: string, language: string, serverVersion?: string): string | undefined;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import semver from "semver";
|
|
4
|
+
import { ProgrammingLanguage } from "../types/TestTypes.js";
|
|
5
|
+
import { findUp, installedManifest } from "./findUp.js";
|
|
6
|
+
import { logger } from "./logger.js";
|
|
7
|
+
import { SKYRAMP_SDK_VERSION } from "./skyrampSdkVersion.js";
|
|
8
|
+
import { manifestNames, pinnedSkyrampSdkVersion, SKYRAMP_SDK_PACKAGE, } from "./testDependencyPolicy.js";
|
|
9
|
+
const ECOSYSTEM = {
|
|
10
|
+
[ProgrammingLanguage.TYPESCRIPT]: "javascript",
|
|
11
|
+
[ProgrammingLanguage.JAVASCRIPT]: "javascript",
|
|
12
|
+
[ProgrammingLanguage.PYTHON]: "python",
|
|
13
|
+
[ProgrammingLanguage.JAVA]: "java",
|
|
14
|
+
};
|
|
15
|
+
/** The one edit that moves the repository onto the SDK that generated the
|
|
16
|
+
* test, and that skyramp_verify_test_dependencies accepts. */
|
|
17
|
+
const PIN_EDIT = {
|
|
18
|
+
javascript: (v) => `Declare ${SKYRAMP_SDK_PACKAGE.javascript} under devDependencies only (move it out of dependencies if it is there), at exactly "${SKYRAMP_SDK_PACKAGE.javascript}": "${v}" with no range operator`,
|
|
19
|
+
python: (v) => `Change only the requirement to ${SKYRAMP_SDK_PACKAGE.python}==${v}`,
|
|
20
|
+
java: (v) => `Change only the ${SKYRAMP_SDK_PACKAGE.java} version to ${v}`,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The Skyramp SDK version the repository that owns `testFile` runs, as far as it
|
|
24
|
+
* can be read without its toolchain: the installed npm package, else the exact
|
|
25
|
+
* pin in the nearest manifest. Undefined when neither says. Both lookups stop at
|
|
26
|
+
* the repository root.
|
|
27
|
+
*/
|
|
28
|
+
export function repoSkyrampSdkVersion(testFile, ecosystem) {
|
|
29
|
+
const from = path.dirname(path.resolve(testFile));
|
|
30
|
+
if (ecosystem === "javascript") {
|
|
31
|
+
const version = installedManifest(SKYRAMP_SDK_PACKAGE.javascript, from, {
|
|
32
|
+
stopAtRepoRoot: true,
|
|
33
|
+
})?.version;
|
|
34
|
+
if (typeof version === "string")
|
|
35
|
+
return version;
|
|
36
|
+
}
|
|
37
|
+
const names = manifestNames(ecosystem);
|
|
38
|
+
const nearest = findUp(from, names, { stopAtRepoRoot: true });
|
|
39
|
+
if (!nearest)
|
|
40
|
+
return undefined;
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
const manifest = path.join(path.dirname(nearest), name);
|
|
43
|
+
if (!fs.existsSync(manifest))
|
|
44
|
+
continue;
|
|
45
|
+
try {
|
|
46
|
+
const pinned = pinnedSkyrampSdkVersion(name, fs.readFileSync(manifest, "utf8"));
|
|
47
|
+
if (pinned)
|
|
48
|
+
return pinned;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.warning(`Could not read the Skyramp SDK version in ${manifest}`, {
|
|
52
|
+
error: String(error),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* SKYR-4548: the refusal for a host run whose repository uses an older Skyramp
|
|
60
|
+
* SDK than the one that generated the test, or undefined when the run may go
|
|
61
|
+
* ahead. Such a run fails at import, which reads as a broken test. No readable
|
|
62
|
+
* version means no refusal: a missing SDK is an import error that
|
|
63
|
+
* fix-test-import-errors owns.
|
|
64
|
+
*/
|
|
65
|
+
export function staleSdkRefusal(testFile, language, serverVersion = SKYRAMP_SDK_VERSION) {
|
|
66
|
+
const ecosystem = ECOSYSTEM[language];
|
|
67
|
+
if (!ecosystem)
|
|
68
|
+
return undefined;
|
|
69
|
+
const repoVersion = repoSkyrampSdkVersion(testFile, ecosystem);
|
|
70
|
+
if (!isOlderRelease(repoVersion, serverVersion))
|
|
71
|
+
return undefined;
|
|
72
|
+
return (`Not executed: this repository uses Skyramp SDK ${repoVersion}, but the test was generated by ${serverVersion}. ` +
|
|
73
|
+
`${PIN_EDIT[ecosystem](serverVersion)}, update the lockfile if the repository commits one, reinstall, ` +
|
|
74
|
+
`run skyramp_verify_test_dependencies, and call this tool again. Report the SDK change as an issuesFound entry with category config.`);
|
|
75
|
+
}
|
|
76
|
+
/** True when `repo` is an older release than `server`. Pre-release tags are
|
|
77
|
+
* ignored (1.3.47-rc.1 is 1.3.47), and a version semver cannot read is never
|
|
78
|
+
* older: the gate must not refuse a run on a guess. */
|
|
79
|
+
function isOlderRelease(repo, server) {
|
|
80
|
+
const a = repo ? semver.parse(repo) : null;
|
|
81
|
+
const b = semver.parse(server);
|
|
82
|
+
if (!a || !b) {
|
|
83
|
+
if (repo)
|
|
84
|
+
logger.warning(`Cannot compare Skyramp SDK ${repo} to ${server}`);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return a.compareMain(b) < 0;
|
|
88
|
+
}
|
|
@@ -7,3 +7,23 @@ export interface TestDependencyVerification {
|
|
|
7
7
|
violations: string[];
|
|
8
8
|
}
|
|
9
9
|
export declare function verifyTestDependencyChanges(repositoryPath: string): Promise<TestDependencyVerification>;
|
|
10
|
+
/** The Skyramp SDK's name in each ecosystem. All three are released in lockstep
|
|
11
|
+
* with the npm SDK this server imports, so SKYRAMP_SDK_VERSION is the version
|
|
12
|
+
* that generated a test in any of them. */
|
|
13
|
+
export declare const SKYRAMP_SDK_PACKAGE: {
|
|
14
|
+
readonly javascript: "@skyramp/skyramp";
|
|
15
|
+
readonly python: "skyramp";
|
|
16
|
+
readonly java: "dev.skyramp:skyramp-library";
|
|
17
|
+
};
|
|
18
|
+
export type SdkEcosystem = keyof typeof SKYRAMP_SDK_PACKAGE;
|
|
19
|
+
/** The manifest file names an ecosystem declares its dependencies in. */
|
|
20
|
+
export declare function manifestNames(ecosystem: SdkEcosystem): readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* The exact Skyramp SDK version a manifest pins where the change verifier lets
|
|
23
|
+
* the agent raise it, or undefined: the SDK is absent, declared as a range,
|
|
24
|
+
* resolved elsewhere (a Maven property or BOM, a Gradle catalog), or pinned in
|
|
25
|
+
* a place the verifier refuses to change (`dependencies`, requirements.txt, a
|
|
26
|
+
* Maven block without test scope, a Gradle `implementation` line). A refusal
|
|
27
|
+
* over such a pin would have no fix the agent may make.
|
|
28
|
+
*/
|
|
29
|
+
export declare function pinnedSkyrampSdkVersion(fileName: string, raw: string): string | undefined;
|
|
@@ -4,6 +4,7 @@ import * as path from "path";
|
|
|
4
4
|
import { parse as parseToml } from "smol-toml";
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
import fg from "fast-glob";
|
|
7
|
+
import semver from "semver";
|
|
7
8
|
import { canonicalJson as canonical, isPlainObject as isObject, } from "./canonicalJson.js";
|
|
8
9
|
import { listChangedFiles } from "./reportVerification.js";
|
|
9
10
|
import { contentAtHead } from "./utils-verify/head.js";
|
|
@@ -278,18 +279,77 @@ function verifyPackageJson(file, beforeRaw, afterRaw, violations) {
|
|
|
278
279
|
canonical(beforeDev) !== canonical(afterDev));
|
|
279
280
|
}
|
|
280
281
|
/** The SDK the tests were generated by is the one this server imports, so the
|
|
281
|
-
* repository must declare exactly that version. Only
|
|
282
|
-
*
|
|
283
|
-
*
|
|
282
|
+
* repository must declare exactly that version. Only a changed npm entry is
|
|
283
|
+
* checked here: the guidance declares the Python and Java SDK without a
|
|
284
|
+
* version. A pin a repository already has is read by pinnedSkyrampSdkVersion.
|
|
284
285
|
*
|
|
285
286
|
* The specifier must be the bare version. A range is refused, including
|
|
286
287
|
* `^1.2.3`: it permits 1.3.0, so a clean install elsewhere can resolve an SDK
|
|
287
288
|
* that did not write these tests, which is the whole thing this prevents. */
|
|
288
289
|
function declaresServerSdkVersion(name, specifier) {
|
|
289
|
-
if (name !==
|
|
290
|
+
if (name !== SKYRAMP_SDK_PACKAGE.javascript)
|
|
290
291
|
return true;
|
|
291
292
|
return specifier.trim() === SKYRAMP_SDK_VERSION;
|
|
292
293
|
}
|
|
294
|
+
/** The Skyramp SDK's name in each ecosystem. All three are released in lockstep
|
|
295
|
+
* with the npm SDK this server imports, so SKYRAMP_SDK_VERSION is the version
|
|
296
|
+
* that generated a test in any of them. */
|
|
297
|
+
export const SKYRAMP_SDK_PACKAGE = {
|
|
298
|
+
javascript: "@skyramp/skyramp",
|
|
299
|
+
python: "skyramp",
|
|
300
|
+
java: "dev.skyramp:skyramp-library",
|
|
301
|
+
};
|
|
302
|
+
/** The manifest file names an ecosystem declares its dependencies in. */
|
|
303
|
+
export function manifestNames(ecosystem) {
|
|
304
|
+
return DEPENDENCY_FILES[ecosystem].manifests;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* The exact Skyramp SDK version a manifest pins where the change verifier lets
|
|
308
|
+
* the agent raise it, or undefined: the SDK is absent, declared as a range,
|
|
309
|
+
* resolved elsewhere (a Maven property or BOM, a Gradle catalog), or pinned in
|
|
310
|
+
* a place the verifier refuses to change (`dependencies`, requirements.txt, a
|
|
311
|
+
* Maven block without test scope, a Gradle `implementation` line). A refusal
|
|
312
|
+
* over such a pin would have no fix the agent may make.
|
|
313
|
+
*/
|
|
314
|
+
export function pinnedSkyrampSdkVersion(fileName, raw) {
|
|
315
|
+
switch (fileName) {
|
|
316
|
+
case "package.json":
|
|
317
|
+
return exactVersion(stringMap(JSON.parse(raw).devDependencies)[SKYRAMP_SDK_PACKAGE.javascript]);
|
|
318
|
+
case "requirements-dev.txt":
|
|
319
|
+
return pep440Pin(requirementsMap(raw).dependencies[SKYRAMP_SDK_PACKAGE.python]);
|
|
320
|
+
case "pyproject.toml": {
|
|
321
|
+
// Poetry reads a bare version as an exact pin; PEP 735 carries `==`.
|
|
322
|
+
const spec = pythonDevView(raw).dependencies[SKYRAMP_SDK_PACKAGE.python];
|
|
323
|
+
return pep440Pin(spec) ?? exactVersion(spec);
|
|
324
|
+
}
|
|
325
|
+
case "pom.xml": {
|
|
326
|
+
const blocks = mavenView(raw.replace(/<!--[\s\S]*?-->/g, ""))
|
|
327
|
+
.dependencies[SKYRAMP_SDK_PACKAGE.java];
|
|
328
|
+
return blocks?.length === 1 && !mavenDependency(blocks[0]).error
|
|
329
|
+
? exactVersion(singleXmlTag(blocks[0], "version"))
|
|
330
|
+
: undefined;
|
|
331
|
+
}
|
|
332
|
+
case "build.gradle":
|
|
333
|
+
case "build.gradle.kts": {
|
|
334
|
+
const lines = gradleView(raw).dependencies[SKYRAMP_SDK_PACKAGE.java];
|
|
335
|
+
if (lines?.length !== 1)
|
|
336
|
+
return undefined;
|
|
337
|
+
const [configuration, value] = lines[0].split("\0");
|
|
338
|
+
return configuration === "testImplementation" ||
|
|
339
|
+
configuration === "testRuntimeOnly"
|
|
340
|
+
? exactVersion(value.split(":")[2])
|
|
341
|
+
: undefined;
|
|
342
|
+
}
|
|
343
|
+
default:
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function exactVersion(spec) {
|
|
348
|
+
return (spec && semver.valid(spec.trim())) || undefined;
|
|
349
|
+
}
|
|
350
|
+
function pep440Pin(requirement) {
|
|
351
|
+
return exactVersion(/==\s*([^,;\s]+)/.exec(requirement ?? "")?.[1]);
|
|
352
|
+
}
|
|
293
353
|
function verifyPyproject(file, beforeRaw, afterRaw, violations) {
|
|
294
354
|
const before = pythonDevView(beforeRaw);
|
|
295
355
|
const after = pythonDevView(afterRaw);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
|
-
import {
|
|
2
|
+
import { installedManifest } from "../findUp.js";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
let tsModule;
|
|
5
5
|
/** The compiler, loaded on first use and once per process. Loaded statically it
|
|
@@ -64,6 +64,8 @@ function executorPeerPackages(mcpRoot) {
|
|
|
64
64
|
return peers;
|
|
65
65
|
peers = [];
|
|
66
66
|
for (const pkg of EXECUTOR_PROVIDED_PACKAGES) {
|
|
67
|
+
// Nearest `node_modules/<pkg>` walking UP: `npm install @skyramp/mcp`,
|
|
68
|
+
// testbot's default, hoists the MCP's dependencies BESIDE it, not under it.
|
|
67
69
|
const manifest = installedManifest(pkg, mcpRoot);
|
|
68
70
|
if (manifest)
|
|
69
71
|
peers.push(...Object.keys(manifest.peerDependencies ?? {}));
|
|
@@ -74,23 +76,6 @@ function executorPeerPackages(mcpRoot) {
|
|
|
74
76
|
peersByRoot.set(mcpRoot, peers);
|
|
75
77
|
return peers;
|
|
76
78
|
}
|
|
77
|
-
/** The package's manifest as Node would find it from `from`: the nearest
|
|
78
|
-
* `node_modules/<pkg>` walking UP from there. The MCP's dependencies are not
|
|
79
|
-
* always nested under its own root — `npm install @skyramp/mcp`, testbot's
|
|
80
|
-
* default, hoists them BESIDE `@skyramp/mcp` — and a read at one fixed path
|
|
81
|
-
* found nothing on that layout, left the peer list empty, and brought the two
|
|
82
|
-
* Playwright identities back exactly where customers run. */
|
|
83
|
-
function installedManifest(pkg, from) {
|
|
84
|
-
for (let dir = from;; dir = path.dirname(dir)) {
|
|
85
|
-
try {
|
|
86
|
-
return JSON.parse(readFileSync(path.join(dir, "node_modules", pkg, "package.json"), "utf8"));
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
if (path.dirname(dir) === dir)
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
79
|
const isPackageOrSubpath = (specifier, packages) => packages.some((p) => specifier === p || specifier.startsWith(`${p}/`));
|
|
95
80
|
function executorProvides(specifier) {
|
|
96
81
|
return isPackageOrSubpath(specifier, EXECUTOR_PROVIDED_PACKAGES);
|
package/build/utils/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/
|
|
1
|
+
import { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/server";
|
|
2
2
|
export declare function toolError(message: string): CallToolResult;
|
|
3
3
|
/**
|
|
4
4
|
* Build a successful tool result whose payload is delivered on BOTH MCP result
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const SKYRAMP_IMAGE_VERSION = "v1.3.
|
|
2
|
-
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.
|
|
3
|
-
export declare const WORKER_DOCKER_IMAGE = "skyramp/worker:v1.3.
|
|
1
|
+
export declare const SKYRAMP_IMAGE_VERSION = "v1.3.50";
|
|
2
|
+
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.50";
|
|
3
|
+
export declare const WORKER_DOCKER_IMAGE = "skyramp/worker:v1.3.50";
|
|
4
4
|
export declare const WORKER_CONTROL_PORT = 35142;
|
package/build/utils/versions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const SKYRAMP_IMAGE_VERSION = "v1.3.
|
|
1
|
+
export const SKYRAMP_IMAGE_VERSION = "v1.3.50";
|
|
2
2
|
export const EXECUTOR_DOCKER_IMAGE = `skyramp/executor:${SKYRAMP_IMAGE_VERSION}`;
|
|
3
3
|
export const WORKER_DOCKER_IMAGE = `skyramp/worker:${SKYRAMP_IMAGE_VERSION}`;
|
|
4
4
|
// Control port the Skyramp worker listens on (SDK `CONTAINER_PORT`).
|