@zibby/core 0.1.21 → 0.1.22
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/dist/agents/base.js +17 -0
- package/dist/backend-client.js +1 -0
- package/dist/constants/tool-names.js +1 -0
- package/dist/constants/zibby-scratch.js +1 -0
- package/dist/constants.js +1 -0
- package/dist/enrichment/base.js +1 -0
- package/dist/enrichment/enrichers/accessibility-enricher.js +1 -0
- package/dist/enrichment/enrichers/dom-enricher.js +1 -0
- package/dist/enrichment/enrichers/page-state-enricher.js +1 -0
- package/dist/enrichment/enrichers/position-enricher.js +1 -0
- package/dist/enrichment/index.js +1 -0
- package/dist/enrichment/mcp-integration.js +1 -0
- package/dist/enrichment/mcp-ref-enricher.js +1 -0
- package/dist/enrichment/pipeline.js +3 -0
- package/dist/enrichment/trace-text-enricher.js +1 -0
- package/dist/framework/agents/assistant-strategy.js +5 -0
- package/dist/framework/agents/base.js +1 -0
- package/dist/framework/agents/claude-strategy.js +4 -0
- package/dist/framework/agents/codex-strategy.js +4 -0
- package/dist/framework/agents/cursor-strategy.js +32 -0
- package/dist/framework/agents/gemini-strategy.js +11 -0
- package/dist/framework/agents/index.js +13 -0
- package/dist/framework/agents/middleware/assistant-round-pipeline.js +3 -0
- package/dist/framework/agents/providers/base.js +1 -0
- package/dist/framework/agents/providers/index.js +1 -0
- package/dist/framework/agents/providers/openai-transport.js +2 -0
- package/dist/framework/agents/providers/openai.js +1 -0
- package/dist/framework/agents/providers/transport-base.js +1 -0
- package/dist/framework/agents/utils/auth-resolver.js +1 -0
- package/dist/framework/agents/utils/cursor-output-formatter.js +1 -0
- package/dist/framework/agents/utils/openai-proxy-formatter.js +9 -0
- package/dist/framework/agents/utils/payload-budget.js +3 -0
- package/dist/framework/agents/utils/structured-output-formatter.js +21 -0
- package/dist/framework/code-generator.js +10 -0
- package/dist/framework/constants.js +1 -0
- package/dist/framework/context-loader.js +5 -0
- package/dist/framework/function-bridge.js +2 -0
- package/dist/framework/function-skill-registry.js +1 -0
- package/dist/framework/graph-compiler.js +1 -0
- package/dist/framework/graph.js +5 -0
- package/dist/framework/index.js +1 -0
- package/dist/framework/mcp-client.js +2 -0
- package/dist/framework/node-registry.js +9 -0
- package/dist/framework/node.js +5 -0
- package/dist/framework/output-parser.js +3 -0
- package/dist/framework/skill-registry.js +1 -0
- package/dist/framework/state-utils.js +1 -0
- package/dist/framework/state.js +1 -0
- package/dist/framework/tool-resolver.js +1 -0
- package/dist/index.js +8 -0
- package/dist/runtime/generation/base.js +1 -0
- package/dist/runtime/generation/index.js +3 -0
- package/dist/runtime/generation/mcp-ref-strategy.js +41 -0
- package/dist/runtime/generation/stable-id-strategy.js +16 -0
- package/dist/runtime/stable-id-runtime.js +1 -0
- package/dist/runtime/verification/base.js +1 -0
- package/dist/runtime/verification/index.js +3 -0
- package/dist/runtime/verification/playwright-json-strategy.js +1 -0
- package/dist/runtime/zibby-runtime.js +1 -0
- package/dist/sync/index.js +1 -0
- package/dist/sync/uploader.js +1 -0
- package/dist/tools/run-playwright-test.js +5 -0
- package/dist/utils/adf-converter.js +7 -0
- package/dist/utils/ast-utils.js +1 -0
- package/dist/utils/ci-setup.js +5 -0
- package/dist/utils/cursor-mcp-isolated-home.js +1 -0
- package/dist/utils/cursor-utils.js +18 -0
- package/dist/utils/live-frame-discovery.js +1 -0
- package/dist/utils/logger.js +1 -0
- package/dist/utils/mcp-config-writer.js +10 -0
- package/dist/utils/mission-control-from-run-states.js +1 -0
- package/dist/utils/node-schema-parser.js +1 -0
- package/dist/utils/parallel-config.js +1 -0
- package/dist/utils/post-process-events.js +1 -0
- package/dist/utils/result-handler.js +1 -0
- package/{src → dist}/utils/ripple-effect.js +3 -12
- package/dist/utils/run-capacity-coordinator.js +1 -0
- package/dist/utils/run-capacity-queue.js +2 -0
- package/dist/utils/run-index-merge.js +1 -0
- package/dist/utils/run-index-post-cli.js +1 -0
- package/dist/utils/run-registry.js +3 -0
- package/dist/utils/run-state-session.js +2 -0
- package/dist/utils/selector-generator.js +4 -0
- package/dist/utils/session-state-constants.js +1 -0
- package/dist/utils/session-state-live-runs.js +1 -0
- package/dist/utils/streaming-parser.js +4 -0
- package/dist/utils/test-post-processor.js +18 -0
- package/dist/utils/timeline.js +14 -0
- package/dist/utils/trace-parser.js +2 -0
- package/dist/utils/video-organizer.js +3 -0
- package/package.json +49 -35
- package/templates/browser-test-automation/README.md +29 -7
- package/templates/browser-test-automation/chat.mjs +36 -0
- package/templates/browser-test-automation/graph.mjs +5 -9
- package/templates/browser-test-automation/nodes/execute-live.mjs +30 -58
- package/templates/browser-test-automation/nodes/generate-script.mjs +32 -12
- package/templates/browser-test-automation/nodes/utils.mjs +153 -10
- package/templates/browser-test-automation/pipeline-ids.js +12 -0
- package/templates/browser-test-automation/result-handler.mjs +78 -2
- package/templates/browser-test-automation/run-index.mjs +418 -0
- package/scripts/export-default-workflows.js +0 -51
- package/scripts/patch-cursor-mcp.js +0 -174
- package/scripts/setup-ci.sh +0 -115
- package/scripts/setup-official-playwright-mcp.sh +0 -226
- package/scripts/test-with-video.sh +0 -49
- package/src/agents/base.js +0 -361
- package/src/constants.js +0 -47
- package/src/enrichment/base.js +0 -49
- package/src/enrichment/enrichers/accessibility-enricher.js +0 -197
- package/src/enrichment/enrichers/dom-enricher.js +0 -171
- package/src/enrichment/enrichers/page-state-enricher.js +0 -129
- package/src/enrichment/enrichers/position-enricher.js +0 -67
- package/src/enrichment/index.js +0 -96
- package/src/enrichment/mcp-integration.js +0 -149
- package/src/enrichment/mcp-ref-enricher.js +0 -78
- package/src/enrichment/pipeline.js +0 -192
- package/src/enrichment/trace-text-enricher.js +0 -115
- package/src/framework/AGENTS.md +0 -98
- package/src/framework/agents/base.js +0 -72
- package/src/framework/agents/claude-strategy.js +0 -278
- package/src/framework/agents/cursor-strategy.js +0 -544
- package/src/framework/agents/index.js +0 -105
- package/src/framework/agents/utils/cursor-output-formatter.js +0 -67
- package/src/framework/agents/utils/openai-proxy-formatter.js +0 -249
- package/src/framework/code-generator.js +0 -301
- package/src/framework/constants.js +0 -33
- package/src/framework/context-loader.js +0 -101
- package/src/framework/function-bridge.js +0 -78
- package/src/framework/function-skill-registry.js +0 -20
- package/src/framework/graph-compiler.js +0 -342
- package/src/framework/graph.js +0 -610
- package/src/framework/index.js +0 -28
- package/src/framework/node-registry.js +0 -163
- package/src/framework/node.js +0 -259
- package/src/framework/output-parser.js +0 -71
- package/src/framework/skill-registry.js +0 -55
- package/src/framework/state-utils.js +0 -52
- package/src/framework/state.js +0 -67
- package/src/framework/tool-resolver.js +0 -65
- package/src/index.js +0 -345
- package/src/runtime/generation/base.js +0 -46
- package/src/runtime/generation/index.js +0 -70
- package/src/runtime/generation/mcp-ref-strategy.js +0 -197
- package/src/runtime/generation/stable-id-strategy.js +0 -170
- package/src/runtime/stable-id-runtime.js +0 -248
- package/src/runtime/verification/base.js +0 -44
- package/src/runtime/verification/index.js +0 -67
- package/src/runtime/verification/playwright-json-strategy.js +0 -119
- package/src/runtime/zibby-runtime.js +0 -299
- package/src/sync/index.js +0 -2
- package/src/sync/uploader.js +0 -29
- package/src/tools/run-playwright-test.js +0 -158
- package/src/utils/adf-converter.js +0 -68
- package/src/utils/ast-utils.js +0 -37
- package/src/utils/ci-setup.js +0 -124
- package/src/utils/cursor-utils.js +0 -71
- package/src/utils/logger.js +0 -144
- package/src/utils/mcp-config-writer.js +0 -115
- package/src/utils/node-schema-parser.js +0 -522
- package/src/utils/post-process-events.js +0 -55
- package/src/utils/result-handler.js +0 -102
- package/src/utils/selector-generator.js +0 -239
- package/src/utils/streaming-parser.js +0 -387
- package/src/utils/test-post-processor.js +0 -211
- package/src/utils/timeline.js +0 -217
- package/src/utils/trace-parser.js +0 -325
- package/src/utils/video-organizer.js +0 -91
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser test automation — run-index.jsonl rows (execute_live / generate_script layout).
|
|
3
|
+
* Used by CLI runTest; generic JSONL I/O is in @zibby/core/utils/run-registry.js.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readdirSync, statSync } from 'fs';
|
|
7
|
+
import { join, relative, sep, resolve as pathResolve } from 'path';
|
|
8
|
+
import {
|
|
9
|
+
appendRunIndexRecord,
|
|
10
|
+
readRunIndexRecordsFromFile,
|
|
11
|
+
resolveRunIndexPath,
|
|
12
|
+
} from '../../src/utils/run-registry.js';
|
|
13
|
+
import { mergeSessionRunState, readSessionRunState } from '../../src/utils/run-state-session.js';
|
|
14
|
+
import { partitionRunIndexBySession, runIndexSessionEntryIsLive } from '../../src/utils/run-index-merge.js';
|
|
15
|
+
import { DEFAULT_OUTPUT_BASE, SESSIONS_DIR } from '../../src/framework/constants.js';
|
|
16
|
+
import { BROWSER_TEST_PIPELINE_NODE_IDS } from './pipeline-ids.js';
|
|
17
|
+
|
|
18
|
+
export { BROWSER_TEST_PIPELINE_NODE_IDS };
|
|
19
|
+
|
|
20
|
+
/** Stable id for Studio + run-index: Studio env, else session folder id (CLI/chat). */
|
|
21
|
+
function runIndexCorrelationId(sessionId) {
|
|
22
|
+
const env = process.env.ZIBBY_STUDIO_TEST_CASE_ID;
|
|
23
|
+
if (env != null && String(env).trim() !== '') return String(env).trim();
|
|
24
|
+
return sessionId != null ? String(sessionId) : '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SCRIPT_CANDIDATES = [
|
|
28
|
+
join('generate_script', 'generated-test.spec.js'),
|
|
29
|
+
join('generate_script', 'generated-test.spec.ts'),
|
|
30
|
+
join('generate_script', 'playwright.spec.ts'),
|
|
31
|
+
join('generate_script', 'test.spec.ts'),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function firstExistingVideo(sessionAbs) {
|
|
35
|
+
const dirs = [
|
|
36
|
+
join(sessionAbs, 'execute_live', 'videos'),
|
|
37
|
+
join(sessionAbs, 'execute_live'),
|
|
38
|
+
sessionAbs,
|
|
39
|
+
];
|
|
40
|
+
for (const d of dirs) {
|
|
41
|
+
if (!existsSync(d)) continue;
|
|
42
|
+
let names;
|
|
43
|
+
try {
|
|
44
|
+
names = readdirSync(d);
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const webm = names.find((f) => f.endsWith('.webm'));
|
|
49
|
+
if (webm) return join(d, webm);
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function firstExistingEvents(sessionAbs) {
|
|
55
|
+
const c = [join(sessionAbs, 'execute_live', 'events.json'), join(sessionAbs, 'events.json')];
|
|
56
|
+
for (const p of c) {
|
|
57
|
+
if (existsSync(p)) return p;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function firstExistingScript(sessionAbs) {
|
|
63
|
+
for (const rel of SCRIPT_CANDIDATES) {
|
|
64
|
+
const p = join(sessionAbs, rel);
|
|
65
|
+
if (existsSync(p)) return p;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Discover absolute artifact paths for a browser-test session directory.
|
|
72
|
+
* @param {string} sessionPathAbs
|
|
73
|
+
*/
|
|
74
|
+
export function discoverBrowserTestSessionArtifacts(sessionPathAbs) {
|
|
75
|
+
if (!sessionPathAbs || !existsSync(sessionPathAbs)) {
|
|
76
|
+
return { videoPathAbs: null, eventsPathAbs: null, scriptPathAbs: null };
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
videoPathAbs: firstExistingVideo(sessionPathAbs),
|
|
80
|
+
eventsPathAbs: firstExistingEvents(sessionPathAbs),
|
|
81
|
+
scriptPathAbs: firstExistingScript(sessionPathAbs),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {object} opts
|
|
87
|
+
* @param {string} opts.cwd
|
|
88
|
+
* @param {object} opts.result - return value from agent.run / runSingleNode
|
|
89
|
+
* @param {boolean} opts.success
|
|
90
|
+
* @param {string} [opts.outputBase]
|
|
91
|
+
* @param {string} [opts.specPath]
|
|
92
|
+
* @param {string} [opts.errorMessage]
|
|
93
|
+
* @param {string} [opts.status] — override default completed/failed (e.g. `interrupted`)
|
|
94
|
+
*/
|
|
95
|
+
export function buildBrowserTestRunIndexRecord(opts) {
|
|
96
|
+
const cwd = opts.cwd || process.cwd();
|
|
97
|
+
const outputBase = opts.outputBase || DEFAULT_OUTPUT_BASE;
|
|
98
|
+
const result = opts.result || {};
|
|
99
|
+
const state = result.state || {};
|
|
100
|
+
const sessionPathAbs = state.sessionPath;
|
|
101
|
+
if (!sessionPathAbs || typeof sessionPathAbs !== 'string') {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const sessionId = sessionPathAbs.split(/[/\\]/).filter(Boolean).pop();
|
|
106
|
+
if (!sessionId) return null;
|
|
107
|
+
|
|
108
|
+
const { videoPathAbs, eventsPathAbs, scriptPathAbs } =
|
|
109
|
+
discoverBrowserTestSessionArtifacts(sessionPathAbs);
|
|
110
|
+
|
|
111
|
+
const toRel = (abs) => {
|
|
112
|
+
if (!abs) return null;
|
|
113
|
+
try {
|
|
114
|
+
return relative(cwd, abs).split(sep).join('/');
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let specRel = null;
|
|
121
|
+
if (opts.specPath) {
|
|
122
|
+
try {
|
|
123
|
+
const absSpec = pathResolve(cwd, opts.specPath);
|
|
124
|
+
specRel = relative(cwd, absSpec).split(sep).join('/');
|
|
125
|
+
} catch {
|
|
126
|
+
specRel = String(opts.specPath).split(sep).join('/');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
v: 1,
|
|
132
|
+
recordKind: 'summary',
|
|
133
|
+
ts: Date.now(),
|
|
134
|
+
sessionId,
|
|
135
|
+
status: opts.status ?? (opts.success ? 'completed' : 'failed'),
|
|
136
|
+
cwd,
|
|
137
|
+
outputBase,
|
|
138
|
+
sessionPathAbs,
|
|
139
|
+
sessionDirRel: toRel(sessionPathAbs),
|
|
140
|
+
videoPathAbs: videoPathAbs || null,
|
|
141
|
+
eventsPathAbs: eventsPathAbs || null,
|
|
142
|
+
scriptPathAbs: scriptPathAbs || null,
|
|
143
|
+
videoRel: toRel(videoPathAbs),
|
|
144
|
+
eventsRel: toRel(eventsPathAbs),
|
|
145
|
+
scriptRel: toRel(scriptPathAbs),
|
|
146
|
+
specRel,
|
|
147
|
+
source: process.env.ZIBBY_RUN_SOURCE || 'cli',
|
|
148
|
+
studioTestCaseId: runIndexCorrelationId(sessionId) || null,
|
|
149
|
+
errorMessage: opts.errorMessage || null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function tryAppendBrowserTestRunIndex({
|
|
154
|
+
cwd,
|
|
155
|
+
config,
|
|
156
|
+
result,
|
|
157
|
+
success,
|
|
158
|
+
specPath,
|
|
159
|
+
errorMessage,
|
|
160
|
+
}) {
|
|
161
|
+
try {
|
|
162
|
+
const record = buildBrowserTestRunIndexRecord({
|
|
163
|
+
cwd: cwd || process.cwd(),
|
|
164
|
+
result,
|
|
165
|
+
success,
|
|
166
|
+
outputBase: config?.paths?.output || DEFAULT_OUTPUT_BASE,
|
|
167
|
+
specPath,
|
|
168
|
+
errorMessage,
|
|
169
|
+
});
|
|
170
|
+
if (record) {
|
|
171
|
+
appendRunIndexRecord(record);
|
|
172
|
+
if (record.sessionPathAbs) {
|
|
173
|
+
mergeSessionRunState(record.sessionPathAbs, {
|
|
174
|
+
sessionId: record.sessionId,
|
|
175
|
+
studioTestCaseId: record.studioTestCaseId || record.sessionId,
|
|
176
|
+
status: record.status,
|
|
177
|
+
activeNode: null,
|
|
178
|
+
activeStageIndex: null,
|
|
179
|
+
errorMessage: record.errorMessage || null,
|
|
180
|
+
runSource: record.source || 'cli',
|
|
181
|
+
cwd: record.cwd,
|
|
182
|
+
outputBase: record.outputBase,
|
|
183
|
+
sessionPathAbs: record.sessionPathAbs,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {
|
|
188
|
+
console.warn(`[zibby browser-test run-index] ${e.message}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Append one JSONL row when a pipeline node starts (live Mission Control).
|
|
194
|
+
* @param {object} payload
|
|
195
|
+
* @param {string} payload.currentNode — graph node id
|
|
196
|
+
* @param {string} payload.sessionPath — absolute session dir
|
|
197
|
+
* @param {string} [payload.sessionId]
|
|
198
|
+
* @param {string} [payload.cwd]
|
|
199
|
+
* @param {string} [payload.outputBase]
|
|
200
|
+
*/
|
|
201
|
+
/**
|
|
202
|
+
* Single absolute session directory for pipeline progress + zibby-run-state.json.
|
|
203
|
+
* Studio spawns the CLI with ZIBBY_SESSION_PATH; cwd may still be wrong (home, app bundle, etc.).
|
|
204
|
+
* Never merge under join(cwd, …/sessions/id) when studio env points elsewhere — that caused duplicate dirs.
|
|
205
|
+
*/
|
|
206
|
+
export function resolveBrowserTestPipelineSessionPathAbs({
|
|
207
|
+
sessionPath,
|
|
208
|
+
sessionId,
|
|
209
|
+
cwd,
|
|
210
|
+
outputBase = DEFAULT_OUTPUT_BASE,
|
|
211
|
+
} = {}) {
|
|
212
|
+
const cwd0 = cwd || process.cwd();
|
|
213
|
+
const ob = outputBase || DEFAULT_OUTPUT_BASE;
|
|
214
|
+
const sid =
|
|
215
|
+
sessionId != null && String(sessionId).trim() !== ''
|
|
216
|
+
? String(sessionId).trim()
|
|
217
|
+
: null;
|
|
218
|
+
|
|
219
|
+
const studio = process.env.ZIBBY_RUN_SOURCE === 'studio';
|
|
220
|
+
const envSession = process.env.ZIBBY_SESSION_PATH && String(process.env.ZIBBY_SESSION_PATH).trim();
|
|
221
|
+
if (studio && envSession) {
|
|
222
|
+
return pathResolve(envSession);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const trimmed = sessionPath && String(sessionPath).trim();
|
|
226
|
+
if (trimmed) {
|
|
227
|
+
return pathResolve(trimmed);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const sessionsRoot = process.env.ZIBBY_SESSIONS_ROOT && String(process.env.ZIBBY_SESSIONS_ROOT).trim();
|
|
231
|
+
if (sessionsRoot && sid) {
|
|
232
|
+
return pathResolve(join(sessionsRoot, sid));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (process.env.ZIBBY_SESSION_PATH && String(process.env.ZIBBY_SESSION_PATH).trim()) {
|
|
236
|
+
return pathResolve(String(process.env.ZIBBY_SESSION_PATH).trim());
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return pathResolve(join(cwd0, ob, SESSIONS_DIR, sid || 'invalid'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function tryAppendBrowserTestPipelineProgress(payload) {
|
|
243
|
+
try {
|
|
244
|
+
const currentNode = payload?.currentNode;
|
|
245
|
+
if (!currentNode || !BROWSER_TEST_PIPELINE_NODE_IDS.includes(currentNode)) return;
|
|
246
|
+
|
|
247
|
+
const sessionPath = payload.sessionPath;
|
|
248
|
+
const sessionId =
|
|
249
|
+
payload.sessionId ||
|
|
250
|
+
(sessionPath && String(sessionPath).split(/[/\\]/).filter(Boolean).pop()) ||
|
|
251
|
+
null;
|
|
252
|
+
if (!sessionId) return;
|
|
253
|
+
|
|
254
|
+
const cwd = payload.cwd || process.cwd();
|
|
255
|
+
const outputBase = payload.outputBase || DEFAULT_OUTPUT_BASE;
|
|
256
|
+
const activeStageIndex = BROWSER_TEST_PIPELINE_NODE_IDS.indexOf(currentNode);
|
|
257
|
+
const specPathRaw = payload?.specPath != null ? String(payload.specPath).trim() : '';
|
|
258
|
+
const taskDescription =
|
|
259
|
+
payload?.taskDescription != null ? String(payload.taskDescription) : '';
|
|
260
|
+
let specRel = null;
|
|
261
|
+
if (specPathRaw) {
|
|
262
|
+
try {
|
|
263
|
+
const absSpec = pathResolve(cwd, specPathRaw);
|
|
264
|
+
specRel = relative(cwd, absSpec).split(sep).join('/');
|
|
265
|
+
} catch {
|
|
266
|
+
specRel = specPathRaw.split(sep).join('/');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const sessionPathAbs = resolveBrowserTestPipelineSessionPathAbs({
|
|
271
|
+
sessionPath,
|
|
272
|
+
sessionId,
|
|
273
|
+
cwd,
|
|
274
|
+
outputBase,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
appendRunIndexRecord({
|
|
278
|
+
v: 1,
|
|
279
|
+
recordKind: 'progress',
|
|
280
|
+
ts: Date.now(),
|
|
281
|
+
sessionId,
|
|
282
|
+
cwd,
|
|
283
|
+
outputBase,
|
|
284
|
+
sessionPathAbs,
|
|
285
|
+
activeNode: currentNode,
|
|
286
|
+
activeStageIndex,
|
|
287
|
+
specRel,
|
|
288
|
+
taskDescription: taskDescription || null,
|
|
289
|
+
studioTestCaseId: runIndexCorrelationId(sessionId) || null,
|
|
290
|
+
source: process.env.ZIBBY_RUN_SOURCE || 'cli',
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
mergeSessionRunState(sessionPathAbs, {
|
|
294
|
+
sessionId,
|
|
295
|
+
studioTestCaseId: runIndexCorrelationId(sessionId) || sessionId,
|
|
296
|
+
status: 'running',
|
|
297
|
+
activeNode: currentNode,
|
|
298
|
+
activeStageIndex,
|
|
299
|
+
sessionPathAbs,
|
|
300
|
+
cwd,
|
|
301
|
+
outputBase,
|
|
302
|
+
specPath: specRel || null,
|
|
303
|
+
task: taskDescription || null,
|
|
304
|
+
taskDescription: taskDescription || null,
|
|
305
|
+
runSource: process.env.ZIBBY_RUN_SOURCE || 'cli',
|
|
306
|
+
pid: typeof process.pid === 'number' ? process.pid : null,
|
|
307
|
+
});
|
|
308
|
+
} catch (e) {
|
|
309
|
+
console.warn(`[zibby browser-test run-index progress] ${e.message}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Factory for graph.run `initialState.onPipelineProgress`. */
|
|
314
|
+
export function createBrowserTestPipelineProgressAppender({ cwd, config } = {}) {
|
|
315
|
+
const cwd0 = cwd || process.cwd();
|
|
316
|
+
const ob0 = config?.paths?.output || DEFAULT_OUTPUT_BASE;
|
|
317
|
+
return (payload) => {
|
|
318
|
+
tryAppendBrowserTestPipelineProgress({
|
|
319
|
+
cwd: payload?.cwd || cwd0,
|
|
320
|
+
outputBase: payload?.outputBase || ob0,
|
|
321
|
+
sessionPath: payload?.sessionPath,
|
|
322
|
+
sessionId: payload?.sessionId,
|
|
323
|
+
currentNode: payload?.currentNode,
|
|
324
|
+
specPath: payload?.specPath,
|
|
325
|
+
taskDescription: payload?.taskDescription,
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Append a terminal `summary` row for every session that still looks “live” in run-index (progress newer than summary).
|
|
332
|
+
* Call from SIGINT/SIGTERM so Mission Control clears after Ctrl+C in the terminal.
|
|
333
|
+
* @param {object} [opts]
|
|
334
|
+
* @param {string} [opts.cwd]
|
|
335
|
+
* @param {object} [opts.config] — uses `config.paths.output`
|
|
336
|
+
*/
|
|
337
|
+
export function tryAppendBrowserTestInterruptedRunIndex(opts = {}) {
|
|
338
|
+
try {
|
|
339
|
+
const cwd0 = opts.cwd || process.cwd();
|
|
340
|
+
const outputBase = opts.config?.paths?.output || opts.outputBase || DEFAULT_OUTPUT_BASE;
|
|
341
|
+
const indexPath = resolveRunIndexPath(cwd0, outputBase);
|
|
342
|
+
const records = readRunIndexRecordsFromFile(indexPath);
|
|
343
|
+
const partitioned = partitionRunIndexBySession(records);
|
|
344
|
+
const handled = new Set();
|
|
345
|
+
|
|
346
|
+
const errMsg =
|
|
347
|
+
opts.errorMessage ||
|
|
348
|
+
'Run stopped (SIGINT/SIGTERM) before a normal summary was written.';
|
|
349
|
+
|
|
350
|
+
const flushInterrupted = (sessionId, sessionPathAbs) => {
|
|
351
|
+
if (!sessionId || !sessionPathAbs) return;
|
|
352
|
+
if (handled.has(sessionId)) return;
|
|
353
|
+
handled.add(sessionId);
|
|
354
|
+
const record = buildBrowserTestRunIndexRecord({
|
|
355
|
+
cwd: cwd0,
|
|
356
|
+
outputBase,
|
|
357
|
+
result: { state: { sessionPath: sessionPathAbs } },
|
|
358
|
+
success: false,
|
|
359
|
+
specPath: null,
|
|
360
|
+
status: 'interrupted',
|
|
361
|
+
errorMessage: errMsg,
|
|
362
|
+
});
|
|
363
|
+
if (record) {
|
|
364
|
+
appendRunIndexRecord(record);
|
|
365
|
+
mergeSessionRunState(sessionPathAbs, {
|
|
366
|
+
sessionId,
|
|
367
|
+
studioTestCaseId: record.studioTestCaseId || sessionId,
|
|
368
|
+
status: 'interrupted',
|
|
369
|
+
activeNode: null,
|
|
370
|
+
activeStageIndex: null,
|
|
371
|
+
errorMessage: record.errorMessage || null,
|
|
372
|
+
runSource: record.source || 'cli',
|
|
373
|
+
cwd: cwd0,
|
|
374
|
+
outputBase,
|
|
375
|
+
sessionPathAbs,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// 1) JSONL “live” sessions (progress newer than summary)
|
|
381
|
+
for (const [folderSessionId, entry] of partitioned) {
|
|
382
|
+
if (!runIndexSessionEntryIsLive(entry)) continue;
|
|
383
|
+
const prog = entry.progress;
|
|
384
|
+
if (!prog) continue;
|
|
385
|
+
const sessionId = String(folderSessionId);
|
|
386
|
+
const sessionPathAbs =
|
|
387
|
+
(prog.sessionPathAbs && String(prog.sessionPathAbs)) ||
|
|
388
|
+
join(cwd0, outputBase, SESSIONS_DIR, sessionId);
|
|
389
|
+
flushInterrupted(sessionId, sessionPathAbs);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 2) Disk-only live: Studio (or early CLI) wrote zibby-run-state.json as running before any
|
|
393
|
+
// run-index progress line — JSONL partition omits them, so Ctrl+C must clear by scanning sessions/.
|
|
394
|
+
const sessionsRoot = join(cwd0, outputBase, SESSIONS_DIR);
|
|
395
|
+
if (!existsSync(sessionsRoot)) return;
|
|
396
|
+
let names;
|
|
397
|
+
try {
|
|
398
|
+
names = readdirSync(sessionsRoot);
|
|
399
|
+
} catch {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
for (const name of names) {
|
|
403
|
+
const sessionPathAbs = join(sessionsRoot, name);
|
|
404
|
+
let st;
|
|
405
|
+
try {
|
|
406
|
+
st = statSync(sessionPathAbs);
|
|
407
|
+
} catch {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (!st.isDirectory()) continue;
|
|
411
|
+
const doc = readSessionRunState(sessionPathAbs);
|
|
412
|
+
if (!doc || doc.status !== 'running') continue;
|
|
413
|
+
flushInterrupted(String(name), sessionPathAbs);
|
|
414
|
+
}
|
|
415
|
+
} catch (e) {
|
|
416
|
+
console.warn(`[zibby browser-test run-index interrupt] ${e.message}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Export default workflows as JSON for the backend API
|
|
4
|
-
* Run: node packages/core/scripts/export-default-workflows.js
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
8
|
-
import { join, dirname } from 'path';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
import { WorkflowGraph } from '../src/framework/graph.js';
|
|
11
|
-
import { buildAnalysisGraph } from '../templates/graphs/analysisGraph.js';
|
|
12
|
-
|
|
13
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const outputDir = join(__dirname, '../../../backend/src/data');
|
|
15
|
-
|
|
16
|
-
// Ensure output directory exists
|
|
17
|
-
if (!existsSync(outputDir)) {
|
|
18
|
-
mkdirSync(outputDir, { recursive: true });
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Build and serialize analysis workflow
|
|
22
|
-
console.log('Building analysis workflow...');
|
|
23
|
-
const analysisGraph = new WorkflowGraph();
|
|
24
|
-
buildAnalysisGraph(analysisGraph);
|
|
25
|
-
const serializedAnalysis = analysisGraph.serialize();
|
|
26
|
-
|
|
27
|
-
// Add workflow metadata
|
|
28
|
-
const analysisWorkflow = {
|
|
29
|
-
id: 'default_analysis',
|
|
30
|
-
name: 'Analysis Workflow',
|
|
31
|
-
description: 'Analyzes tickets and generates code implementation',
|
|
32
|
-
...serializedAnalysis,
|
|
33
|
-
createdAt: new Date().toISOString(),
|
|
34
|
-
isDefault: true
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Write to JSON file
|
|
38
|
-
const outputPath = join(outputDir, 'default-workflows.json');
|
|
39
|
-
const workflows = {
|
|
40
|
-
analysis: analysisWorkflow
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
writeFileSync(outputPath, JSON.stringify(workflows, null, 2));
|
|
44
|
-
console.log(`✅ Exported default workflows to ${outputPath}`);
|
|
45
|
-
|
|
46
|
-
// Also export as individual files for clarity
|
|
47
|
-
writeFileSync(
|
|
48
|
-
join(outputDir, 'default-analysis-workflow.json'),
|
|
49
|
-
JSON.stringify(analysisWorkflow, null, 2)
|
|
50
|
-
);
|
|
51
|
-
console.log('✅ Exported individual workflow files');
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Patch cursor-agent CLI to auto-approve MCP tool calls for CI/CD use.
|
|
5
|
-
*
|
|
6
|
-
* This script modifies the cursor-agent source code to bypass the manual
|
|
7
|
-
* approval prompt for MCP tool executions, making it suitable for automated
|
|
8
|
-
* CI/CD pipelines.
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* node patch-cursor-mcp.js
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
15
|
-
import { join, dirname } from 'path';
|
|
16
|
-
import { homedir } from 'os';
|
|
17
|
-
import { fileURLToPath } from 'url';
|
|
18
|
-
|
|
19
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
-
const __dirname = dirname(__filename);
|
|
21
|
-
|
|
22
|
-
function findLatestCursorAgent() {
|
|
23
|
-
const versionsDir = join(homedir(), '.local/share/cursor-agent/versions');
|
|
24
|
-
|
|
25
|
-
if (!existsSync(versionsDir)) {
|
|
26
|
-
console.error(`❌ Cursor agent versions directory not found: ${versionsDir}`);
|
|
27
|
-
console.error(' Make sure cursor-agent is installed: curl https://cursor.com/install -fsS | bash');
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const versions = readdirSync(versionsDir)
|
|
32
|
-
.map(name => join(versionsDir, name))
|
|
33
|
-
.filter(path => statSync(path).isDirectory());
|
|
34
|
-
|
|
35
|
-
if (versions.length === 0) {
|
|
36
|
-
console.error(`❌ No cursor-agent versions found in ${versionsDir}`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Get the latest version by creation time
|
|
41
|
-
const latestVersion = versions
|
|
42
|
-
.map(path => ({ path, ctime: statSync(path).ctimeMs }))
|
|
43
|
-
.sort((a, b) => b.ctime - a.ctime)[0].path;
|
|
44
|
-
|
|
45
|
-
return join(latestVersion, 'index.js');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function backupFile(filePath) {
|
|
49
|
-
const backupPath = `${filePath }.backup`;
|
|
50
|
-
|
|
51
|
-
if (existsSync(backupPath)) {
|
|
52
|
-
console.log(`ℹ️ Backup already exists: ${backupPath}`);
|
|
53
|
-
return backupPath;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const content = readFileSync(filePath, 'utf8');
|
|
57
|
-
writeFileSync(backupPath, content, 'utf8');
|
|
58
|
-
|
|
59
|
-
console.log(`✅ Backup created: ${backupPath}`);
|
|
60
|
-
return backupPath;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function patchMcpApproval(filePath) {
|
|
64
|
-
const content = readFileSync(filePath, 'utf8');
|
|
65
|
-
|
|
66
|
-
// Check if already patched
|
|
67
|
-
if (content.includes('AUTO-APPROVE MCP TOOLS FOR CI/CD')) {
|
|
68
|
-
console.log('✅ File is already patched!');
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Pattern to match the requestApproval call for MCP
|
|
73
|
-
const pattern = /(\s+const result = await this\.pendingDecisionProvider\.requestApproval\(\{\s+type: OperationType\.Mcp,\s+details: \{\s+name: approvalDetails\.name,\s+toolName: approvalDetails\.toolName,\s+providerIdentifier: approvalDetails\.providerIdentifier,\s+args: approvalDetails\.args,\s+\},\s+toolCallId: args\.toolCallId,\s+\}\);)/;
|
|
74
|
-
|
|
75
|
-
const replacement = ` // AUTO-APPROVE MCP TOOLS FOR CI/CD
|
|
76
|
-
const result = { approved: true };
|
|
77
|
-
/*
|
|
78
|
-
const result = await this.pendingDecisionProvider.requestApproval({
|
|
79
|
-
type: OperationType.Mcp,
|
|
80
|
-
details: {
|
|
81
|
-
name: approvalDetails.name,
|
|
82
|
-
toolName: approvalDetails.toolName,
|
|
83
|
-
providerIdentifier: approvalDetails.providerIdentifier,
|
|
84
|
-
args: approvalDetails.args,
|
|
85
|
-
},
|
|
86
|
-
toolCallId: args.toolCallId,
|
|
87
|
-
});
|
|
88
|
-
*/`;
|
|
89
|
-
|
|
90
|
-
const newContent = content.replace(pattern, replacement);
|
|
91
|
-
|
|
92
|
-
if (newContent === content) {
|
|
93
|
-
console.log('ℹ️ Pattern not found - cursor-agent code structure has changed.');
|
|
94
|
-
console.log(' This is OKAY! Newer versions might not need this patch.');
|
|
95
|
-
console.log(' If auto-approval doesn\'t work, cursor-agent team may have fixed it!');
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
writeFileSync(filePath, newContent, 'utf8');
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function addApprovalKeyLogging(filePath) {
|
|
104
|
-
const content = readFileSync(filePath, 'utf8');
|
|
105
|
-
const lines = content.split('\n');
|
|
106
|
-
|
|
107
|
-
// Check if already added
|
|
108
|
-
if (lines.some(line => line.includes('🔑 APPROVAL KEY'))) {
|
|
109
|
-
console.log('✅ Approval key logging already enabled!');
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Find the line with generateApprovalKey and add logging after it
|
|
114
|
-
let targetLineNum = -1;
|
|
115
|
-
for (let i = 0; i < lines.length; i++) {
|
|
116
|
-
if (lines[i].includes('const approvalKey = generateApprovalKey(serverName, server, this.cwd);')) {
|
|
117
|
-
targetLineNum = i;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (targetLineNum === -1) {
|
|
123
|
-
console.log('⚠️ Could not find approval key generation line');
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Insert the console.log after the generateApprovalKey line
|
|
128
|
-
const indent = ' ';
|
|
129
|
-
const logLine = `${indent}console.log("🔑 APPROVAL KEY:", serverName, "=>", approvalKey);`;
|
|
130
|
-
lines.splice(targetLineNum + 1, 0, logLine);
|
|
131
|
-
|
|
132
|
-
writeFileSync(filePath, lines.join('\n'), 'utf8');
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function main() {
|
|
137
|
-
console.log('🔧 Patching cursor-agent for CI/CD MCP usage...\n');
|
|
138
|
-
console.log('ℹ️ Note: This patch is temporary and may not be needed in future versions.\n');
|
|
139
|
-
|
|
140
|
-
// Find cursor-agent
|
|
141
|
-
const indexFile = findLatestCursorAgent();
|
|
142
|
-
console.log(`📍 Found cursor-agent: ${indexFile}\n`);
|
|
143
|
-
|
|
144
|
-
// Backup
|
|
145
|
-
backupFile(indexFile);
|
|
146
|
-
console.log();
|
|
147
|
-
|
|
148
|
-
// Apply patches
|
|
149
|
-
const patchedApproval = patchMcpApproval(indexFile);
|
|
150
|
-
if (patchedApproval) {
|
|
151
|
-
console.log('✅ Patched MCP tool auto-approval');
|
|
152
|
-
}
|
|
153
|
-
console.log();
|
|
154
|
-
|
|
155
|
-
const patchedLogging = addApprovalKeyLogging(indexFile);
|
|
156
|
-
if (patchedLogging) {
|
|
157
|
-
console.log('✅ Added approval key logging');
|
|
158
|
-
}
|
|
159
|
-
console.log();
|
|
160
|
-
|
|
161
|
-
if (patchedApproval || patchedLogging) {
|
|
162
|
-
console.log('🎉 cursor-agent is now ready for CI/CD!');
|
|
163
|
-
console.log('\nNext steps:');
|
|
164
|
-
console.log('1. Run: cursor-agent mcp list');
|
|
165
|
-
console.log('2. Copy the approval key shown');
|
|
166
|
-
console.log('3. Add it to ~/.cursor/projects/YOUR_PROJECT/mcp-approvals.json');
|
|
167
|
-
console.log('\n⚠️ If cursor-agent updates break this patch, MCP may work without it.');
|
|
168
|
-
} else {
|
|
169
|
-
console.log('ℹ️ No changes needed - already configured or cursor-agent version doesn\'t need patching!');
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
main();
|
|
174
|
-
|