deepline 0.1.23 → 0.1.24
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 +506 -49
- package/dist/cli/index.mjs +506 -49
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +95 -81
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +18 -5
- package/dist/repo/apps/play-runner-workers/src/entry.ts +37 -32
- package/dist/repo/apps/play-runner-workers/src/runtime/live-progress.ts +18 -0
- package/dist/repo/sdk/src/plays/harness-stub.ts +2 -0
- package/dist/repo/sdk/src/version.ts +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
COORDINATOR_INTERNAL_TOKEN_HEADER,
|
|
34
34
|
COORDINATOR_RUN_SCOPE_HEADER,
|
|
35
35
|
} from '../../../shared_libs/play-runtime/coordinator-headers';
|
|
36
|
+
import { sanitizeLiveLogLines } from './runtime/live-progress';
|
|
36
37
|
|
|
37
38
|
export { DynamicWorkflowBinding };
|
|
38
39
|
|
|
@@ -125,6 +126,8 @@ type CoordinatorTerminalState = {
|
|
|
125
126
|
totalRows?: unknown;
|
|
126
127
|
durationMs?: unknown;
|
|
127
128
|
playName?: string | null;
|
|
129
|
+
liveLogs?: string[] | null;
|
|
130
|
+
liveNodeProgress?: unknown;
|
|
128
131
|
completedAt?: number;
|
|
129
132
|
};
|
|
130
133
|
|
|
@@ -154,6 +157,7 @@ type CoordinatorRunEvent =
|
|
|
154
157
|
activeNodeId?: string | null;
|
|
155
158
|
activeArtifactTableNamespace?: string | null;
|
|
156
159
|
updatedAt?: number | null;
|
|
160
|
+
liveNodeProgress?: unknown;
|
|
157
161
|
}
|
|
158
162
|
| {
|
|
159
163
|
seq?: number;
|
|
@@ -166,6 +170,8 @@ type CoordinatorRunEvent =
|
|
|
166
170
|
totalRows?: unknown;
|
|
167
171
|
durationMs?: unknown;
|
|
168
172
|
playName?: string | null;
|
|
173
|
+
liveLogs?: string[] | null;
|
|
174
|
+
liveNodeProgress?: unknown;
|
|
169
175
|
};
|
|
170
176
|
|
|
171
177
|
type InlineWorkerRunResponse = {
|
|
@@ -229,12 +235,7 @@ interface CoordinatorEnv {
|
|
|
229
235
|
HARNESS?: import('../../play-harness-worker/src/rpc-types').PlayHarnessRpc;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
const WORKFLOW_READ_ONLY_ACTIONS = new Set([
|
|
233
|
-
'',
|
|
234
|
-
'result',
|
|
235
|
-
'status',
|
|
236
|
-
'tail',
|
|
237
|
-
]);
|
|
238
|
+
const WORKFLOW_READ_ONLY_ACTIONS = new Set(['', 'result', 'status', 'tail']);
|
|
238
239
|
|
|
239
240
|
function authorizeCoordinatorControlRequest(input: {
|
|
240
241
|
request: Request;
|
|
@@ -411,11 +412,14 @@ async function appendCoordinatorRunEvent(
|
|
|
411
412
|
event: CoordinatorRunEvent,
|
|
412
413
|
): Promise<void> {
|
|
413
414
|
const stub = env.PLAY_DEDUP.get(env.PLAY_DEDUP.idFromName(event.runId));
|
|
414
|
-
const response = await stub.fetch(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
415
|
+
const response = await stub.fetch(
|
|
416
|
+
'https://deepline.dedup.internal/event-add',
|
|
417
|
+
{
|
|
418
|
+
method: 'POST',
|
|
419
|
+
headers: { 'content-type': 'application/json' },
|
|
420
|
+
body: JSON.stringify(event),
|
|
421
|
+
},
|
|
422
|
+
);
|
|
419
423
|
if (!response.ok) {
|
|
420
424
|
throw new Error(`coordinator event append failed ${response.status}`);
|
|
421
425
|
}
|
|
@@ -455,7 +459,8 @@ async function listCoordinatorRunEvents(input: {
|
|
|
455
459
|
typeof event.ts === 'number',
|
|
456
460
|
)
|
|
457
461
|
: [],
|
|
458
|
-
latestSeq:
|
|
462
|
+
latestSeq:
|
|
463
|
+
typeof body.latestSeq === 'number' ? body.latestSeq : input.afterSeq,
|
|
459
464
|
};
|
|
460
465
|
}
|
|
461
466
|
|
|
@@ -546,7 +551,9 @@ function buildDynamicWorkflowMetadata(
|
|
|
546
551
|
};
|
|
547
552
|
}
|
|
548
553
|
|
|
549
|
-
function buildDispatcherEnvelope(
|
|
554
|
+
function buildDispatcherEnvelope(
|
|
555
|
+
params: PlayWorkflowParams,
|
|
556
|
+
): DispatcherEnvelope {
|
|
550
557
|
// Mirrors @cloudflare/dynamic-workflows' envelope. We need to send the
|
|
551
558
|
// dispatcher payload via Workflow sendEvent for prewarmed instances; the
|
|
552
559
|
// public wrapper only applies this envelope to create() params.
|
|
@@ -739,7 +746,9 @@ type WorkflowPoolListEntry = {
|
|
|
739
746
|
expiresAt: number;
|
|
740
747
|
};
|
|
741
748
|
|
|
742
|
-
async function workflowPoolCount(
|
|
749
|
+
async function workflowPoolCount(
|
|
750
|
+
env: CoordinatorEnv,
|
|
751
|
+
): Promise<WorkflowPoolCounts> {
|
|
743
752
|
const body = await callWorkflowPool<{
|
|
744
753
|
available?: unknown;
|
|
745
754
|
warming?: unknown;
|
|
@@ -810,17 +819,13 @@ async function markWorkflowPoolIdReady(
|
|
|
810
819
|
env: CoordinatorEnv,
|
|
811
820
|
poolId: string,
|
|
812
821
|
): Promise<boolean> {
|
|
813
|
-
const body = await callWorkflowPool<{ ready?: unknown }>(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
version: WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
821
|
-
}),
|
|
822
|
-
},
|
|
823
|
-
);
|
|
822
|
+
const body = await callWorkflowPool<{ ready?: unknown }>(env, '/pool-ready', {
|
|
823
|
+
method: 'POST',
|
|
824
|
+
body: JSON.stringify({
|
|
825
|
+
poolId,
|
|
826
|
+
version: WORKFLOW_POOL_PROTOCOL_VERSION,
|
|
827
|
+
}),
|
|
828
|
+
});
|
|
824
829
|
return body.ready === true;
|
|
825
830
|
}
|
|
826
831
|
|
|
@@ -948,7 +953,9 @@ async function waitForWorkflowPoolReadySignal(input: {
|
|
|
948
953
|
polls += 1;
|
|
949
954
|
const [entry, status] = await Promise.all([
|
|
950
955
|
listWorkflowPoolEntries(input.env)
|
|
951
|
-
.then((entries) =>
|
|
956
|
+
.then((entries) =>
|
|
957
|
+
entries.find((candidate) => candidate.id === input.poolId),
|
|
958
|
+
)
|
|
952
959
|
.catch(() => undefined),
|
|
953
960
|
input.instance.status().catch(() => null),
|
|
954
961
|
]);
|
|
@@ -1206,7 +1213,8 @@ async function submitViaPooledWorkflow(input: {
|
|
|
1206
1213
|
if (!pooledInstanceId) {
|
|
1207
1214
|
// A pool miss must not block the user path. Refilling is handled by the
|
|
1208
1215
|
// caller's waitUntil after submit, so fall through to cold create now.
|
|
1209
|
-
const counts =
|
|
1216
|
+
const counts =
|
|
1217
|
+
missCounts ?? (await workflowPoolCount(input.env).catch(() => null));
|
|
1210
1218
|
input.recordSubmitTiming({
|
|
1211
1219
|
phase: 'coordinator.workflow_pool_refill_on_miss',
|
|
1212
1220
|
ms: 0,
|
|
@@ -1404,7 +1412,9 @@ async function mintChildWorkflowExecutorToken(input: {
|
|
|
1404
1412
|
}
|
|
1405
1413
|
const executorToken = parsed.executorToken;
|
|
1406
1414
|
if (typeof executorToken !== 'string' || !executorToken.trim()) {
|
|
1407
|
-
throw new Error(
|
|
1415
|
+
throw new Error(
|
|
1416
|
+
'Origin child executor token response was missing executorToken.',
|
|
1417
|
+
);
|
|
1408
1418
|
}
|
|
1409
1419
|
return executorToken;
|
|
1410
1420
|
}
|
|
@@ -1447,10 +1457,15 @@ function normalizeRuntimeBaseUrl(value: unknown): string | null {
|
|
|
1447
1457
|
return parsed.toString().replace(/\/$/, '');
|
|
1448
1458
|
}
|
|
1449
1459
|
|
|
1450
|
-
function resolveRuntimeBaseUrl(
|
|
1451
|
-
|
|
1460
|
+
function resolveRuntimeBaseUrl(
|
|
1461
|
+
env: CoordinatorEnv,
|
|
1462
|
+
body: Record<string, unknown>,
|
|
1463
|
+
): string {
|
|
1464
|
+
return (
|
|
1465
|
+
normalizeRuntimeBaseUrl(body.callbackBaseUrl) ??
|
|
1452
1466
|
normalizeRuntimeBaseUrl(body.baseUrl) ??
|
|
1453
|
-
env.DEEPLINE_API_BASE_URL.replace(/\/$/, '')
|
|
1467
|
+
env.DEEPLINE_API_BASE_URL.replace(/\/$/, '')
|
|
1468
|
+
);
|
|
1454
1469
|
}
|
|
1455
1470
|
|
|
1456
1471
|
function validateChildSubmitBody(input: {
|
|
@@ -1603,7 +1618,9 @@ function buildChildWorkflowParams(input: {
|
|
|
1603
1618
|
};
|
|
1604
1619
|
}
|
|
1605
1620
|
|
|
1606
|
-
function runRequestFromPlayWorkflowParams(
|
|
1621
|
+
function runRequestFromPlayWorkflowParams(
|
|
1622
|
+
params: PlayWorkflowParams,
|
|
1623
|
+
): Record<string, unknown> {
|
|
1607
1624
|
return {
|
|
1608
1625
|
runId: params.runId,
|
|
1609
1626
|
callbackUrl: params.baseUrl,
|
|
@@ -1616,7 +1633,8 @@ function runRequestFromPlayWorkflowParams(params: PlayWorkflowParams): Record<st
|
|
|
1616
1633
|
runtimeInput: params.input,
|
|
1617
1634
|
inlineCsv: params.inlineCsv ?? null,
|
|
1618
1635
|
inputR2Keys:
|
|
1619
|
-
params.inputFile?.r2Key &&
|
|
1636
|
+
params.inputFile?.r2Key &&
|
|
1637
|
+
(params.inputFile.name || params.inputFile.path)
|
|
1620
1638
|
? {
|
|
1621
1639
|
[String(params.inputFile.name ?? params.inputFile.path)]:
|
|
1622
1640
|
params.inputFile.r2Key,
|
|
@@ -1720,13 +1738,8 @@ async function executeChildInline(input: {
|
|
|
1720
1738
|
if (!validated.ok) {
|
|
1721
1739
|
throw new Error(String(validated.error.message ?? 'Invalid child submit.'));
|
|
1722
1740
|
}
|
|
1723
|
-
const {
|
|
1724
|
-
|
|
1725
|
-
governance,
|
|
1726
|
-
childPlayName,
|
|
1727
|
-
orgId,
|
|
1728
|
-
parentExecutorToken,
|
|
1729
|
-
} = validated;
|
|
1741
|
+
const { manifest, governance, childPlayName, orgId, parentExecutorToken } =
|
|
1742
|
+
validated;
|
|
1730
1743
|
const childRunId = buildChildRunId(childPlayName);
|
|
1731
1744
|
const timings: CoordinatorTiming[] = [];
|
|
1732
1745
|
const trace = (
|
|
@@ -1801,9 +1814,7 @@ async function executeChildInline(input: {
|
|
|
1801
1814
|
artifactStorageKey: manifest.artifactStorageKey,
|
|
1802
1815
|
artifactHash: manifest.artifactHash,
|
|
1803
1816
|
dynamicWorkerCode:
|
|
1804
|
-
typeof manifest.bundledCode === 'string'
|
|
1805
|
-
? manifest.bundledCode
|
|
1806
|
-
: null,
|
|
1817
|
+
typeof manifest.bundledCode === 'string' ? manifest.bundledCode : null,
|
|
1807
1818
|
packagedFiles: null,
|
|
1808
1819
|
});
|
|
1809
1820
|
trace('coordinator.inline_child_loader_get', loaderStartedAt);
|
|
@@ -2027,9 +2038,7 @@ export class RuntimeApi extends WorkerEntrypoint<CoordinatorEnv, undefined> {
|
|
|
2027
2038
|
type: 'progress',
|
|
2028
2039
|
status,
|
|
2029
2040
|
ts: Date.now(),
|
|
2030
|
-
logs:
|
|
2031
|
-
? body.liveLogs.filter((line): line is string => typeof line === 'string')
|
|
2032
|
-
: undefined,
|
|
2041
|
+
logs: sanitizeLiveLogLines(body.liveLogs) ?? undefined,
|
|
2033
2042
|
activeNodeId:
|
|
2034
2043
|
typeof body.activeNodeId === 'string' ? body.activeNodeId : null,
|
|
2035
2044
|
activeArtifactTableNamespace:
|
|
@@ -2040,6 +2049,8 @@ export class RuntimeApi extends WorkerEntrypoint<CoordinatorEnv, undefined> {
|
|
|
2040
2049
|
typeof body.lastCheckpointAt === 'number'
|
|
2041
2050
|
? body.lastCheckpointAt
|
|
2042
2051
|
: null,
|
|
2052
|
+
liveNodeProgress:
|
|
2053
|
+
body.liveNodeProgress !== undefined ? body.liveNodeProgress : undefined,
|
|
2043
2054
|
});
|
|
2044
2055
|
}
|
|
2045
2056
|
}
|
|
@@ -2110,7 +2121,10 @@ export class CoordinatorControl extends WorkerEntrypoint<
|
|
|
2110
2121
|
await appendCoordinatorPerfTrace(this.env, payload);
|
|
2111
2122
|
}
|
|
2112
2123
|
|
|
2113
|
-
async recordRunEvent(
|
|
2124
|
+
async recordRunEvent(
|
|
2125
|
+
runId: string,
|
|
2126
|
+
event: CoordinatorRunEvent,
|
|
2127
|
+
): Promise<void> {
|
|
2114
2128
|
if (!runId || event.runId !== runId) {
|
|
2115
2129
|
throw new Error('Run event runId mismatch.');
|
|
2116
2130
|
}
|
|
@@ -2280,9 +2294,9 @@ export class DynamicWorkflow extends WorkflowEntrypoint<
|
|
|
2280
2294
|
totalRows: output?.totalRows ?? output?.outputRows ?? null,
|
|
2281
2295
|
durationMs: output?.durationMs ?? null,
|
|
2282
2296
|
playName:
|
|
2283
|
-
typeof output?.playName === 'string'
|
|
2284
|
-
|
|
2285
|
-
|
|
2297
|
+
typeof output?.playName === 'string' ? output.playName : null,
|
|
2298
|
+
liveLogs: sanitizeLiveLogLines(output?.liveLogs),
|
|
2299
|
+
liveNodeProgress: output?.liveNodeProgress ?? null,
|
|
2286
2300
|
});
|
|
2287
2301
|
trace({
|
|
2288
2302
|
runId: runIdForTrace,
|
|
@@ -2407,8 +2421,12 @@ const coordinatorEntrypoint = {
|
|
|
2407
2421
|
return new Response('unauthorized', { status: 401 });
|
|
2408
2422
|
}
|
|
2409
2423
|
const startedAt = Date.now();
|
|
2410
|
-
const minAvailableRaw = Number(
|
|
2411
|
-
|
|
2424
|
+
const minAvailableRaw = Number(
|
|
2425
|
+
url.searchParams.get('minAvailable') ?? '',
|
|
2426
|
+
);
|
|
2427
|
+
const waitTimeoutMsRaw = Number(
|
|
2428
|
+
url.searchParams.get('waitTimeoutMs') ?? '',
|
|
2429
|
+
);
|
|
2412
2430
|
const result = await refillWorkflowPool(env, {
|
|
2413
2431
|
waitReady: url.searchParams.get('waitReady') === '1',
|
|
2414
2432
|
minAvailable:
|
|
@@ -3018,6 +3036,8 @@ async function handleWorkflowRoute(input: {
|
|
|
3018
3036
|
totalRows: terminalEvent.totalRows ?? null,
|
|
3019
3037
|
durationMs: terminalEvent.durationMs ?? null,
|
|
3020
3038
|
completedAt: terminalEvent.ts,
|
|
3039
|
+
liveLogs: sanitizeLiveLogLines(terminalEvent.liveLogs),
|
|
3040
|
+
liveNodeProgress: terminalEvent.liveNodeProgress ?? null,
|
|
3021
3041
|
events: eventResult?.events ?? [],
|
|
3022
3042
|
latestSeq: eventResult?.latestSeq ?? afterSeq,
|
|
3023
3043
|
wait: null,
|
|
@@ -3040,8 +3060,7 @@ async function handleWorkflowRoute(input: {
|
|
|
3040
3060
|
coordinatorObserve: {
|
|
3041
3061
|
ms: Date.now() - statusStartedAt,
|
|
3042
3062
|
waitMs,
|
|
3043
|
-
workflowStatus:
|
|
3044
|
-
eventResult?.events.length ? 'event' : 'event-timeout',
|
|
3063
|
+
workflowStatus: eventResult?.events.length ? 'event' : 'event-timeout',
|
|
3045
3064
|
statusPolls: 0,
|
|
3046
3065
|
instanceId: null,
|
|
3047
3066
|
},
|
|
@@ -3127,28 +3146,31 @@ async function handleWorkflowRoute(input: {
|
|
|
3127
3146
|
status: mapWorkflowStatus(await instance.status()),
|
|
3128
3147
|
});
|
|
3129
3148
|
}
|
|
3130
|
-
if (
|
|
3131
|
-
action === 'result' || action === 'status' || action === ''
|
|
3132
|
-
) {
|
|
3149
|
+
if (action === 'result' || action === 'status' || action === '') {
|
|
3133
3150
|
const includeTrace =
|
|
3134
3151
|
new URL(request.url).searchParams.get('trace') === '1';
|
|
3135
3152
|
const statusStartedAt = Date.now();
|
|
3136
|
-
const terminalState = await readCoordinatorTerminalState(
|
|
3137
|
-
|
|
3138
|
-
|
|
3153
|
+
const terminalState = await readCoordinatorTerminalState(
|
|
3154
|
+
env,
|
|
3155
|
+
runId,
|
|
3156
|
+
).catch(() => null);
|
|
3139
3157
|
if (terminalState) {
|
|
3140
3158
|
const coordinatorTrace = includeTrace
|
|
3141
3159
|
? await listCoordinatorPerfTrace(env, runId).catch(() => [])
|
|
3142
3160
|
: [];
|
|
3143
3161
|
return Response.json({
|
|
3144
3162
|
runId,
|
|
3145
|
-
...(terminalState.playName
|
|
3163
|
+
...(terminalState.playName
|
|
3164
|
+
? { playName: terminalState.playName }
|
|
3165
|
+
: {}),
|
|
3146
3166
|
status: terminalState.status,
|
|
3147
3167
|
result: terminalState.result ?? null,
|
|
3148
3168
|
error: terminalState.error ?? null,
|
|
3149
3169
|
totalRows: terminalState.totalRows ?? null,
|
|
3150
3170
|
durationMs: terminalState.durationMs ?? null,
|
|
3151
3171
|
completedAt: terminalState.completedAt ?? null,
|
|
3172
|
+
liveLogs: sanitizeLiveLogLines(terminalState.liveLogs),
|
|
3173
|
+
liveNodeProgress: terminalState.liveNodeProgress ?? null,
|
|
3152
3174
|
wait: null,
|
|
3153
3175
|
coordinatorObserve: {
|
|
3154
3176
|
ms: Date.now() - statusStartedAt,
|
|
@@ -3230,8 +3252,7 @@ function stableHash(value: string): string {
|
|
|
3230
3252
|
return (hash >>> 0).toString(36);
|
|
3231
3253
|
}
|
|
3232
3254
|
|
|
3233
|
-
const DYNAMIC_PLAY_WORKER_HARNESS_VERSION =
|
|
3234
|
-
'h7-skip-high-volume-tool-traces';
|
|
3255
|
+
const DYNAMIC_PLAY_WORKER_HARNESS_VERSION = 'h7-skip-high-volume-tool-traces';
|
|
3235
3256
|
const DYNAMIC_WORKER_BUNDLED_CODE_CACHE_MAX_ENTRIES = 64;
|
|
3236
3257
|
const dynamicWorkerBundledCodeCache = new Map<string, string>();
|
|
3237
3258
|
|
|
@@ -3616,8 +3637,7 @@ async function handleStagedFilePut(
|
|
|
3616
3637
|
if (rawKey) {
|
|
3617
3638
|
const key = rawKey;
|
|
3618
3639
|
const contentType =
|
|
3619
|
-
url.searchParams.get('contentType')?.trim() ||
|
|
3620
|
-
'application/octet-stream';
|
|
3640
|
+
url.searchParams.get('contentType')?.trim() || 'application/octet-stream';
|
|
3621
3641
|
const expectedBytes = Number(url.searchParams.get('bytes') ?? 'NaN');
|
|
3622
3642
|
if (!isAllowedStagedFileStorageKey(key)) {
|
|
3623
3643
|
return Response.json(
|
|
@@ -3676,14 +3696,12 @@ async function handleStagedFilePut(
|
|
|
3676
3696
|
timingsMs: { read: readMs, put: putMs },
|
|
3677
3697
|
});
|
|
3678
3698
|
}
|
|
3679
|
-
const body = (await request.json().catch(() => null)) as
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
}
|
|
3686
|
-
| null;
|
|
3699
|
+
const body = (await request.json().catch(() => null)) as {
|
|
3700
|
+
key?: unknown;
|
|
3701
|
+
contentBase64?: unknown;
|
|
3702
|
+
contentType?: unknown;
|
|
3703
|
+
bytes?: unknown;
|
|
3704
|
+
} | null;
|
|
3687
3705
|
const key = typeof body?.key === 'string' ? body.key.trim() : '';
|
|
3688
3706
|
const contentBase64 =
|
|
3689
3707
|
typeof body?.contentBase64 === 'string' ? body.contentBase64 : '';
|
|
@@ -3693,10 +3711,7 @@ async function handleStagedFilePut(
|
|
|
3693
3711
|
: 'application/octet-stream';
|
|
3694
3712
|
const expectedBytes = typeof body?.bytes === 'number' ? body.bytes : NaN;
|
|
3695
3713
|
if (!isAllowedStagedFileStorageKey(key)) {
|
|
3696
|
-
return Response.json(
|
|
3697
|
-
{ error: 'invalid staged file key' },
|
|
3698
|
-
{ status: 400 },
|
|
3699
|
-
);
|
|
3714
|
+
return Response.json({ error: 'invalid staged file key' }, { status: 400 });
|
|
3700
3715
|
}
|
|
3701
3716
|
if (
|
|
3702
3717
|
!contentBase64 ||
|
|
@@ -4005,10 +4020,7 @@ function makeCoordinatorControlBinding():
|
|
|
4005
4020
|
runId: string,
|
|
4006
4021
|
payload: CoordinatorPerfTracePayload,
|
|
4007
4022
|
): Promise<void>;
|
|
4008
|
-
recordRunEvent(
|
|
4009
|
-
runId: string,
|
|
4010
|
-
event: CoordinatorRunEvent,
|
|
4011
|
-
): Promise<void>;
|
|
4023
|
+
recordRunEvent(runId: string, event: CoordinatorRunEvent): Promise<void>;
|
|
4012
4024
|
};
|
|
4013
4025
|
};
|
|
4014
4026
|
const ctor = exports.CoordinatorControl;
|
|
@@ -4066,6 +4078,8 @@ function mapWorkflowResult(
|
|
|
4066
4078
|
error,
|
|
4067
4079
|
totalRows: output?.totalRows ?? output?.outputRows ?? null,
|
|
4068
4080
|
durationMs: output?.durationMs ?? null,
|
|
4081
|
+
liveLogs: sanitizeLiveLogLines(output?.liveLogs),
|
|
4082
|
+
liveNodeProgress: output?.liveNodeProgress ?? null,
|
|
4069
4083
|
wait:
|
|
4070
4084
|
mapped === 'sleeping'
|
|
4071
4085
|
? {
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* Latency: ~5ms per request (DO storage is sub-ms; HTTP add ~3-5ms).
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { sanitizeLiveLogLines } from './runtime/live-progress';
|
|
20
|
+
|
|
19
21
|
type DedupEntry =
|
|
20
22
|
| { status: 'in_flight'; claimedBy: string; claimedAt: number }
|
|
21
23
|
| { status: 'completed'; result: unknown; completedAt: number }
|
|
@@ -91,6 +93,8 @@ type CoordinatorTerminalState = {
|
|
|
91
93
|
totalRows?: unknown;
|
|
92
94
|
durationMs?: unknown;
|
|
93
95
|
playName?: string | null;
|
|
96
|
+
liveLogs?: string[] | null;
|
|
97
|
+
liveNodeProgress?: unknown;
|
|
94
98
|
completedAt: number;
|
|
95
99
|
};
|
|
96
100
|
|
|
@@ -120,6 +124,7 @@ type CoordinatorRunEvent =
|
|
|
120
124
|
activeNodeId?: string | null;
|
|
121
125
|
activeArtifactTableNamespace?: string | null;
|
|
122
126
|
updatedAt?: number | null;
|
|
127
|
+
liveNodeProgress?: unknown;
|
|
123
128
|
}
|
|
124
129
|
| {
|
|
125
130
|
seq: number;
|
|
@@ -132,6 +137,8 @@ type CoordinatorRunEvent =
|
|
|
132
137
|
totalRows?: unknown;
|
|
133
138
|
durationMs?: unknown;
|
|
134
139
|
playName?: string | null;
|
|
140
|
+
liveLogs?: string[] | null;
|
|
141
|
+
liveNodeProgress?: unknown;
|
|
135
142
|
};
|
|
136
143
|
|
|
137
144
|
type OmitRunEventSequence<T> = T extends unknown ? Omit<T, 'seq'> : never;
|
|
@@ -851,7 +858,8 @@ export class PlayDedup implements DurableObject {
|
|
|
851
858
|
state: startedAt !== null ? 'started' : 'claimed',
|
|
852
859
|
blockedInstanceId: null,
|
|
853
860
|
claimedAt:
|
|
854
|
-
existing?.version === version &&
|
|
861
|
+
existing?.version === version &&
|
|
862
|
+
typeof existing.claimedAt === 'number'
|
|
855
863
|
? existing.claimedAt
|
|
856
864
|
: now,
|
|
857
865
|
startedAt,
|
|
@@ -904,7 +912,8 @@ export class PlayDedup implements DurableObject {
|
|
|
904
912
|
state: 'blocked',
|
|
905
913
|
blockedInstanceId: instanceId,
|
|
906
914
|
claimedAt:
|
|
907
|
-
existing?.version === version &&
|
|
915
|
+
existing?.version === version &&
|
|
916
|
+
typeof existing.claimedAt === 'number'
|
|
908
917
|
? existing.claimedAt
|
|
909
918
|
: now,
|
|
910
919
|
startedAt: null,
|
|
@@ -1056,6 +1065,8 @@ export class PlayDedup implements DurableObject {
|
|
|
1056
1065
|
totalRows: body.totalRows,
|
|
1057
1066
|
durationMs: body.durationMs,
|
|
1058
1067
|
playName: typeof body.playName === 'string' ? body.playName : null,
|
|
1068
|
+
liveLogs: sanitizeLiveLogLines(body.liveLogs),
|
|
1069
|
+
liveNodeProgress: body.liveNodeProgress,
|
|
1059
1070
|
completedAt:
|
|
1060
1071
|
typeof body.completedAt === 'number' ? body.completedAt : Date.now(),
|
|
1061
1072
|
};
|
|
@@ -1069,6 +1080,8 @@ export class PlayDedup implements DurableObject {
|
|
|
1069
1080
|
totalRows: state.totalRows,
|
|
1070
1081
|
durationMs: state.durationMs,
|
|
1071
1082
|
playName: state.playName,
|
|
1083
|
+
liveLogs: state.liveLogs,
|
|
1084
|
+
liveNodeProgress: state.liveNodeProgress,
|
|
1072
1085
|
ts: state.completedAt,
|
|
1073
1086
|
});
|
|
1074
1087
|
this.wakeRunEventWaiters(event);
|
|
@@ -1123,9 +1136,9 @@ export class PlayDedup implements DurableObject {
|
|
|
1123
1136
|
}
|
|
1124
1137
|
|
|
1125
1138
|
private async handleRunEventAdd(req: Request): Promise<Response> {
|
|
1126
|
-
const body = (await req
|
|
1127
|
-
|
|
1128
|
-
| null;
|
|
1139
|
+
const body = (await req
|
|
1140
|
+
.json()
|
|
1141
|
+
.catch(() => null)) as Partial<CoordinatorRunEvent> | null;
|
|
1129
1142
|
if (!body || typeof body.runId !== 'string') {
|
|
1130
1143
|
return new Response('invalid run event', { status: 400 });
|
|
1131
1144
|
}
|