@zibby/core 0.1.20 → 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.
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 -540
  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
@@ -1,211 +0,0 @@
1
- import fs from 'fs/promises';
2
- import { SelectorGenerator } from './selector-generator.js';
3
-
4
- export class TestPostProcessor {
5
- static async generateFromEvents(testFilePath, eventsPath, traceActions, testTitle) {
6
- try {
7
- console.log(`[TestPostProcessor] 🎯 Generating test from events.json (100% accurate)`);
8
-
9
- const { readFileSync } = await import('fs');
10
- const events = JSON.parse(readFileSync(eventsPath, 'utf-8'));
11
-
12
- const actionEvents = events.filter(e => ['navigate', 'type', 'fill', 'click', 'select_option'].includes(e.type));
13
-
14
- // Match trace actions with events by sequence order (both are chronological)
15
- // Skip navigate events when matching trace actions (trace doesn't record navigations)
16
- const _actionEventsNoNav = actionEvents.filter(e => e.type !== 'navigate');
17
-
18
- console.log(`[TestPostProcessor] Found ${actionEvents.length} action events, ${traceActions.length} trace actions`);
19
-
20
- let testCode = `import { ZibbyRuntime } from '@zibby/core';\nimport { test, expect } from '@playwright/test';\n\n`;
21
- testCode += `test('${testTitle}', async ({ page }) => {\n`;
22
- testCode += ` const timestamp = Date.now();\n\n`;
23
-
24
- let traceIdx = 0;
25
-
26
- for (const event of actionEvents) {
27
- if (event.type === 'navigate') {
28
- testCode += ` await page.goto('${event.data.params.url}');\n\n`;
29
- } else if (event.type === 'type' || event.type === 'fill') {
30
- const { element, text } = event.data.params;
31
- const strategies = (traceActions[traceIdx]?.strategies) || [];
32
-
33
- const fingerprint = {
34
- name: element,
35
- action: 'fill',
36
- value: text,
37
- strategies
38
- };
39
-
40
- testCode += ` await ZibbyRuntime.step(page, ${JSON.stringify(fingerprint, null, 2)});\n\n`;
41
- traceIdx++;
42
- } else if (event.type === 'click') {
43
- const { element } = event.data.params;
44
- const strategies = (traceActions[traceIdx]?.strategies) || [];
45
-
46
- const fingerprint = {
47
- name: element,
48
- action: 'click',
49
- value: '',
50
- strategies
51
- };
52
-
53
- testCode += ` await ZibbyRuntime.step(page, ${JSON.stringify(fingerprint, null, 2)});\n\n`;
54
- traceIdx++;
55
- } else if (event.type === 'select_option') {
56
- const { element, values } = event.data.params;
57
- const strategies = (traceActions[traceIdx]?.strategies) || [];
58
-
59
- const fingerprint = {
60
- name: element,
61
- action: 'selectOption',
62
- value: Array.isArray(values) ? values[0] : values,
63
- strategies
64
- };
65
-
66
- testCode += ` await ZibbyRuntime.step(page, ${JSON.stringify(fingerprint, null, 2)});\n\n`;
67
- traceIdx++;
68
- }
69
- }
70
-
71
- testCode += `});\n`;
72
-
73
- const { dirname } = await import('path');
74
- const { mkdirSync } = await import('fs');
75
- mkdirSync(dirname(testFilePath), { recursive: true });
76
- await fs.writeFile(testFilePath, testCode, 'utf-8');
77
-
78
- console.log(`[TestPostProcessor] ✅ Generated test with ${events.filter(e => ['type', 'fill', 'click', 'select_option'].includes(e.type)).length} actions`);
79
-
80
- return true;
81
- } catch (err) {
82
- console.warn('[TestPostProcessor] Failed to generate from events:', err.message);
83
- return false;
84
- }
85
- }
86
-
87
- static async enhanceSelectorsWithTrace(testFilePath, traceActions, _executionData) {
88
- try {
89
- console.log(`[TestPostProcessor] 🛡️ Applying Zibby Safe Action Wrappers...`);
90
-
91
- let testCode = await fs.readFile(testFilePath, 'utf-8');
92
-
93
- if (!testCode.includes('ZibbyRuntime')) {
94
- testCode = `import { ZibbyRuntime } from '@zibby/core';\n${ testCode}`;
95
- }
96
-
97
- for (let i = 0; i < traceActions.length; i++) {
98
- const action = traceActions[i];
99
- const elementVar = `element${i}`;
100
-
101
- const actionPattern = new RegExp(
102
- `const ${elementVar}\\b\\s*=\\s*page\\.[^;]+;(\\s*await ${elementVar}\\.waitFor\\([^)]*\\);)?\\s*await ${elementVar}\\.(click|fill|type|selectOption|pressSequentially)\\(([^)]*)\\);`,
103
- 's'
104
- );
105
-
106
- const match = testCode.match(actionPattern);
107
- if (!match) continue;
108
-
109
- const fingerprint = {
110
- name: action.name || `Action ${i}`,
111
- action: action.method === 'type' ? 'fill' : action.method,
112
- value: match[3].trim().replace(/^['"]|['"]$/g, ''),
113
- strategies: action.strategies || []
114
- };
115
-
116
- const zibbyStep = `await ZibbyRuntime.step(page, ${JSON.stringify(fingerprint, null, 2)});`;
117
-
118
- testCode = testCode.replace(match[0], zibbyStep);
119
- }
120
-
121
- await fs.writeFile(testFilePath, testCode, 'utf-8');
122
- console.log(`[TestPostProcessor] ✅ Successfully converted test to Resilient Zibby format`);
123
-
124
- return true;
125
- } catch (err) {
126
- console.warn('[TestPostProcessor] Failed to apply safe wrappers:', err.message);
127
- return false;
128
- }
129
- }
130
-
131
- static async enhanceSelectors(testFilePath, executionData) {
132
- try {
133
- const { actions = [] } = executionData;
134
- if (!actions.length) return false;
135
-
136
- let testCode = await fs.readFile(testFilePath, 'utf-8');
137
-
138
- const selectorMap = this.buildSelectorMap(actions);
139
- testCode = this.replaceSimpleSelectors(testCode, selectorMap);
140
-
141
- await fs.writeFile(testFilePath, testCode, 'utf-8');
142
-
143
- return true;
144
- } catch (err) {
145
- console.warn('Failed to enhance selectors:', err);
146
- return false;
147
- }
148
- }
149
-
150
- static buildSelectorMap(actions) {
151
- const map = new Map();
152
-
153
- for (let i = 0; i < actions.length; i++) {
154
- const action = actions[i];
155
- if (!action.selectors || action.type === 'navigate') continue;
156
-
157
- const varName = `element${i}`;
158
- const robustCode = SelectorGenerator.generate(action, varName);
159
-
160
- const match = robustCode.match(/= (.+);/s);
161
- if (match) {
162
- const locatorExpr = match[1].trim();
163
-
164
- const key = `${action.type}:${this.normalizeDescription(action.description)}`;
165
- map.set(key, locatorExpr);
166
-
167
- if (action.selectors.role) {
168
- map.set(`role:${action.selectors.role.name}`, locatorExpr);
169
- }
170
- if (action.selectors.attributes?.placeholder) {
171
- map.set(`placeholder:${action.selectors.attributes.placeholder}`, locatorExpr);
172
- }
173
- }
174
- }
175
-
176
- return map;
177
- }
178
-
179
- static replaceSimpleSelectors(code, selectorMap) {
180
- let enhanced = code;
181
-
182
- const selectorPatterns = [
183
- /await page\.(getByRole|getByPlaceholder|getByText|getByLabel|locator)\([^)]+\)\.(fill|click|type)\([^)]*\)/g
184
- ];
185
-
186
- for (const pattern of selectorPatterns) {
187
- enhanced = enhanced.replace(pattern, (match) => {
188
- for (const [key, robustLocator] of selectorMap.entries()) {
189
- if (match.includes(key.split(':')[1])) {
190
- const actionMatch = match.match(/\.(fill|click|type)\(([^)]*)\)/);
191
- if (actionMatch) {
192
- const [, method, args] = actionMatch;
193
- const varName = 'element';
194
- return `const ${varName} = ${robustLocator};\n await ${varName}.${method}(${args})`;
195
- }
196
- }
197
- }
198
- return match;
199
- });
200
- }
201
-
202
- return enhanced;
203
- }
204
-
205
- static normalizeDescription(desc) {
206
- if (!desc) return '';
207
- return desc.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
208
- }
209
- }
210
-
211
- export default TestPostProcessor;
@@ -1,217 +0,0 @@
1
- /**
2
- * Timeline UI - OpenClaw-style progressive output with vertical line connector
3
- *
4
- * When inside a node, ALL console output is automatically prefixed with │
5
- * and soft-wrapped at terminal width so the pipe stays visible on every
6
- * visual line, even when text wraps.
7
- *
8
- * Milestone dots (◆) sit ON the line, replacing │ at that point.
9
- */
10
-
11
- import chalk from 'chalk';
12
-
13
- const PIPE = chalk.gray('│');
14
- const PIPE_START = chalk.gray('┌');
15
- const PIPE_END = chalk.gray('└');
16
- const DOT = chalk.green('◆');
17
- const DOT_TOOL = chalk.hex('#c084fc')('◆');
18
- const DOT_MEMORY = chalk.hex('#2dd4bf')('◆');
19
- const DOT_FAIL = chalk.red('◆');
20
-
21
- const PIPE_PREFIX = `${PIPE} `;
22
- const PIPE_VISUAL_WIDTH = 2;
23
-
24
- function formatDuration(ms) {
25
- if (ms < 1000) return `${ms}ms`;
26
- return `${(ms / 1000).toFixed(1)}s`;
27
- }
28
-
29
- function makeWrapWriter(orig, state) {
30
- return (chunk, encoding, callback) => {
31
- if (typeof chunk !== 'string') {
32
- return orig(chunk, encoding, callback);
33
- }
34
-
35
- const cols = process.stdout.columns || 120;
36
- let out = '';
37
-
38
- for (let i = 0; i < chunk.length; i++) {
39
- const ch = chunk[i];
40
-
41
- if (state.lineStart) {
42
- out += PIPE_PREFIX;
43
- state.col = PIPE_VISUAL_WIDTH;
44
- state.lineStart = false;
45
- }
46
-
47
- if (ch === '\n') {
48
- out += ch;
49
- state.lineStart = true;
50
- state.col = 0;
51
- state.inEsc = false;
52
- } else if (ch === '\x1b') {
53
- state.inEsc = true;
54
- out += ch;
55
- } else if (state.inEsc) {
56
- out += ch;
57
- if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
58
- state.inEsc = false;
59
- }
60
- } else {
61
- state.col++;
62
- out += ch;
63
- if (state.col >= cols) {
64
- out += `\n${ PIPE_PREFIX}`;
65
- state.col = PIPE_VISUAL_WIDTH;
66
- }
67
- }
68
- }
69
-
70
- return orig(out, encoding, callback);
71
- };
72
- }
73
-
74
- class Timeline {
75
- constructor() {
76
- this._currentNode = null;
77
- this._origStdoutWrite = null;
78
- this._origStderrWrite = null;
79
- }
80
-
81
- get isInsideNode() {
82
- return this._currentNode !== null;
83
- }
84
-
85
- _startIntercepting() {
86
- this._origStdoutWrite = process.stdout.write.bind(process.stdout);
87
- this._origStderrWrite = process.stderr.write.bind(process.stderr);
88
-
89
- const outState = { lineStart: true, col: 0, inEsc: false };
90
- const errState = { lineStart: true, col: 0, inEsc: false };
91
- this._outState = outState;
92
- this._errState = errState;
93
-
94
- process.stdout.write = makeWrapWriter(this._origStdoutWrite, outState);
95
- process.stderr.write = makeWrapWriter(this._origStderrWrite, errState);
96
- }
97
-
98
- _stopIntercepting() {
99
- if (this._origStdoutWrite) {
100
- if (this._outState && !this._outState.lineStart) {
101
- this._origStdoutWrite('\n');
102
- }
103
- process.stdout.write = this._origStdoutWrite;
104
- }
105
- if (this._origStderrWrite) {
106
- if (this._errState && !this._errState.lineStart) {
107
- this._origStderrWrite('\n');
108
- }
109
- process.stderr.write = this._origStderrWrite;
110
- }
111
- this._origStdoutWrite = null;
112
- this._origStderrWrite = null;
113
- }
114
-
115
- _rawWrite(msg) {
116
- const write = this._origStdoutWrite || process.stdout.write.bind(process.stdout);
117
- write(`${msg }\n`);
118
- }
119
-
120
- /**
121
- * Write a milestone dot ON the line. If interceptor is active and we're
122
- * mid-line, finish that line first so the dot starts on a fresh line.
123
- */
124
- _writeDot(dot, message) {
125
- if (this._origStdoutWrite) {
126
- if (this._outState && !this._outState.lineStart) {
127
- this._origStdoutWrite('\n');
128
- this._outState.lineStart = true;
129
- this._outState.col = 0;
130
- }
131
- this._origStdoutWrite(`${dot} ${message}\n`);
132
- } else {
133
- const write = process.stdout.write.bind(process.stdout);
134
- write(`${dot} ${message}\n`);
135
- }
136
- }
137
-
138
- /**
139
- * Milestone dot ON the line (inside a node) or with pipe prefix (outside).
140
- */
141
- step(message) {
142
- if (this._origStdoutWrite) {
143
- this._writeDot(DOT, message);
144
- } else {
145
- const write = process.stdout.write.bind(process.stdout);
146
- write(`${PIPE} ${DOT} ${message}\n`);
147
- }
148
- }
149
-
150
- stepTool(message) {
151
- if (this._origStdoutWrite) {
152
- this._writeDot(DOT_TOOL, message);
153
- } else {
154
- const write = process.stdout.write.bind(process.stdout);
155
- write(`${PIPE} ${DOT_TOOL} ${message}\n`);
156
- }
157
- }
158
-
159
- stepMemory(message) {
160
- const colored = chalk.hex('#2dd4bf')(message);
161
- if (this._origStdoutWrite) {
162
- this._writeDot(DOT_MEMORY, colored);
163
- } else {
164
- const write = process.stdout.write.bind(process.stdout);
165
- write(`${PIPE} ${DOT_MEMORY} ${colored}\n`);
166
- }
167
- }
168
-
169
- stepFail(message) {
170
- if (this._origStdoutWrite) {
171
- this._writeDot(DOT_FAIL, chalk.red(message));
172
- } else {
173
- const write = process.stdout.write.bind(process.stdout);
174
- write(`${PIPE} ${DOT_FAIL} ${chalk.red(message)}\n`);
175
- }
176
- }
177
-
178
- nodeStart(name) {
179
- this._currentNode = name;
180
- this._rawWrite(`${PIPE_START} ${name}`);
181
- this._startIntercepting();
182
- }
183
-
184
- nodeComplete(name, opts = {}) {
185
- this._stopIntercepting();
186
- const { duration, details } = opts;
187
- if (details) {
188
- for (const d of details) {
189
- this._rawWrite(`${DOT} ${d}`);
190
- }
191
- }
192
- const durationStr = duration ? chalk.dim(` ${formatDuration(duration)}`) : '';
193
- this._rawWrite(`${PIPE_END} ${chalk.green('done')}${durationStr}`);
194
- this._rawWrite('');
195
- }
196
-
197
- nodeFailed(name, error, opts = {}) {
198
- this._stopIntercepting();
199
- const { duration } = opts;
200
- const durationStr = duration ? chalk.dim(` ${formatDuration(duration)}`) : '';
201
- this._rawWrite(`${DOT_FAIL} ${chalk.red(error)}`);
202
- this._rawWrite(`${PIPE_END} ${chalk.red('failed')}${durationStr}`);
203
- this._rawWrite('');
204
- }
205
-
206
- route(from, to) {
207
- this._rawWrite(chalk.dim(` ${from} → ${to}`));
208
- this._rawWrite('');
209
- }
210
-
211
- graphComplete() {
212
- this._rawWrite(chalk.green.bold('✓ Workflow completed'));
213
- }
214
- }
215
-
216
- export const timeline = new Timeline();
217
- export default timeline;