@runcofactor/cli 0.10.0
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/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1516 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
parseTraceActions,
|
|
4
|
+
reconstructSnapshots
|
|
5
|
+
} from "./chunk-52IO7ACJ.js";
|
|
6
|
+
import "./chunk-VKVL7WBN.js";
|
|
7
|
+
|
|
8
|
+
// ../diagnostic-workspace/src/assemble-workspace.ts
|
|
9
|
+
import * as fs10 from "fs/promises";
|
|
10
|
+
|
|
11
|
+
// ../diagnostic-workspace/src/_shared.ts
|
|
12
|
+
var NAVIGATION_ACTIONS = /* @__PURE__ */ new Set([
|
|
13
|
+
"navigate",
|
|
14
|
+
"navigation",
|
|
15
|
+
"pushstate",
|
|
16
|
+
"popstate",
|
|
17
|
+
"pushState",
|
|
18
|
+
"popState",
|
|
19
|
+
"redirect",
|
|
20
|
+
"load"
|
|
21
|
+
]);
|
|
22
|
+
var hostOf = (raw) => {
|
|
23
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
24
|
+
try {
|
|
25
|
+
return new URL(raw).hostname.toLowerCase().replace(/^www\./, "");
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var formatRelative = (ms) => {
|
|
31
|
+
const sign = ms < 0 ? "-" : "+";
|
|
32
|
+
const abs = Math.abs(ms);
|
|
33
|
+
const seconds = Math.floor(abs / 1e3);
|
|
34
|
+
const millis = abs % 1e3;
|
|
35
|
+
return `T${sign}${seconds}.${String(millis).padStart(3, "0")}s`;
|
|
36
|
+
};
|
|
37
|
+
var formatAbsolute = (ms) => {
|
|
38
|
+
const d = new Date(ms);
|
|
39
|
+
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
40
|
+
const mm = String(d.getUTCMinutes()).padStart(2, "0");
|
|
41
|
+
const ss = String(d.getUTCSeconds()).padStart(2, "0");
|
|
42
|
+
const mil = String(d.getUTCMilliseconds()).padStart(3, "0");
|
|
43
|
+
return `${hh}:${mm}:${ss}.${mil}`;
|
|
44
|
+
};
|
|
45
|
+
var formatPrefix = (timestampMs, baselineMs) => {
|
|
46
|
+
if (timestampMs === null) return "[unknown @ unknown]";
|
|
47
|
+
const rel = baselineMs !== null ? formatRelative(timestampMs - baselineMs) : "T+?";
|
|
48
|
+
const abs = formatAbsolute(timestampMs);
|
|
49
|
+
return `[${rel} @ ${abs}]`;
|
|
50
|
+
};
|
|
51
|
+
var oneLine = (s) => s.replace(/\s+/g, " ").trim();
|
|
52
|
+
var truncate = (s, max) => s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
53
|
+
|
|
54
|
+
// ../diagnostic-workspace/src/paths.ts
|
|
55
|
+
import * as path from "path";
|
|
56
|
+
var WORKSPACE_PATHS = {
|
|
57
|
+
manifest: "manifest.json",
|
|
58
|
+
reportedFailure: "reported-failure.md",
|
|
59
|
+
summary: "summary.md",
|
|
60
|
+
ledger: "ledger.md",
|
|
61
|
+
ledgerIndex: "ledger-index.md",
|
|
62
|
+
steps: "steps",
|
|
63
|
+
// steps/<NNN>-<stepId>.md
|
|
64
|
+
timeline: "timeline",
|
|
65
|
+
timelineSteps: "timeline/steps.jsonl",
|
|
66
|
+
timelineActions: "timeline/actions.jsonl",
|
|
67
|
+
timelineNavigation: "timeline/navigation.jsonl",
|
|
68
|
+
timelineAssertions: "timeline/assertions.jsonl",
|
|
69
|
+
timelineCacheDecisions: "timeline/cache-decisions.jsonl",
|
|
70
|
+
thoughts: "thoughts",
|
|
71
|
+
thoughtsAll: "thoughts/all.jsonl",
|
|
72
|
+
thoughtsByStep: "thoughts/by-step",
|
|
73
|
+
// thoughts/by-step/<stepId>.md
|
|
74
|
+
browser: "browser",
|
|
75
|
+
browserTraceZip: "browser/trace.zip",
|
|
76
|
+
browserActions: "browser/actions.jsonl",
|
|
77
|
+
browserDoms: "browser/doms",
|
|
78
|
+
// browser/doms/<NNNN>-<timestamp>.html
|
|
79
|
+
browserFrames: "browser/frames",
|
|
80
|
+
// browser/frames/<NNNN>-<timestamp>.png
|
|
81
|
+
browserA11y: "browser/a11y",
|
|
82
|
+
// browser/a11y/<NNNN>-<timestamp>.yaml
|
|
83
|
+
browserResources: "browser/resources",
|
|
84
|
+
logs: "logs",
|
|
85
|
+
logsNetwork: "logs/network.jsonl",
|
|
86
|
+
logsNetworkErrors: "logs/network-errors.jsonl",
|
|
87
|
+
logsConsole: "logs/console.jsonl",
|
|
88
|
+
logsConsoleErrors: "logs/console-errors.jsonl",
|
|
89
|
+
logsSentry: "logs/sentry-logs.jsonl",
|
|
90
|
+
logsSentrySpans: "logs/sentry-spans.jsonl",
|
|
91
|
+
workflow: "workflow",
|
|
92
|
+
workflowDefinition: "workflow/definition.json",
|
|
93
|
+
workflowSnapshot: "workflow/snapshot.json",
|
|
94
|
+
workflowNodes: "workflow/nodes",
|
|
95
|
+
// workflow/nodes/<nodeId>.json
|
|
96
|
+
variables: "variables",
|
|
97
|
+
variablesInputs: "variables/inputs.json",
|
|
98
|
+
variablesLineage: "variables/lineage.jsonl",
|
|
99
|
+
history: "history",
|
|
100
|
+
historyRecentRuns: "history/recent-runs.jsonl",
|
|
101
|
+
historyLastGreen: "history/last-green",
|
|
102
|
+
notes: "notes"
|
|
103
|
+
};
|
|
104
|
+
var WORKSPACE_DIRS = [
|
|
105
|
+
WORKSPACE_PATHS.steps,
|
|
106
|
+
WORKSPACE_PATHS.timeline,
|
|
107
|
+
WORKSPACE_PATHS.thoughts,
|
|
108
|
+
WORKSPACE_PATHS.thoughtsByStep,
|
|
109
|
+
WORKSPACE_PATHS.browser,
|
|
110
|
+
WORKSPACE_PATHS.browserDoms,
|
|
111
|
+
WORKSPACE_PATHS.browserFrames,
|
|
112
|
+
WORKSPACE_PATHS.browserA11y,
|
|
113
|
+
WORKSPACE_PATHS.browserResources,
|
|
114
|
+
WORKSPACE_PATHS.logs,
|
|
115
|
+
WORKSPACE_PATHS.workflow,
|
|
116
|
+
WORKSPACE_PATHS.workflowNodes,
|
|
117
|
+
WORKSPACE_PATHS.variables,
|
|
118
|
+
WORKSPACE_PATHS.history,
|
|
119
|
+
WORKSPACE_PATHS.notes
|
|
120
|
+
];
|
|
121
|
+
var resolveWorkspacePath = (root, relative) => path.join(root, relative);
|
|
122
|
+
var stepRollupRelativePath = (orderIndex, stepId) => path.join(WORKSPACE_PATHS.steps, `${String(orderIndex).padStart(3, "0")}-${stepId}.md`);
|
|
123
|
+
var thoughtsByStepRelativePath = (stepId) => path.join(WORKSPACE_PATHS.thoughtsByStep, `${stepId}.md`);
|
|
124
|
+
var workflowNodeRelativePath = (nodeId) => path.join(WORKSPACE_PATHS.workflowNodes, `${nodeId}.json`);
|
|
125
|
+
var actionArtifactBasename = (actionIndex, absoluteTsMs, ext) => `${String(actionIndex).padStart(4, "0")}-${absoluteTsMs}.${ext}`;
|
|
126
|
+
|
|
127
|
+
// ../diagnostic-workspace/src/manifest-builder.ts
|
|
128
|
+
import * as fs from "fs/promises";
|
|
129
|
+
var writeManifest = async (input) => {
|
|
130
|
+
const startedMs = input.startedAt ? Date.parse(input.startedAt) : null;
|
|
131
|
+
const finishedMs = input.finishedAt ? Date.parse(input.finishedAt) : null;
|
|
132
|
+
const durationMs = startedMs !== null && finishedMs !== null ? finishedMs - startedMs : null;
|
|
133
|
+
const manifest = {
|
|
134
|
+
runId: input.runId,
|
|
135
|
+
workflowId: input.workflowId,
|
|
136
|
+
orgId: input.orgId,
|
|
137
|
+
status: input.status,
|
|
138
|
+
startedAt: input.startedAt,
|
|
139
|
+
finishedAt: input.finishedAt,
|
|
140
|
+
durationMs,
|
|
141
|
+
errorMessage: input.errorMessage,
|
|
142
|
+
workflowName: input.workflowName,
|
|
143
|
+
failedStepId: input.failedStepId,
|
|
144
|
+
failedNodeId: input.failedNodeId,
|
|
145
|
+
failedStepName: input.failedStepName,
|
|
146
|
+
traceStorageKey: input.traceStorageKey,
|
|
147
|
+
diagnosticsStorageKey: input.diagnosticsStorageKey,
|
|
148
|
+
videoStorageKey: input.videoStorageKey,
|
|
149
|
+
materializedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
150
|
+
partial: input.partial
|
|
151
|
+
};
|
|
152
|
+
await fs.writeFile(
|
|
153
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.manifest),
|
|
154
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
155
|
+
`,
|
|
156
|
+
"utf-8"
|
|
157
|
+
);
|
|
158
|
+
return manifest;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// ../diagnostic-workspace/src/reported-failure-builder.ts
|
|
162
|
+
import * as fs2 from "fs/promises";
|
|
163
|
+
var writeReportedFailure = async (input) => {
|
|
164
|
+
const lines = [];
|
|
165
|
+
lines.push("# Reported Failure");
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push(
|
|
168
|
+
"**This is the test runner's hypothesis. Verify with evidence before trusting it \u2014 the root cause is often upstream of the visible failure.**"
|
|
169
|
+
);
|
|
170
|
+
lines.push("");
|
|
171
|
+
lines.push(`Run status: \`${input.runStatus}\``);
|
|
172
|
+
lines.push("");
|
|
173
|
+
if (input.runStatus !== "failed") {
|
|
174
|
+
lines.push(
|
|
175
|
+
`> The run did not end in a \`failed\` status. The test runner did not designate a failed step.`
|
|
176
|
+
);
|
|
177
|
+
lines.push("");
|
|
178
|
+
} else if (!input.failedStepName) {
|
|
179
|
+
lines.push(
|
|
180
|
+
`> The run is \`failed\` but no failed step was identified. Investigate the run-level error message and walk the timeline.`
|
|
181
|
+
);
|
|
182
|
+
lines.push("");
|
|
183
|
+
} else {
|
|
184
|
+
const ordinal = input.failedStepOrderIndex !== null ? ` (step ${input.failedStepOrderIndex})` : "";
|
|
185
|
+
lines.push(`Failed step${ordinal}: **${input.failedStepName}**`);
|
|
186
|
+
lines.push("");
|
|
187
|
+
}
|
|
188
|
+
if (input.errorMessage) {
|
|
189
|
+
lines.push("## Error message");
|
|
190
|
+
lines.push("");
|
|
191
|
+
lines.push("```");
|
|
192
|
+
lines.push(input.errorMessage);
|
|
193
|
+
lines.push("```");
|
|
194
|
+
lines.push("");
|
|
195
|
+
}
|
|
196
|
+
lines.push("## What to do next");
|
|
197
|
+
lines.push("");
|
|
198
|
+
lines.push(
|
|
199
|
+
"1. Read `summary.md` for the run-level overview and step table."
|
|
200
|
+
);
|
|
201
|
+
lines.push(
|
|
202
|
+
"2. Open the failed step's rollup at `steps/<NNN>-<stepId>.md` to gather evidence at the failure point."
|
|
203
|
+
);
|
|
204
|
+
lines.push(
|
|
205
|
+
"3. Walk backwards through prior `steps/*.md` rollups \u2014 the divergence often happens upstream."
|
|
206
|
+
);
|
|
207
|
+
lines.push("");
|
|
208
|
+
await fs2.writeFile(
|
|
209
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.reportedFailure),
|
|
210
|
+
`${lines.join("\n")}
|
|
211
|
+
`,
|
|
212
|
+
"utf-8"
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// ../diagnostic-workspace/src/jsonl-writers.ts
|
|
217
|
+
import * as fs3 from "fs/promises";
|
|
218
|
+
var writeJsonl = (absolutePath, lines) => fs3.writeFile(
|
|
219
|
+
absolutePath,
|
|
220
|
+
lines.length === 0 ? "" : `${lines.map((l) => JSON.stringify(l)).join("\n")}
|
|
221
|
+
`,
|
|
222
|
+
"utf-8"
|
|
223
|
+
);
|
|
224
|
+
var writeJson = (absolutePath, value) => fs3.writeFile(absolutePath, `${JSON.stringify(value, null, 2)}
|
|
225
|
+
`, "utf-8");
|
|
226
|
+
var writeText = (absolutePath, text) => fs3.writeFile(absolutePath, text, "utf-8");
|
|
227
|
+
var isoToMs = (iso) => {
|
|
228
|
+
if (!iso) return null;
|
|
229
|
+
const parsed = Date.parse(iso);
|
|
230
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
231
|
+
};
|
|
232
|
+
var isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
233
|
+
var writeTimelineSteps = async (workspaceRoot, steps) => {
|
|
234
|
+
const lines = steps.slice().sort((a, b) => a.orderIndex - b.orderIndex).map((step) => {
|
|
235
|
+
const meta = isObject(step.metadataJson) ? step.metadataJson : {};
|
|
236
|
+
const startedMs = isoToMs(step.startedAt);
|
|
237
|
+
const finishedMs = isoToMs(step.finishedAt);
|
|
238
|
+
return {
|
|
239
|
+
id: step.id,
|
|
240
|
+
nodeId: step.nodeId,
|
|
241
|
+
orderIndex: step.orderIndex,
|
|
242
|
+
name: step.nodeName,
|
|
243
|
+
nodeType: step.nodeType,
|
|
244
|
+
status: step.status,
|
|
245
|
+
startedAt: step.startedAt,
|
|
246
|
+
finishedAt: step.finishedAt,
|
|
247
|
+
startedAtMs: startedMs,
|
|
248
|
+
finishedAtMs: finishedMs,
|
|
249
|
+
durationMs: startedMs !== null && finishedMs !== null ? finishedMs - startedMs : null,
|
|
250
|
+
errorMessage: step.errorMessage,
|
|
251
|
+
cacheOutcome: meta.cacheOutcome ?? null,
|
|
252
|
+
cacheHitSource: meta.cacheHitSource ?? null,
|
|
253
|
+
cacheProcessingStatus: meta.cacheProcessingStatus ?? null,
|
|
254
|
+
cacheOutcomeReason: meta.cacheOutcomeReason ?? null
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
await writeJsonl(
|
|
258
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.timelineSteps),
|
|
259
|
+
lines
|
|
260
|
+
);
|
|
261
|
+
};
|
|
262
|
+
var writeTimelineActionsAndNavigation = async (workspaceRoot, timeline) => {
|
|
263
|
+
const baselineMs = timeline.startTimestamp ? Date.parse(timeline.startTimestamp) : null;
|
|
264
|
+
const actions = timeline.entries.map((e) => ({
|
|
265
|
+
action: e.action,
|
|
266
|
+
target: e.target ?? null,
|
|
267
|
+
value: e.value ?? null,
|
|
268
|
+
url: e.url ?? null,
|
|
269
|
+
relativeTimestampMs: e.timestampMs,
|
|
270
|
+
timestampMs: baselineMs !== null ? baselineMs + e.timestampMs : null,
|
|
271
|
+
success: e.success,
|
|
272
|
+
isAnnotation: e.isAnnotation ?? false
|
|
273
|
+
}));
|
|
274
|
+
await writeJsonl(
|
|
275
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.timelineActions),
|
|
276
|
+
actions
|
|
277
|
+
);
|
|
278
|
+
const navRows = [];
|
|
279
|
+
let prevUrl = null;
|
|
280
|
+
for (const a of actions) {
|
|
281
|
+
const isNavAction = NAVIGATION_ACTIONS.has(a.action);
|
|
282
|
+
const urlChanged = a.url !== null && prevUrl !== null && a.url !== prevUrl;
|
|
283
|
+
if (!isNavAction && !urlChanged) {
|
|
284
|
+
if (a.url) prevUrl = a.url;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const fromHost = hostOf(prevUrl);
|
|
288
|
+
const toHost = hostOf(a.url);
|
|
289
|
+
navRows.push({
|
|
290
|
+
relativeTimestampMs: a.relativeTimestampMs,
|
|
291
|
+
timestampMs: a.timestampMs,
|
|
292
|
+
action: a.action,
|
|
293
|
+
from: prevUrl,
|
|
294
|
+
to: a.url,
|
|
295
|
+
crossHost: fromHost !== null && toHost !== null && fromHost !== toHost
|
|
296
|
+
});
|
|
297
|
+
if (a.url) prevUrl = a.url;
|
|
298
|
+
}
|
|
299
|
+
await writeJsonl(
|
|
300
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.timelineNavigation),
|
|
301
|
+
navRows
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
var writeAssertionsAndCacheDecisions = async (workspaceRoot, steps) => {
|
|
305
|
+
const sortedSteps = steps.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
306
|
+
const assertions = [];
|
|
307
|
+
const cacheDecisions = [];
|
|
308
|
+
for (const step of sortedSteps) {
|
|
309
|
+
const meta = isObject(step.metadataJson) ? step.metadataJson : {};
|
|
310
|
+
const startedMs = isoToMs(step.startedAt);
|
|
311
|
+
const rawAssertions = Array.isArray(meta.assertionResults) ? meta.assertionResults : [];
|
|
312
|
+
for (const r of rawAssertions) {
|
|
313
|
+
if (!isObject(r)) continue;
|
|
314
|
+
assertions.push({
|
|
315
|
+
stepId: step.id,
|
|
316
|
+
nodeId: step.nodeId,
|
|
317
|
+
orderIndex: step.orderIndex,
|
|
318
|
+
stepStatus: step.status,
|
|
319
|
+
timestampMs: startedMs,
|
|
320
|
+
assertionId: r.inlineAssertionId ?? r.assertionId ?? r.key ?? null,
|
|
321
|
+
condition: r.condition ?? null,
|
|
322
|
+
severity: r.severity ?? null,
|
|
323
|
+
nonBlocking: Boolean(r.nonBlocking),
|
|
324
|
+
passed: Boolean(r.passed),
|
|
325
|
+
reason: r.reason ?? null
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
cacheDecisions.push({
|
|
329
|
+
stepId: step.id,
|
|
330
|
+
nodeId: step.nodeId,
|
|
331
|
+
orderIndex: step.orderIndex,
|
|
332
|
+
stepStatus: step.status,
|
|
333
|
+
timestampMs: startedMs,
|
|
334
|
+
cacheOutcome: meta.cacheOutcome ?? null,
|
|
335
|
+
cacheHitSource: meta.cacheHitSource ?? null,
|
|
336
|
+
cacheProcessingStatus: meta.cacheProcessingStatus ?? null,
|
|
337
|
+
cacheOutcomeReason: meta.cacheOutcomeReason ?? null
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
await writeJsonl(
|
|
341
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.timelineAssertions),
|
|
342
|
+
assertions
|
|
343
|
+
);
|
|
344
|
+
await writeJsonl(
|
|
345
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.timelineCacheDecisions),
|
|
346
|
+
cacheDecisions
|
|
347
|
+
);
|
|
348
|
+
};
|
|
349
|
+
var writeThoughts = async (workspaceRoot, thoughts) => {
|
|
350
|
+
const sorted = thoughts.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
351
|
+
const allRows = sorted.map((t) => ({
|
|
352
|
+
id: t.id,
|
|
353
|
+
stepId: t.stepId,
|
|
354
|
+
nodeId: t.nodeId,
|
|
355
|
+
toolName: t.toolName,
|
|
356
|
+
orderIndex: t.orderIndex,
|
|
357
|
+
timestampMs: isoToMs(t.createdAt),
|
|
358
|
+
createdAt: t.createdAt,
|
|
359
|
+
content: t.content
|
|
360
|
+
}));
|
|
361
|
+
await writeJsonl(
|
|
362
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.thoughtsAll),
|
|
363
|
+
allRows
|
|
364
|
+
);
|
|
365
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
366
|
+
for (const t of sorted) {
|
|
367
|
+
const key = t.stepId ?? "_no-step";
|
|
368
|
+
const list = byStep.get(key) ?? [];
|
|
369
|
+
list.push(t);
|
|
370
|
+
byStep.set(key, list);
|
|
371
|
+
}
|
|
372
|
+
const renderStep = ([stepId, stepThoughts]) => {
|
|
373
|
+
const md = [];
|
|
374
|
+
md.push(`# Thoughts for ${stepId === "_no-step" ? "(no step)" : `step ${stepId}`}`);
|
|
375
|
+
md.push("");
|
|
376
|
+
for (const t of stepThoughts) {
|
|
377
|
+
const tool = t.toolName ? ` [tool: ${t.toolName}]` : "";
|
|
378
|
+
md.push(`## ${t.createdAt}${tool}`);
|
|
379
|
+
md.push("");
|
|
380
|
+
md.push(t.content);
|
|
381
|
+
md.push("");
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
relativePath: thoughtsByStepRelativePath(stepId),
|
|
385
|
+
body: `${md.join("\n")}
|
|
386
|
+
`
|
|
387
|
+
};
|
|
388
|
+
};
|
|
389
|
+
const rendered = [...byStep.entries()].map(renderStep);
|
|
390
|
+
await Promise.all(
|
|
391
|
+
rendered.map(
|
|
392
|
+
({ relativePath, body }) => writeText(resolveWorkspacePath(workspaceRoot, relativePath), body)
|
|
393
|
+
)
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
var normalizeTimestamp = (ts) => {
|
|
397
|
+
if (ts === null || ts === void 0) return null;
|
|
398
|
+
if (typeof ts === "number") return ts;
|
|
399
|
+
const parsed = Date.parse(ts);
|
|
400
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
401
|
+
};
|
|
402
|
+
var writeLogs = async (workspaceRoot, network, consoleLogs) => {
|
|
403
|
+
const networkRows = network.map((e) => ({
|
|
404
|
+
timestampMs: normalizeTimestamp(e.timestamp ?? null),
|
|
405
|
+
url: e.url,
|
|
406
|
+
method: e.method,
|
|
407
|
+
status: e.status ?? null,
|
|
408
|
+
durationMs: e.durationMs ?? null,
|
|
409
|
+
requestBody: e.requestBody ?? null,
|
|
410
|
+
responseBody: e.responseBody ?? null,
|
|
411
|
+
contentType: e.contentType ?? null
|
|
412
|
+
}));
|
|
413
|
+
const networkErrors = networkRows.filter(
|
|
414
|
+
(r) => typeof r.status === "number" && r.status >= 400
|
|
415
|
+
);
|
|
416
|
+
await writeJsonl(
|
|
417
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.logsNetwork),
|
|
418
|
+
networkRows
|
|
419
|
+
);
|
|
420
|
+
await writeJsonl(
|
|
421
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.logsNetworkErrors),
|
|
422
|
+
networkErrors
|
|
423
|
+
);
|
|
424
|
+
const consoleRows = consoleLogs.map((e) => ({
|
|
425
|
+
timestampMs: normalizeTimestamp(e.timestamp),
|
|
426
|
+
type: e.type,
|
|
427
|
+
text: e.text,
|
|
428
|
+
location: e.location ?? null
|
|
429
|
+
}));
|
|
430
|
+
const consoleErrors = consoleRows.filter(
|
|
431
|
+
(r) => r.type === "error" || r.type === "warning"
|
|
432
|
+
);
|
|
433
|
+
await writeJsonl(
|
|
434
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.logsConsole),
|
|
435
|
+
consoleRows
|
|
436
|
+
);
|
|
437
|
+
await writeJsonl(
|
|
438
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.logsConsoleErrors),
|
|
439
|
+
consoleErrors
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
var writeVariables = async (workspaceRoot, runMetadata, steps) => {
|
|
443
|
+
const inputs = isObject(runMetadata) && isObject(runMetadata.workflowInputValues) ? runMetadata.workflowInputValues : {};
|
|
444
|
+
await writeJson(
|
|
445
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.variablesInputs),
|
|
446
|
+
inputs
|
|
447
|
+
);
|
|
448
|
+
const sortedSteps = steps.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
449
|
+
const lineageRows = [];
|
|
450
|
+
for (const step of sortedSteps) {
|
|
451
|
+
const meta = isObject(step.metadataJson) ? step.metadataJson : {};
|
|
452
|
+
const captures = Array.isArray(meta.variablesSet) ? meta.variablesSet : [];
|
|
453
|
+
const startedMs = isoToMs(step.startedAt);
|
|
454
|
+
for (const c of captures) {
|
|
455
|
+
if (!isObject(c)) continue;
|
|
456
|
+
lineageRows.push({
|
|
457
|
+
name: c.name ?? c.key ?? null,
|
|
458
|
+
value: c.value ?? null,
|
|
459
|
+
definedAtStepId: step.id,
|
|
460
|
+
definedAtNodeId: step.nodeId,
|
|
461
|
+
definedAtOrderIndex: step.orderIndex,
|
|
462
|
+
definedAtTimestampMs: startedMs
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
await writeJsonl(
|
|
467
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.variablesLineage),
|
|
468
|
+
lineageRows
|
|
469
|
+
);
|
|
470
|
+
};
|
|
471
|
+
var writeRecentRuns = async (workspaceRoot, runs) => {
|
|
472
|
+
const rows = runs.map((r) => ({
|
|
473
|
+
testRunId: r.id,
|
|
474
|
+
workflowRunId: r.workflowRunId,
|
|
475
|
+
status: r.status,
|
|
476
|
+
startedAt: r.startedAt,
|
|
477
|
+
finishedAt: r.finishedAt,
|
|
478
|
+
startedAtMs: isoToMs(r.startedAt),
|
|
479
|
+
finishedAtMs: isoToMs(r.finishedAt),
|
|
480
|
+
errorMessage: r.errorMessage
|
|
481
|
+
}));
|
|
482
|
+
await writeJsonl(
|
|
483
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.historyRecentRuns),
|
|
484
|
+
rows
|
|
485
|
+
);
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// ../diagnostic-workspace/src/workflow-writer.ts
|
|
489
|
+
import * as fs4 from "fs/promises";
|
|
490
|
+
var writeWorkflowDefinition = async (input) => {
|
|
491
|
+
const sortedNodes = input.nodes.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
492
|
+
const definition = {
|
|
493
|
+
workflowId: input.workflowId,
|
|
494
|
+
name: input.workflowName,
|
|
495
|
+
nodes: sortedNodes.map((n) => ({
|
|
496
|
+
id: n.id,
|
|
497
|
+
nodeType: n.nodeType,
|
|
498
|
+
orderIndex: n.orderIndex,
|
|
499
|
+
summary: n.summary
|
|
500
|
+
})),
|
|
501
|
+
edges: input.edges.map((e) => ({
|
|
502
|
+
id: e.id,
|
|
503
|
+
source: e.source,
|
|
504
|
+
target: e.target
|
|
505
|
+
}))
|
|
506
|
+
};
|
|
507
|
+
await fs4.writeFile(
|
|
508
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.workflowDefinition),
|
|
509
|
+
`${JSON.stringify(definition, null, 2)}
|
|
510
|
+
`,
|
|
511
|
+
"utf-8"
|
|
512
|
+
);
|
|
513
|
+
const snapshot = {
|
|
514
|
+
workflowId: input.workflowId,
|
|
515
|
+
name: input.workflowName,
|
|
516
|
+
nodes: sortedNodes.map((n) => ({
|
|
517
|
+
id: n.id,
|
|
518
|
+
nodeType: n.nodeType,
|
|
519
|
+
orderIndex: n.orderIndex,
|
|
520
|
+
configJson: n.configJson
|
|
521
|
+
})),
|
|
522
|
+
edges: input.edges.map((e) => ({ id: e.id, source: e.source, target: e.target }))
|
|
523
|
+
};
|
|
524
|
+
await fs4.writeFile(
|
|
525
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.workflowSnapshot),
|
|
526
|
+
`${JSON.stringify(snapshot, null, 2)}
|
|
527
|
+
`,
|
|
528
|
+
"utf-8"
|
|
529
|
+
);
|
|
530
|
+
await Promise.all(
|
|
531
|
+
sortedNodes.map((node) => {
|
|
532
|
+
const filePath = resolveWorkspacePath(
|
|
533
|
+
input.workspaceRoot,
|
|
534
|
+
workflowNodeRelativePath(node.id)
|
|
535
|
+
);
|
|
536
|
+
const body = {
|
|
537
|
+
id: node.id,
|
|
538
|
+
nodeType: node.nodeType,
|
|
539
|
+
orderIndex: node.orderIndex,
|
|
540
|
+
summary: node.summary,
|
|
541
|
+
configJson: node.configJson
|
|
542
|
+
};
|
|
543
|
+
return fs4.writeFile(filePath, `${JSON.stringify(body, null, 2)}
|
|
544
|
+
`, "utf-8");
|
|
545
|
+
})
|
|
546
|
+
);
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// ../diagnostic-workspace/src/sentry-writer.ts
|
|
550
|
+
import * as fs5 from "fs/promises";
|
|
551
|
+
var writeJsonlWithSentinel = async (absolutePath, partial, rows) => {
|
|
552
|
+
const lines = [];
|
|
553
|
+
if (partial) lines.push(JSON.stringify(partial));
|
|
554
|
+
for (const r of rows) lines.push(JSON.stringify(r));
|
|
555
|
+
await fs5.writeFile(
|
|
556
|
+
absolutePath,
|
|
557
|
+
lines.length === 0 ? "" : `${lines.join("\n")}
|
|
558
|
+
`,
|
|
559
|
+
"utf-8"
|
|
560
|
+
);
|
|
561
|
+
};
|
|
562
|
+
var writeSentryLogsAndSpans = async (input) => {
|
|
563
|
+
await writeJsonlWithSentinel(
|
|
564
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.logsSentry),
|
|
565
|
+
input.logs.partial,
|
|
566
|
+
input.logs.rows
|
|
567
|
+
);
|
|
568
|
+
await writeJsonlWithSentinel(
|
|
569
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.logsSentrySpans),
|
|
570
|
+
input.spans.partial,
|
|
571
|
+
input.spans.spans
|
|
572
|
+
);
|
|
573
|
+
return Boolean(input.logs.partial || input.spans.partial);
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// ../diagnostic-workspace/src/trace-extractor.ts
|
|
577
|
+
import * as fs6 from "fs/promises";
|
|
578
|
+
var writeTrace = async (input) => {
|
|
579
|
+
if (!input.traceZipPath) {
|
|
580
|
+
await writePartialMarker(input.workspaceRoot, "no-trace");
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
const destTraceZip = resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.browserTraceZip);
|
|
584
|
+
try {
|
|
585
|
+
await fs6.copyFile(input.traceZipPath, destTraceZip);
|
|
586
|
+
} catch {
|
|
587
|
+
await writePartialMarker(input.workspaceRoot, "trace-copy-failed");
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
let actions;
|
|
591
|
+
try {
|
|
592
|
+
actions = parseTraceActions(input.traceZipPath);
|
|
593
|
+
} catch {
|
|
594
|
+
await writePartialMarker(input.workspaceRoot, "trace-parse-failed");
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
await writeActionsJsonl(input.workspaceRoot, actions, input.runStartedAtMs);
|
|
598
|
+
return reconstructAndWriteArtifacts({
|
|
599
|
+
workspaceRoot: input.workspaceRoot,
|
|
600
|
+
tracePath: input.traceZipPath,
|
|
601
|
+
actions,
|
|
602
|
+
runStartedAtMs: input.runStartedAtMs
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
var writePartialMarker = async (workspaceRoot, reason) => {
|
|
606
|
+
await fs6.writeFile(
|
|
607
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.browserActions),
|
|
608
|
+
`${JSON.stringify({ partial: true, reason })}
|
|
609
|
+
`,
|
|
610
|
+
"utf-8"
|
|
611
|
+
);
|
|
612
|
+
};
|
|
613
|
+
var reconstructAndWriteArtifacts = async (input) => {
|
|
614
|
+
const traceBase = input.actions.length > 0 ? input.actions[0].startTime : 0;
|
|
615
|
+
const computeAbsoluteTs = (action) => {
|
|
616
|
+
if (input.runStartedAtMs === null) return 0;
|
|
617
|
+
return input.runStartedAtMs + (action.startTime - traceBase);
|
|
618
|
+
};
|
|
619
|
+
let results;
|
|
620
|
+
try {
|
|
621
|
+
results = await reconstructSnapshots({
|
|
622
|
+
traceZipPath: input.tracePath,
|
|
623
|
+
filter: "after",
|
|
624
|
+
includeScreenshots: true,
|
|
625
|
+
includeHtml: true,
|
|
626
|
+
raw: false
|
|
627
|
+
});
|
|
628
|
+
} catch {
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
const byActionIndex = /* @__PURE__ */ new Map();
|
|
632
|
+
for (const result of results) {
|
|
633
|
+
if (!byActionIndex.has(result.actionIndex)) {
|
|
634
|
+
byActionIndex.set(result.actionIndex, result);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const domsDir = resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.browserDoms);
|
|
638
|
+
const framesDir = resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.browserFrames);
|
|
639
|
+
const a11yDir = resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.browserA11y);
|
|
640
|
+
const writeAction = async (i) => {
|
|
641
|
+
const action = input.actions[i];
|
|
642
|
+
const result = byActionIndex.get(i);
|
|
643
|
+
if (!result) return false;
|
|
644
|
+
const tsMs = computeAbsoluteTs(action);
|
|
645
|
+
try {
|
|
646
|
+
await Promise.all([
|
|
647
|
+
result.html ? fs6.writeFile(
|
|
648
|
+
`${domsDir}/${actionArtifactBasename(i, tsMs, "html")}`,
|
|
649
|
+
result.html,
|
|
650
|
+
"utf-8"
|
|
651
|
+
) : Promise.resolve(),
|
|
652
|
+
result.screenshotBuffer ? fs6.writeFile(
|
|
653
|
+
`${framesDir}/${actionArtifactBasename(i, tsMs, "png")}`,
|
|
654
|
+
result.screenshotBuffer
|
|
655
|
+
) : Promise.resolve(),
|
|
656
|
+
result.yaml ? fs6.writeFile(
|
|
657
|
+
`${a11yDir}/${actionArtifactBasename(i, tsMs, "yaml")}`,
|
|
658
|
+
result.yaml,
|
|
659
|
+
"utf-8"
|
|
660
|
+
) : Promise.resolve()
|
|
661
|
+
]);
|
|
662
|
+
return true;
|
|
663
|
+
} catch {
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const indexes = Array.from({ length: input.actions.length }, (_, i) => i);
|
|
668
|
+
const missingResults = indexes.filter((i) => !byActionIndex.has(i)).length;
|
|
669
|
+
const writeOutcomes = await Promise.all(indexes.map((i) => writeAction(i)));
|
|
670
|
+
const writeFailures = writeOutcomes.filter((ok) => !ok).length;
|
|
671
|
+
return missingResults > 0 || writeFailures > 0;
|
|
672
|
+
};
|
|
673
|
+
var writeActionsJsonl = async (workspaceRoot, actions, runStartedAtMs) => {
|
|
674
|
+
const traceBase = actions.length > 0 ? actions[0].startTime : 0;
|
|
675
|
+
const lines = actions.map((a, idx) => ({
|
|
676
|
+
actionIndex: idx,
|
|
677
|
+
callId: a.callId,
|
|
678
|
+
title: a.title,
|
|
679
|
+
startTimeRelativeMs: a.startTime - traceBase,
|
|
680
|
+
endTimeRelativeMs: a.endTime - traceBase,
|
|
681
|
+
timestampMs: runStartedAtMs !== null ? runStartedAtMs + (a.startTime - traceBase) : null,
|
|
682
|
+
params: a.params ?? null,
|
|
683
|
+
beforeSnapshot: a.beforeSnapshot ?? null,
|
|
684
|
+
afterSnapshot: a.afterSnapshot ?? null,
|
|
685
|
+
inputSnapshot: a.inputSnapshot ?? null,
|
|
686
|
+
pageId: a.pageId,
|
|
687
|
+
error: a.error ?? null,
|
|
688
|
+
point: a.point ?? null
|
|
689
|
+
}));
|
|
690
|
+
await fs6.writeFile(
|
|
691
|
+
resolveWorkspacePath(workspaceRoot, WORKSPACE_PATHS.browserActions),
|
|
692
|
+
lines.length === 0 ? "" : `${lines.map((l) => JSON.stringify(l)).join("\n")}
|
|
693
|
+
`,
|
|
694
|
+
"utf-8"
|
|
695
|
+
);
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
// ../diagnostic-workspace/src/ledger-builder.ts
|
|
699
|
+
import * as fs7 from "fs/promises";
|
|
700
|
+
var writeLedger = async (input) => {
|
|
701
|
+
const baselineMs = input.runStartedAt ? Date.parse(input.runStartedAt) : null;
|
|
702
|
+
const events = [];
|
|
703
|
+
for (const step of input.steps) {
|
|
704
|
+
events.push({ kind: "step", step, timestampMs: step.startedAtMs });
|
|
705
|
+
}
|
|
706
|
+
for (const thought of input.thoughts) {
|
|
707
|
+
events.push({ kind: "thought", thought, stepId: thought.stepId });
|
|
708
|
+
}
|
|
709
|
+
for (const action of input.actions) {
|
|
710
|
+
events.push({ kind: "action", action });
|
|
711
|
+
}
|
|
712
|
+
for (const nav of input.navigation) {
|
|
713
|
+
events.push({ kind: "nav", nav });
|
|
714
|
+
}
|
|
715
|
+
for (const net of input.network) {
|
|
716
|
+
events.push({ kind: "network", net });
|
|
717
|
+
}
|
|
718
|
+
for (const con of input.consoleLogs) {
|
|
719
|
+
events.push({ kind: "console", con });
|
|
720
|
+
}
|
|
721
|
+
for (const assertion of input.assertions) {
|
|
722
|
+
events.push({ kind: "assertion", assertion });
|
|
723
|
+
}
|
|
724
|
+
events.sort((a, b) => {
|
|
725
|
+
const ta = eventTimestamp(a);
|
|
726
|
+
const tb = eventTimestamp(b);
|
|
727
|
+
if (ta === null && tb === null) return 0;
|
|
728
|
+
if (ta === null) return 1;
|
|
729
|
+
if (tb === null) return -1;
|
|
730
|
+
if (ta !== tb) return ta - tb;
|
|
731
|
+
const ra = eventRank(a);
|
|
732
|
+
const rb = eventRank(b);
|
|
733
|
+
return ra - rb;
|
|
734
|
+
});
|
|
735
|
+
const lines = [];
|
|
736
|
+
const indexEntries = [];
|
|
737
|
+
lines.push(`# Run Ledger \u2014 runId: ${input.runId} \u2014 workflow: ${input.workflowName} \u2014 status: ${input.runStatus}`);
|
|
738
|
+
if (baselineMs !== null) {
|
|
739
|
+
lines.push(`runStartedAt: ${input.runStartedAt} (all T+ values are relative to this)`);
|
|
740
|
+
} else {
|
|
741
|
+
lines.push(`runStartedAt: (unknown \u2014 relative timestamps unavailable)`);
|
|
742
|
+
}
|
|
743
|
+
lines.push("");
|
|
744
|
+
if (input.runStatus === "failed" && input.reportedFailure.failedStepName) {
|
|
745
|
+
const ordinalPart = input.reportedFailure.failedStepOrderIndex !== null ? ` (step ${input.reportedFailure.failedStepOrderIndex})` : "";
|
|
746
|
+
lines.push(`## \u26A0 Reported failure`);
|
|
747
|
+
lines.push(`Step${ordinalPart}: **${input.reportedFailure.failedStepName}**`);
|
|
748
|
+
if (input.reportedFailure.errorMessage) {
|
|
749
|
+
lines.push("");
|
|
750
|
+
lines.push("```");
|
|
751
|
+
lines.push(input.reportedFailure.errorMessage);
|
|
752
|
+
lines.push("```");
|
|
753
|
+
}
|
|
754
|
+
lines.push("");
|
|
755
|
+
lines.push("**Verify before trusting \u2014 root cause may be upstream.**");
|
|
756
|
+
lines.push("");
|
|
757
|
+
lines.push("---");
|
|
758
|
+
lines.push("");
|
|
759
|
+
}
|
|
760
|
+
for (const event of events) {
|
|
761
|
+
const ts = eventTimestamp(event);
|
|
762
|
+
const prefix = formatPrefix(ts, baselineMs);
|
|
763
|
+
if (event.kind === "step") {
|
|
764
|
+
const s = event.step;
|
|
765
|
+
const meta = [
|
|
766
|
+
`status: ${s.status}`,
|
|
767
|
+
s.durationMs !== null ? `dur ${s.durationMs}ms` : null,
|
|
768
|
+
s.cacheOutcome ? `cache: ${s.cacheOutcome}${s.cacheHitSource ? `/${s.cacheHitSource}` : ""}` : null
|
|
769
|
+
].filter((x) => x !== null).join(", ");
|
|
770
|
+
const heading = `## ${prefix} Step ${s.orderIndex}: ${s.name ?? s.nodeId} (${meta})`;
|
|
771
|
+
if (lines.length > 0 && lines[lines.length - 1] !== "") lines.push("");
|
|
772
|
+
indexEntries.push({
|
|
773
|
+
orderIndex: s.orderIndex,
|
|
774
|
+
name: s.name,
|
|
775
|
+
line: lines.length + 1
|
|
776
|
+
// 1-indexed
|
|
777
|
+
});
|
|
778
|
+
lines.push(heading);
|
|
779
|
+
if (s.errorMessage) {
|
|
780
|
+
lines.push(`${prefix} step.error: ${oneLine(s.errorMessage)}`);
|
|
781
|
+
}
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
if (event.kind === "thought") {
|
|
785
|
+
const t = event.thought;
|
|
786
|
+
const tool = t.toolName ? ` [${t.toolName}]` : "";
|
|
787
|
+
lines.push(`${prefix} thought${tool}: ${oneLine(t.content)}`);
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (event.kind === "action") {
|
|
791
|
+
const a = event.action;
|
|
792
|
+
const target = a.target ? ` ${oneLine(a.target)}` : "";
|
|
793
|
+
const url = a.url ? ` \u2192 ${a.url}` : "";
|
|
794
|
+
const status = a.success ? "" : " (failed)";
|
|
795
|
+
const annotation = a.isAnnotation ? " [annotation]" : "";
|
|
796
|
+
lines.push(`${prefix} action.${a.action}${annotation}:${target}${url}${status}`);
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (event.kind === "nav") {
|
|
800
|
+
const n = event.nav;
|
|
801
|
+
const cross = n.crossHost ? " \u26A0 cross-host" : "";
|
|
802
|
+
lines.push(`${prefix} nav.${n.action}: ${n.from ?? "?"} \u2192 ${n.to ?? "?"}${cross}`);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (event.kind === "network") {
|
|
806
|
+
const n = event.net;
|
|
807
|
+
const status = n.status !== null ? ` ${n.status}` : "";
|
|
808
|
+
lines.push(`${prefix} network: ${n.method} ${n.url}${status}`);
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (event.kind === "console") {
|
|
812
|
+
const c = event.con;
|
|
813
|
+
lines.push(`${prefix} console.${c.type}: ${oneLine(c.text)}`);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (event.kind === "assertion") {
|
|
817
|
+
const a = event.assertion;
|
|
818
|
+
const tag = a.passed ? "assertion.passed" : "assertion.failed";
|
|
819
|
+
const severity = a.severity ? ` [${a.severity}${a.nonBlocking ? " nonBlocking" : ""}]` : "";
|
|
820
|
+
const cond = a.condition ? oneLine(a.condition) : "(no condition)";
|
|
821
|
+
const reason = a.reason ? ` \u2014 ${oneLine(a.reason)}` : "";
|
|
822
|
+
lines.push(`${prefix} ${tag}${severity}: ${cond}${reason}`);
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (lines[lines.length - 1] !== "") lines.push("");
|
|
827
|
+
await fs7.writeFile(
|
|
828
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.ledger),
|
|
829
|
+
`${lines.join("\n")}`,
|
|
830
|
+
"utf-8"
|
|
831
|
+
);
|
|
832
|
+
const indexLines = [];
|
|
833
|
+
indexLines.push(`# Ledger Index \u2014 runId: ${input.runId}`);
|
|
834
|
+
indexLines.push("");
|
|
835
|
+
indexLines.push("Use `bash(\"sed -n '<a>,<b>p' ledger.md\")` to read a step's window.");
|
|
836
|
+
indexLines.push("");
|
|
837
|
+
if (indexEntries.length === 0) {
|
|
838
|
+
indexLines.push("(no steps recorded for this run)");
|
|
839
|
+
} else {
|
|
840
|
+
for (let i = 0; i < indexEntries.length; i++) {
|
|
841
|
+
const entry = indexEntries[i];
|
|
842
|
+
const next = indexEntries[i + 1];
|
|
843
|
+
const startLine = entry.line;
|
|
844
|
+
const endLine = next ? next.line - 1 : lines.length;
|
|
845
|
+
const name = entry.name ?? "(unnamed step)";
|
|
846
|
+
indexLines.push(`Step ${entry.orderIndex}: ${name} \u2014 lines ${startLine}-${endLine}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
indexLines.push("");
|
|
850
|
+
await fs7.writeFile(
|
|
851
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.ledgerIndex),
|
|
852
|
+
`${indexLines.join("\n")}`,
|
|
853
|
+
"utf-8"
|
|
854
|
+
);
|
|
855
|
+
};
|
|
856
|
+
var eventTimestamp = (e) => {
|
|
857
|
+
switch (e.kind) {
|
|
858
|
+
case "step":
|
|
859
|
+
return e.timestampMs;
|
|
860
|
+
case "thought":
|
|
861
|
+
return e.thought.timestampMs;
|
|
862
|
+
case "action":
|
|
863
|
+
return e.action.timestampMs;
|
|
864
|
+
case "nav":
|
|
865
|
+
return e.nav.timestampMs;
|
|
866
|
+
case "network":
|
|
867
|
+
return e.net.timestampMs;
|
|
868
|
+
case "console":
|
|
869
|
+
return e.con.timestampMs;
|
|
870
|
+
case "assertion":
|
|
871
|
+
return e.assertion.timestampMs;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
var eventRank = (e) => {
|
|
875
|
+
switch (e.kind) {
|
|
876
|
+
case "step":
|
|
877
|
+
return 0;
|
|
878
|
+
case "nav":
|
|
879
|
+
return 1;
|
|
880
|
+
case "action":
|
|
881
|
+
return 2;
|
|
882
|
+
case "thought":
|
|
883
|
+
return 3;
|
|
884
|
+
case "network":
|
|
885
|
+
return 4;
|
|
886
|
+
case "console":
|
|
887
|
+
return 5;
|
|
888
|
+
case "assertion":
|
|
889
|
+
return 6;
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
// ../diagnostic-workspace/src/step-rollup-builder.ts
|
|
894
|
+
import * as fs8 from "fs/promises";
|
|
895
|
+
var writeStepRollups = async (input) => {
|
|
896
|
+
const baselineMs = input.runStartedAt ? Date.parse(input.runStartedAt) : null;
|
|
897
|
+
const sortedSteps = input.steps.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
898
|
+
const thoughtsByStepId = /* @__PURE__ */ new Map();
|
|
899
|
+
for (const t of input.thoughts) {
|
|
900
|
+
if (!t.stepId) continue;
|
|
901
|
+
const list = thoughtsByStepId.get(t.stepId) ?? [];
|
|
902
|
+
list.push(t);
|
|
903
|
+
thoughtsByStepId.set(t.stepId, list);
|
|
904
|
+
}
|
|
905
|
+
const assertionsByStepId = /* @__PURE__ */ new Map();
|
|
906
|
+
for (const a of input.assertions) {
|
|
907
|
+
const list = assertionsByStepId.get(a.stepId) ?? [];
|
|
908
|
+
list.push(a);
|
|
909
|
+
assertionsByStepId.set(a.stepId, list);
|
|
910
|
+
}
|
|
911
|
+
const bounds = computeStepBounds(sortedSteps);
|
|
912
|
+
const writes = [];
|
|
913
|
+
for (let i = 0; i < sortedSteps.length; i++) {
|
|
914
|
+
const step = sortedSteps[i];
|
|
915
|
+
const window = bounds[i];
|
|
916
|
+
const lines = [];
|
|
917
|
+
const startPrefix = formatPrefix(step.startedAtMs, baselineMs);
|
|
918
|
+
const finishPrefix = formatPrefix(step.finishedAtMs, baselineMs);
|
|
919
|
+
lines.push(
|
|
920
|
+
`# Step ${step.orderIndex}: ${step.name ?? step.nodeId}`
|
|
921
|
+
);
|
|
922
|
+
lines.push("");
|
|
923
|
+
lines.push(`- nodeId: \`${step.nodeId}\``);
|
|
924
|
+
if (step.nodeType) lines.push(`- nodeType: \`${step.nodeType}\``);
|
|
925
|
+
lines.push(`- status: **${step.status}**`);
|
|
926
|
+
lines.push(`- started: ${startPrefix}`);
|
|
927
|
+
lines.push(`- finished: ${finishPrefix}`);
|
|
928
|
+
if (step.durationMs !== null) lines.push(`- duration: ${step.durationMs}ms`);
|
|
929
|
+
if (step.cacheOutcome) {
|
|
930
|
+
const src = step.cacheHitSource ? `/${step.cacheHitSource}` : "";
|
|
931
|
+
lines.push(`- cache: ${step.cacheOutcome}${src}`);
|
|
932
|
+
}
|
|
933
|
+
if (step.errorMessage) {
|
|
934
|
+
lines.push("");
|
|
935
|
+
lines.push("## Error message");
|
|
936
|
+
lines.push("");
|
|
937
|
+
lines.push("```");
|
|
938
|
+
lines.push(step.errorMessage);
|
|
939
|
+
lines.push("```");
|
|
940
|
+
}
|
|
941
|
+
const stepThoughts = (thoughtsByStepId.get(step.id) ?? []).slice().sort(
|
|
942
|
+
(a, b) => a.timestampMs - b.timestampMs
|
|
943
|
+
);
|
|
944
|
+
if (stepThoughts.length > 0) {
|
|
945
|
+
lines.push("");
|
|
946
|
+
lines.push("## Agent thoughts");
|
|
947
|
+
lines.push("");
|
|
948
|
+
for (const t of stepThoughts) {
|
|
949
|
+
const tool = t.toolName ? ` [${t.toolName}]` : "";
|
|
950
|
+
lines.push(`${formatPrefix(t.timestampMs, baselineMs)} thought${tool}: ${oneLine(t.content)}`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const stepAssertions = (assertionsByStepId.get(step.id) ?? []).slice();
|
|
954
|
+
if (stepAssertions.length > 0) {
|
|
955
|
+
lines.push("");
|
|
956
|
+
lines.push("## Assertions");
|
|
957
|
+
lines.push("");
|
|
958
|
+
for (const a of stepAssertions) {
|
|
959
|
+
const tag = a.passed ? "passed" : "FAILED";
|
|
960
|
+
const severity = a.severity ? ` [${a.severity}${a.nonBlocking ? " nonBlocking" : ""}]` : "";
|
|
961
|
+
const cond = a.condition ?? "(no condition)";
|
|
962
|
+
const reason = a.reason ? ` \u2014 ${oneLine(a.reason)}` : "";
|
|
963
|
+
lines.push(`- ${tag}${severity}: ${oneLine(cond)}${reason}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
const inWindow = (ts) => {
|
|
967
|
+
if (ts === null) return false;
|
|
968
|
+
if (window.startMs !== null && ts < window.startMs) return false;
|
|
969
|
+
if (window.endMs !== null && ts >= window.endMs) return false;
|
|
970
|
+
return true;
|
|
971
|
+
};
|
|
972
|
+
const windowActions = input.actions.filter((a) => inWindow(a.timestampMs)).slice().sort(byTs);
|
|
973
|
+
if (windowActions.length > 0) {
|
|
974
|
+
lines.push("");
|
|
975
|
+
lines.push("## Action timeline (in step window)");
|
|
976
|
+
lines.push("");
|
|
977
|
+
for (const a of windowActions) {
|
|
978
|
+
const target = a.target ? ` ${oneLine(a.target)}` : "";
|
|
979
|
+
const url = a.url ? ` \u2192 ${a.url}` : "";
|
|
980
|
+
const fail = a.success ? "" : " (failed)";
|
|
981
|
+
lines.push(`${formatPrefix(a.timestampMs, baselineMs)} action.${a.action}:${target}${url}${fail}`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
const windowNav = input.navigation.filter((n) => inWindow(n.timestampMs)).slice().sort(byTs);
|
|
985
|
+
if (windowNav.length > 0) {
|
|
986
|
+
lines.push("");
|
|
987
|
+
lines.push("## Navigation (in step window)");
|
|
988
|
+
lines.push("");
|
|
989
|
+
for (const n of windowNav) {
|
|
990
|
+
const cross = n.crossHost ? " \u26A0 cross-host" : "";
|
|
991
|
+
lines.push(
|
|
992
|
+
`${formatPrefix(n.timestampMs, baselineMs)} nav.${n.action}: ${n.from ?? "?"} \u2192 ${n.to ?? "?"}${cross}`
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const windowNet = input.network.filter((n) => inWindow(n.timestampMs)).slice().sort(byTs);
|
|
997
|
+
if (windowNet.length > 0) {
|
|
998
|
+
lines.push("");
|
|
999
|
+
lines.push("## Network (in step window)");
|
|
1000
|
+
lines.push("");
|
|
1001
|
+
for (const n of windowNet) {
|
|
1002
|
+
const status = n.status !== null ? ` ${n.status}` : "";
|
|
1003
|
+
lines.push(`${formatPrefix(n.timestampMs, baselineMs)} ${n.method} ${n.url}${status}`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const windowCon = input.consoleLogs.filter((c) => inWindow(c.timestampMs)).slice().sort(byTs);
|
|
1007
|
+
if (windowCon.length > 0) {
|
|
1008
|
+
lines.push("");
|
|
1009
|
+
lines.push("## Console (in step window)");
|
|
1010
|
+
lines.push("");
|
|
1011
|
+
for (const c of windowCon) {
|
|
1012
|
+
lines.push(`${formatPrefix(c.timestampMs, baselineMs)} console.${c.type}: ${oneLine(c.text)}`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
lines.push("");
|
|
1016
|
+
lines.push("## Evidence files");
|
|
1017
|
+
lines.push("");
|
|
1018
|
+
lines.push(`- Thoughts: \`${thoughtsByStepRelativePath(step.id)}\``);
|
|
1019
|
+
lines.push(`- Workflow node: \`${workflowNodeRelativePath(step.nodeId)}\``);
|
|
1020
|
+
lines.push(
|
|
1021
|
+
`- Browser snapshots (if available): grep this step's timestamp in \`${WORKSPACE_PATHS.browserActions}\`, then \`browser/doms/...\` / \`browser/frames/...\` / \`browser/a11y/...\``
|
|
1022
|
+
);
|
|
1023
|
+
lines.push("");
|
|
1024
|
+
writes.push({
|
|
1025
|
+
relativePath: stepRollupRelativePath(step.orderIndex, step.id),
|
|
1026
|
+
body: lines.join("\n")
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
await Promise.all(
|
|
1030
|
+
writes.map(
|
|
1031
|
+
({ relativePath, body }) => fs8.writeFile(resolveWorkspacePath(input.workspaceRoot, relativePath), body, "utf-8")
|
|
1032
|
+
)
|
|
1033
|
+
);
|
|
1034
|
+
};
|
|
1035
|
+
var computeStepBounds = (sorted) => {
|
|
1036
|
+
const bounds = [];
|
|
1037
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
1038
|
+
const step = sorted[i];
|
|
1039
|
+
const next = sorted[i + 1];
|
|
1040
|
+
const startMs = step.startedAtMs;
|
|
1041
|
+
let endMs = step.finishedAtMs;
|
|
1042
|
+
if (endMs === null && next?.startedAtMs !== void 0) endMs = next.startedAtMs;
|
|
1043
|
+
bounds.push({ startMs, endMs });
|
|
1044
|
+
}
|
|
1045
|
+
return bounds;
|
|
1046
|
+
};
|
|
1047
|
+
var byTs = (a, b) => {
|
|
1048
|
+
if (a.timestampMs === null && b.timestampMs === null) return 0;
|
|
1049
|
+
if (a.timestampMs === null) return 1;
|
|
1050
|
+
if (b.timestampMs === null) return -1;
|
|
1051
|
+
return a.timestampMs - b.timestampMs;
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
// ../diagnostic-workspace/src/summary-builder.ts
|
|
1055
|
+
import * as fs9 from "fs/promises";
|
|
1056
|
+
var MAX_NOTABLE_EVENTS = 5;
|
|
1057
|
+
var MAX_HISTORY_LOOKBACK = 10;
|
|
1058
|
+
var isCleanPassStatus = (s) => s === "passed" || s === "success";
|
|
1059
|
+
var isFlakyPassStatus = (s) => s === "flaky_success";
|
|
1060
|
+
var isPassStatus = (s) => isCleanPassStatus(s) || isFlakyPassStatus(s);
|
|
1061
|
+
var isFailStatus = (s) => s === "failed" || s === "timeout" || s === "degraded";
|
|
1062
|
+
var classifyHistoryRegime = (others) => {
|
|
1063
|
+
const seq = others.map((r) => r.status).filter((s) => isPassStatus(s) || isFailStatus(s));
|
|
1064
|
+
if (seq.length < 2) return "NEW";
|
|
1065
|
+
const passes = seq.filter(isPassStatus).length;
|
|
1066
|
+
if (passes === 0) return "PERSISTENT_FAIL";
|
|
1067
|
+
const fails = seq.filter(isFailStatus).length;
|
|
1068
|
+
const hasFlaky = seq.some(isFlakyPassStatus);
|
|
1069
|
+
let cleanGreenStreak = 0;
|
|
1070
|
+
for (const s of seq) {
|
|
1071
|
+
if (isCleanPassStatus(s)) cleanGreenStreak++;
|
|
1072
|
+
else break;
|
|
1073
|
+
}
|
|
1074
|
+
if (fails === 0 && !hasFlaky || cleanGreenStreak >= 3) return "STABLE_REGRESSION";
|
|
1075
|
+
return "FLAKY";
|
|
1076
|
+
};
|
|
1077
|
+
var writeSummary = async (input) => {
|
|
1078
|
+
const baselineMs = input.runStartedAt ? Date.parse(input.runStartedAt) : null;
|
|
1079
|
+
const finishedMs = input.runFinishedAt ? Date.parse(input.runFinishedAt) : null;
|
|
1080
|
+
const durationMs = baselineMs !== null && finishedMs !== null ? finishedMs - baselineMs : null;
|
|
1081
|
+
const lines = [];
|
|
1082
|
+
lines.push(
|
|
1083
|
+
`# Run ${input.runId} \u2014 workflow "${input.workflowName}" \u2014 status: ${input.runStatus}`
|
|
1084
|
+
);
|
|
1085
|
+
const durationStr = durationMs !== null ? `${(durationMs / 1e3).toFixed(3)}s` : "unknown";
|
|
1086
|
+
lines.push(
|
|
1087
|
+
`runStartedAt: ${input.runStartedAt ?? "unknown"} duration: ${durationStr}`
|
|
1088
|
+
);
|
|
1089
|
+
lines.push("");
|
|
1090
|
+
if (input.runStatus === "failed") {
|
|
1091
|
+
lines.push("## Reported failure");
|
|
1092
|
+
if (input.reportedFailure.failedStepName) {
|
|
1093
|
+
const ordinal = input.reportedFailure.failedStepOrderIndex !== null ? ` (step ${input.reportedFailure.failedStepOrderIndex})` : "";
|
|
1094
|
+
const err = input.reportedFailure.errorMessage ? ` \u2014 ${oneLine(input.reportedFailure.errorMessage)}` : "";
|
|
1095
|
+
lines.push(
|
|
1096
|
+
`Step${ordinal}: **${input.reportedFailure.failedStepName}**${err}`
|
|
1097
|
+
);
|
|
1098
|
+
} else if (input.reportedFailure.errorMessage) {
|
|
1099
|
+
lines.push(`Run-level error: ${oneLine(input.reportedFailure.errorMessage)}`);
|
|
1100
|
+
} else {
|
|
1101
|
+
lines.push("(no failed step identified \u2014 investigate run-level state)");
|
|
1102
|
+
}
|
|
1103
|
+
lines.push("**Hypothesis. Verify before trusting \u2014 root cause may be upstream.**");
|
|
1104
|
+
lines.push("");
|
|
1105
|
+
}
|
|
1106
|
+
const sortedSteps = input.steps.slice().sort((a, b) => a.orderIndex - b.orderIndex);
|
|
1107
|
+
const counts = countByStatus(sortedSteps);
|
|
1108
|
+
const summaryLine = `${sortedSteps.length} total \u2014 ${formatCounts(counts)}`;
|
|
1109
|
+
lines.push(`## Steps (${summaryLine})`);
|
|
1110
|
+
const renderedSteps = sortedSteps.map((s) => formatStepRow(s, baselineMs));
|
|
1111
|
+
const visibleSteps = renderedSteps.length > 30 ? [
|
|
1112
|
+
...renderedSteps.slice(0, 25),
|
|
1113
|
+
` \u2026 (${renderedSteps.length - 30} more steps elided \u2014 see ledger.md)`,
|
|
1114
|
+
...renderedSteps.slice(-5)
|
|
1115
|
+
] : renderedSteps;
|
|
1116
|
+
for (const row of visibleSteps) lines.push(row);
|
|
1117
|
+
lines.push("");
|
|
1118
|
+
const networkErrors = input.network.filter(
|
|
1119
|
+
(n) => typeof n.status === "number" && n.status >= 400
|
|
1120
|
+
);
|
|
1121
|
+
const consoleErrors = input.consoleLogs.filter(
|
|
1122
|
+
(c) => c.type === "error" || c.type === "warning"
|
|
1123
|
+
);
|
|
1124
|
+
if (networkErrors.length > 0 || consoleErrors.length > 0) {
|
|
1125
|
+
lines.push("## Errors observed");
|
|
1126
|
+
const ne = networkErrors.length;
|
|
1127
|
+
const ce = consoleErrors.length;
|
|
1128
|
+
if (ne > 0) {
|
|
1129
|
+
const sample = networkErrors[0];
|
|
1130
|
+
lines.push(
|
|
1131
|
+
`- ${ne}\xD7 network ${sample.status ?? "?"}: ${sample.method} ${sample.url}${sample.timestampMs !== null ? ` ${formatRelativePrefix(sample.timestampMs, baselineMs)}` : ""}${ne > 1 ? ` (+${ne - 1} more \u2014 see logs/network-errors.jsonl)` : ""}`
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
if (ce > 0) {
|
|
1135
|
+
const sample = consoleErrors[0];
|
|
1136
|
+
lines.push(
|
|
1137
|
+
`- ${ce}\xD7 console.${sample.type}: ${truncate(oneLine(sample.text), 100)}${sample.timestampMs !== null ? ` ${formatRelativePrefix(sample.timestampMs, baselineMs)}` : ""}${ce > 1 ? ` (+${ce - 1} more \u2014 see logs/console-errors.jsonl)` : ""}`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
lines.push("");
|
|
1141
|
+
}
|
|
1142
|
+
const notables = [];
|
|
1143
|
+
const crossHost = input.navigation.filter((n) => n.crossHost);
|
|
1144
|
+
for (const n of crossHost.slice(0, MAX_NOTABLE_EVENTS)) {
|
|
1145
|
+
notables.push(
|
|
1146
|
+
`- cross-host nav: ${n.from ?? "?"} \u2192 ${n.to ?? "?"}${n.timestampMs !== null ? ` ${formatRelativePrefix(n.timestampMs, baselineMs)}` : ""}`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
const failedStep = sortedSteps.find((s) => s.status === "failed");
|
|
1150
|
+
if (failedStep && failedStep.cacheOutcome) {
|
|
1151
|
+
notables.push(
|
|
1152
|
+
`- failed step cache outcome: ${failedStep.cacheOutcome}${failedStep.cacheHitSource ? `/${failedStep.cacheHitSource}` : ""}`
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
if (notables.length > 0) {
|
|
1156
|
+
lines.push("## Notable events");
|
|
1157
|
+
for (const n of notables) lines.push(n);
|
|
1158
|
+
lines.push("");
|
|
1159
|
+
}
|
|
1160
|
+
const historyHint = buildHistoryHint(
|
|
1161
|
+
input.recentRuns,
|
|
1162
|
+
input.runId,
|
|
1163
|
+
input.lastGreenComparison ?? null
|
|
1164
|
+
);
|
|
1165
|
+
if (historyHint.length > 0) {
|
|
1166
|
+
lines.push("## History");
|
|
1167
|
+
for (const h of historyHint) lines.push(h);
|
|
1168
|
+
lines.push("");
|
|
1169
|
+
}
|
|
1170
|
+
lines.push("## Drill-down");
|
|
1171
|
+
const failedRollup = input.reportedFailure.failedStepId && failedStep ? stepRollupRelativePath(failedStep.orderIndex, failedStep.id) : null;
|
|
1172
|
+
if (failedRollup) {
|
|
1173
|
+
lines.push(`- Failed step rollup: \`${failedRollup}\``);
|
|
1174
|
+
}
|
|
1175
|
+
lines.push(`- Per-step rollups: \`${WORKSPACE_PATHS.steps}/*.md\` (ls steps/ for the full list)`);
|
|
1176
|
+
lines.push(`- Full ledger TOC: \`${WORKSPACE_PATHS.ledgerIndex}\` (read before windowing into ledger.md)`);
|
|
1177
|
+
lines.push(`- Screenshots: \`${WORKSPACE_PATHS.browserFrames}/<NNNN>-...png\` (read with vision)`);
|
|
1178
|
+
lines.push(`- Workflow nodes: \`${WORKSPACE_PATHS.workflowNodes}/<nodeId>.json\``);
|
|
1179
|
+
lines.push("");
|
|
1180
|
+
await fs9.writeFile(
|
|
1181
|
+
resolveWorkspacePath(input.workspaceRoot, WORKSPACE_PATHS.summary),
|
|
1182
|
+
lines.join("\n"),
|
|
1183
|
+
"utf-8"
|
|
1184
|
+
);
|
|
1185
|
+
};
|
|
1186
|
+
var STATUS_GLYPH = {
|
|
1187
|
+
success: "\u2713",
|
|
1188
|
+
passed: "\u2713",
|
|
1189
|
+
failed: "\u2717",
|
|
1190
|
+
skipped: "-",
|
|
1191
|
+
pending: "\xB7",
|
|
1192
|
+
running: "\u22EF",
|
|
1193
|
+
waiting: "\u22EF",
|
|
1194
|
+
canceled: "\u2298"
|
|
1195
|
+
};
|
|
1196
|
+
var formatStepRow = (s, baselineMs) => {
|
|
1197
|
+
const glyph = STATUS_GLYPH[s.status] ?? "?";
|
|
1198
|
+
const ord = String(s.orderIndex).padStart(3, " ");
|
|
1199
|
+
const tplus = s.startedAtMs !== null && baselineMs !== null ? formatRelative(s.startedAtMs - baselineMs).padStart(11, " ") : " ?";
|
|
1200
|
+
const name = s.name ?? "(unnamed)";
|
|
1201
|
+
const namePadded = truncate(name, 35).padEnd(35, " ");
|
|
1202
|
+
const dur = s.status === "failed" ? `FAILED${s.errorMessage ? ` \u2014 ${truncate(oneLine(s.errorMessage), 40)}` : ""}` : s.status === "skipped" ? "skipped" : s.durationMs !== null ? `${s.durationMs}ms` : "?";
|
|
1203
|
+
const cache = s.cacheOutcome ? ` cache: ${s.cacheOutcome}${s.cacheHitSource ? `/${s.cacheHitSource}` : ""}` : "";
|
|
1204
|
+
return ` ${ord} ${glyph} ${tplus} ${namePadded} ${dur}${cache}`;
|
|
1205
|
+
};
|
|
1206
|
+
var countByStatus = (steps) => {
|
|
1207
|
+
const m = /* @__PURE__ */ new Map();
|
|
1208
|
+
for (const s of steps) m.set(s.status, (m.get(s.status) ?? 0) + 1);
|
|
1209
|
+
return m;
|
|
1210
|
+
};
|
|
1211
|
+
var formatCounts = (counts) => {
|
|
1212
|
+
const order = ["success", "passed", "failed", "skipped", "pending", "running", "waiting", "canceled"];
|
|
1213
|
+
const parts = [];
|
|
1214
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1215
|
+
for (const key of order) {
|
|
1216
|
+
if (counts.has(key)) {
|
|
1217
|
+
const count = counts.get(key);
|
|
1218
|
+
const emphasis = key === "failed" && count > 0 ? `**${count} ${key.toUpperCase()}**` : `${count} ${key}`;
|
|
1219
|
+
parts.push(emphasis);
|
|
1220
|
+
seen.add(key);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
for (const [k, v] of counts) {
|
|
1224
|
+
if (!seen.has(k)) parts.push(`${v} ${k}`);
|
|
1225
|
+
}
|
|
1226
|
+
return parts.join(", ");
|
|
1227
|
+
};
|
|
1228
|
+
var buildHistoryHint = (runs, currentRunId, lastGreenComparison) => {
|
|
1229
|
+
const lookback = runs.slice(0, MAX_HISTORY_LOOKBACK);
|
|
1230
|
+
if (lookback.length <= 1) return [];
|
|
1231
|
+
const others = lookback.filter((r) => r.workflowRunId !== currentRunId);
|
|
1232
|
+
if (others.length === 0) return [];
|
|
1233
|
+
const passed = others.filter((r) => isPassStatus(r.status)).length;
|
|
1234
|
+
const failed = others.filter((r) => isFailStatus(r.status)).length;
|
|
1235
|
+
const lastGreen = others.find((r) => isPassStatus(r.status));
|
|
1236
|
+
const regime = classifyHistoryRegime(others);
|
|
1237
|
+
const out = [];
|
|
1238
|
+
out.push(
|
|
1239
|
+
`${passed} of last ${others.length} ran passed; ${failed} failed. Regime: ${regime}.`
|
|
1240
|
+
);
|
|
1241
|
+
if (lastGreen) {
|
|
1242
|
+
out.push(
|
|
1243
|
+
`Most recent green: testRunId=${lastGreen.testRunId}${lastGreen.startedAt ? ` (${lastGreen.startedAt.slice(0, 10)})` : ""}`
|
|
1244
|
+
);
|
|
1245
|
+
const greenRunId = lastGreen.workflowRunId ?? lastGreen.testRunId;
|
|
1246
|
+
out.push(
|
|
1247
|
+
`Comparison is REQUIRED \u2014 materialize via: canary workflow debug materialize ${greenRunId} --out history/last-green/`
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
const authoredUnchanged = lastGreenComparison?.authoredUnchanged ?? null;
|
|
1251
|
+
const greenDate = lastGreen?.startedAt ? ` (${lastGreen.startedAt.slice(0, 10)})` : "";
|
|
1252
|
+
if (regime === "STABLE_REGRESSION" && lastGreen) {
|
|
1253
|
+
if (authoredUnchanged === true) {
|
|
1254
|
+
out.push(
|
|
1255
|
+
`AUTHORED TEST UNCHANGED since last green${greenDate} \u2014 the authored workflow is byte-identical across this pass\u2192fail boundary, so this CANNOT be test_underspecified. A pass\u2192fail here means APP BEHAVIOR CHANGED: classify app_changed (or software_bug if the delta is clearly a defect), name the concrete behavior delta, and note in openQuestions that intent could not be verified from the sandbox.`
|
|
1256
|
+
);
|
|
1257
|
+
} else if (authoredUnchanged === false) {
|
|
1258
|
+
out.push(
|
|
1259
|
+
`AUTHORED TEST CHANGED since last green${greenDate} \u2014 the test definition was edited. Compare to judge whether the new instructions are ambiguous (test_underspecified) before blaming the app.`
|
|
1260
|
+
);
|
|
1261
|
+
} else {
|
|
1262
|
+
out.push(
|
|
1263
|
+
`Stably green before this break \u2014 comparison REQUIRED. If the authored workflow is unchanged vs the green run, a pass\u2192fail means APP BEHAVIOR CHANGED (app_changed), not the test.`
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
} else if (regime === "FLAKY") {
|
|
1267
|
+
out.push(
|
|
1268
|
+
`This test is FLAKY (pass/fail oscillated over the window) \u2014 a single green\u2192red does NOT imply an app change. Weigh agent_confusion / intermittent dependency_issue / data_collision. Comparison may still help, but do not force an app_changed verdict without a clear, persistent behavior delta.`
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
return out;
|
|
1272
|
+
};
|
|
1273
|
+
var formatRelativePrefix = (timestampMs, baselineMs) => baselineMs !== null ? `[${formatRelative(timestampMs - baselineMs)}]` : "[T+?]";
|
|
1274
|
+
|
|
1275
|
+
// ../diagnostic-workspace/src/assemble-workspace.ts
|
|
1276
|
+
var assembleWorkspace = async (input) => {
|
|
1277
|
+
const { runData, workspaceRoot, traceZipPath } = input;
|
|
1278
|
+
await fs10.mkdir(workspaceRoot, { recursive: true, mode: 448 });
|
|
1279
|
+
for (const dir of WORKSPACE_DIRS) {
|
|
1280
|
+
await fs10.mkdir(resolveWorkspacePath(workspaceRoot, dir), { recursive: true, mode: 448 });
|
|
1281
|
+
}
|
|
1282
|
+
let partial = runData.partial;
|
|
1283
|
+
await writeReportedFailure({
|
|
1284
|
+
workspaceRoot,
|
|
1285
|
+
runStatus: runData.status,
|
|
1286
|
+
failedStepName: runData.failedStepName,
|
|
1287
|
+
failedStepOrderIndex: runData.failedStepOrderIndex,
|
|
1288
|
+
errorMessage: runData.errorMessage
|
|
1289
|
+
});
|
|
1290
|
+
await Promise.all([
|
|
1291
|
+
writeTimelineSteps(workspaceRoot, runData.steps),
|
|
1292
|
+
writeTimelineActionsAndNavigation(workspaceRoot, runData.timeline),
|
|
1293
|
+
writeAssertionsAndCacheDecisions(workspaceRoot, runData.steps),
|
|
1294
|
+
writeThoughts(workspaceRoot, runData.thoughts),
|
|
1295
|
+
writeLogs(workspaceRoot, runData.networkLogs, runData.consoleLogs),
|
|
1296
|
+
writeVariables(workspaceRoot, runData.metadataJson, runData.steps),
|
|
1297
|
+
writeRecentRuns(workspaceRoot, recentRunsToWriterShape(runData.recentRuns)),
|
|
1298
|
+
writeWorkflowDefinition({
|
|
1299
|
+
workspaceRoot,
|
|
1300
|
+
workflowId: runData.workflowId,
|
|
1301
|
+
workflowName: runData.workflowName,
|
|
1302
|
+
nodes: runData.nodes,
|
|
1303
|
+
edges: runData.edges
|
|
1304
|
+
})
|
|
1305
|
+
]);
|
|
1306
|
+
const sentryPartial = await writeSentryLogsAndSpans({
|
|
1307
|
+
workspaceRoot,
|
|
1308
|
+
logs: runData.sentryLogs,
|
|
1309
|
+
spans: runData.sentrySpans
|
|
1310
|
+
});
|
|
1311
|
+
if (sentryPartial) partial = true;
|
|
1312
|
+
const tracePartial = await writeTrace({
|
|
1313
|
+
workspaceRoot,
|
|
1314
|
+
traceZipPath,
|
|
1315
|
+
runStartedAtMs: runData.startedAt ? Date.parse(runData.startedAt) : null
|
|
1316
|
+
});
|
|
1317
|
+
if (tracePartial) partial = true;
|
|
1318
|
+
const eventProjection = projectEvents(runData);
|
|
1319
|
+
await writeLedger({
|
|
1320
|
+
workspaceRoot,
|
|
1321
|
+
runId: runData.runId,
|
|
1322
|
+
workflowId: runData.workflowId,
|
|
1323
|
+
workflowName: runData.workflowName,
|
|
1324
|
+
runStatus: runData.status,
|
|
1325
|
+
runStartedAt: runData.startedAt,
|
|
1326
|
+
reportedFailure: {
|
|
1327
|
+
failedStepName: runData.failedStepName,
|
|
1328
|
+
failedStepOrderIndex: runData.failedStepOrderIndex,
|
|
1329
|
+
errorMessage: runData.errorMessage
|
|
1330
|
+
},
|
|
1331
|
+
steps: eventProjection.steps,
|
|
1332
|
+
thoughts: eventProjection.thoughts,
|
|
1333
|
+
actions: eventProjection.actions,
|
|
1334
|
+
navigation: eventProjection.navigation,
|
|
1335
|
+
network: eventProjection.network,
|
|
1336
|
+
consoleLogs: eventProjection.consoleLogs,
|
|
1337
|
+
assertions: eventProjection.assertions
|
|
1338
|
+
});
|
|
1339
|
+
await writeStepRollups({
|
|
1340
|
+
workspaceRoot,
|
|
1341
|
+
runStartedAt: runData.startedAt,
|
|
1342
|
+
steps: eventProjection.steps,
|
|
1343
|
+
thoughts: eventProjection.thoughts,
|
|
1344
|
+
actions: eventProjection.actions,
|
|
1345
|
+
navigation: eventProjection.navigation,
|
|
1346
|
+
network: eventProjection.network,
|
|
1347
|
+
consoleLogs: eventProjection.consoleLogs,
|
|
1348
|
+
assertions: eventProjection.assertions
|
|
1349
|
+
});
|
|
1350
|
+
await writeSummary({
|
|
1351
|
+
workspaceRoot,
|
|
1352
|
+
runId: runData.runId,
|
|
1353
|
+
workflowName: runData.workflowName,
|
|
1354
|
+
runStatus: runData.status,
|
|
1355
|
+
runStartedAt: runData.startedAt,
|
|
1356
|
+
runFinishedAt: runData.finishedAt,
|
|
1357
|
+
reportedFailure: {
|
|
1358
|
+
failedStepId: runData.failedStepId,
|
|
1359
|
+
failedStepName: runData.failedStepName,
|
|
1360
|
+
failedStepOrderIndex: runData.failedStepOrderIndex,
|
|
1361
|
+
errorMessage: runData.errorMessage
|
|
1362
|
+
},
|
|
1363
|
+
steps: eventProjection.steps.map((s) => ({
|
|
1364
|
+
id: s.id,
|
|
1365
|
+
orderIndex: s.orderIndex,
|
|
1366
|
+
name: s.name,
|
|
1367
|
+
status: s.status,
|
|
1368
|
+
startedAtMs: s.startedAtMs,
|
|
1369
|
+
durationMs: s.durationMs,
|
|
1370
|
+
cacheOutcome: s.cacheOutcome,
|
|
1371
|
+
cacheHitSource: s.cacheHitSource,
|
|
1372
|
+
errorMessage: s.errorMessage
|
|
1373
|
+
})),
|
|
1374
|
+
network: eventProjection.network,
|
|
1375
|
+
consoleLogs: eventProjection.consoleLogs,
|
|
1376
|
+
navigation: eventProjection.navigation,
|
|
1377
|
+
recentRuns: runData.recentRuns,
|
|
1378
|
+
lastGreenComparison: runData.lastGreenComparison
|
|
1379
|
+
});
|
|
1380
|
+
await writeManifest({
|
|
1381
|
+
workspaceRoot,
|
|
1382
|
+
runId: runData.runId,
|
|
1383
|
+
workflowId: runData.workflowId,
|
|
1384
|
+
orgId: runData.orgId,
|
|
1385
|
+
status: runData.status,
|
|
1386
|
+
startedAt: runData.startedAt,
|
|
1387
|
+
finishedAt: runData.finishedAt,
|
|
1388
|
+
errorMessage: runData.errorMessage,
|
|
1389
|
+
workflowName: runData.workflowName,
|
|
1390
|
+
failedStepId: runData.failedStepId,
|
|
1391
|
+
failedNodeId: runData.failedNodeId,
|
|
1392
|
+
failedStepName: runData.failedStepName,
|
|
1393
|
+
traceStorageKey: runData.traceStorageKey,
|
|
1394
|
+
diagnosticsStorageKey: runData.diagnosticsStorageKey,
|
|
1395
|
+
videoStorageKey: runData.videoStorageKey,
|
|
1396
|
+
partial
|
|
1397
|
+
});
|
|
1398
|
+
return {
|
|
1399
|
+
root: workspaceRoot,
|
|
1400
|
+
runId: runData.runId,
|
|
1401
|
+
orgId: runData.orgId,
|
|
1402
|
+
materializedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1403
|
+
runStartedAt: runData.startedAt
|
|
1404
|
+
};
|
|
1405
|
+
};
|
|
1406
|
+
var isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1407
|
+
var stringField = (v) => typeof v === "string" && v.length > 0 ? v : null;
|
|
1408
|
+
var normalizeTs = (ts) => {
|
|
1409
|
+
if (ts === null || ts === void 0) return null;
|
|
1410
|
+
if (typeof ts === "number") return ts;
|
|
1411
|
+
const parsed = Date.parse(ts);
|
|
1412
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1413
|
+
};
|
|
1414
|
+
var projectEvents = (runData) => {
|
|
1415
|
+
const baselineMs = runData.timeline.startTimestamp ? Date.parse(runData.timeline.startTimestamp) : null;
|
|
1416
|
+
const steps = runData.steps.map((s) => {
|
|
1417
|
+
const meta = isPlainObject(s.metadataJson) ? s.metadataJson : {};
|
|
1418
|
+
const startedAtMs = s.startedAt ? Date.parse(s.startedAt) : null;
|
|
1419
|
+
const finishedAtMs = s.finishedAt ? Date.parse(s.finishedAt) : null;
|
|
1420
|
+
return {
|
|
1421
|
+
id: s.id,
|
|
1422
|
+
nodeId: s.nodeId,
|
|
1423
|
+
orderIndex: s.orderIndex,
|
|
1424
|
+
name: s.nodeName,
|
|
1425
|
+
nodeType: s.nodeType,
|
|
1426
|
+
status: s.status,
|
|
1427
|
+
startedAtMs,
|
|
1428
|
+
finishedAtMs,
|
|
1429
|
+
durationMs: startedAtMs !== null && finishedAtMs !== null ? finishedAtMs - startedAtMs : null,
|
|
1430
|
+
errorMessage: s.errorMessage,
|
|
1431
|
+
cacheOutcome: stringField(meta.cacheOutcome),
|
|
1432
|
+
cacheHitSource: stringField(meta.cacheHitSource)
|
|
1433
|
+
};
|
|
1434
|
+
});
|
|
1435
|
+
const thoughts = runData.thoughts.map((t) => ({
|
|
1436
|
+
stepId: t.stepId,
|
|
1437
|
+
timestampMs: Date.parse(t.createdAt),
|
|
1438
|
+
toolName: t.toolName,
|
|
1439
|
+
content: t.content
|
|
1440
|
+
}));
|
|
1441
|
+
const actions = runData.timeline.entries.map((e) => ({
|
|
1442
|
+
action: e.action,
|
|
1443
|
+
target: e.target ?? null,
|
|
1444
|
+
url: e.url ?? null,
|
|
1445
|
+
timestampMs: baselineMs !== null ? baselineMs + e.timestampMs : null,
|
|
1446
|
+
success: e.success,
|
|
1447
|
+
isAnnotation: e.isAnnotation ?? false
|
|
1448
|
+
}));
|
|
1449
|
+
const navigation = [];
|
|
1450
|
+
let prevUrl = null;
|
|
1451
|
+
for (const a of actions) {
|
|
1452
|
+
const isNav = NAVIGATION_ACTIONS.has(a.action);
|
|
1453
|
+
const urlChanged = a.url !== null && prevUrl !== null && a.url !== prevUrl;
|
|
1454
|
+
if (!isNav && !urlChanged) {
|
|
1455
|
+
if (a.url) prevUrl = a.url;
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
navigation.push({
|
|
1459
|
+
timestampMs: a.timestampMs,
|
|
1460
|
+
action: a.action,
|
|
1461
|
+
from: prevUrl,
|
|
1462
|
+
to: a.url,
|
|
1463
|
+
crossHost: hostOf(prevUrl) !== null && hostOf(a.url) !== null && hostOf(prevUrl) !== hostOf(a.url)
|
|
1464
|
+
});
|
|
1465
|
+
if (a.url) prevUrl = a.url;
|
|
1466
|
+
}
|
|
1467
|
+
const network = runData.networkLogs.map((n) => ({
|
|
1468
|
+
timestampMs: normalizeTs(n.timestamp ?? null),
|
|
1469
|
+
url: n.url,
|
|
1470
|
+
method: n.method,
|
|
1471
|
+
status: n.status ?? null
|
|
1472
|
+
}));
|
|
1473
|
+
const consoleLogs = runData.consoleLogs.map((c) => ({
|
|
1474
|
+
timestampMs: normalizeTs(c.timestamp),
|
|
1475
|
+
type: c.type,
|
|
1476
|
+
text: c.text
|
|
1477
|
+
}));
|
|
1478
|
+
const assertions = [];
|
|
1479
|
+
for (const s of runData.steps) {
|
|
1480
|
+
const meta = isPlainObject(s.metadataJson) ? s.metadataJson : {};
|
|
1481
|
+
const raw = Array.isArray(meta.assertionResults) ? meta.assertionResults : [];
|
|
1482
|
+
for (const r of raw) {
|
|
1483
|
+
if (!isPlainObject(r)) continue;
|
|
1484
|
+
assertions.push({
|
|
1485
|
+
stepId: s.id,
|
|
1486
|
+
timestampMs: s.startedAt ? Date.parse(s.startedAt) : null,
|
|
1487
|
+
condition: stringField(r.condition),
|
|
1488
|
+
severity: stringField(r.severity),
|
|
1489
|
+
nonBlocking: Boolean(r.nonBlocking),
|
|
1490
|
+
passed: Boolean(r.passed),
|
|
1491
|
+
reason: stringField(r.reason)
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return { steps, thoughts, actions, navigation, network, consoleLogs, assertions };
|
|
1496
|
+
};
|
|
1497
|
+
var recentRunsToWriterShape = (runs) => runs.map((r) => ({
|
|
1498
|
+
id: r.testRunId,
|
|
1499
|
+
workflowRunId: r.workflowRunId,
|
|
1500
|
+
status: r.status,
|
|
1501
|
+
startedAt: r.startedAt,
|
|
1502
|
+
finishedAt: r.finishedAt,
|
|
1503
|
+
errorMessage: r.errorMessage
|
|
1504
|
+
}));
|
|
1505
|
+
export {
|
|
1506
|
+
WORKSPACE_DIRS,
|
|
1507
|
+
WORKSPACE_PATHS,
|
|
1508
|
+
assembleWorkspace,
|
|
1509
|
+
classifyHistoryRegime,
|
|
1510
|
+
resolveWorkspacePath,
|
|
1511
|
+
writeLedger,
|
|
1512
|
+
writeManifest,
|
|
1513
|
+
writeStepRollups,
|
|
1514
|
+
writeSummary
|
|
1515
|
+
};
|
|
1516
|
+
//# sourceMappingURL=src-LB4HBTJQ.js.map
|