@zibby/core 0.1.21 → 0.1.23

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.
Files changed (167) hide show
  1. package/dist/agents/base.js +17 -0
  2. package/dist/backend-client.js +1 -0
  3. package/dist/constants/tool-names.js +1 -0
  4. package/dist/constants/zibby-scratch.js +1 -0
  5. package/dist/constants.js +1 -0
  6. package/dist/enrichment/base.js +1 -0
  7. package/dist/enrichment/enrichers/accessibility-enricher.js +1 -0
  8. package/dist/enrichment/enrichers/dom-enricher.js +1 -0
  9. package/dist/enrichment/enrichers/page-state-enricher.js +1 -0
  10. package/dist/enrichment/enrichers/position-enricher.js +1 -0
  11. package/dist/enrichment/index.js +1 -0
  12. package/dist/enrichment/mcp-integration.js +1 -0
  13. package/dist/enrichment/mcp-ref-enricher.js +1 -0
  14. package/dist/enrichment/pipeline.js +3 -0
  15. package/dist/enrichment/trace-text-enricher.js +1 -0
  16. package/dist/framework/agents/assistant-strategy.js +5 -0
  17. package/dist/framework/agents/base.js +1 -0
  18. package/dist/framework/agents/claude-strategy.js +4 -0
  19. package/dist/framework/agents/codex-strategy.js +4 -0
  20. package/dist/framework/agents/cursor-strategy.js +32 -0
  21. package/dist/framework/agents/gemini-strategy.js +11 -0
  22. package/dist/framework/agents/index.js +13 -0
  23. package/dist/framework/agents/middleware/assistant-round-pipeline.js +3 -0
  24. package/dist/framework/agents/providers/base.js +1 -0
  25. package/dist/framework/agents/providers/index.js +1 -0
  26. package/dist/framework/agents/providers/openai-transport.js +2 -0
  27. package/dist/framework/agents/providers/openai.js +1 -0
  28. package/dist/framework/agents/providers/transport-base.js +1 -0
  29. package/dist/framework/agents/utils/auth-resolver.js +1 -0
  30. package/dist/framework/agents/utils/cursor-output-formatter.js +1 -0
  31. package/dist/framework/agents/utils/openai-proxy-formatter.js +9 -0
  32. package/dist/framework/agents/utils/payload-budget.js +3 -0
  33. package/dist/framework/agents/utils/structured-output-formatter.js +21 -0
  34. package/dist/framework/code-generator.js +10 -0
  35. package/dist/framework/constants.js +1 -0
  36. package/dist/framework/context-loader.js +5 -0
  37. package/dist/framework/function-bridge.js +2 -0
  38. package/dist/framework/function-skill-registry.js +1 -0
  39. package/dist/framework/graph-compiler.js +1 -0
  40. package/dist/framework/graph.js +5 -0
  41. package/dist/framework/index.js +1 -0
  42. package/dist/framework/mcp-client.js +2 -0
  43. package/dist/framework/node-registry.js +9 -0
  44. package/dist/framework/node.js +5 -0
  45. package/dist/framework/output-parser.js +3 -0
  46. package/dist/framework/skill-registry.js +1 -0
  47. package/dist/framework/state-utils.js +1 -0
  48. package/dist/framework/state.js +1 -0
  49. package/dist/framework/tool-resolver.js +1 -0
  50. package/dist/index.js +8 -0
  51. package/dist/runtime/generation/base.js +1 -0
  52. package/dist/runtime/generation/index.js +3 -0
  53. package/dist/runtime/generation/mcp-ref-strategy.js +41 -0
  54. package/dist/runtime/generation/stable-id-strategy.js +16 -0
  55. package/dist/runtime/stable-id-runtime.js +1 -0
  56. package/dist/runtime/verification/base.js +1 -0
  57. package/dist/runtime/verification/index.js +3 -0
  58. package/dist/runtime/verification/playwright-json-strategy.js +1 -0
  59. package/dist/runtime/zibby-runtime.js +1 -0
  60. package/dist/sync/index.js +1 -0
  61. package/dist/sync/uploader.js +1 -0
  62. package/dist/tools/run-playwright-test.js +5 -0
  63. package/dist/utils/adf-converter.js +7 -0
  64. package/dist/utils/ast-utils.js +1 -0
  65. package/dist/utils/ci-setup.js +5 -0
  66. package/dist/utils/cursor-mcp-isolated-home.js +1 -0
  67. package/dist/utils/cursor-utils.js +18 -0
  68. package/dist/utils/live-frame-discovery.js +1 -0
  69. package/dist/utils/logger.js +1 -0
  70. package/dist/utils/mcp-config-writer.js +10 -0
  71. package/dist/utils/mission-control-from-run-states.js +1 -0
  72. package/dist/utils/node-schema-parser.js +1 -0
  73. package/dist/utils/parallel-config.js +1 -0
  74. package/dist/utils/post-process-events.js +1 -0
  75. package/dist/utils/result-handler.js +1 -0
  76. package/{src → dist}/utils/ripple-effect.js +3 -12
  77. package/dist/utils/run-capacity-coordinator.js +1 -0
  78. package/dist/utils/run-capacity-queue.js +2 -0
  79. package/dist/utils/run-index-merge.js +1 -0
  80. package/dist/utils/run-index-post-cli.js +1 -0
  81. package/dist/utils/run-registry.js +3 -0
  82. package/dist/utils/run-state-session.js +2 -0
  83. package/dist/utils/selector-generator.js +4 -0
  84. package/dist/utils/session-state-constants.js +1 -0
  85. package/dist/utils/session-state-live-runs.js +1 -0
  86. package/dist/utils/streaming-parser.js +4 -0
  87. package/dist/utils/test-post-processor.js +18 -0
  88. package/dist/utils/timeline.js +14 -0
  89. package/dist/utils/trace-parser.js +2 -0
  90. package/dist/utils/video-organizer.js +3 -0
  91. package/package.json +49 -35
  92. package/templates/browser-test-automation/README.md +29 -7
  93. package/templates/browser-test-automation/chat.mjs +36 -0
  94. package/templates/browser-test-automation/graph.mjs +5 -9
  95. package/templates/browser-test-automation/nodes/execute-live.mjs +30 -58
  96. package/templates/browser-test-automation/nodes/generate-script.mjs +32 -12
  97. package/templates/browser-test-automation/nodes/utils.mjs +153 -10
  98. package/templates/browser-test-automation/pipeline-ids.js +12 -0
  99. package/templates/browser-test-automation/result-handler.mjs +78 -2
  100. package/templates/browser-test-automation/run-index.mjs +418 -0
  101. package/scripts/export-default-workflows.js +0 -51
  102. package/scripts/patch-cursor-mcp.js +0 -174
  103. package/scripts/setup-ci.sh +0 -115
  104. package/scripts/setup-official-playwright-mcp.sh +0 -226
  105. package/scripts/test-with-video.sh +0 -49
  106. package/src/agents/base.js +0 -361
  107. package/src/constants.js +0 -47
  108. package/src/enrichment/base.js +0 -49
  109. package/src/enrichment/enrichers/accessibility-enricher.js +0 -197
  110. package/src/enrichment/enrichers/dom-enricher.js +0 -171
  111. package/src/enrichment/enrichers/page-state-enricher.js +0 -129
  112. package/src/enrichment/enrichers/position-enricher.js +0 -67
  113. package/src/enrichment/index.js +0 -96
  114. package/src/enrichment/mcp-integration.js +0 -149
  115. package/src/enrichment/mcp-ref-enricher.js +0 -78
  116. package/src/enrichment/pipeline.js +0 -192
  117. package/src/enrichment/trace-text-enricher.js +0 -115
  118. package/src/framework/AGENTS.md +0 -98
  119. package/src/framework/agents/base.js +0 -72
  120. package/src/framework/agents/claude-strategy.js +0 -278
  121. package/src/framework/agents/cursor-strategy.js +0 -544
  122. package/src/framework/agents/index.js +0 -105
  123. package/src/framework/agents/utils/cursor-output-formatter.js +0 -67
  124. package/src/framework/agents/utils/openai-proxy-formatter.js +0 -249
  125. package/src/framework/code-generator.js +0 -301
  126. package/src/framework/constants.js +0 -33
  127. package/src/framework/context-loader.js +0 -101
  128. package/src/framework/function-bridge.js +0 -78
  129. package/src/framework/function-skill-registry.js +0 -20
  130. package/src/framework/graph-compiler.js +0 -342
  131. package/src/framework/graph.js +0 -610
  132. package/src/framework/index.js +0 -28
  133. package/src/framework/node-registry.js +0 -163
  134. package/src/framework/node.js +0 -259
  135. package/src/framework/output-parser.js +0 -71
  136. package/src/framework/skill-registry.js +0 -55
  137. package/src/framework/state-utils.js +0 -52
  138. package/src/framework/state.js +0 -67
  139. package/src/framework/tool-resolver.js +0 -65
  140. package/src/index.js +0 -345
  141. package/src/runtime/generation/base.js +0 -46
  142. package/src/runtime/generation/index.js +0 -70
  143. package/src/runtime/generation/mcp-ref-strategy.js +0 -197
  144. package/src/runtime/generation/stable-id-strategy.js +0 -170
  145. package/src/runtime/stable-id-runtime.js +0 -248
  146. package/src/runtime/verification/base.js +0 -44
  147. package/src/runtime/verification/index.js +0 -67
  148. package/src/runtime/verification/playwright-json-strategy.js +0 -119
  149. package/src/runtime/zibby-runtime.js +0 -299
  150. package/src/sync/index.js +0 -2
  151. package/src/sync/uploader.js +0 -29
  152. package/src/tools/run-playwright-test.js +0 -158
  153. package/src/utils/adf-converter.js +0 -68
  154. package/src/utils/ast-utils.js +0 -37
  155. package/src/utils/ci-setup.js +0 -124
  156. package/src/utils/cursor-utils.js +0 -71
  157. package/src/utils/logger.js +0 -144
  158. package/src/utils/mcp-config-writer.js +0 -115
  159. package/src/utils/node-schema-parser.js +0 -522
  160. package/src/utils/post-process-events.js +0 -55
  161. package/src/utils/result-handler.js +0 -102
  162. package/src/utils/selector-generator.js +0 -239
  163. package/src/utils/streaming-parser.js +0 -387
  164. package/src/utils/test-post-processor.js +0 -211
  165. package/src/utils/timeline.js +0 -217
  166. package/src/utils/trace-parser.js +0 -325
  167. 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
-