deepline 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +518 -230
- package/dist/cli/index.mjs +575 -282
- package/dist/index.d.mts +21 -58
- package/dist/index.d.ts +21 -58
- package/dist/index.js +148 -90
- package/dist/index.mjs +148 -90
- package/dist/repo/apps/play-runner-workers/src/entry.ts +147 -0
- package/dist/repo/sdk/src/client.ts +205 -120
- package/dist/repo/sdk/src/types.ts +8 -14
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/profiles.ts +4 -14
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -195,7 +195,7 @@ function resolveConfig(options) {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
// src/version.ts
|
|
198
|
-
var SDK_VERSION = "0.1.
|
|
198
|
+
var SDK_VERSION = "0.1.22";
|
|
199
199
|
var SDK_API_CONTRACT = "2026-05-runs-v2";
|
|
200
200
|
|
|
201
201
|
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
@@ -487,7 +487,7 @@ function isRecord(value) {
|
|
|
487
487
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
488
488
|
}
|
|
489
489
|
function normalizePlayStatus(raw) {
|
|
490
|
-
const status = typeof raw.status === "string" ? raw.status :
|
|
490
|
+
const status = typeof raw.status === "string" ? raw.status : "running";
|
|
491
491
|
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
492
492
|
return {
|
|
493
493
|
...raw,
|
|
@@ -495,23 +495,6 @@ function normalizePlayStatus(raw) {
|
|
|
495
495
|
status
|
|
496
496
|
};
|
|
497
497
|
}
|
|
498
|
-
function mapLegacyTemporalStatus(status) {
|
|
499
|
-
switch (status.trim().toUpperCase()) {
|
|
500
|
-
case "PENDING":
|
|
501
|
-
return "queued";
|
|
502
|
-
case "COMPLETED":
|
|
503
|
-
return "completed";
|
|
504
|
-
case "FAILED":
|
|
505
|
-
return "failed";
|
|
506
|
-
case "CANCELLED":
|
|
507
|
-
case "TERMINATED":
|
|
508
|
-
case "TIMED_OUT":
|
|
509
|
-
return "cancelled";
|
|
510
|
-
case "RUNNING":
|
|
511
|
-
default:
|
|
512
|
-
return "running";
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
498
|
function decodeBase64Bytes(value) {
|
|
516
499
|
const binary = atob(value);
|
|
517
500
|
const bytes = new Uint8Array(binary.length);
|
|
@@ -520,6 +503,79 @@ function decodeBase64Bytes(value) {
|
|
|
520
503
|
}
|
|
521
504
|
return bytes;
|
|
522
505
|
}
|
|
506
|
+
function readStringArray(value) {
|
|
507
|
+
return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
|
|
508
|
+
}
|
|
509
|
+
function getPlayLiveEventPayload(event) {
|
|
510
|
+
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
511
|
+
}
|
|
512
|
+
function normalizeLiveStatus(value) {
|
|
513
|
+
if (value === "queued" || value === "running" || value === "waiting" || value === "completed" || value === "failed" || value === "cancelled") {
|
|
514
|
+
return value;
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
function updatePlayLiveStatusState(state, event) {
|
|
519
|
+
const payload = getPlayLiveEventPayload(event);
|
|
520
|
+
if (event.type === "play.run.log") {
|
|
521
|
+
state.logs.push(...readStringArray(payload.lines));
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : state.runId;
|
|
528
|
+
const status = normalizeLiveStatus(payload.status) ?? state.status;
|
|
529
|
+
const logs = readStringArray(payload.logs);
|
|
530
|
+
if (logs.length > 0 || event.type === "play.run.snapshot") {
|
|
531
|
+
state.logs = logs;
|
|
532
|
+
}
|
|
533
|
+
if ("result" in payload) {
|
|
534
|
+
state.result = payload.result;
|
|
535
|
+
}
|
|
536
|
+
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
537
|
+
state.error = payload.error;
|
|
538
|
+
}
|
|
539
|
+
state.runId = runId;
|
|
540
|
+
state.status = status;
|
|
541
|
+
const progressRecord = payload.progress && typeof payload.progress === "object" && !Array.isArray(payload.progress) ? payload.progress : {};
|
|
542
|
+
const next = {
|
|
543
|
+
...payload,
|
|
544
|
+
runId,
|
|
545
|
+
status,
|
|
546
|
+
progress: {
|
|
547
|
+
...progressRecord,
|
|
548
|
+
status: typeof progressRecord.status === "string" ? progressRecord.status : status,
|
|
549
|
+
logs: state.logs,
|
|
550
|
+
...state.error ? { error: state.error } : {}
|
|
551
|
+
},
|
|
552
|
+
..."result" in state ? { result: state.result } : {}
|
|
553
|
+
};
|
|
554
|
+
state.latest = next;
|
|
555
|
+
return next;
|
|
556
|
+
}
|
|
557
|
+
function playRunResultFromStatus(status, startedAt, fallbackRunId) {
|
|
558
|
+
return {
|
|
559
|
+
success: status.status === "completed",
|
|
560
|
+
runId: status.runId || fallbackRunId,
|
|
561
|
+
result: status.result,
|
|
562
|
+
logs: status.progress?.logs ?? [],
|
|
563
|
+
durationMs: Date.now() - startedAt,
|
|
564
|
+
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function playRunStatusFromState(state) {
|
|
568
|
+
return {
|
|
569
|
+
runId: state.runId,
|
|
570
|
+
status: state.status,
|
|
571
|
+
progress: {
|
|
572
|
+
status: state.status,
|
|
573
|
+
logs: state.logs,
|
|
574
|
+
...state.error ? { error: state.error } : {}
|
|
575
|
+
},
|
|
576
|
+
..."result" in state ? { result: state.result } : {}
|
|
577
|
+
};
|
|
578
|
+
}
|
|
523
579
|
var DeeplineClient = class {
|
|
524
580
|
http;
|
|
525
581
|
config;
|
|
@@ -629,7 +685,7 @@ var DeeplineClient = class {
|
|
|
629
685
|
/**
|
|
630
686
|
* Search available tools using Deepline's ranked backend search.
|
|
631
687
|
*
|
|
632
|
-
* This is the same discovery surface used by the
|
|
688
|
+
* This is the same discovery surface used by the CLI: it ranks across
|
|
633
689
|
* tool metadata, categories, agent guidance, and input schema fields.
|
|
634
690
|
*/
|
|
635
691
|
async searchTools(options = {}) {
|
|
@@ -722,7 +778,7 @@ var DeeplineClient = class {
|
|
|
722
778
|
* `progress.logs`; they are not part of the user output object.
|
|
723
779
|
*
|
|
724
780
|
* @param request - Play run configuration (name, code, input, etc.)
|
|
725
|
-
* @returns
|
|
781
|
+
* @returns Run metadata including the public `workflowId`
|
|
726
782
|
*
|
|
727
783
|
* @example
|
|
728
784
|
* ```typescript
|
|
@@ -1014,9 +1070,6 @@ var DeeplineClient = class {
|
|
|
1014
1070
|
* Internal/advanced primitive. Public callers should usually prefer
|
|
1015
1071
|
* {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
|
|
1016
1072
|
*
|
|
1017
|
-
* Poll this method until `status` reaches a terminal state:
|
|
1018
|
-
* `'completed'`, `'failed'`, or `'cancelled'`.
|
|
1019
|
-
*
|
|
1020
1073
|
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
1021
1074
|
* @returns Current status with progress logs and partial results
|
|
1022
1075
|
*
|
|
@@ -1038,35 +1091,11 @@ var DeeplineClient = class {
|
|
|
1038
1091
|
);
|
|
1039
1092
|
return normalizePlayStatus(response);
|
|
1040
1093
|
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Get the lightweight tail-polling status for a play execution.
|
|
1043
|
-
*
|
|
1044
|
-
* This is intentionally smaller than {@link getPlayStatus}: it returns the
|
|
1045
|
-
* fields needed for CLI log tailing while the run is in flight, without
|
|
1046
|
-
* forcing the API to rebuild final result views on every poll. Call
|
|
1047
|
-
* {@link getPlayStatus} once after a terminal state for the full result.
|
|
1048
|
-
*/
|
|
1049
|
-
async getPlayTailStatus(workflowId, options) {
|
|
1050
|
-
const params = new URLSearchParams({ mode: "tail" });
|
|
1051
|
-
if (typeof options?.afterLogIndex === "number") {
|
|
1052
|
-
params.set("afterLogIndex", String(options.afterLogIndex));
|
|
1053
|
-
}
|
|
1054
|
-
if (typeof options?.waitMs === "number") {
|
|
1055
|
-
params.set("waitMs", String(options.waitMs));
|
|
1056
|
-
}
|
|
1057
|
-
if (options?.terminalOnly) {
|
|
1058
|
-
params.set("terminalOnly", "true");
|
|
1059
|
-
}
|
|
1060
|
-
const response = await this.http.get(
|
|
1061
|
-
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`
|
|
1062
|
-
);
|
|
1063
|
-
return normalizePlayStatus(response);
|
|
1064
|
-
}
|
|
1065
1094
|
/**
|
|
1066
1095
|
* Stream semantic play-run events using the same SSE feed as the dashboard.
|
|
1067
1096
|
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1097
|
+
* The server emits a canonical `play.run.snapshot` event first for every
|
|
1098
|
+
* connection, then incremental live events until terminal state or reconnect.
|
|
1070
1099
|
*/
|
|
1071
1100
|
async *streamPlayRunEvents(workflowId, options) {
|
|
1072
1101
|
const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
|
|
@@ -1086,7 +1115,7 @@ var DeeplineClient = class {
|
|
|
1086
1115
|
*
|
|
1087
1116
|
* Sends a stop request for the run.
|
|
1088
1117
|
*
|
|
1089
|
-
* @param workflowId -
|
|
1118
|
+
* @param workflowId - Public Deepline play-run id to cancel
|
|
1090
1119
|
*
|
|
1091
1120
|
* @example
|
|
1092
1121
|
* ```typescript
|
|
@@ -1102,7 +1131,7 @@ var DeeplineClient = class {
|
|
|
1102
1131
|
/**
|
|
1103
1132
|
* Stop a running play execution, including open HITL waits.
|
|
1104
1133
|
*
|
|
1105
|
-
* @param workflowId -
|
|
1134
|
+
* @param workflowId - Public Deepline play-run id to stop
|
|
1106
1135
|
* @param options.reason - Optional audit/debug reason
|
|
1107
1136
|
*/
|
|
1108
1137
|
async stopPlay(workflowId, options) {
|
|
@@ -1174,32 +1203,42 @@ var DeeplineClient = class {
|
|
|
1174
1203
|
);
|
|
1175
1204
|
return response.runs ?? [];
|
|
1176
1205
|
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Fetch the lightweight tail status for a run using the public runs resource model.
|
|
1179
|
-
*
|
|
1180
|
-
* This is the SDK equivalent of:
|
|
1181
|
-
*
|
|
1182
|
-
* ```bash
|
|
1183
|
-
* deepline runs tail <run-id> --json
|
|
1184
|
-
* ```
|
|
1185
|
-
*/
|
|
1206
|
+
/** Read the canonical run stream and return the latest run snapshot. */
|
|
1186
1207
|
async tailRun(runId, options) {
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1208
|
+
const state = {
|
|
1209
|
+
runId,
|
|
1210
|
+
status: "running",
|
|
1211
|
+
logs: [],
|
|
1212
|
+
latest: null
|
|
1213
|
+
};
|
|
1214
|
+
let terminal = false;
|
|
1215
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
1216
|
+
mode: "cli",
|
|
1217
|
+
signal: options?.signal
|
|
1218
|
+
})) {
|
|
1219
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1220
|
+
if (!status) {
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
terminal = TERMINAL_PLAY_STATUSES.has(status.status);
|
|
1224
|
+
if (terminal) {
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1191
1227
|
}
|
|
1192
|
-
if (
|
|
1193
|
-
|
|
1228
|
+
if (terminal && state.latest) {
|
|
1229
|
+
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
1230
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
1231
|
+
);
|
|
1194
1232
|
}
|
|
1195
|
-
if (
|
|
1196
|
-
|
|
1233
|
+
if (state.latest) {
|
|
1234
|
+
return state.latest;
|
|
1197
1235
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1236
|
+
throw new DeeplineError(
|
|
1237
|
+
`Run stream for ${runId} ended before the initial snapshot.`,
|
|
1238
|
+
void 0,
|
|
1239
|
+
"PLAY_RUN_STREAM_EMPTY",
|
|
1240
|
+
{ runId }
|
|
1201
1241
|
);
|
|
1202
|
-
return normalizePlayStatus(response);
|
|
1203
1242
|
}
|
|
1204
1243
|
/**
|
|
1205
1244
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
@@ -1356,11 +1395,11 @@ var DeeplineClient = class {
|
|
|
1356
1395
|
// Plays — high-level orchestration
|
|
1357
1396
|
// ——————————————————————————————————————————————————————————
|
|
1358
1397
|
/**
|
|
1359
|
-
* Run a play end-to-end: submit,
|
|
1398
|
+
* Run a play end-to-end: submit, stream until terminal, return result.
|
|
1360
1399
|
*
|
|
1361
1400
|
* This is the highest-level play execution method. It submits the play,
|
|
1362
|
-
*
|
|
1363
|
-
* and timing. Supports cancellation via `AbortSignal`.
|
|
1401
|
+
* reads the canonical run stream for status updates, and returns a structured
|
|
1402
|
+
* result with logs and timing. Supports cancellation via `AbortSignal`.
|
|
1364
1403
|
*
|
|
1365
1404
|
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
1366
1405
|
* @param csvPath - Input CSV path, or `null`
|
|
@@ -1376,7 +1415,6 @@ var DeeplineClient = class {
|
|
|
1376
1415
|
* const logs = status.progress?.logs ?? [];
|
|
1377
1416
|
* console.log(`[${status.status}] ${logs.length} log lines`);
|
|
1378
1417
|
* },
|
|
1379
|
-
* pollIntervalMs: 1000,
|
|
1380
1418
|
* });
|
|
1381
1419
|
*
|
|
1382
1420
|
* if (result.success) {
|
|
@@ -1407,33 +1445,53 @@ var DeeplineClient = class {
|
|
|
1407
1445
|
packagedFiles: options?.packagedFiles,
|
|
1408
1446
|
force: options?.force
|
|
1409
1447
|
});
|
|
1410
|
-
const pollInterval = options?.pollIntervalMs ?? 500;
|
|
1411
1448
|
const start = Date.now();
|
|
1412
|
-
|
|
1449
|
+
const state = {
|
|
1450
|
+
runId: workflowId,
|
|
1451
|
+
status: "running",
|
|
1452
|
+
logs: [],
|
|
1453
|
+
latest: null
|
|
1454
|
+
};
|
|
1455
|
+
if (options?.signal?.aborted) {
|
|
1456
|
+
await this.cancelPlay(workflowId);
|
|
1457
|
+
return {
|
|
1458
|
+
success: false,
|
|
1459
|
+
runId: workflowId,
|
|
1460
|
+
logs: [],
|
|
1461
|
+
durationMs: Date.now() - start,
|
|
1462
|
+
error: "Cancelled by user"
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
for await (const event of this.streamPlayRunEvents(workflowId, {
|
|
1466
|
+
mode: "cli",
|
|
1467
|
+
signal: options?.signal
|
|
1468
|
+
})) {
|
|
1413
1469
|
if (options?.signal?.aborted) {
|
|
1414
1470
|
await this.cancelPlay(workflowId);
|
|
1415
1471
|
return {
|
|
1416
1472
|
success: false,
|
|
1417
1473
|
runId: workflowId,
|
|
1418
|
-
logs:
|
|
1474
|
+
logs: state.logs,
|
|
1419
1475
|
durationMs: Date.now() - start,
|
|
1420
1476
|
error: "Cancelled by user"
|
|
1421
1477
|
};
|
|
1422
1478
|
}
|
|
1423
|
-
const status =
|
|
1479
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
1480
|
+
if (!status) {
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1424
1483
|
options?.onProgress?.(status);
|
|
1425
1484
|
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
runId: status.runId || workflowId,
|
|
1429
|
-
result: status.result,
|
|
1430
|
-
logs: status.progress?.logs ?? [],
|
|
1431
|
-
durationMs: Date.now() - start,
|
|
1432
|
-
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
1433
|
-
};
|
|
1485
|
+
const finalStatus = await this.getPlayStatus(status.runId || workflowId).catch(() => status);
|
|
1486
|
+
return playRunResultFromStatus(finalStatus, start, workflowId);
|
|
1434
1487
|
}
|
|
1435
|
-
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
1436
1488
|
}
|
|
1489
|
+
throw new DeeplineError(
|
|
1490
|
+
`Run stream for ${workflowId} ended before the run reached a terminal state.`,
|
|
1491
|
+
void 0,
|
|
1492
|
+
"PLAY_RUN_STREAM_ENDED",
|
|
1493
|
+
{ runId: workflowId, workflowId }
|
|
1494
|
+
);
|
|
1437
1495
|
}
|
|
1438
1496
|
// ——————————————————————————————————————————————————————————
|
|
1439
1497
|
// Health
|
|
@@ -524,6 +524,26 @@ type WorkflowRunOutput = {
|
|
|
524
524
|
durationMs: number;
|
|
525
525
|
};
|
|
526
526
|
|
|
527
|
+
type LiveNodeProgressSnapshot = {
|
|
528
|
+
completed?: number;
|
|
529
|
+
total?: number;
|
|
530
|
+
failed?: number;
|
|
531
|
+
message?: string;
|
|
532
|
+
updatedAt?: number;
|
|
533
|
+
startedAt?: number;
|
|
534
|
+
completedAt?: number;
|
|
535
|
+
artifactTableNamespace?: string | null;
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
type LiveNodeProgressMap = Record<string, LiveNodeProgressSnapshot>;
|
|
539
|
+
|
|
540
|
+
type WorkerCtxCallbacks = {
|
|
541
|
+
onNodeProgress?: (input: {
|
|
542
|
+
nodeId: string;
|
|
543
|
+
progress: LiveNodeProgressSnapshot;
|
|
544
|
+
}) => void;
|
|
545
|
+
};
|
|
546
|
+
|
|
527
547
|
function nowMs(): number {
|
|
528
548
|
return Date.now();
|
|
529
549
|
}
|
|
@@ -1092,6 +1112,29 @@ async function callToolDirect(
|
|
|
1092
1112
|
let lastError: Error | null = null;
|
|
1093
1113
|
|
|
1094
1114
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1115
|
+
if (toolId === 'test_transient_500' || toolId === 'test_transient_429') {
|
|
1116
|
+
const syntheticResult = executeSyntheticTransientRetry(
|
|
1117
|
+
toolId,
|
|
1118
|
+
input,
|
|
1119
|
+
attempt,
|
|
1120
|
+
);
|
|
1121
|
+
if (syntheticResult.ok) {
|
|
1122
|
+
return wrapWorkerToolResult(
|
|
1123
|
+
toolId,
|
|
1124
|
+
syntheticResult.result,
|
|
1125
|
+
syntheticToolMetadata(toolId),
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
lastError = new Error(
|
|
1129
|
+
`tool ${toolId} ${syntheticResult.status} attempt ${attempt}/${maxAttempts}: ${syntheticResult.message}`,
|
|
1130
|
+
);
|
|
1131
|
+
if (attempt >= maxAttempts) {
|
|
1132
|
+
throw lastError;
|
|
1133
|
+
}
|
|
1134
|
+
await new Promise((resolve) => setTimeout(resolve, 1_000));
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1095
1138
|
const res = await fetchRuntimeApi(req.baseUrl, path, {
|
|
1096
1139
|
method: 'POST',
|
|
1097
1140
|
headers: {
|
|
@@ -1277,6 +1320,41 @@ async function executeSyntheticTestRateLimitBatch(
|
|
|
1277
1320
|
};
|
|
1278
1321
|
}
|
|
1279
1322
|
|
|
1323
|
+
type SyntheticTransientRetryResult =
|
|
1324
|
+
| { ok: true; result: Record<string, unknown> }
|
|
1325
|
+
| { ok: false; status: number; message: string };
|
|
1326
|
+
|
|
1327
|
+
function executeSyntheticTransientRetry(
|
|
1328
|
+
toolId: string,
|
|
1329
|
+
input: Record<string, unknown>,
|
|
1330
|
+
attempt: number,
|
|
1331
|
+
): SyntheticTransientRetryResult {
|
|
1332
|
+
const failuresBeforeSuccess =
|
|
1333
|
+
typeof input.failures_before_success === 'number' &&
|
|
1334
|
+
Number.isInteger(input.failures_before_success) &&
|
|
1335
|
+
input.failures_before_success >= 0
|
|
1336
|
+
? input.failures_before_success
|
|
1337
|
+
: 1;
|
|
1338
|
+
if (attempt <= failuresBeforeSuccess) {
|
|
1339
|
+
const status = toolId === 'test_transient_429' ? 429 : 502;
|
|
1340
|
+
return {
|
|
1341
|
+
ok: false,
|
|
1342
|
+
status,
|
|
1343
|
+
message: `Synthetic transient ${status} for attempt ${attempt}`,
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
return {
|
|
1347
|
+
ok: true,
|
|
1348
|
+
result: {
|
|
1349
|
+
status: 'completed',
|
|
1350
|
+
provider: 'test',
|
|
1351
|
+
key: String(input.key ?? 'transient'),
|
|
1352
|
+
attempts: attempt,
|
|
1353
|
+
recovered: attempt > 1,
|
|
1354
|
+
},
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1280
1358
|
function executeSyntheticTestRateLimit(
|
|
1281
1359
|
input: Record<string, unknown>,
|
|
1282
1360
|
): Record<string, unknown> {
|
|
@@ -2561,6 +2639,7 @@ function createMinimalWorkerCtx(
|
|
|
2561
2639
|
env: WorkerEnv,
|
|
2562
2640
|
workflowStep?: WorkflowStep,
|
|
2563
2641
|
abortSignal?: AbortSignal,
|
|
2642
|
+
callbacks?: WorkerCtxCallbacks,
|
|
2564
2643
|
): unknown {
|
|
2565
2644
|
let playCallCount = 0;
|
|
2566
2645
|
const parentChildCalls: Record<string, number> = {};
|
|
@@ -2635,6 +2714,7 @@ function createMinimalWorkerCtx(
|
|
|
2635
2714
|
opts?: WorkerMapOptions,
|
|
2636
2715
|
): Promise<unknown> => {
|
|
2637
2716
|
const mapStartedAt = nowMs();
|
|
2717
|
+
const mapNodeId = `map:${name}`;
|
|
2638
2718
|
const sliced = rows;
|
|
2639
2719
|
const baseOffset = 0;
|
|
2640
2720
|
const fieldEntries = Object.entries(fieldsDef);
|
|
@@ -2655,6 +2735,30 @@ function createMinimalWorkerCtx(
|
|
|
2655
2735
|
softWorkflowStepBudget: plan?.chunkPlan.softWorkflowStepBudget,
|
|
2656
2736
|
});
|
|
2657
2737
|
const outputFields = fieldEntries.map(([field]) => field);
|
|
2738
|
+
const updateMapProgress = (progress: LiveNodeProgressSnapshot) => {
|
|
2739
|
+
callbacks?.onNodeProgress?.({
|
|
2740
|
+
nodeId: mapNodeId,
|
|
2741
|
+
progress: {
|
|
2742
|
+
artifactTableNamespace: name,
|
|
2743
|
+
failed: 0,
|
|
2744
|
+
...progress,
|
|
2745
|
+
updatedAt: progress.updatedAt ?? nowMs(),
|
|
2746
|
+
},
|
|
2747
|
+
});
|
|
2748
|
+
};
|
|
2749
|
+
const formatMapProgressMessage = (completed: number, total?: number) =>
|
|
2750
|
+
typeof total === 'number' && Number.isFinite(total) && total > 0
|
|
2751
|
+
? `${completed.toLocaleString()} / ${total.toLocaleString()} rows processed`
|
|
2752
|
+
: `${completed.toLocaleString()} rows processed`;
|
|
2753
|
+
updateMapProgress({
|
|
2754
|
+
completed: 0,
|
|
2755
|
+
total: streaming ? undefined : sliced.length,
|
|
2756
|
+
startedAt: mapStartedAt,
|
|
2757
|
+
message: formatMapProgressMessage(
|
|
2758
|
+
0,
|
|
2759
|
+
streaming ? undefined : sliced.length,
|
|
2760
|
+
),
|
|
2761
|
+
});
|
|
2658
2762
|
const explicitRowKeysSeen =
|
|
2659
2763
|
opts?.key === undefined ? null : new Map<string, number>();
|
|
2660
2764
|
const resolveExplicitKeyValue = (
|
|
@@ -3156,6 +3260,14 @@ function createMinimalWorkerCtx(
|
|
|
3156
3260
|
`Map completed: ${totalRowsWritten} results ` +
|
|
3157
3261
|
`(${totalRowsExecuted} executed, ${totalRowsCached} already satisfied) ` +
|
|
3158
3262
|
`inserted=${totalRowsInserted} skipped=${totalRowsSkipped}`;
|
|
3263
|
+
const completedAt = nowMs();
|
|
3264
|
+
updateMapProgress({
|
|
3265
|
+
completed: totalRowsWritten,
|
|
3266
|
+
total: totalRowsWritten,
|
|
3267
|
+
completedAt,
|
|
3268
|
+
updatedAt: completedAt,
|
|
3269
|
+
message: formatMapProgressMessage(totalRowsWritten, totalRowsWritten),
|
|
3270
|
+
});
|
|
3159
3271
|
emitEvent({
|
|
3160
3272
|
type: 'log',
|
|
3161
3273
|
level: 'info',
|
|
@@ -3199,6 +3311,10 @@ function createMinimalWorkerCtx(
|
|
|
3199
3311
|
totalRowsDuplicateReused += chunkResult.rowsDuplicateReused;
|
|
3200
3312
|
totalRowsInserted += chunkResult.rowsInserted;
|
|
3201
3313
|
totalRowsSkipped += chunkResult.rowsSkipped;
|
|
3314
|
+
updateMapProgress({
|
|
3315
|
+
completed: totalRowsWritten,
|
|
3316
|
+
message: formatMapProgressMessage(totalRowsWritten),
|
|
3317
|
+
});
|
|
3202
3318
|
if (out.length < 10) {
|
|
3203
3319
|
out.push(...chunkResult.preview.slice(0, 10 - out.length));
|
|
3204
3320
|
}
|
|
@@ -3230,6 +3346,11 @@ function createMinimalWorkerCtx(
|
|
|
3230
3346
|
totalRowsDuplicateReused += chunkResult.rowsDuplicateReused;
|
|
3231
3347
|
totalRowsInserted += chunkResult.rowsInserted;
|
|
3232
3348
|
totalRowsSkipped += chunkResult.rowsSkipped;
|
|
3349
|
+
updateMapProgress({
|
|
3350
|
+
completed: totalRowsWritten,
|
|
3351
|
+
total: sliced.length,
|
|
3352
|
+
message: formatMapProgressMessage(totalRowsWritten, sliced.length),
|
|
3353
|
+
});
|
|
3233
3354
|
if (out.length < 10) {
|
|
3234
3355
|
out.push(...chunkResult.preview.slice(0, 10 - out.length));
|
|
3235
3356
|
}
|
|
@@ -3252,6 +3373,11 @@ function createMinimalWorkerCtx(
|
|
|
3252
3373
|
totalRowsInserted = chunkResult.rowsInserted;
|
|
3253
3374
|
totalRowsSkipped = chunkResult.rowsSkipped;
|
|
3254
3375
|
out.push(...chunkResult.preview);
|
|
3376
|
+
updateMapProgress({
|
|
3377
|
+
completed: chunkResult.rowsWritten,
|
|
3378
|
+
total: sliced.length,
|
|
3379
|
+
message: formatMapProgressMessage(chunkResult.rowsWritten, sliced.length),
|
|
3380
|
+
});
|
|
3255
3381
|
const dataset = finalize(chunkResult.rowsWritten);
|
|
3256
3382
|
recordRunnerPerfTrace({
|
|
3257
3383
|
req,
|
|
@@ -3919,6 +4045,7 @@ async function executeRunRequest(
|
|
|
3919
4045
|
// sees the final terminal status with no intermediate logs/progress.
|
|
3920
4046
|
let liveLogs: string[] = [];
|
|
3921
4047
|
let liveLogsDirty = false;
|
|
4048
|
+
let liveNodeProgress: LiveNodeProgressMap = {};
|
|
3922
4049
|
let lastLiveLogFlushAt =
|
|
3923
4050
|
nowMs() - LIVE_LOG_FLUSH_INTERVAL_MS + LIVE_LOG_FIRST_FLUSH_DELAY_MS;
|
|
3924
4051
|
let liveLogFlushInFlight: Promise<void> = Promise.resolve();
|
|
@@ -3928,6 +4055,21 @@ async function executeRunRequest(
|
|
|
3928
4055
|
liveLogs = [...liveLogs, trimmed].slice(-LIVE_LOG_BUFFER_LIMIT);
|
|
3929
4056
|
liveLogsDirty = true;
|
|
3930
4057
|
};
|
|
4058
|
+
const updateLiveNodeProgress = (input: {
|
|
4059
|
+
nodeId: string;
|
|
4060
|
+
progress: LiveNodeProgressSnapshot;
|
|
4061
|
+
}) => {
|
|
4062
|
+
const nodeId = input.nodeId.trim();
|
|
4063
|
+
if (!nodeId) return;
|
|
4064
|
+
liveNodeProgress = {
|
|
4065
|
+
...liveNodeProgress,
|
|
4066
|
+
[nodeId]: {
|
|
4067
|
+
...(liveNodeProgress[nodeId] ?? {}),
|
|
4068
|
+
...input.progress,
|
|
4069
|
+
},
|
|
4070
|
+
};
|
|
4071
|
+
};
|
|
4072
|
+
const liveNodeProgressSnapshot = () => ({ ...liveNodeProgress });
|
|
3931
4073
|
const flushLiveLogs = (force: boolean): void => {
|
|
3932
4074
|
if (!options?.persistResultDatasets) return;
|
|
3933
4075
|
if (!liveLogsDirty && !force) return;
|
|
@@ -3946,6 +4088,7 @@ async function executeRunRequest(
|
|
|
3946
4088
|
status: 'running',
|
|
3947
4089
|
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
3948
4090
|
liveLogs: snapshot,
|
|
4091
|
+
liveNodeProgress: liveNodeProgressSnapshot(),
|
|
3949
4092
|
lastCheckpointAt: now,
|
|
3950
4093
|
});
|
|
3951
4094
|
} catch {
|
|
@@ -3975,6 +4118,7 @@ async function executeRunRequest(
|
|
|
3975
4118
|
env,
|
|
3976
4119
|
workflowStep,
|
|
3977
4120
|
abortSignal,
|
|
4121
|
+
{ onNodeProgress: updateLiveNodeProgress },
|
|
3978
4122
|
);
|
|
3979
4123
|
try {
|
|
3980
4124
|
const playStartedAt = nowMs();
|
|
@@ -4017,12 +4161,14 @@ async function executeRunRequest(
|
|
|
4017
4161
|
action: 'update_run_status',
|
|
4018
4162
|
playId: req.runId,
|
|
4019
4163
|
status: 'completed',
|
|
4164
|
+
error: null,
|
|
4020
4165
|
result: terminalResult,
|
|
4021
4166
|
runtimeBackend: 'cf_workflows_dynamic_worker',
|
|
4022
4167
|
waitKind: null,
|
|
4023
4168
|
waitUntil: null,
|
|
4024
4169
|
activeBoundaryId: null,
|
|
4025
4170
|
liveLogs,
|
|
4171
|
+
liveNodeProgress: liveNodeProgressSnapshot(),
|
|
4026
4172
|
lastCheckpointAt: nowMs(),
|
|
4027
4173
|
});
|
|
4028
4174
|
recordRunnerPerfTrace({
|
|
@@ -4092,6 +4238,7 @@ async function executeRunRequest(
|
|
|
4092
4238
|
waitUntil: null,
|
|
4093
4239
|
activeBoundaryId: null,
|
|
4094
4240
|
liveLogs,
|
|
4241
|
+
liveNodeProgress: liveNodeProgressSnapshot(),
|
|
4095
4242
|
lastCheckpointAt: nowMs(),
|
|
4096
4243
|
});
|
|
4097
4244
|
await finalizeWorkerComputeBilling({
|