@skyramp/mcp 0.4.4-rc.2 → 0.5.0-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/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 +15 -0
- package/build/index.js +4 -4
- package/build/playwright/essentialTools.d.ts +1 -0
- package/build/playwright/essentialTools.js +43 -0
- package/build/playwright/registerPlaywrightTools.js +1 -43
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +46 -177
- package/build/prompts/test-maintenance/driftAnalysisShared.js +1 -1
- package/build/tool-phases.d.ts +14 -8
- package/build/tool-phases.js +83 -43
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +11 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +100 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +40 -4
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +51 -121
- package/build/workspace/workspace.js +34 -47
- package/build/workspace/workspaceVerifier.d.ts +22 -0
- package/build/workspace/workspaceVerifier.js +65 -0
- package/package.json +2 -2
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/skills/setup-testbot-target-sut/SKILL.md +54 -0
- package/plugin/skills/setup-testbot-target-sut/reference/ci-workflow.md +26 -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/build/tool-phases.js
CHANGED
|
@@ -1,25 +1,87 @@
|
|
|
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_trace_collection: { entitlement: "execute" },
|
|
77
|
+
skyramp_stop_trace_collection: { entitlement: "execute" },
|
|
78
|
+
skyramp_submit_report: { phase: "reporting", entitlement: NOT_GATED },
|
|
79
|
+
skyramp_login: { entitlement: NOT_GATED },
|
|
80
|
+
skyramp_logout: { entitlement: NOT_GATED },
|
|
81
|
+
skyramp_init_scan: { entitlement: NOT_GATED },
|
|
82
|
+
skyramp_init_workspace: { entitlement: NOT_GATED },
|
|
22
83
|
};
|
|
84
|
+
export const TOOL_PHASE_MAP = Object.fromEntries(Object.entries(TOOLS).flatMap(([name, entry]) => entry.phase === undefined ? [] : [[name, entry.phase]]));
|
|
23
85
|
/** Tools whose phase depends on context — listed here for consumer discovery. */
|
|
24
86
|
export const CONTEXT_DEPENDENT_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
25
87
|
.filter((entry) => typeof entry[1] === "object")
|
|
@@ -28,25 +90,3 @@ export const CONTEXT_DEPENDENT_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
|
28
90
|
export const GENERATING_TOOLS = new Set(Object.entries(TOOL_PHASE_MAP)
|
|
29
91
|
.filter((entry) => typeof entry[1] === "string" && entry[1] === "generating")
|
|
30
92
|
.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
|
-
]);
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
-
|
|
2
|
+
import { type ScanRelatedRepository } from "../../prompts/initialize-workspace/initializeWorkspacePrompt.js";
|
|
3
|
+
/** What a scan token was issued for: when it expires, and the related
|
|
4
|
+
* repositories its scan named, which skyramp_init_workspace verifies the
|
|
5
|
+
* services against. */
|
|
6
|
+
export interface ScanGrant {
|
|
7
|
+
expiry: number;
|
|
8
|
+
relatedRepositories: ScanRelatedRepository[];
|
|
9
|
+
}
|
|
10
|
+
export declare function issueScanToken(relatedRepositories?: ScanRelatedRepository[]): string;
|
|
11
|
+
/** The grant of a valid, unexpired token, without consuming it. */
|
|
12
|
+
export declare function scanTokenGrant(token: string): ScanGrant | undefined;
|
|
3
13
|
export declare function validateAndConsumeScanToken(token: string): boolean;
|
|
4
14
|
export declare function registerInitScanWorkspaceTool(server: McpServer): void;
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
3
4
|
import { WorkspaceConfigManager } from "../../workspace/workspace.js";
|
|
5
|
+
import { resolveRealPath, tryRepositorySlug, } from "../../utils/repositorySlug.js";
|
|
4
6
|
import { logger } from "../../utils/logger.js";
|
|
5
7
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
6
|
-
import {
|
|
8
|
+
import { buildInitWorkspaceInstructions, } from "../../prompts/initialize-workspace/initializeWorkspacePrompt.js";
|
|
7
9
|
const TOOL_NAME = "skyramp_init_scan";
|
|
8
|
-
// Token store: token -> expiry timestamp (ms)
|
|
9
10
|
const scanTokens = new Map();
|
|
10
11
|
const TOKEN_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
11
|
-
export function issueScanToken() {
|
|
12
|
+
export function issueScanToken(relatedRepositories = []) {
|
|
12
13
|
const token = randomUUID();
|
|
13
|
-
scanTokens.set(token,
|
|
14
|
+
scanTokens.set(token, {
|
|
15
|
+
expiry: Date.now() + TOKEN_TTL_MS,
|
|
16
|
+
relatedRepositories,
|
|
17
|
+
});
|
|
14
18
|
return token;
|
|
15
19
|
}
|
|
20
|
+
/** The grant of a valid, unexpired token, without consuming it. */
|
|
21
|
+
export function scanTokenGrant(token) {
|
|
22
|
+
const grant = scanTokens.get(token);
|
|
23
|
+
return grant && Date.now() < grant.expiry ? grant : undefined;
|
|
24
|
+
}
|
|
16
25
|
export function validateAndConsumeScanToken(token) {
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
26
|
+
const grant = scanTokens.get(token);
|
|
27
|
+
if (!grant)
|
|
19
28
|
return false;
|
|
20
29
|
scanTokens.delete(token);
|
|
21
|
-
return Date.now() < expiry;
|
|
30
|
+
return Date.now() < grant.expiry;
|
|
22
31
|
}
|
|
23
32
|
const initScanWorkspaceSchema = z.object({
|
|
24
33
|
workspacePath: z.string().describe("Path to the workspace/repository root"),
|
|
@@ -26,12 +35,66 @@ const initScanWorkspaceSchema = z.object({
|
|
|
26
35
|
.boolean()
|
|
27
36
|
.default(false)
|
|
28
37
|
.describe("Set to true to force a re-scan even if .skyramp/workspace.yml already exists. Default is false."),
|
|
38
|
+
relatedRepositories: z
|
|
39
|
+
.array(z.object({
|
|
40
|
+
repositoryPath: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe("Path to the related repository's checkout"),
|
|
43
|
+
repository: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("The related repository's owner/repo. Defaults to the one its checkout's origin remote names."),
|
|
47
|
+
}))
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("The other repositories of a multi-repository Target. Each is scanned into the one workspace.yml at workspacePath, its services carrying its owner/repo as `repository`; none gets a workspace.yml of its own. Omit for a single repository."),
|
|
50
|
+
testRepositoryPath: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Path to the separate test repository generated tests are delivered into, when there is one. Each service's testDirectory is then read from, and relative to, that checkout."),
|
|
29
54
|
});
|
|
55
|
+
const OWNER_REPO = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
56
|
+
function isDirectory(p) {
|
|
57
|
+
try {
|
|
58
|
+
return fs.statSync(p).isDirectory();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Each related repository with its owner/repo, or the first problem with them:
|
|
66
|
+
* a checkout that is not a directory, is the workspace itself, or names no
|
|
67
|
+
* owner/repo, and a repository listed twice. Paths compare with symlinks
|
|
68
|
+
* resolved, and owner/repo case-insensitively, as GitHub does.
|
|
69
|
+
*/
|
|
70
|
+
async function resolveRelatedRepositories(workspacePath, entries) {
|
|
71
|
+
const repos = [];
|
|
72
|
+
const workspaceReal = await resolveRealPath(workspacePath);
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
const at = entry.repositoryPath;
|
|
75
|
+
if (!isDirectory(at))
|
|
76
|
+
return { error: `relatedRepositories: ${at} is not a directory` };
|
|
77
|
+
if ((await resolveRealPath(at)) === workspaceReal)
|
|
78
|
+
return {
|
|
79
|
+
error: `relatedRepositories: ${at} is workspacePath itself; list only the other repositories`,
|
|
80
|
+
};
|
|
81
|
+
const repository = entry.repository?.trim() || (await tryRepositorySlug(at));
|
|
82
|
+
if (!repository || !OWNER_REPO.test(repository))
|
|
83
|
+
return {
|
|
84
|
+
error: `relatedRepositories: no owner/repo for ${at}${repository ? ` ("${repository}" is not owner/repo)` : " (its checkout has no origin remote)"}; pass its repository`,
|
|
85
|
+
};
|
|
86
|
+
if (repos.some((r) => r.repository.toLowerCase() === repository.toLowerCase()))
|
|
87
|
+
return { error: `relatedRepositories: ${repository} is listed twice` };
|
|
88
|
+
repos.push({ repository, repositoryPath: at });
|
|
89
|
+
}
|
|
90
|
+
return { repos };
|
|
91
|
+
}
|
|
30
92
|
export function registerInitScanWorkspaceTool(server) {
|
|
31
93
|
server.registerTool(TOOL_NAME, {
|
|
32
94
|
description: "Call this FIRST, before any other Skyramp tool (test generation, execution, analysis), whenever workspacePath is a git repository and .skyramp/workspace.yml does not exist at its root. " +
|
|
33
95
|
"PREREQUISITE for skyramp_init_workspace. " +
|
|
34
96
|
"Scans a repository and returns instructions for discovering all services, along with a scanToken. " +
|
|
97
|
+
"For a Target spanning several repositories, call it ONCE with the others in relatedRepositories, so all their services go into one workspace.yml. " +
|
|
35
98
|
"Follow the instructions to build the services array, then pass both to skyramp_init_workspace to create the workspace config.",
|
|
36
99
|
inputSchema: initScanWorkspaceSchema,
|
|
37
100
|
}, async (params) => {
|
|
@@ -39,6 +102,8 @@ export function registerInitScanWorkspaceTool(server) {
|
|
|
39
102
|
const recordParams = {
|
|
40
103
|
workspacePath: params.workspacePath,
|
|
41
104
|
force: String(params.force || false),
|
|
105
|
+
relatedRepositories: String(params.relatedRepositories?.length ?? 0),
|
|
106
|
+
testRepository: String(Boolean(params.testRepositoryPath)),
|
|
42
107
|
};
|
|
43
108
|
try {
|
|
44
109
|
const manager = new WorkspaceConfigManager(params.workspacePath);
|
|
@@ -53,12 +118,38 @@ export function registerInitScanWorkspaceTool(server) {
|
|
|
53
118
|
isError: false,
|
|
54
119
|
};
|
|
55
120
|
}
|
|
56
|
-
const
|
|
121
|
+
const related = await resolveRelatedRepositories(params.workspacePath, params.relatedRepositories ?? []);
|
|
122
|
+
if ("error" in related) {
|
|
123
|
+
errorResult = {
|
|
124
|
+
content: [{ type: "text", text: related.error }],
|
|
125
|
+
isError: true,
|
|
126
|
+
};
|
|
127
|
+
return errorResult;
|
|
128
|
+
}
|
|
129
|
+
if (params.testRepositoryPath &&
|
|
130
|
+
!isDirectory(params.testRepositoryPath)) {
|
|
131
|
+
errorResult = {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: `testRepositoryPath: ${params.testRepositoryPath} is not a directory`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
return errorResult;
|
|
141
|
+
}
|
|
142
|
+
const token = issueScanToken(related.repos);
|
|
143
|
+
const instructions = buildInitWorkspaceInstructions({
|
|
144
|
+
workspacePath: params.workspacePath,
|
|
145
|
+
relatedRepositories: related.repos,
|
|
146
|
+
testRepositoryPath: params.testRepositoryPath,
|
|
147
|
+
});
|
|
57
148
|
return {
|
|
58
149
|
content: [
|
|
59
150
|
{
|
|
60
151
|
type: "text",
|
|
61
|
-
text: `SCAN TOKEN: ${token}\n\nSave this token — you MUST pass it as scanToken when calling skyramp_init_workspace.\n\n${
|
|
152
|
+
text: `SCAN TOKEN: ${token}\n\nSave this token — you MUST pass it as scanToken when calling skyramp_init_workspace.\n\n${instructions}`,
|
|
62
153
|
},
|
|
63
154
|
],
|
|
64
155
|
isError: false,
|
|
@@ -7,7 +7,8 @@ import yaml from "js-yaml";
|
|
|
7
7
|
import { logger } from "../../utils/logger.js";
|
|
8
8
|
import { AnalyticsService } from "../../services/AnalyticsService.js";
|
|
9
9
|
import { SKYRAMP_IMAGE_VERSION } from "../../utils/versions.js";
|
|
10
|
-
import { validateAndConsumeScanToken } from "./initScanWorkspaceTool.js";
|
|
10
|
+
import { scanTokenGrant, validateAndConsumeScanToken, } from "./initScanWorkspaceTool.js";
|
|
11
|
+
import { verifyWorkspaceServices } from "../../workspace/workspaceVerifier.js";
|
|
11
12
|
import { upsertServicesByRepo } from "./serviceUpsert.js";
|
|
12
13
|
import { isInsideDir } from "../../utils/gitStaging.js";
|
|
13
14
|
import { EXECUTOR_VIDEOS_DIR, EXECUTOR_WORK_DIR, isInsideExecutorWorkDir, } from "../../utils/executorWorkDir.js";
|
|
@@ -110,7 +111,7 @@ export function registerInitializeWorkspaceTool(server) {
|
|
|
110
111
|
"1. Fresh init: PREREQUISITE: Call skyramp_init_scan first to get a scanToken and discover services. " +
|
|
111
112
|
"Then call this tool to write .skyramp/workspace.yml with the discovered services and scanToken. " +
|
|
112
113
|
"2. Edit existing workspace.yml: omit scanToken, set force: true, and pass the FULL updated services array (overwrite, not merge — fields not in the array are dropped). " +
|
|
113
|
-
"The services schema is strict: any field not in the schema (e.g. inventing `
|
|
114
|
+
"The services schema is strict: any field not in the schema (e.g. inventing `healthUrl` on `runtimeDetails`) fails validation and the call is rejected.",
|
|
114
115
|
inputSchema: initializeWorkspaceSchema,
|
|
115
116
|
_meta: {
|
|
116
117
|
keywords: [
|
|
@@ -182,11 +183,46 @@ export function registerInitializeWorkspaceTool(server) {
|
|
|
182
183
|
if (badMerged)
|
|
183
184
|
return badTestDirectoryResult(badMerged);
|
|
184
185
|
}
|
|
186
|
+
// The verifier, before the token is spent: a refusal asks for a
|
|
187
|
+
// corrected retry, which must be able to reuse the same token. Only the
|
|
188
|
+
// incoming services: a merge must not be blocked by an older entry.
|
|
189
|
+
const grant = params.scanToken
|
|
190
|
+
? scanTokenGrant(params.scanToken)
|
|
191
|
+
: undefined;
|
|
192
|
+
const issues = verifyWorkspaceServices(params.services, {
|
|
193
|
+
workspacePath,
|
|
194
|
+
relatedRepositories: grant?.relatedRepositories,
|
|
195
|
+
});
|
|
196
|
+
if (issues.length > 0) {
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: `${manager.getConfigPath()} was not written: ${issues.length} problem(s) with the services.\n${issues.map((i) => `- ${i}`).join("\n")}\nFix each one and call skyramp_init_workspace again with the full corrected services array${grant ? " and the same scanToken, which is still valid" : ""}.`,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
185
207
|
// scanToken is required for fresh init; for edits (force:true on an
|
|
186
208
|
// existing workspace.yml) the caller supplies the full services array
|
|
187
|
-
// directly and
|
|
209
|
+
// directly and no token is needed. A token passed with an edit is still
|
|
210
|
+
// validated and spent, so a successful write never leaves it reusable.
|
|
188
211
|
const isEdit = alreadyExists && params.force;
|
|
189
|
-
if (
|
|
212
|
+
if (isEdit && params.scanToken) {
|
|
213
|
+
if (!validateAndConsumeScanToken(params.scanToken)) {
|
|
214
|
+
return {
|
|
215
|
+
content: [
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
text: "Invalid or expired scanToken. An edit of an existing workspace.yml needs none: call again with force: true and no scanToken.",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
isError: true,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else if (!isEdit) {
|
|
190
226
|
if (!params.scanToken) {
|
|
191
227
|
return {
|
|
192
228
|
content: [
|
|
@@ -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.51";
|
|
2
|
+
export declare const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.51";
|
|
3
|
+
export declare const WORKER_DOCKER_IMAGE = "skyramp/worker:v1.3.51";
|
|
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.51";
|
|
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`).
|
|
@@ -5,24 +5,8 @@ export { SUPPORTED_FRAMEWORKS } from "./frameworks.js";
|
|
|
5
5
|
export declare const serviceSchema: z.ZodObject<{
|
|
6
6
|
serviceName: z.ZodString;
|
|
7
7
|
repository: z.ZodOptional<z.ZodString>;
|
|
8
|
-
language: z.ZodOptional<z.
|
|
9
|
-
|
|
10
|
-
typescript: "typescript";
|
|
11
|
-
javascript: "javascript";
|
|
12
|
-
java: "java";
|
|
13
|
-
}>>;
|
|
14
|
-
framework: z.ZodOptional<z.ZodEnum<{
|
|
15
|
-
playwright: "playwright";
|
|
16
|
-
pytest: "pytest";
|
|
17
|
-
robot: "robot";
|
|
18
|
-
junit: "junit";
|
|
19
|
-
vitest: "vitest";
|
|
20
|
-
jest: "jest";
|
|
21
|
-
mocha: "mocha";
|
|
22
|
-
go: "go";
|
|
23
|
-
rspec: "rspec";
|
|
24
|
-
jasmine: "jasmine";
|
|
25
|
-
}>>;
|
|
8
|
+
language: z.ZodOptional<z.ZodString>;
|
|
9
|
+
framework: z.ZodOptional<z.ZodString>;
|
|
26
10
|
testDirectory: z.ZodOptional<z.ZodString>;
|
|
27
11
|
api: z.ZodOptional<z.ZodObject<{
|
|
28
12
|
schemaPath: z.ZodOptional<z.ZodString>;
|
|
@@ -46,49 +30,30 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
46
30
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
47
31
|
skipTLSVerify: z.ZodOptional<z.ZodBoolean>;
|
|
48
32
|
}, z.core.$strict>>;
|
|
49
|
-
runtimeDetails: z.ZodOptional<z.ZodObject<{
|
|
33
|
+
runtimeDetails: z.ZodOptional<z.ZodPipe<z.ZodObject<{
|
|
50
34
|
serverStartCommand: z.ZodOptional<z.ZodString>;
|
|
51
35
|
serverStopCommand: z.ZodOptional<z.ZodString>;
|
|
52
36
|
runtime: z.ZodPreprocess<z.ZodEnum<typeof import("../types/TestTypes.js").RuntimeEnvironment>, unknown>;
|
|
53
37
|
dockerNetwork: z.ZodOptional<z.ZodString>;
|
|
54
38
|
k8sNamespace: z.ZodOptional<z.ZodString>;
|
|
55
39
|
k8sContext: z.ZodOptional<z.ZodString>;
|
|
56
|
-
testSuites: z.ZodOptional<z.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
unproven: "unproven";
|
|
74
|
-
}>>;
|
|
75
|
-
canaryDetail: z.ZodOptional<z.ZodString>;
|
|
76
|
-
testEnv: z.ZodOptional<z.ZodUnknown>;
|
|
77
|
-
}, z.core.$strict>, z.ZodTransform<{
|
|
78
|
-
testRunCommand: string;
|
|
79
|
-
framework?: "playwright" | "pytest" | "robot" | "junit" | "vitest" | "jest" | "mocha" | "go" | "rspec" | "jasmine" | undefined;
|
|
80
|
-
pathGlobs?: string[] | undefined;
|
|
81
|
-
canaryStatus?: "proven" | "unproven" | undefined;
|
|
82
|
-
canaryDetail?: string | undefined;
|
|
83
|
-
}, {
|
|
84
|
-
testRunCommand: string;
|
|
85
|
-
framework?: "playwright" | "pytest" | "robot" | "junit" | "vitest" | "jest" | "mocha" | "go" | "rspec" | "jasmine" | undefined;
|
|
86
|
-
pathGlobs?: string[] | undefined;
|
|
87
|
-
canaryStatus?: "proven" | "unproven" | undefined;
|
|
88
|
-
canaryDetail?: string | undefined;
|
|
89
|
-
testEnv?: unknown;
|
|
90
|
-
}>>>>;
|
|
91
|
-
}, z.core.$strict>>;
|
|
40
|
+
testSuites: z.ZodOptional<z.ZodUnknown>;
|
|
41
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
42
|
+
runtime: import("../types/TestTypes.js").RuntimeEnvironment;
|
|
43
|
+
serverStartCommand?: string | undefined;
|
|
44
|
+
serverStopCommand?: string | undefined;
|
|
45
|
+
dockerNetwork?: string | undefined;
|
|
46
|
+
k8sNamespace?: string | undefined;
|
|
47
|
+
k8sContext?: string | undefined;
|
|
48
|
+
}, {
|
|
49
|
+
runtime: import("../types/TestTypes.js").RuntimeEnvironment;
|
|
50
|
+
serverStartCommand?: string | undefined;
|
|
51
|
+
serverStopCommand?: string | undefined;
|
|
52
|
+
dockerNetwork?: string | undefined;
|
|
53
|
+
k8sNamespace?: string | undefined;
|
|
54
|
+
k8sContext?: string | undefined;
|
|
55
|
+
testSuites?: unknown;
|
|
56
|
+
}>>>;
|
|
92
57
|
}, z.core.$strict>;
|
|
93
58
|
export declare const workspaceConfigSchema: z.ZodObject<{
|
|
94
59
|
workspace: z.ZodOptional<z.ZodObject<{
|
|
@@ -105,24 +70,8 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
105
70
|
services: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
106
71
|
serviceName: z.ZodString;
|
|
107
72
|
repository: z.ZodOptional<z.ZodString>;
|
|
108
|
-
language: z.ZodOptional<z.
|
|
109
|
-
|
|
110
|
-
typescript: "typescript";
|
|
111
|
-
javascript: "javascript";
|
|
112
|
-
java: "java";
|
|
113
|
-
}>>;
|
|
114
|
-
framework: z.ZodOptional<z.ZodEnum<{
|
|
115
|
-
playwright: "playwright";
|
|
116
|
-
pytest: "pytest";
|
|
117
|
-
robot: "robot";
|
|
118
|
-
junit: "junit";
|
|
119
|
-
vitest: "vitest";
|
|
120
|
-
jest: "jest";
|
|
121
|
-
mocha: "mocha";
|
|
122
|
-
go: "go";
|
|
123
|
-
rspec: "rspec";
|
|
124
|
-
jasmine: "jasmine";
|
|
125
|
-
}>>;
|
|
73
|
+
language: z.ZodOptional<z.ZodString>;
|
|
74
|
+
framework: z.ZodOptional<z.ZodString>;
|
|
126
75
|
testDirectory: z.ZodOptional<z.ZodString>;
|
|
127
76
|
api: z.ZodOptional<z.ZodObject<{
|
|
128
77
|
schemaPath: z.ZodOptional<z.ZodString>;
|
|
@@ -146,55 +95,35 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
146
95
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
147
96
|
skipTLSVerify: z.ZodOptional<z.ZodBoolean>;
|
|
148
97
|
}, z.core.$strict>>;
|
|
149
|
-
runtimeDetails: z.ZodOptional<z.ZodObject<{
|
|
98
|
+
runtimeDetails: z.ZodOptional<z.ZodPipe<z.ZodObject<{
|
|
150
99
|
serverStartCommand: z.ZodOptional<z.ZodString>;
|
|
151
100
|
serverStopCommand: z.ZodOptional<z.ZodString>;
|
|
152
101
|
runtime: z.ZodPreprocess<z.ZodEnum<typeof import("../types/TestTypes.js").RuntimeEnvironment>, unknown>;
|
|
153
102
|
dockerNetwork: z.ZodOptional<z.ZodString>;
|
|
154
103
|
k8sNamespace: z.ZodOptional<z.ZodString>;
|
|
155
104
|
k8sContext: z.ZodOptional<z.ZodString>;
|
|
156
|
-
testSuites: z.ZodOptional<z.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
unproven: "unproven";
|
|
174
|
-
}>>;
|
|
175
|
-
canaryDetail: z.ZodOptional<z.ZodString>;
|
|
176
|
-
testEnv: z.ZodOptional<z.ZodUnknown>;
|
|
177
|
-
}, z.core.$strict>, z.ZodTransform<{
|
|
178
|
-
testRunCommand: string;
|
|
179
|
-
framework?: "playwright" | "pytest" | "robot" | "junit" | "vitest" | "jest" | "mocha" | "go" | "rspec" | "jasmine" | undefined;
|
|
180
|
-
pathGlobs?: string[] | undefined;
|
|
181
|
-
canaryStatus?: "proven" | "unproven" | undefined;
|
|
182
|
-
canaryDetail?: string | undefined;
|
|
183
|
-
}, {
|
|
184
|
-
testRunCommand: string;
|
|
185
|
-
framework?: "playwright" | "pytest" | "robot" | "junit" | "vitest" | "jest" | "mocha" | "go" | "rspec" | "jasmine" | undefined;
|
|
186
|
-
pathGlobs?: string[] | undefined;
|
|
187
|
-
canaryStatus?: "proven" | "unproven" | undefined;
|
|
188
|
-
canaryDetail?: string | undefined;
|
|
189
|
-
testEnv?: unknown;
|
|
190
|
-
}>>>>;
|
|
191
|
-
}, z.core.$strict>>;
|
|
105
|
+
testSuites: z.ZodOptional<z.ZodUnknown>;
|
|
106
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
107
|
+
runtime: import("../types/TestTypes.js").RuntimeEnvironment;
|
|
108
|
+
serverStartCommand?: string | undefined;
|
|
109
|
+
serverStopCommand?: string | undefined;
|
|
110
|
+
dockerNetwork?: string | undefined;
|
|
111
|
+
k8sNamespace?: string | undefined;
|
|
112
|
+
k8sContext?: string | undefined;
|
|
113
|
+
}, {
|
|
114
|
+
runtime: import("../types/TestTypes.js").RuntimeEnvironment;
|
|
115
|
+
serverStartCommand?: string | undefined;
|
|
116
|
+
serverStopCommand?: string | undefined;
|
|
117
|
+
dockerNetwork?: string | undefined;
|
|
118
|
+
k8sNamespace?: string | undefined;
|
|
119
|
+
k8sContext?: string | undefined;
|
|
120
|
+
testSuites?: unknown;
|
|
121
|
+
}>>>;
|
|
192
122
|
}, z.core.$strict>>>;
|
|
193
123
|
}, z.core.$strict>;
|
|
194
124
|
export type Service = z.infer<typeof serviceSchema>;
|
|
195
125
|
export type ServiceApi = NonNullable<Service["api"]>;
|
|
196
126
|
export type ServiceRuntimeDetails = NonNullable<Service["runtimeDetails"]>;
|
|
197
|
-
export type TestSuite = NonNullable<ServiceRuntimeDetails["testSuites"]>[number];
|
|
198
127
|
export type WorkspaceConfig = z.infer<typeof workspaceConfigSchema>;
|
|
199
128
|
export type WorkspaceSection = NonNullable<WorkspaceConfig["workspace"]>;
|
|
200
129
|
export type MetadataSection = NonNullable<WorkspaceConfig["metadata"]>;
|
|
@@ -214,8 +143,8 @@ export declare function validateWorkspaceConfig(config: unknown): z.ZodSafeParse
|
|
|
214
143
|
services?: {
|
|
215
144
|
serviceName: string;
|
|
216
145
|
repository?: string | undefined;
|
|
217
|
-
language?:
|
|
218
|
-
framework?:
|
|
146
|
+
language?: string | undefined;
|
|
147
|
+
framework?: string | undefined;
|
|
219
148
|
testDirectory?: string | undefined;
|
|
220
149
|
api?: {
|
|
221
150
|
schemaPath?: string | undefined;
|
|
@@ -237,13 +166,6 @@ export declare function validateWorkspaceConfig(config: unknown): z.ZodSafeParse
|
|
|
237
166
|
dockerNetwork?: string | undefined;
|
|
238
167
|
k8sNamespace?: string | undefined;
|
|
239
168
|
k8sContext?: string | undefined;
|
|
240
|
-
testSuites?: {
|
|
241
|
-
testRunCommand: string;
|
|
242
|
-
framework?: "playwright" | "pytest" | "robot" | "junit" | "vitest" | "jest" | "mocha" | "go" | "rspec" | "jasmine" | undefined;
|
|
243
|
-
pathGlobs?: string[] | undefined;
|
|
244
|
-
canaryStatus?: "proven" | "unproven" | undefined;
|
|
245
|
-
canaryDetail?: string | undefined;
|
|
246
|
-
}[] | undefined;
|
|
247
169
|
} | undefined;
|
|
248
170
|
}[] | undefined;
|
|
249
171
|
}>;
|
|
@@ -273,9 +195,17 @@ export declare class WorkspaceConfigManager {
|
|
|
273
195
|
/**
|
|
274
196
|
* Initialize workspace — creates .skyramp/workspace.yml with repo info.
|
|
275
197
|
* Auto-detects repoName and repoUrl from git when not provided.
|
|
276
|
-
* Produces an empty services array and default metadata timestamps.
|
|
198
|
+
* Produces an empty services array and default metadata timestamps. Over an
|
|
199
|
+
* existing file it keeps that file's createdAt: the workspace was created
|
|
200
|
+
* then, however often it is rewritten.
|
|
277
201
|
*/
|
|
278
202
|
initialize(workspaceInfo?: WorkspaceSection): Promise<WorkspaceConfig>;
|
|
203
|
+
/**
|
|
204
|
+
* The existing file's metadata.createdAt, read leniently: a rewrite is often
|
|
205
|
+
* the repair of a file that no longer validates, and its createdAt still
|
|
206
|
+
* stands. Undefined when there is no file or it records none.
|
|
207
|
+
*/
|
|
208
|
+
private existingCreatedAt;
|
|
279
209
|
/**
|
|
280
210
|
* Update the metadata section of an existing workspace config.
|
|
281
211
|
* Only the provided fields are merged; metadata.updatedAt is refreshed
|