@qaick/runner 0.32.15 → 0.32.18
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/package-dist/package.json +4 -4
- package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
- package/package-dist/src/runner/RunnerModule.js +78 -23
- package/package-dist/src/runner/RunnerModule.js.map +1 -1
- package/package-dist/src/runner/history.d.ts +11 -4
- package/package-dist/src/runner/history.d.ts.map +1 -1
- package/package-dist/src/runner/history.js +47 -2
- package/package-dist/src/runner/history.js.map +1 -1
- package/package-dist/src/runner/presentation-state.js +5 -1
- package/package-dist/src/runner/presentation-state.js.map +1 -1
- package/package-dist/src/runner/redaction.d.ts +7 -4
- package/package-dist/src/runner/redaction.d.ts.map +1 -1
- package/package-dist/src/runner/redaction.js +96 -20
- package/package-dist/src/runner/redaction.js.map +1 -1
- package/package-dist/src/runner/runtime-client-adapter.d.ts +2 -2
- package/package-dist/src/runner/runtime-client-adapter.d.ts.map +1 -1
- package/package-dist/src/runner/runtime-client-adapter.js +78 -27
- package/package-dist/src/runner/runtime-client-adapter.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qaick/runner",
|
|
3
3
|
"description": "Reusable QAick Runner module and standalone execution application.",
|
|
4
|
-
"version": "0.32.
|
|
4
|
+
"version": "0.32.18",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package-dist",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@qaick/contract": "0.7.3",
|
|
66
|
-
"@qaick/executor": "0.4.
|
|
67
|
-
"@qaick/http-executor": "0.2.
|
|
68
|
-
"@qaick/runtime": "0.4.
|
|
66
|
+
"@qaick/executor": "0.4.9",
|
|
67
|
+
"@qaick/http-executor": "0.2.19",
|
|
68
|
+
"@qaick/runtime": "0.4.10",
|
|
69
69
|
"zustand": "^5.0.15"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerModule.d.ts","sourceRoot":"","sources":["../../../src/runner/RunnerModule.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,+BAA4B,CAAC;AAUvF,OAAO,KAAK,EAAqB,cAAc,EAAe,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAQvG,OAAO,KAAK,EAAkB,gBAAgB,EAAmB,eAAe,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAkB,CAAC;AA6B5D,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC;IAChC,WAAW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;CACd;AACD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;CAClD;AACD,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,EAAE,qBAAqB,CAAC;IACxC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAoC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,iBAAiB,+BAExH;
|
|
1
|
+
{"version":3,"file":"RunnerModule.d.ts","sourceRoot":"","sources":["../../../src/runner/RunnerModule.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,+BAA4B,CAAC;AAUvF,OAAO,KAAK,EAAqB,cAAc,EAAe,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAQvG,OAAO,KAAK,EAAkB,gBAAgB,EAAmB,eAAe,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAkB,CAAC;AA6B5D,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC;IAChC,WAAW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;CACd;AACD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;CAClD;AACD,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,EAAE,qBAAqB,CAAC;IACxC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAoC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,iBAAiB,+BAExH;AA6YD,KAAK,WAAW,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAC,MAAM,CAAC;IAAA,iBAAiB,CAAC,EAAC,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAC,MAAM,GAAC,MAAM,GAAC,QAAQ,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAC,OAAO,CAAC;IAAC,YAAY,CAAC,EAAC,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAC,gBAAgB,CAAC;IAAC,cAAc,CAAC,EAAC,MAAM,CAAA;CAAE,CAAC;AAkB1a,wBAAgB,yBAAyB,CAAC,KAAK,EAAC,WAAW,EAAE,EAAC,QAAQ,EAAC,UAAU,EAAE,GAAE,WAAW,EAAE,CAAe;AACjH,eAAO,MAAM,aAAa,GAAE,UAAS,gBAAgB,WAA8D,CAAC;AACpH,wBAAgB,oBAAoB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,EAAC,UAAU,CAAC,EAAC,MAAM,GAAE,UAAU,EAAE,CAAuI;AAC3P,wBAAgB,mBAAmB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,EAAC,UAAU,CAAC,EAAC,MAAM,GAAE,UAAU,GAAC,SAAS,CAAiH;AAC5O,wBAAgB,kBAAkB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,GAAE,eAAe,GAAC,SAAS,CAS3G;AAID,wBAAgB,mBAAmB,CAAC,EAAC,OAAO,EAAC,UAAU,EAAC,EAAC;IAAC,OAAO,EAAC,UAAU,EAAE,CAAC;IAAA,UAAU,CAAC,EAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;CAAC,+BAK5H;AAcD,kGAAkG;AAClG,wBAAgB,uBAAuB,CAAC,IAAI,EAAC,WAAW,EAAE,EAAC,MAAM,EAAC;IAAC,MAAM,CAAC,EAAC,MAAM,CAAC;IAAA,UAAU,CAAC,EAAC,MAAM,CAAC;IAAA,OAAO,CAAC,EAAC,gBAAgB,CAAA;CAAC,EAAC,OAAO,CAAC,EAAC,cAAc,GAAE;IAAC,IAAI,CAAC,EAAC,WAAW,CAAC;IAAA,MAAM,CAAC,EAAC,MAAM,CAAA;CAAC,CAa1L"}
|
|
@@ -17,10 +17,10 @@ import { PackageCatalog } from "./catalog.js";
|
|
|
17
17
|
import { RUNNER_CONTRACT_VERSION } from "./types.js";
|
|
18
18
|
import { RunnerWalkthrough } from "../demo/RunnerWalkthrough.js";
|
|
19
19
|
import { RUNNER_BUILD_LABEL, RUNNER_UI_VERSION } from "../build-info.js";
|
|
20
|
-
import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs } from "./redaction.js";
|
|
20
|
+
import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs, redactValue, redactText, isSensitiveKey } from "./redaction.js";
|
|
21
21
|
import { createResultExport } from "./result-export.js";
|
|
22
22
|
import { inspectDeclaredInputs, inputDrafts, resolvedArguments } from "./invocation-inputs.js";
|
|
23
|
-
import { persistRunHistory, redactHistoryEntry, captureHistoryStructure, assertHistoryIdentity } from "./history.js";
|
|
23
|
+
import { persistRunHistory, redactHistoryEntry, captureHistoryStructure, assertHistoryIdentity, recoverRunHistoryEntry, historyPresentationEntry } from "./history.js";
|
|
24
24
|
import { prepareStudioDocument } from "./studio-execution-document.js";
|
|
25
25
|
const statusGlyph = { pending: '', 'not executed': '', running: '', finished: '✓', failed: '✕', waiting: '◷', skipped: '–', cancelled: '⊘' };
|
|
26
26
|
const HISTORY_KEY = 'qaick-runner.history.v1';
|
|
@@ -45,7 +45,7 @@ function saveInspectorWidth(width) { try {
|
|
|
45
45
|
}
|
|
46
46
|
catch { /* Resizing still works when preferences cannot be stored. */ } }
|
|
47
47
|
function readRunHistory() { try {
|
|
48
|
-
return JSON.parse(localStorage.getItem(HISTORY_KEY) ?? '[]');
|
|
48
|
+
return JSON.parse(localStorage.getItem(HISTORY_KEY) ?? '[]').map(entry => !entry.result && entry.admittedAt ? { ...entry, state: 'unknown', eventCollectionIncomplete: true } : entry);
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
51
51
|
return [];
|
|
@@ -188,12 +188,14 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
188
188
|
setHistorySaveError(undefined);
|
|
189
189
|
if (replace)
|
|
190
190
|
setHistoricalEntry(previous => previous?.requestId === entry.requestId ? safe : previous);
|
|
191
|
+
return true;
|
|
191
192
|
}
|
|
192
193
|
catch {
|
|
193
194
|
updateHistory(next);
|
|
194
195
|
if (replace)
|
|
195
196
|
setHistoricalEntry(previous => previous?.requestId === entry.requestId ? safe : previous);
|
|
196
197
|
setHistorySaveError('History could not be saved. Execution evidence remains available in this session. Export JSON before closing Runner.');
|
|
198
|
+
return false;
|
|
197
199
|
}
|
|
198
200
|
}
|
|
199
201
|
const [historyRetention, setHistoryRetention] = useState(readHistoryRetention);
|
|
@@ -202,6 +204,16 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
202
204
|
const [result, setResult] = useState();
|
|
203
205
|
const [workspace, setWorkspace] = useState('execution');
|
|
204
206
|
const [historicalEntry, setHistoricalEntry] = useState();
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
let current = true;
|
|
209
|
+
for (const entry of historyRef.current.filter(item => !item.result && item.admittedAt)) {
|
|
210
|
+
void recoverRunHistoryEntry(entry, client).then(recovered => {
|
|
211
|
+
if (current)
|
|
212
|
+
saveHistoryEntry(recovered, [], true);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return () => { current = false; };
|
|
216
|
+
}, [client]);
|
|
205
217
|
const [historySelectedStep, setHistorySelectedStep] = useState(0);
|
|
206
218
|
const [historyCollapsedSteps, setHistoryCollapsedSteps] = useState(new Set());
|
|
207
219
|
const [loadedContexts, setLoadedContexts] = useState({});
|
|
@@ -219,7 +231,11 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
219
231
|
const legacyDisplaySteps = orderDisplayStepsByResult(flattenDisplaySteps(packageSteps, 0, undefined, undefined, collapsedSteps), result?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(result?.steps ?? []));
|
|
220
232
|
const liveDisplaySteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, collapsedSteps, preparedDocument) : authoringSource ? [] : legacyDisplaySteps;
|
|
221
233
|
const historyPackage = undefined;
|
|
222
|
-
const
|
|
234
|
+
const presentedHistory = historicalEntry ? historyPresentationEntry(historicalEntry) : undefined;
|
|
235
|
+
const historicalEvents = presentedHistory?.events ?? [];
|
|
236
|
+
const invocationSecrets = collectSecretValues(inputs, runtimeValues, inputContract, (preparedDocument ?? executionPackage));
|
|
237
|
+
const shownSecrets = readOnly ? collectSecretValues(historicalEntry?.result) : invocationSecrets;
|
|
238
|
+
const shownResult = readOnly ? presentedHistory?.result && redactExecutionResult(presentedHistory.result, shownSecrets) : result;
|
|
223
239
|
const shownPackage = readOnly ? historyPackage : authoringSource ? undefined : executionPackage;
|
|
224
240
|
const shownPackageSteps = shownPackage ? packageStepsFor(shownPackage) : [];
|
|
225
241
|
const shownSelectedStep = readOnly ? historySelectedStep : selectedStep;
|
|
@@ -227,12 +243,12 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
227
243
|
const shownRunState = readOnly ? historicalEntry?.state ?? 'ready' : runState;
|
|
228
244
|
const allResults = shownResult?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(shownResult?.steps ?? []);
|
|
229
245
|
const displaySteps = readOnly ? historicalEntry?.structure ? contextDisplaySteps(historicalEntry.structure.context, historyCollapsedSteps) : recordedDisplaySteps(allResults) : liveDisplaySteps;
|
|
230
|
-
const selectedStart = selectedStepStartedAt(displaySteps[shownSelectedStep], { results: allResults, result: shownResult, context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, scope: readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root, events: readOnly ?
|
|
246
|
+
const selectedStart = selectedStepStartedAt(displaySteps[shownSelectedStep], { results: allResults, result: shownResult, context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, scope: readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root, events: readOnly ? historicalEvents.map(event => redactExecutionEvent(event, shownSecrets)) : events, requestId: shownRequestId });
|
|
231
247
|
const guides = hierarchyGuides(displaySteps);
|
|
232
248
|
const hasNestedSteps = displaySteps.some(step => step.depth > 0 || step.hasChildren);
|
|
233
249
|
const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress, step.occurrence));
|
|
234
250
|
const selectedInvocations = resultsForOccurrence(allResults, displaySteps[shownSelectedStep]?.structuralAddress, displaySteps[shownSelectedStep]?.occurrence);
|
|
235
|
-
const selectedPaths = readableInvocationPaths(displaySteps[shownSelectedStep], { context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, invocations: selectedInvocations, allResults, result: shownResult, events: readOnly ?
|
|
251
|
+
const selectedPaths = readableInvocationPaths(displaySteps[shownSelectedStep], { context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, invocations: selectedInvocations, allResults, result: shownResult, events: readOnly ? historicalEvents.map(event => redactExecutionEvent(event, shownSecrets)) : events, requestId: shownRequestId });
|
|
236
252
|
const selectedError = displayResults[shownSelectedStep]?.error;
|
|
237
253
|
const errorCode = selectedError?.code ?? shownResult?.error?.code;
|
|
238
254
|
const errorMessage = selectedError?.message ?? shownResult?.error?.message;
|
|
@@ -251,7 +267,7 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
251
267
|
switchWorkspace(next);
|
|
252
268
|
document.getElementById(`runner-tab-${next}`)?.focus();
|
|
253
269
|
}
|
|
254
|
-
const executionEvents = (readOnly ?
|
|
270
|
+
const executionEvents = (readOnly ? historicalEvents.map(event => redactExecutionEvent(event, shownSecrets)) : events).filter(event => event.requestId === shownRequestId);
|
|
255
271
|
const visibleEvents = executionEvents.filter(event => { const categoryMatches = eventFilter === 'all' || eventFilter === 'parallel' && event.parallel || eventFilter === 'steps' && event.stepId || eventFilter === 'lifecycle' && !event.stepId && !event.parallel || eventFilter === 'failures' && (event.state === 'failed' || event.type.includes('error')); const searchMatches = !eventSearch.trim() || `${event.type} ${event.state} ${event.message} ${event.stepName ?? ''}`.toLowerCase().includes(eventSearch.trim().toLowerCase()); return categoryMatches && searchMatches; });
|
|
256
272
|
function selectRoot(key) { if (activeInputRequest.current)
|
|
257
273
|
return; setSelectedRoot(key); setSelectedStep(0); stepStateCache.current = {}; setStepStates(liveDisplaySteps.map(() => 'pending')); setResult(undefined); setEvents([]); setRequestId(undefined); setRunState('ready'); setControlState('idle'); setLoadError(undefined); }
|
|
@@ -278,8 +294,14 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
278
294
|
setRunState('preflighting');
|
|
279
295
|
setControlState('running');
|
|
280
296
|
let structure;
|
|
297
|
+
let admission;
|
|
298
|
+
const secretValues = invocationSecrets;
|
|
281
299
|
try {
|
|
282
300
|
structure = await captureHistoryStructure(currentPackage);
|
|
301
|
+
admission = { historyVersion: 2, structure, requestId: id, admittedAt: new Date().toISOString(), target: currentPackage.target.name, packageId: currentPackage.packageId, executionSchemaVersion: currentPackage.schemaVersion, state: 'admitted', events: [], eventCollectionIncomplete: true,
|
|
302
|
+
debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? currentPackage.schemaVersion === 8 ? { stopAfter: stopAfterStepId } : { stopAfterStepId } : {}) } };
|
|
303
|
+
if (!saveHistoryEntry(admission, secretValues))
|
|
304
|
+
throw new Error('Execution did not start because its admission evidence could not be saved.');
|
|
283
305
|
const common = { requestId: id, inputs, runtimeValues, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } };
|
|
284
306
|
if (currentPackage.schemaVersion === 8)
|
|
285
307
|
await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfter: stopAfterStepId } : {}) } });
|
|
@@ -287,6 +309,8 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
287
309
|
await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) } });
|
|
288
310
|
}
|
|
289
311
|
catch (error) {
|
|
312
|
+
if (admission)
|
|
313
|
+
saveHistoryEntry({ ...admission, state: 'unknown', eventCollectionIncomplete: true }, secretValues, true);
|
|
290
314
|
activeInputRequest.current = undefined;
|
|
291
315
|
setRunState('failed');
|
|
292
316
|
setControlState('failed');
|
|
@@ -294,7 +318,6 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
294
318
|
return;
|
|
295
319
|
}
|
|
296
320
|
let completed = false;
|
|
297
|
-
const secretValues = collectSecretValues(inputs, runtimeValues);
|
|
298
321
|
const recordedEvents = [];
|
|
299
322
|
let collectionFinished = false, collectionFailed = false;
|
|
300
323
|
const collectEvents = (async () => {
|
|
@@ -305,6 +328,10 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
305
328
|
const recorded = structuredClone(redactExecutionEvent(event, secretValues));
|
|
306
329
|
recordedEvents.push(recorded);
|
|
307
330
|
setEvents(previous => [...previous, recorded]);
|
|
331
|
+
admission = { ...admission, events: [...recordedEvents], lastObservedState: event.state,
|
|
332
|
+
...(event.type === 'run_started' ? { startedAt: event.timestamp } : {}) };
|
|
333
|
+
const saved = historyRef.current.find(item => item.requestId === id);
|
|
334
|
+
saveHistoryEntry(saved?.result ? { ...saved, events: [...recordedEvents], eventCollectionIncomplete: true } : admission, secretValues, true);
|
|
308
335
|
if (completed)
|
|
309
336
|
continue;
|
|
310
337
|
if (event.state === 'running') {
|
|
@@ -317,12 +344,12 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
317
344
|
continue;
|
|
318
345
|
setControlState(state ?? 'running');
|
|
319
346
|
}
|
|
320
|
-
if ('occurrence' in event && event.occurrence) {
|
|
347
|
+
if (!event.control && 'occurrence' in event && event.occurrence) {
|
|
321
348
|
const nextState = event.state;
|
|
322
349
|
stepStateCache.current[event.occurrence] = nextState;
|
|
323
350
|
setStepStates(previous => previous.map((state, index) => liveDisplaySteps[index]?.occurrence === event.occurrence ? nextState : state));
|
|
324
351
|
}
|
|
325
|
-
else if (event.structuralAddress) {
|
|
352
|
+
else if (!event.control && event.structuralAddress) {
|
|
326
353
|
const nextState = event.state;
|
|
327
354
|
stepStateCache.current[occurrenceKey(event.structuralAddress)] = nextState;
|
|
328
355
|
setStepStates(previous => previous.map((state, index) => occurrenceKey(liveDisplaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
|
|
@@ -336,7 +363,22 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
336
363
|
collectionFinished = true;
|
|
337
364
|
}
|
|
338
365
|
})();
|
|
339
|
-
|
|
366
|
+
let executionResult;
|
|
367
|
+
try {
|
|
368
|
+
const actualResult = await client.result(id);
|
|
369
|
+
secretValues.push(...collectSecretValues(actualResult));
|
|
370
|
+
executionResult = redactExecutionResult(actualResult, secretValues);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
completed = true;
|
|
374
|
+
admission = { ...admission, state: 'unknown', eventCollectionIncomplete: true };
|
|
375
|
+
activeInputRequest.current = undefined;
|
|
376
|
+
setRunState('unknown');
|
|
377
|
+
setControlState('failed');
|
|
378
|
+
saveHistoryEntry(admission, secretValues, true);
|
|
379
|
+
setLoadError('Outcome unknown — evidence incomplete. The execution result could not be retrieved.');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
340
382
|
completed = true;
|
|
341
383
|
try {
|
|
342
384
|
assertHistoryIdentity(id, structure, executionResult);
|
|
@@ -358,11 +400,11 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
358
400
|
setRunState(executionResult.state);
|
|
359
401
|
setControlState(executionResult.state === 'cancelled' ? 'stopped' : executionResult.state);
|
|
360
402
|
const finishedAt = executionResult.finishedAt ?? new Date().toISOString();
|
|
361
|
-
const entry = { historyVersion: 2, structure, requestId: id, startedAt: executionResult.startedAt, finishedAt, target: currentPackage.target.name, packageId: currentPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: currentPackage.schemaVersion, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
|
|
403
|
+
const entry = { admittedAt: admission.admittedAt, debugSession: admission.debugSession, historyVersion: 2, structure, requestId: id, startedAt: executionResult.startedAt, finishedAt, target: currentPackage.target.name, packageId: currentPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: currentPackage.schemaVersion, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
|
|
362
404
|
const withEvents = () => ({ ...entry, events: [...recordedEvents],
|
|
363
405
|
...(collectionFailed || recordedEvents.at(-1)?.type !== 'run_finished' ? { eventCollectionIncomplete: true } : {}) });
|
|
364
406
|
const captured = collectionFinished;
|
|
365
|
-
saveHistoryEntry(captured ? withEvents() : entry, secretValues);
|
|
407
|
+
saveHistoryEntry(captured ? withEvents() : entry, secretValues, true);
|
|
366
408
|
await collectEvents;
|
|
367
409
|
if (!captured)
|
|
368
410
|
saveHistoryEntry(withEvents(), secretValues, true);
|
|
@@ -388,7 +430,19 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
388
430
|
next.add(stepId); breakpointsRef.current = next; return next; }); }
|
|
389
431
|
function toggleStopAfter(stepId) { setStopAfterStepId(previous => previous === stepId ? undefined : stepId); }
|
|
390
432
|
function exportResult(value, packageId, schemaVersion, provenance, executionVersion) { if (!value)
|
|
391
|
-
return; const storedEntry = runHistory.find(entry => entry.requestId === value.requestId); const exportPayload = storedEntry?.historyVersion === 2 ? { runnerResultVersion: 2, entry: storedEntry } : createResultExport(value, packageId ?? 'unknown', schemaVersion, collectSecretValues(
|
|
433
|
+
return; const storedEntry = runHistory.find(entry => entry.requestId === value.requestId); const exportPayload = storedEntry?.historyVersion === 2 ? { runnerResultVersion: 2, entry: redactHistoryEntry(storedEntry, collectSecretValues(storedEntry.result)) } : createResultExport(value, packageId ?? 'unknown', schemaVersion, storedEntry ? collectSecretValues(storedEntry.result) : invocationSecrets, provenance, executionVersion); const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${value.target.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${value.requestId}.result.json`; link.click(); URL.revokeObjectURL(url); }
|
|
434
|
+
function exportHistoryEntry(entry) {
|
|
435
|
+
if (entry.result && entry.historyVersion !== 2) {
|
|
436
|
+
exportResult(entry.result, entry.packageId, entry.artifactSchemaVersion, entry.artifactProvenance, entry.executionSchemaVersion);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const blob = new Blob([JSON.stringify({ runnerResultVersion: 2, entry: redactHistoryEntry(entry, collectSecretValues(entry.result)) }, null, 2)], { type: 'application/json' });
|
|
440
|
+
const url = URL.createObjectURL(blob), link = document.createElement('a');
|
|
441
|
+
link.href = url;
|
|
442
|
+
link.download = `${entry.target.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${entry.requestId}.result.json`;
|
|
443
|
+
link.click();
|
|
444
|
+
URL.revokeObjectURL(url);
|
|
445
|
+
}
|
|
392
446
|
function reloadHistoryEntry(entry) { setHistoricalEntry(entry); setHistorySelectedStep(0); setHistoryCollapsedSteps(new Set()); }
|
|
393
447
|
function clearHistory() {
|
|
394
448
|
try {
|
|
@@ -519,7 +573,7 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
519
573
|
setLoadIssues(error instanceof PackageLoadError ? error.issues : []);
|
|
520
574
|
}
|
|
521
575
|
}
|
|
522
|
-
const statusLabel = !readOnly && runState === 'running' && controlState === 'paused' ? 'Paused' : shownRunState === 'ready' ? 'Ready' : shownRunState[0].toUpperCase() + shownRunState.slice(1);
|
|
576
|
+
const statusLabel = readOnly && historicalEntry?.admittedAt && !historicalEntry.result ? historyOutcome(historicalEntry) : !readOnly && runState === 'running' && controlState === 'paused' ? 'Paused' : shownRunState === 'unknown' ? 'Outcome unknown — evidence incomplete' : shownRunState === 'ready' ? 'Ready' : shownRunState[0].toUpperCase() + shownRunState.slice(1);
|
|
523
577
|
useEffect(() => {
|
|
524
578
|
document.documentElement.dataset.theme = theme;
|
|
525
579
|
document.documentElement.style.colorScheme = theme;
|
|
@@ -589,18 +643,18 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
589
643
|
about: () => menuActions.current.about(),
|
|
590
644
|
demo: () => menuActions.current.demo(),
|
|
591
645
|
}), [registerMenu]);
|
|
592
|
-
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _jsx("span", { className: "header-spacer" }), _jsx(ScreenshotButton, { className: "about-button" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _jsx("div", { className: "runner-tabs", role: "tablist", "aria-label": "Runner workspace", children: ['execution', 'history'].map(tab => _jsx("button", { id: `runner-tab-${tab}`, role: "tab", "aria-selected": workspace === tab, "aria-controls": `runner-panel-${tab}`, tabIndex: workspace === tab ? 0 : -1, onKeyDown: tabKey, onClick: () => switchWorkspace(tab), children: tab === 'execution' ? 'Execution' : 'History' }, tab)) }), _jsx("div", { className: "runner-pane-scroll", role: "region", "aria-label": "Runner workspace and Inspector", tabIndex: 0, children: _jsxs("main", { style: { gridTemplateColumns: `minmax(560px, 1fr) 8px ${inspectorWidth}px`, minWidth: `${568 + inspectorWidth}px` }, className: "runner-main runner-tabbed-main", id: `runner-panel-${workspace}`, role: "tabpanel", "aria-labelledby": `runner-tab-${workspace}`, children: [_jsxs(_Fragment, { children: [_jsxs("section", { className: "runner-workspace", children: [diagnosticNotice && _jsx("p", { className: "notice", role: "status", children: diagnosticNotice }), historySaveError && _jsx("p", { className: "load-error", role: "alert", children: historySaveError }), readOnly && loadError && _jsx("p", { className: "load-error", role: "alert", children: loadError }), readOnly && loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), readOnly && historicalEntry && !historicalEntry.structure && _jsx("p", { className: "notice", children: "Original Project structure was not recorded for this run. Only recorded execution evidence is available." }), readOnly && _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport:
|
|
593
|
-
setEditingInputs(value => !value); }, children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || preparing || !!preparationError || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || preparing || !!preparationError || runState === "preflighting" || runState === "running", children: "\u25CE\u00A0 Pre-run validation" }), _jsx("button", { className: "button stop", onClick: () => void stop(), disabled: runState !== 'preflighting' && runState !== 'running', children: "\u25A0\u00A0 Stop" }), _jsx("button", { className: "button", onClick: () => void pause(), disabled: controlState !== 'running', children: "\u2161\u00A0 Pause" }), _jsx("button", { className: `button ${controlState === 'paused' ? 'resume-highlight' : ''}`, onClick: () => void resume(), disabled: controlState !== 'paused', children: "\u25B6\u00A0 Resume" }), _jsx("button", { className: "button", onClick: () => void stepExecution(), disabled: controlState !== 'paused', children: "\u21A6\u00A0 Step" }), breakpoints.size > 0 && _jsxs("span", { className: "debug-session-badge", children: [breakpoints.size, " breakpoint", breakpoints.size === 1 ? "" : "s", " armed"] })] })] }), preparing && _jsx("p", { role: "status", children: "Preparing Project\u2026" }), preparationError && _jsx("p", { role: "alert", children: preparationError }), preparedDocument && !preparedDocument.selection.executable && _jsx("section", { role: "alert", children: preparedDocument.selection.issues.map((i, index) => _jsxs("p", { children: [preparedDocument.context.nodes.find(n => n.key === i.at)?.name, ": ", i.message] }, index)) }), conflict && _jsxs("section", { role: "alert", children: [_jsx("p", { children: conflict.message }), _jsx("ul", { children: conflict.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.declaration.name, " \u00B7 ", entry.declaration.type, " \u00B7 ", entry.declaration.required ? 'required' : 'optional', " \u00B7 default: ", Object.hasOwn(entry.declaration, 'defaultValue') ? JSON.stringify(entry.declaration.defaultValue) : 'none'] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), unresolved && _jsxs("section", { role: "alert", children: [_jsx("p", { children: unresolved.message }), _jsx("ul", { children: unresolved.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.name, " \u00B7 required \u00B7 type unresolved"] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), inputError && _jsx("p", { role: "alert", children: inputError }), loadError && _jsx("p", { className: "load-error", children: loadError }), loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), result?.issues !== undefined && _jsx(PreRunValidation, { issues: result.issues }), result?.warnings !== undefined && result.warnings.length > 0 && _jsx(PreRunValidation, { issues: result.warnings, title: "Browser execution warnings" }), editingInputs && _jsxs("section", { className: "inputs-panel panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Invocation Inputs" }), _jsx("span", { className: "panel-spacer" }), _jsx("span", { className: "input-hint", children: inputsLocked ? 'Inputs are locked while execution is active.' : 'Values are sent with the next run' })] }), _jsxs("fieldset", { className: "input-grid input-lock-fields", "aria-label": "Invocation input values", disabled: inputsLocked, children: [inputBlocker ? _jsx("p", { children: "Invocation input editing is unavailable until requirements are resolved." }) : declarations.map(declaration => { const draft = inputEdits[declaration.name] ?? { supplied: false, text: '' }; const change = (text) => { if (!activeInputRequest.current)
|
|
646
|
+
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _jsx("span", { className: "header-spacer" }), _jsx(ScreenshotButton, { className: "about-button" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _jsx("div", { className: "runner-tabs", role: "tablist", "aria-label": "Runner workspace", children: ['execution', 'history'].map(tab => _jsx("button", { id: `runner-tab-${tab}`, role: "tab", "aria-selected": workspace === tab, "aria-controls": `runner-panel-${tab}`, tabIndex: workspace === tab ? 0 : -1, onKeyDown: tabKey, onClick: () => switchWorkspace(tab), children: tab === 'execution' ? 'Execution' : 'History' }, tab)) }), _jsx("div", { className: "runner-pane-scroll", role: "region", "aria-label": "Runner workspace and Inspector", tabIndex: 0, children: _jsxs("main", { style: { gridTemplateColumns: `minmax(560px, 1fr) 8px ${inspectorWidth}px`, minWidth: `${568 + inspectorWidth}px` }, className: "runner-main runner-tabbed-main", id: `runner-panel-${workspace}`, role: "tabpanel", "aria-labelledby": `runner-tab-${workspace}`, children: [_jsxs(_Fragment, { children: [_jsxs("section", { className: "runner-workspace", children: [diagnosticNotice && _jsx("p", { className: "notice", role: "status", children: diagnosticNotice }), historySaveError && _jsx("p", { className: "load-error", role: "alert", children: historySaveError }), readOnly && loadError && _jsx("p", { className: "load-error", role: "alert", children: loadError }), readOnly && loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues.map(issue => ({ ...issue, message: redactText(issue.message, invocationSecrets) })), title: "Artifact validation" }), readOnly && historicalEntry && !historicalEntry.structure && _jsx("p", { className: "notice", children: "Original Project structure was not recorded for this run. Only recorded execution evidence is available." }), readOnly && _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportHistoryEntry }), _jsxs("div", { hidden: readOnly, children: [_jsx("h1", { children: contextTarget.name }), _jsxs("p", { className: "description", children: ["Execute the selected QAick ", contextTarget.kind === 'project' ? 'Project' : 'Flow', " and capture structured evidence."] }), _jsxs("div", { className: "run-controls", children: [_jsxs("div", { className: "run-settings", role: "group", "aria-label": "Execution setup", children: [_jsxs("label", { children: ["Run only", _jsxs("select", { disabled: inputsLocked || preparing, value: selectedRoot, onChange: event => selectRoot(event.target.value), children: [_jsx("option", { value: "", children: "Entire project" }), preparedDocument && contextDisplaySteps(preparedDocument.context, new Set(), preparedDocument).filter(n => n.kind !== 'body' && n.kind !== 'branch').map(n => _jsxs("option", { value: n.id, children: [' '.repeat(n.depth), n.name] }, n.id))] })] }), _jsxs("label", { children: ["Timeout", _jsxs("select", { disabled: inputsLocked, value: timeoutMs, onChange: event => setTimeoutMs(event.target.value), children: [_jsx("option", { value: "none", children: "None" }), _jsx("option", { value: "1000", children: "1 second" }), _jsx("option", { value: "3000", children: "3 seconds" }), _jsx("option", { value: "10000", children: "10 seconds" })] })] })] }), _jsxs("div", { className: "run-actions", role: "group", "aria-label": "Execution actions", children: [_jsx("button", { className: "button", disabled: inputsLocked, onClick: () => { if (!activeInputRequest.current)
|
|
647
|
+
setEditingInputs(value => !value); }, children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || preparing || !!preparationError || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || preparing || !!preparationError || runState === "preflighting" || runState === "running", children: "\u25CE\u00A0 Pre-run validation" }), _jsx("button", { className: "button stop", onClick: () => void stop(), disabled: runState !== 'preflighting' && runState !== 'running', children: "\u25A0\u00A0 Stop" }), _jsx("button", { className: "button", onClick: () => void pause(), disabled: controlState !== 'running', children: "\u2161\u00A0 Pause" }), _jsx("button", { className: `button ${controlState === 'paused' ? 'resume-highlight' : ''}`, onClick: () => void resume(), disabled: controlState !== 'paused', children: "\u25B6\u00A0 Resume" }), _jsx("button", { className: "button", onClick: () => void stepExecution(), disabled: controlState !== 'paused', children: "\u21A6\u00A0 Step" }), breakpoints.size > 0 && _jsxs("span", { className: "debug-session-badge", children: [breakpoints.size, " breakpoint", breakpoints.size === 1 ? "" : "s", " armed"] })] })] }), preparing && _jsx("p", { role: "status", children: "Preparing Project\u2026" }), preparationError && _jsx("p", { role: "alert", children: redactText(preparationError, invocationSecrets) }), preparedDocument && !preparedDocument.selection.executable && _jsx("section", { role: "alert", children: preparedDocument.selection.issues.map((i, index) => _jsxs("p", { children: [preparedDocument.context.nodes.find(n => n.key === i.at)?.name, ": ", i.message] }, index)) }), conflict && _jsxs("section", { role: "alert", children: [_jsx("p", { children: conflict.message }), _jsx("ul", { children: conflict.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.declaration.name, " \u00B7 ", entry.declaration.type, " \u00B7 ", entry.declaration.required ? 'required' : 'optional', " \u00B7 default: ", Object.hasOwn(entry.declaration, 'defaultValue') ? JSON.stringify(redactValue(entry.declaration.defaultValue, invocationSecrets, entry.declaration.name)) : 'none'] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), unresolved && _jsxs("section", { role: "alert", children: [_jsx("p", { children: unresolved.message }), _jsx("ul", { children: unresolved.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.name, " \u00B7 required \u00B7 type unresolved"] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), inputError && _jsx("p", { role: "alert", children: redactText(inputError, invocationSecrets) }), loadError && _jsx("p", { className: "load-error", children: redactText(loadError, invocationSecrets) }), loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues.map(issue => ({ ...issue, message: redactText(issue.message, invocationSecrets) })), title: "Artifact validation" }), result?.issues !== undefined && _jsx(PreRunValidation, { issues: result.issues }), result?.warnings !== undefined && result.warnings.length > 0 && _jsx(PreRunValidation, { issues: result.warnings, title: "Browser execution warnings" }), editingInputs && _jsxs("section", { className: "inputs-panel panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Invocation Inputs" }), _jsx("span", { className: "panel-spacer" }), _jsx("span", { className: "input-hint", children: inputsLocked ? 'Inputs are locked while execution is active.' : 'Values are sent with the next run' })] }), _jsxs("fieldset", { className: "input-grid input-lock-fields", "aria-label": "Invocation input values", disabled: inputsLocked, children: [inputBlocker ? _jsx("p", { children: "Invocation input editing is unavailable until requirements are resolved." }) : declarations.map(declaration => { const draft = inputEdits[declaration.name] ?? { supplied: false, text: '' }; const change = (text) => { if (!activeInputRequest.current)
|
|
594
648
|
setInputEdits(previous => ({ ...previous, [declaration.name]: { supplied: true, text } })); }; return _jsxs("div", { children: [_jsxs("label", { children: [_jsx("input", { type: "checkbox", "aria-label": `Supply ${declaration.name}`, checked: draft.supplied, onChange: event => { if (!activeInputRequest.current)
|
|
595
649
|
setInputEdits(previous => ({ ...previous, [declaration.name]: { ...draft, supplied: event.target.checked } })); } }), "Supply ", declaration.name] }), declaration.type === 'boolean' ? _jsxs("label", { className: "input-field", children: [declaration.name, _jsxs("select", { value: draft.text, onChange: event => change(event.target.value), children: [_jsx("option", { value: "false", children: "false" }), _jsx("option", { value: "true", children: "true" })] })] }) : _jsx(InputField, { label: declaration.name, value: draft.text, onChange: change }), _jsxs("small", { children: [declaration.type, " \u00B7 ", draft.supplied ? 'supplied' : Object.hasOwn(declaration, 'defaultValue') ? 'omitted — declared default applies' : 'omitted', declaration.required ? ' · required' : ''] })] }, declaration.name); }), (executionPackage.runtimeRequirements ?? []).map(name => _jsx(InputField, { label: `Runtime · ${name}`, value: runtimeValues[name] ?? '', onChange: value => { if (!activeInputRequest.current)
|
|
596
|
-
setRuntimeValues(previous => ({ ...previous, [name]: value })); } }, name))] })] })] }), readOnly && historicalEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results: ", historyRecordName(historicalEntry), " \u00B7 ", historicalEntry
|
|
650
|
+
setRuntimeValues(previous => ({ ...previous, [name]: value })); } }, name))] })] })] }), readOnly && historicalEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results: ", historyRecordName(historicalEntry), " \u00B7 ", historyOutcome(historicalEntry), " \u00B7 ", historicalEntry.startedAt ? new Date(historicalEntry.startedAt).toLocaleString() : 'Start time not recorded'] }), _jsxs("section", { className: "panel", hidden: readOnly && !historicalEntry, children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: readOnly ? 'Recorded execution' : 'Live Execution' }), _jsx("span", { className: "panel-spacer" }), _jsxs("span", { className: "execution-menu-label", hidden: readOnly, children: ["Right-click a row to manage its breakpoint \u00B7 Selected: ", displaySteps[shownSelectedStep]?.name ?? 'None'] }), _jsxs("span", { className: shownRunState === 'running' ? 'running' : shownRunState === 'failed' ? 'stopped' : 'run-status', children: ["\u25CF ", statusLabel] })] }), hasNestedSteps && _jsxs("div", { className: "hierarchy-hint", children: [_jsx("strong", { children: "Nested execution" }), _jsx("span", { children: "Use \u2212 / + to collapse or expand nested Operations, Control Blocks, and loops." }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(true), children: "Collapse all" }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(false), children: "Expand all" })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution steps", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "#" }), _jsx("th", { children: "Step hierarchy" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Start time" }), _jsx("th", { children: "Duration" })] }) }), _jsx("tbody", { children: displaySteps.map((step, index) => { const isLoop = step.loopRepeat; const loopContexts = isLoop ? loopPresentation(step.structuralAddress, step.loopTotal, step.loopDuration, shownResult ? allResults : eventPresentationEvidence(executionEvents, shownRequestId), Boolean(shownResult), step.occurrence) : []; const rowEvidence = shownResult ? allResults : eventPresentationEvidence(executionEvents, shownRequestId); const repeatedMatches = rowEvidence.filter(r => step.occurrence ? r.occurrence === step.occurrence : occurrenceKey(r.structuralAddress) === occurrenceKey(step.structuralAddress)); const latestQualifier = step.branchRow ? rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.branchOwner)).length > 1 : repeatedMatches.length > 1; const scope = readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root; const context = readOnly ? historicalEntry?.structure?.context : preparedDocument?.context; const outside = Boolean(step.occurrence && scope && context && !scopeIncludes(context, scope, step.occurrence)); const presentation = rowPresentation(step, rowEvidence, Boolean(shownResult), step.branchRow ? 'pending' : readOnly ? 'pending' : stepStates[index] ?? 'pending', { context, scope, result: shownResult }); const rowStart = selectedStepStartedAt(step, { results: allResults, result: shownResult, context, scope, events: executionEvents, requestId: shownRequestId }); const displayState = presentation.state; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; return _jsxs("tr", { className: `${outside ? 'outside-scope ' : ''}${shownSelectedStep === index ? 'selected ' : ''}${isLoop ? 'loop-row ' : ''}${isLoopContent ? 'loop-content ' : ''}${isLoopEnd ? 'loop-end ' : ''}${step.depth > 0 ? 'nested-row' : 'parent-row'}`, tabIndex: 0, onKeyDown: event => { if (event.key === "ArrowDown") {
|
|
597
651
|
event.preventDefault();
|
|
598
652
|
selectDisplayedStep(Math.min(displaySteps.length - 1, index + 1));
|
|
599
653
|
} if (event.key === "ArrowUp") {
|
|
600
654
|
event.preventDefault();
|
|
601
655
|
selectDisplayedStep(Math.max(0, index - 1));
|
|
602
656
|
} }, onClick: () => selectDisplayedStep(index), onContextMenu: event => { event.preventDefault(); if (readOnly)
|
|
603
|
-
return; selectDisplayedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { className: "hierarchy-cell", children: [_jsx(HierarchyGuides, { segments: guides.get(step.id) ?? [] }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${(readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: (readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? '+' : '−' }), !step.hasChildren && _jsx("span", { className: "collapse-spacer", "aria-hidden": "true" }), _jsx(StructuralIcon, { kind: step.structuralKind }), !readOnly && breakpoints.has(step.id) && _jsx("span", { className: "breakpoint-marker", role: "img", "aria-label": "Breakpoint armed", title: "Breakpoint armed" }), _jsx("span", { children: step.name }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "\u21BB LOOP" }), step.structuralKind === 'branch' && _jsx("small", { className: "kind-label", children: "BRANCH" }), (!step.structuralKind || step.structuralKind === 'unknown') && _jsx("small", { className: "kind-label", children: "TYPE NOT RECORDED" })] }), shownSelectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [!outside && loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label })] }, JSON.stringify(context.context))), _jsxs("span", { className: "execution-status", children: [_jsx("span", { className: `step-icon execution-status-icon ${displayState.replaceAll(' ', '-')}`, "aria-hidden": "true", children: statusGlyph[displayState] }), _jsx("span", { className: `status ${displayState.replaceAll(' ', '-')}`, children: presentation.label })] }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: rowStart ? new Date(rowStart).toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), !readOnly && contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), !readOnly && _jsxs("details", { className: "panel runner-event-log execution-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsxs("div", { className: "event-log-tools", children: [_jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { "aria-label": "Filter execution events", className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx(ExecutionEventRows, { events: visibleEvents }) }), executionEvents.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." }), executionEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No execution events yet." })] })] }, JSON.stringify(['execution', requestId ?? 'ready', selectedTargetId]))] }), _jsx("div", { className: "inspector-divider", role: "separator", "aria-label": "Resize Inspector", "aria-orientation": "vertical", "aria-controls": "runner-inspector", "aria-valuemin": INSPECTOR_MIN_WIDTH, "aria-valuemax": INSPECTOR_MAX_WIDTH, "aria-valuenow": inspectorWidth, "aria-valuetext": `${inspectorWidth} pixels`, tabIndex: 0, hidden: readOnly && !historicalEntry, onPointerDown: startInspectorResize, onPointerMove: moveInspectorResize, onPointerUp: finishInspectorResize, onPointerCancel: finishInspectorResize, onLostPointerCapture: finishInspectorResize, onKeyDown: inspectorResizeKey }), _jsxs("aside", { id: "runner-inspector", className: "inspector", hidden: readOnly && !historicalEntry, children: [_jsx("h2", { children: "SELECTED STEP" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep] ? `${shownSelectedStep + 1}. ${displaySteps[shownSelectedStep].name}` : 'No step selected' }) }), _jsx("p", { className: "selected-step-start", children: selectedStart ? `Started: ${new Date(selectedStart).toLocaleTimeString()}` : 'Not started' }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", (readOnly ? historicalEntry?.result.target.name : contextTarget.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result.target.kind : contextTarget.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : selectedTargetId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : authoringSource ? 8 : executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Not recorded', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.runtimeRequirements?.join(', ') || 'None' : 'Not recorded'] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: shownResult?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), shownResult ? shownResult.state : 'Run the Project to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[shownSelectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : readOnly ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[shownSelectedStep]?.outputs ? Object.entries(displayResults[shownSelectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (shownResult?.error || displayResults[shownSelectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [errorCode && _jsxs(_Fragment, { children: [_jsx("strong", { children: errorCode }), _jsx("br", {})] }), errorMessage, hasErrorDetails && _jsx("pre", { children: JSON.stringify(errorDetails, null, 2) }), !errorCode && !errorMessage && !hasErrorDetails && 'Error details were not recorded.'] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: selectedPaths.length ? selectedPaths.map(path => _jsxs("div", { className: "notice", children: [selectedInvocations.length > 1 && _jsx("small", { children: "Latest recorded invocation" }), path.names.map((name, index) => _jsx("div", { children: name }, index))] }, path.key)) : _jsx("div", { className: "notice", children: "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(shownPackageSteps, displaySteps[shownSelectedStep]?.structuralAddress) }, JSON.stringify([shownRequestId, displaySteps[shownSelectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", shownResult?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", shownResult?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", shownResult?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[shownSelectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), readOnly && _jsxs("details", { className: "inspector-section inspector-event-log runner-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsx("p", { className: "empty-state", children: historicalEntry?.events === undefined ? 'Events were not recorded for this run.' : historicalEntry.eventCollectionIncomplete ? 'Event collection was incomplete. Only captured events are shown.' : executionEvents.length === 0 ? 'No execution events were recorded.' : '' }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Recorded execution events", tabIndex: 0, children: _jsx(ExecutionEventRows, { events: executionEvents }) })] })] }, JSON.stringify(['history', shownRequestId ?? 'ready', historicalEntry?.packageId])), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[shownSelectedStep] ? displayResults[shownSelectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[shownSelectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the Project to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || (!displayResults[shownSelectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[shownSelectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[shownSelectedStep] && toggleStopAfter(displaySteps[shownSelectedStep].id), disabled: readOnly || !displaySteps[shownSelectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: shownSelectedStep <= 0, onClick: () => selectDisplayedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${shownSelectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: shownSelectedStep >= displaySteps.length - 1, onClick: () => selectDisplayedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), " "] }) }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK Projects." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
657
|
+
return; selectDisplayedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { className: "hierarchy-cell", children: [_jsx(HierarchyGuides, { segments: guides.get(step.id) ?? [] }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${(readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: (readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? '+' : '−' }), !step.hasChildren && _jsx("span", { className: "collapse-spacer", "aria-hidden": "true" }), _jsx(StructuralIcon, { kind: step.structuralKind }), !readOnly && breakpoints.has(step.id) && _jsx("span", { className: "breakpoint-marker", role: "img", "aria-label": "Breakpoint armed", title: "Breakpoint armed" }), _jsx("span", { children: step.name }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "\u21BB LOOP" }), step.structuralKind === 'branch' && _jsx("small", { className: "kind-label", children: "BRANCH" }), (!step.structuralKind || step.structuralKind === 'unknown') && _jsx("small", { className: "kind-label", children: "TYPE NOT RECORDED" })] }), shownSelectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [!outside && loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label })] }, JSON.stringify(context.context))), _jsxs("span", { className: "execution-status", children: [_jsx("span", { className: `step-icon execution-status-icon ${displayState.replaceAll(' ', '-')}`, "aria-hidden": "true", children: statusGlyph[displayState] }), _jsx("span", { className: `status ${displayState.replaceAll(' ', '-')}`, children: presentation.label })] }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: rowStart ? new Date(rowStart).toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), !readOnly && contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), !readOnly && _jsxs("details", { className: "panel runner-event-log execution-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsxs("div", { className: "event-log-tools", children: [_jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { "aria-label": "Filter execution events", className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx(ExecutionEventRows, { events: visibleEvents }) }), executionEvents.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." }), executionEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No execution events yet." })] })] }, JSON.stringify(['execution', requestId ?? 'ready', selectedTargetId]))] }), _jsx("div", { className: "inspector-divider", role: "separator", "aria-label": "Resize Inspector", "aria-orientation": "vertical", "aria-controls": "runner-inspector", "aria-valuemin": INSPECTOR_MIN_WIDTH, "aria-valuemax": INSPECTOR_MAX_WIDTH, "aria-valuenow": inspectorWidth, "aria-valuetext": `${inspectorWidth} pixels`, tabIndex: 0, hidden: readOnly && !historicalEntry, onPointerDown: startInspectorResize, onPointerMove: moveInspectorResize, onPointerUp: finishInspectorResize, onPointerCancel: finishInspectorResize, onLostPointerCapture: finishInspectorResize, onKeyDown: inspectorResizeKey }), _jsxs("aside", { id: "runner-inspector", className: "inspector", hidden: readOnly && !historicalEntry, children: [_jsx("h2", { children: "SELECTED STEP" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep] ? `${shownSelectedStep + 1}. ${displaySteps[shownSelectedStep].name}` : 'No step selected' }) }), _jsx("p", { className: "selected-step-start", children: selectedStart ? `Started: ${new Date(selectedStart).toLocaleTimeString()}` : 'Not started' }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { secretValues: shownSecrets, values: ["Target", (readOnly ? historicalEntry?.result?.target.name ?? historicalEntry?.target : contextTarget.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result?.target.kind : contextTarget.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : selectedTargetId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : authoringSource ? 8 : executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Not recorded', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.runtimeRequirements?.join(', ') || 'None' : 'Not recorded'] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: shownResult?.state.toUpperCase() ?? (readOnly && historicalEntry?.admittedAt ? historyOutcome(historicalEntry) : 'NOT STARTED') }), _jsx("br", {}), shownResult ? shownResult.state : readOnly && historicalEntry?.admittedAt ? `Last recorded state: ${historicalEntry.lastObservedState ?? 'Admission recorded'}. No terminal result is available.` : 'Run the Project to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { secretValues: shownSecrets, values: Object.entries(displayResults[shownSelectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : readOnly ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { secretValues: shownSecrets, values: displayResults[shownSelectedStep]?.outputs ? Object.entries(displayResults[shownSelectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (shownResult?.error || displayResults[shownSelectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [errorCode && _jsxs(_Fragment, { children: [_jsx("strong", { children: errorCode }), _jsx("br", {})] }), errorMessage, hasErrorDetails && _jsx("pre", { children: JSON.stringify(errorDetails, null, 2) }), !errorCode && !errorMessage && !hasErrorDetails && 'Error details were not recorded.'] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: selectedPaths.length ? selectedPaths.map(path => _jsxs("div", { className: "notice", children: [selectedInvocations.length > 1 && _jsx("small", { children: "Latest recorded invocation" }), path.names.map((name, index) => _jsx("div", { children: name }, index))] }, path.key)) : _jsx("div", { className: "notice", children: "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(shownPackageSteps, displaySteps[shownSelectedStep]?.structuralAddress) }, JSON.stringify([shownRequestId, displaySteps[shownSelectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", shownResult?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", shownResult?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", shownResult?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[shownSelectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), readOnly && _jsxs("details", { className: "inspector-section inspector-event-log runner-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsx("p", { className: "empty-state", children: historicalEntry?.events === undefined ? 'Events were not recorded for this run.' : historicalEntry.eventCollectionIncomplete ? 'Event collection was incomplete. Only captured events are shown.' : executionEvents.length === 0 ? 'No execution events were recorded.' : '' }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Recorded execution events", tabIndex: 0, children: _jsx(ExecutionEventRows, { events: executionEvents }) })] })] }, JSON.stringify(['history', shownRequestId ?? 'ready', historicalEntry?.packageId])), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[shownSelectedStep] ? displayResults[shownSelectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[shownSelectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the Project to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || (!displayResults[shownSelectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[shownSelectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[shownSelectedStep] && toggleStopAfter(displaySteps[shownSelectedStep].id), disabled: readOnly || !displaySteps[shownSelectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: shownSelectedStep <= 0, onClick: () => selectDisplayedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${shownSelectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: shownSelectedStep >= displaySteps.length - 1, onClick: () => selectDisplayedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), " "] }) }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK Projects." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
604
658
|
}
|
|
605
659
|
function recordedDisplaySteps(results) {
|
|
606
660
|
const unique = new Map();
|
|
@@ -611,6 +665,7 @@ function recordedDisplaySteps(results) {
|
|
|
611
665
|
}
|
|
612
666
|
return [...unique.values()];
|
|
613
667
|
}
|
|
668
|
+
function historyOutcome(entry) { return !entry.result && entry.admittedAt ? entry.state === 'admitted' ? 'Admission recorded — terminal result pending' : 'Outcome unknown — evidence incomplete' : entry.state.toUpperCase(); }
|
|
614
669
|
function historyRecordName(entry) { const context = entry.structure?.context; return context?.nodes.find(node => node.key === context.root)?.name ?? entry.target; }
|
|
615
670
|
function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) {
|
|
616
671
|
return _jsxs("section", { className: "panel history-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Run History" }), _jsx("span", { className: "panel-spacer" }), _jsxs("label", { className: "history-retention", children: ["Keep", _jsxs("select", { "aria-label": "Run history retention", value: retention, onChange: event => onRetentionChange(Number(event.target.value)), children: [_jsx("option", { value: "10", children: "10 runs" }), _jsx("option", { value: "20", children: "20 runs" }), _jsx("option", { value: "50", children: "50 runs" }), _jsx("option", { value: "100", children: "100 runs" })] })] }), _jsx("button", { className: "button", onClick: onClear, disabled: !entries.length, children: "Clear history" })] }), entries.length ? _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Stored runs", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Project" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "Outcome" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => {
|
|
@@ -618,12 +673,12 @@ function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete
|
|
|
618
673
|
const projectName = historyRecordName(entry);
|
|
619
674
|
const scope = entry.structure?.scope;
|
|
620
675
|
const scopeName = !scope ? 'Scope not recorded' : scope.kind === 'document' ? 'Entire project' : context?.nodes.find(node => node.key === scope.key)?.name ?? 'Unavailable';
|
|
621
|
-
const runKind = entry.result
|
|
622
|
-
return _jsxs("tr", { children: [_jsxs("td", { children: [_jsx("strong", { className: "history-project-name", children: projectName }), _jsxs("small", { className: "history-scope", children: [scope ? `Run only: ${scopeName}` : scopeName, runKind !== 'Normal run' && ` · ${runKind}`] }), entry.structure && entry.historyVersion !== 2 && _jsxs("small", { className: "history-meta", children: [entry.structure.context.coverage, " \u00B7 hash ", entry.structure.documentHash] })] }), _jsx("td", { children: _jsx("time", { dateTime: entry.startedAt, children: new Date(entry.startedAt).toLocaleString() }) }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: entry
|
|
676
|
+
const runKind = entry.result?.provenance ? 'Run From Here' : entry.result?.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run';
|
|
677
|
+
return _jsxs("tr", { children: [_jsxs("td", { children: [_jsx("strong", { className: "history-project-name", children: projectName }), _jsxs("small", { className: "history-scope", children: [scope ? `Run only: ${scopeName}` : scopeName, runKind !== 'Normal run' && ` · ${runKind}`] }), entry.structure && entry.historyVersion !== 2 && _jsxs("small", { className: "history-meta", children: [entry.structure.context.coverage, " \u00B7 hash ", entry.structure.documentHash] })] }), _jsx("td", { children: entry.startedAt ? _jsx("time", { dateTime: entry.startedAt, children: new Date(entry.startedAt).toLocaleString() }) : _jsxs(_Fragment, { children: ["Start not recorded", entry.admittedAt && _jsxs("small", { children: ["Admitted: ", new Date(entry.admittedAt).toLocaleString()] })] }) }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: historyOutcome(entry) }) }), _jsx("td", { children: entry.durationMs === undefined ? '—' : `${entry.durationMs} ms` }), _jsxs("td", { children: [_jsx("button", { className: "button", onClick: () => onReload(entry), children: "View results" }), _jsx("button", { className: "button", onClick: () => onExport(entry), children: "Export JSON" }), _jsx("button", { className: "button history-delete", onClick: () => onDelete(entry.requestId), children: "Delete" })] })] }, entry.requestId);
|
|
623
678
|
}) })] }) }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] });
|
|
624
679
|
}
|
|
625
680
|
function InspectorSection({ title, children }) { return _jsxs("section", { className: "inspector-section", children: [_jsx("h3", { children: title }), children] }); }
|
|
626
|
-
function KeyValues({ values }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
|
|
681
|
+
function KeyValues({ values, secretValues = [] }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values, secretValues).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
|
|
627
682
|
function IterationHistory({ result }) { return _jsxs(_Fragment, { children: [_jsx("div", { className: "iteration-history", children: result?.children?.length ? result.children.map((iteration, index) => _jsxs("div", { className: "iteration-entry", children: [_jsx("strong", { children: iteration.name }), _jsxs("span", { children: [iteration.state.toUpperCase(), " \u00B7 ", iteration.durationMs, " ms"] }), _jsx("small", { children: iteration.children?.map(child => `${child.name}: ${Object.entries(child.outputs).map(([key, value]) => `${key}=${String(value)}`).join(', ') || 'no outputs'}`).join(' · ') || 'No nested output.' })] }, `${iteration.id}-${index}`)) : _jsx("div", { className: "notice", children: "No iteration history available." }) }), result?.terminalReason && _jsxs("div", { className: "timing-details", children: [_jsx("strong", { children: "Terminal reason" }), _jsx("span", { children: result.terminalReason })] }), _jsx(TransportTiming, { timing: result?.transportTiming }), _jsx(BranchExecutionPath, { result: result }), _jsx(ParallelGroupDetails, { result: result }), _jsx(ResultSummary, { result: result })] }); }
|
|
628
683
|
function TransportTiming({ timing }) { return _jsxs("div", { className: "timing-details", children: [_jsx("strong", { children: timing ? 'Request timing' : 'Contract version' }), timing ? _jsxs(_Fragment, { children: [_jsxs("span", { children: ["Started ", new Date(timing.requestStartedAt).toLocaleTimeString()] }), _jsxs("span", { children: ["Response ", new Date(timing.responseReceivedAt).toLocaleTimeString()] }), _jsxs("span", { children: ["Transport duration ", timing.durationMs, " ms"] })] }) : _jsxs("span", { children: ["@qaick/contract ", RUNNER_CONTRACT_VERSION] })] }); }
|
|
629
684
|
function BranchExecutionPath({ result }) { const branches = result?.children?.filter(child => child.kind === 'control-block' && child.children?.length); if (!branches?.length)
|
|
@@ -638,7 +693,7 @@ function diagnosticCategory(code) { if (code === 'CONTRACT_INVALID' || code ===
|
|
|
638
693
|
return 'contract-invalid'; if (code === 'INVALID_BINDING' || code === 'MISSING_INPUT_BINDING' || code === 'MISSING_STEP_BINDING' || code === 'MISSING_STEP_OUTPUT' || code === 'BINDING_FAILED')
|
|
639
694
|
return 'binding-unresolved'; if (code.startsWith('HTTP_') || code === 'ACTION_FAILED' || code === 'MISSING_ACTION_ADAPTER')
|
|
640
695
|
return 'runtime-action-failure'; return 'preflight-invalid'; }
|
|
641
|
-
function InputField({ label, value, onChange }) { return _jsxs("label", { className: "input-field", children: [label, _jsx("input", { value: value, onChange: event => onChange(event.target.value) })] }); }
|
|
696
|
+
function InputField({ label, value, onChange }) { return _jsxs("label", { className: "input-field", children: [label, _jsx("input", { type: isSensitiveKey(label) ? 'password' : 'text', autoComplete: "off", value: value, onChange: event => onChange(event.target.value) })] }); }
|
|
642
697
|
function packageStepsFor(pack) { return pack.target.kind === 'project' ? pack.flows?.flatMap(flow => flow.steps) ?? [] : pack.steps; }
|
|
643
698
|
function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new Set()) {
|
|
644
699
|
return steps.flatMap(step => {
|