deepline 0.3.237 → 0.3.239
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/bundling-sources/sdk/src/client.ts +158 -295
- package/dist/bundling-sources/sdk/src/http.ts +1 -1
- package/dist/bundling-sources/sdk/src/index.ts +1 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/observe-transport.ts +5 -468
- package/dist/bundling-sources/sdk/src/runs/watch-polling.ts +220 -0
- package/dist/bundling-sources/sdk/src/stream-reconnect.ts +9 -12
- package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +45 -7
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +27 -7
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +5 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +17 -0
- package/dist/cli/index.js +540 -2056
- package/dist/cli/index.mjs +540 -2056
- package/dist/index.d.mts +71 -57
- package/dist/index.d.ts +71 -57
- package/dist/index.js +297 -1732
- package/dist/index.mjs +297 -1732
- package/dist/install-integrity.json +1 -0
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -64,15 +64,7 @@ import {
|
|
|
64
64
|
type EffectiveProductNotificationGroup,
|
|
65
65
|
type ProductNotificationGroupDraft,
|
|
66
66
|
} from '../../shared_libs/product-notifications/configuration.js';
|
|
67
|
-
import {
|
|
68
|
-
STREAM_HEALTHY_CONNECTION_MS,
|
|
69
|
-
isTransientPlayStreamError,
|
|
70
|
-
streamReconnectDelayMs,
|
|
71
|
-
} from './stream-reconnect.js';
|
|
72
|
-
import {
|
|
73
|
-
observeRunEvents,
|
|
74
|
-
RunObserveTransportUnavailableError,
|
|
75
|
-
} from './runs/observe-transport.js';
|
|
67
|
+
import { watchRunEvents as pollRunSnapshotEvents } from './runs/watch-polling.js';
|
|
76
68
|
import type {
|
|
77
69
|
DeeplineClientOptions,
|
|
78
70
|
ResolvedConfig,
|
|
@@ -627,28 +619,35 @@ export type RunGetStatus =
|
|
|
627
619
|
|
|
628
620
|
const RUNS_FAILED_LOG_LIMIT = 20;
|
|
629
621
|
|
|
630
|
-
/**
|
|
631
|
-
export type
|
|
622
|
+
/** Options for `client.runs.watch(...)`. */
|
|
623
|
+
export type RunsWatchOptions = {
|
|
632
624
|
signal?: AbortSignal;
|
|
633
|
-
/** Observe each canonical
|
|
625
|
+
/** Observe each canonical event while `watch` waits for terminal state. */
|
|
634
626
|
onEvent?: (event: PlayLiveEvent) => void;
|
|
635
627
|
/**
|
|
636
|
-
* Called
|
|
637
|
-
*
|
|
628
|
+
* Called once when a transient REST polling outage begins. Polling retries
|
|
629
|
+
* on its normal fixed interval.
|
|
638
630
|
*/
|
|
631
|
+
onRetry?: (info: {
|
|
632
|
+
attempt: number;
|
|
633
|
+
delayMs: number;
|
|
634
|
+
reason: string;
|
|
635
|
+
}) => void;
|
|
636
|
+
/** @deprecated Use `onRetry`. */
|
|
639
637
|
onReconnect?: (info: {
|
|
640
638
|
attempt: number;
|
|
641
639
|
delayMs: number;
|
|
642
640
|
reason: string;
|
|
643
641
|
}) => void;
|
|
644
642
|
/**
|
|
645
|
-
* Display-only
|
|
646
|
-
* staleness warnings, and the one-time fallback notice when the server
|
|
647
|
-
* cannot serve the Convex subscription transport (ADR-0008).
|
|
643
|
+
* Display-only notices for a transient snapshot polling outage or recovery.
|
|
648
644
|
*/
|
|
649
645
|
onNotice?: (message: string) => void;
|
|
650
646
|
};
|
|
651
647
|
|
|
648
|
+
/** @deprecated Use `RunsWatchOptions`. */
|
|
649
|
+
export type RunsTailOptions = RunsWatchOptions;
|
|
650
|
+
|
|
652
651
|
/** Log fetch options for `client.runs.logs(...)`. */
|
|
653
652
|
export type RunsLogsOptions = {
|
|
654
653
|
/** Return the LAST `limit` stored log lines (default 200). */
|
|
@@ -756,7 +755,7 @@ export type PlaySecretCheck = {
|
|
|
756
755
|
};
|
|
757
756
|
|
|
758
757
|
/**
|
|
759
|
-
* Use `client.runs` (`/api/v2/runs`) to
|
|
758
|
+
* Use `client.runs` (`/api/v2/runs`) to watch, stop, read logs, and export durable dataset rows.
|
|
760
759
|
*
|
|
761
760
|
* @sdkReference client 020 client.runs
|
|
762
761
|
*/
|
|
@@ -782,7 +781,9 @@ export type RunsNamespace = {
|
|
|
782
781
|
list: (options: RunsListOptions) => Promise<PlayRunListItem[]>;
|
|
783
782
|
/** Read one run page with total, offset, limit and completeness metadata. */
|
|
784
783
|
listPage: (options: RunsListOptions) => Promise<RunsListPage>;
|
|
785
|
-
/**
|
|
784
|
+
/** Poll run snapshots every two seconds until terminal status. */
|
|
785
|
+
watch: (runId: string, options?: RunsWatchOptions) => Promise<PlayStatus>;
|
|
786
|
+
/** @deprecated Use `watch`; retained as a source-compatible alias. */
|
|
786
787
|
tail: (runId: string, options?: RunsTailOptions) => Promise<PlayStatus>;
|
|
787
788
|
/** Fetch persisted log lines for a run. */
|
|
788
789
|
logs: (runId: string, options?: RunsLogsOptions) => Promise<RunsLogsResult>;
|
|
@@ -2665,6 +2666,7 @@ export class DeeplineClient {
|
|
|
2665
2666
|
status: (runId) => this.getRunLifecycleStatus(runId),
|
|
2666
2667
|
list: (options) => this.listRuns(options),
|
|
2667
2668
|
listPage: (options) => this.listRunsPage(options),
|
|
2669
|
+
watch: (runId, options) => this.watchRunToTerminal(runId, options),
|
|
2668
2670
|
tail: (runId, options) => this.tailRun(runId, options),
|
|
2669
2671
|
logs: (runId, options) => this.getRunLogs(runId, options),
|
|
2670
2672
|
input: (runId) => this.getRunInput(runId),
|
|
@@ -3391,15 +3393,17 @@ export class DeeplineClient {
|
|
|
3391
3393
|
}
|
|
3392
3394
|
|
|
3393
3395
|
/**
|
|
3394
|
-
*
|
|
3396
|
+
* Submit a play run through the legacy event response.
|
|
3395
3397
|
*
|
|
3396
|
-
*
|
|
3397
|
-
*
|
|
3398
|
+
* This reports admission from the start request; it does not watch an
|
|
3399
|
+
* existing run. Use `startPlayRun()` and `watchRunEvents(runId)` separately.
|
|
3400
|
+
*
|
|
3401
|
+
* @deprecated Use the separate start and watch methods.
|
|
3398
3402
|
*
|
|
3399
3403
|
* @param request - Play run configuration.
|
|
3400
|
-
* @param options - Optional
|
|
3401
|
-
* @param options.signal -
|
|
3402
|
-
* @returns Async
|
|
3404
|
+
* @param options - Optional compatibility response options.
|
|
3405
|
+
* @param options.signal - Abort the start response.
|
|
3406
|
+
* @returns Async generator of play-scoped live events.
|
|
3403
3407
|
*/
|
|
3404
3408
|
async *startPlayRunStream(
|
|
3405
3409
|
request: StartPlayRunRequest,
|
|
@@ -4128,11 +4132,31 @@ export class DeeplineClient {
|
|
|
4128
4132
|
return normalizePlayStatus(response);
|
|
4129
4133
|
}
|
|
4130
4134
|
|
|
4135
|
+
/** Poll authenticated run snapshots every two seconds and emit changed events. */
|
|
4136
|
+
async *watchRunEvents(
|
|
4137
|
+
workflowId: string,
|
|
4138
|
+
options?: {
|
|
4139
|
+
signal?: AbortSignal;
|
|
4140
|
+
onNotice?: (message: string) => void;
|
|
4141
|
+
onRetry?: RunsWatchOptions['onRetry'];
|
|
4142
|
+
},
|
|
4143
|
+
): AsyncGenerator<PlayLiveEvent> {
|
|
4144
|
+
for await (const event of pollRunSnapshotEvents({
|
|
4145
|
+
http: this.http,
|
|
4146
|
+
runId: workflowId,
|
|
4147
|
+
signal: options?.signal,
|
|
4148
|
+
onNotice: options?.onNotice,
|
|
4149
|
+
onRetry: options?.onRetry,
|
|
4150
|
+
})) {
|
|
4151
|
+
yield event as PlayLiveEvent;
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4131
4155
|
/**
|
|
4132
|
-
*
|
|
4156
|
+
* Read raw legacy SSE events for callers that explicitly require that
|
|
4157
|
+
* compatibility endpoint. Ordinary run watching uses REST snapshot polling.
|
|
4133
4158
|
*
|
|
4134
|
-
*
|
|
4135
|
-
* connection, then incremental live events until terminal state or reconnect.
|
|
4159
|
+
* @deprecated Use `watchRunEvents` for ordinary run watching.
|
|
4136
4160
|
*/
|
|
4137
4161
|
async *streamPlayRunEvents(
|
|
4138
4162
|
workflowId: string,
|
|
@@ -4158,6 +4182,82 @@ export class DeeplineClient {
|
|
|
4158
4182
|
}
|
|
4159
4183
|
}
|
|
4160
4184
|
|
|
4185
|
+
/**
|
|
4186
|
+
* Compatibility alias for polling canonical run snapshots and emitting
|
|
4187
|
+
* their changed events.
|
|
4188
|
+
*
|
|
4189
|
+
* @deprecated Use `watchRunEvents`; kept as a source-compatible alias.
|
|
4190
|
+
* The `fallback` option is ignored because watching uses REST polling.
|
|
4191
|
+
*/
|
|
4192
|
+
async *observeRunEvents(
|
|
4193
|
+
runId: string,
|
|
4194
|
+
options?: {
|
|
4195
|
+
signal?: AbortSignal;
|
|
4196
|
+
onNotice?: (message: string) => void;
|
|
4197
|
+
/** @deprecated Ignored; watching always uses REST snapshot polling. */
|
|
4198
|
+
fallback?: 'sse' | 'none';
|
|
4199
|
+
},
|
|
4200
|
+
): AsyncGenerator<PlayLiveEvent> {
|
|
4201
|
+
yield* this.watchRunEvents(runId, {
|
|
4202
|
+
signal: options?.signal,
|
|
4203
|
+
onNotice: options?.onNotice,
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
/** Poll snapshots to terminal status and return the durable package. */
|
|
4208
|
+
private async watchRunToTerminal(
|
|
4209
|
+
runId: string,
|
|
4210
|
+
options?: RunsWatchOptions,
|
|
4211
|
+
): Promise<PlayStatus> {
|
|
4212
|
+
const state: PlayLiveStatusState = {
|
|
4213
|
+
runId,
|
|
4214
|
+
status: 'running',
|
|
4215
|
+
logs: [],
|
|
4216
|
+
lastLogSeq: 0,
|
|
4217
|
+
latest: null,
|
|
4218
|
+
};
|
|
4219
|
+
let terminalObserved = false;
|
|
4220
|
+
for await (const event of this.watchRunEvents(runId, {
|
|
4221
|
+
signal: options?.signal,
|
|
4222
|
+
onNotice: options?.onNotice,
|
|
4223
|
+
onRetry: options?.onRetry ?? options?.onReconnect,
|
|
4224
|
+
})) {
|
|
4225
|
+
options?.onEvent?.(event);
|
|
4226
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
4227
|
+
if (status && TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
4228
|
+
terminalObserved = true;
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
if (options?.signal?.aborted) {
|
|
4232
|
+
throw new DeeplineError('Run watch aborted.', undefined, 'ABORTED');
|
|
4233
|
+
}
|
|
4234
|
+
if (terminalObserved) {
|
|
4235
|
+
return await this.getRunStatus(runId);
|
|
4236
|
+
}
|
|
4237
|
+
const refreshed = await this.getRunStatus(runId);
|
|
4238
|
+
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) return refreshed;
|
|
4239
|
+
throw new DeeplineError(
|
|
4240
|
+
`Run watch for ${runId} ended before a terminal status.`,
|
|
4241
|
+
undefined,
|
|
4242
|
+
'PLAY_WATCH_ENDED',
|
|
4243
|
+
);
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
/**
|
|
4247
|
+
* Wait for a run and return its terminal status.
|
|
4248
|
+
*
|
|
4249
|
+
* This source-compatible alias uses the same authenticated two-second REST
|
|
4250
|
+
* snapshot polling as `client.runs.watch`.
|
|
4251
|
+
*
|
|
4252
|
+
* @deprecated Use `client.runs.watch`; retained for SDK compatibility.
|
|
4253
|
+
* @param runId - Public Deepline play-run id.
|
|
4254
|
+
* @param options - Optional callbacks and cancellation signal.
|
|
4255
|
+
* @returns The final durable run status.
|
|
4256
|
+
*/
|
|
4257
|
+
async tailRun(runId: string, options?: RunsTailOptions): Promise<PlayStatus> {
|
|
4258
|
+
return this.watchRunToTerminal(runId, options);
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4161
4261
|
/**
|
|
4162
4262
|
* Cancel a running play execution.
|
|
4163
4263
|
*
|
|
@@ -4492,263 +4592,6 @@ export class DeeplineClient {
|
|
|
4492
4592
|
return { ...response, runs: response.runs ?? [] };
|
|
4493
4593
|
}
|
|
4494
4594
|
|
|
4495
|
-
/**
|
|
4496
|
-
* Observe one run's live events. Uses the Convex Run Snapshot subscription
|
|
4497
|
-
* transport first (ADR-0008), then falls back to the canonical SSE stream
|
|
4498
|
-
* when the subscription transport or its optional client modules are not
|
|
4499
|
-
* available. Pass `fallback: 'none'` to receive
|
|
4500
|
-
* {@link RunObserveTransportUnavailableError} instead.
|
|
4501
|
-
*/
|
|
4502
|
-
async *observeRunEvents(
|
|
4503
|
-
runId: string,
|
|
4504
|
-
options?: {
|
|
4505
|
-
signal?: AbortSignal;
|
|
4506
|
-
onNotice?: (message: string) => void;
|
|
4507
|
-
fallback?: 'sse' | 'none';
|
|
4508
|
-
},
|
|
4509
|
-
): AsyncGenerator<PlayLiveEvent> {
|
|
4510
|
-
let yieldedObserveEvent = false;
|
|
4511
|
-
try {
|
|
4512
|
-
for await (const event of observeRunEvents({
|
|
4513
|
-
http: this.http,
|
|
4514
|
-
runId,
|
|
4515
|
-
signal: options?.signal,
|
|
4516
|
-
onNotice: options?.onNotice,
|
|
4517
|
-
}) as AsyncGenerator<PlayLiveEvent>) {
|
|
4518
|
-
yieldedObserveEvent = true;
|
|
4519
|
-
yield event;
|
|
4520
|
-
}
|
|
4521
|
-
} catch (error) {
|
|
4522
|
-
if (
|
|
4523
|
-
!(error instanceof RunObserveTransportUnavailableError) ||
|
|
4524
|
-
yieldedObserveEvent ||
|
|
4525
|
-
options?.fallback === 'none'
|
|
4526
|
-
) {
|
|
4527
|
-
throw error;
|
|
4528
|
-
}
|
|
4529
|
-
options?.onNotice?.(
|
|
4530
|
-
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`,
|
|
4531
|
-
);
|
|
4532
|
-
yield* this.streamPlayRunEventsUntilTerminal(runId, options);
|
|
4533
|
-
}
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
|
-
private async *streamPlayRunEventsUntilTerminal(
|
|
4537
|
-
runId: string,
|
|
4538
|
-
options?: { signal?: AbortSignal; onNotice?: (message: string) => void },
|
|
4539
|
-
): AsyncGenerator<PlayLiveEvent> {
|
|
4540
|
-
const state: PlayLiveStatusState = {
|
|
4541
|
-
runId,
|
|
4542
|
-
status: 'running',
|
|
4543
|
-
logs: [],
|
|
4544
|
-
lastLogSeq: 0,
|
|
4545
|
-
latest: null,
|
|
4546
|
-
};
|
|
4547
|
-
let lastEventId: string | undefined;
|
|
4548
|
-
let reconnectAttempt = 0;
|
|
4549
|
-
|
|
4550
|
-
for (;;) {
|
|
4551
|
-
if (options?.signal?.aborted) {
|
|
4552
|
-
return;
|
|
4553
|
-
}
|
|
4554
|
-
const connectedAt = Date.now();
|
|
4555
|
-
let sawEvent = false;
|
|
4556
|
-
let endedReason = 'stream window ended before a terminal event';
|
|
4557
|
-
try {
|
|
4558
|
-
for await (const event of this.streamPlayRunEvents(runId, {
|
|
4559
|
-
mode: 'cli',
|
|
4560
|
-
signal: options?.signal,
|
|
4561
|
-
...(lastEventId ? { lastEventId } : {}),
|
|
4562
|
-
})) {
|
|
4563
|
-
sawEvent = true;
|
|
4564
|
-
if (event.cursor?.trim()) {
|
|
4565
|
-
lastEventId = event.cursor;
|
|
4566
|
-
}
|
|
4567
|
-
yield event;
|
|
4568
|
-
const status = updatePlayLiveStatusState(state, event);
|
|
4569
|
-
if (status && TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
4570
|
-
return;
|
|
4571
|
-
}
|
|
4572
|
-
}
|
|
4573
|
-
} catch (error) {
|
|
4574
|
-
if (options?.signal?.aborted) {
|
|
4575
|
-
return;
|
|
4576
|
-
}
|
|
4577
|
-
if (!isTransientPlayStreamError(error)) {
|
|
4578
|
-
throw error;
|
|
4579
|
-
}
|
|
4580
|
-
endedReason = error instanceof Error ? error.message : String(error);
|
|
4581
|
-
}
|
|
4582
|
-
|
|
4583
|
-
let refreshed: PlayStatus | null = null;
|
|
4584
|
-
try {
|
|
4585
|
-
refreshed = await this.getRunStatus(runId);
|
|
4586
|
-
} catch (error) {
|
|
4587
|
-
if (!isTransientPlayStreamError(error)) {
|
|
4588
|
-
throw error;
|
|
4589
|
-
}
|
|
4590
|
-
}
|
|
4591
|
-
if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
4592
|
-
yield {
|
|
4593
|
-
cursor: String(Date.now()),
|
|
4594
|
-
streamId: `sse-fallback:${runId}`,
|
|
4595
|
-
scope: 'play',
|
|
4596
|
-
type: 'play.run.status',
|
|
4597
|
-
at: new Date().toISOString(),
|
|
4598
|
-
payload: refreshed as unknown as Record<string, unknown>,
|
|
4599
|
-
};
|
|
4600
|
-
return;
|
|
4601
|
-
}
|
|
4602
|
-
|
|
4603
|
-
if (
|
|
4604
|
-
sawEvent ||
|
|
4605
|
-
Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS
|
|
4606
|
-
) {
|
|
4607
|
-
reconnectAttempt = 0;
|
|
4608
|
-
}
|
|
4609
|
-
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
4610
|
-
reconnectAttempt += 1;
|
|
4611
|
-
options?.onNotice?.(
|
|
4612
|
-
`[observe] SSE tail window ended before terminal status (${endedReason}); reconnecting to run ${runId}`,
|
|
4613
|
-
);
|
|
4614
|
-
await sleep(delayMs);
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
|
|
4618
|
-
/**
|
|
4619
|
-
* Tail one run through the subscription transport until terminal, then
|
|
4620
|
-
* return one durable REST status read (the final Run Response Package).
|
|
4621
|
-
*/
|
|
4622
|
-
private async tailRunViaObserveTransport(
|
|
4623
|
-
runId: string,
|
|
4624
|
-
options?: RunsTailOptions,
|
|
4625
|
-
): Promise<PlayStatus> {
|
|
4626
|
-
const state: PlayLiveStatusState = {
|
|
4627
|
-
runId,
|
|
4628
|
-
status: 'running',
|
|
4629
|
-
logs: [],
|
|
4630
|
-
lastLogSeq: 0,
|
|
4631
|
-
latest: null,
|
|
4632
|
-
};
|
|
4633
|
-
for await (const event of this.observeRunEvents(runId, {
|
|
4634
|
-
signal: options?.signal,
|
|
4635
|
-
onNotice: options?.onNotice,
|
|
4636
|
-
fallback: 'none',
|
|
4637
|
-
})) {
|
|
4638
|
-
options?.onEvent?.(event);
|
|
4639
|
-
const status = updatePlayLiveStatusState(state, event);
|
|
4640
|
-
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
4641
|
-
continue;
|
|
4642
|
-
}
|
|
4643
|
-
return await this.getRunStatus(status.runId || runId);
|
|
4644
|
-
}
|
|
4645
|
-
if (options?.signal?.aborted) {
|
|
4646
|
-
throw new DeeplineError('Run observation aborted.', undefined, 'ABORTED');
|
|
4647
|
-
}
|
|
4648
|
-
// The transport ends only after a terminal snapshot; the differ always
|
|
4649
|
-
// emits a terminal `play.run.status` first, so reaching here means the
|
|
4650
|
-
// terminal package read raced — re-check durable status once, loudly.
|
|
4651
|
-
const refreshed = await this.getRunStatus(runId);
|
|
4652
|
-
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
4653
|
-
return refreshed;
|
|
4654
|
-
}
|
|
4655
|
-
throw new DeeplineError(
|
|
4656
|
-
`Run observation for ${runId} ended before a terminal status.`,
|
|
4657
|
-
undefined,
|
|
4658
|
-
'PLAY_LIVE_STREAM_ENDED',
|
|
4659
|
-
);
|
|
4660
|
-
}
|
|
4661
|
-
|
|
4662
|
-
/**
|
|
4663
|
-
* Read the canonical run stream until a terminal run status is observed.
|
|
4664
|
-
*
|
|
4665
|
-
* Tries the Convex Run Snapshot subscription transport first (ADR-0008);
|
|
4666
|
-
* when the server cannot serve it (grant endpoint missing/unconfigured or
|
|
4667
|
-
* Convex unreachable) it falls back — with one `onNotice` message — to the
|
|
4668
|
-
* support-window SSE stream below.
|
|
4669
|
-
*
|
|
4670
|
-
* Server stream windows are finite: they end cleanly at the function
|
|
4671
|
-
* ceiling even while the run keeps executing. A window that ends (cleanly
|
|
4672
|
-
* or via transient network error) without a terminal event triggers one
|
|
4673
|
-
* durable-status re-check followed by a backed-off reconnect, so long runs
|
|
4674
|
-
* tail to completion. Abort via `options.signal` to stop waiting.
|
|
4675
|
-
*/
|
|
4676
|
-
async tailRun(runId: string, options?: RunsTailOptions): Promise<PlayStatus> {
|
|
4677
|
-
try {
|
|
4678
|
-
return await this.tailRunViaObserveTransport(runId, options);
|
|
4679
|
-
} catch (error) {
|
|
4680
|
-
if (!(error instanceof RunObserveTransportUnavailableError)) {
|
|
4681
|
-
throw error;
|
|
4682
|
-
}
|
|
4683
|
-
options?.onNotice?.(
|
|
4684
|
-
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`,
|
|
4685
|
-
);
|
|
4686
|
-
}
|
|
4687
|
-
const state: PlayLiveStatusState = {
|
|
4688
|
-
runId,
|
|
4689
|
-
status: 'running',
|
|
4690
|
-
logs: [],
|
|
4691
|
-
lastLogSeq: 0,
|
|
4692
|
-
latest: null,
|
|
4693
|
-
};
|
|
4694
|
-
let reconnectAttempt = 0;
|
|
4695
|
-
|
|
4696
|
-
for (;;) {
|
|
4697
|
-
const connectedAt = Date.now();
|
|
4698
|
-
let sawEvent = false;
|
|
4699
|
-
let terminalRunId: string | null = null;
|
|
4700
|
-
let endedReason = 'stream window ended before a terminal event';
|
|
4701
|
-
try {
|
|
4702
|
-
for await (const event of this.streamPlayRunEvents(runId, {
|
|
4703
|
-
mode: 'cli',
|
|
4704
|
-
signal: options?.signal,
|
|
4705
|
-
})) {
|
|
4706
|
-
options?.onEvent?.(event);
|
|
4707
|
-
sawEvent = true;
|
|
4708
|
-
const status = updatePlayLiveStatusState(state, event);
|
|
4709
|
-
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
4710
|
-
continue;
|
|
4711
|
-
}
|
|
4712
|
-
terminalRunId = status.runId || runId;
|
|
4713
|
-
break;
|
|
4714
|
-
}
|
|
4715
|
-
} catch (error) {
|
|
4716
|
-
if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
|
|
4717
|
-
throw error;
|
|
4718
|
-
}
|
|
4719
|
-
endedReason = error instanceof Error ? error.message : String(error);
|
|
4720
|
-
}
|
|
4721
|
-
|
|
4722
|
-
if (terminalRunId) {
|
|
4723
|
-
return await this.getRunStatus(terminalRunId);
|
|
4724
|
-
}
|
|
4725
|
-
|
|
4726
|
-
// Window ended without a terminal event. The run may have finished
|
|
4727
|
-
// during the gap — re-check durable status once before reconnecting.
|
|
4728
|
-
// `runs.get` owns the durable snapshot; its optional status evidence is
|
|
4729
|
-
// additive and must not prevent old servers from ending this stream.
|
|
4730
|
-
const refreshed = await this.getRunStatus(runId);
|
|
4731
|
-
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
4732
|
-
return refreshed;
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
if (
|
|
4736
|
-
sawEvent ||
|
|
4737
|
-
Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS
|
|
4738
|
-
) {
|
|
4739
|
-
reconnectAttempt = 0;
|
|
4740
|
-
}
|
|
4741
|
-
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
4742
|
-
reconnectAttempt += 1;
|
|
4743
|
-
options?.onReconnect?.({
|
|
4744
|
-
attempt: reconnectAttempt,
|
|
4745
|
-
delayMs,
|
|
4746
|
-
reason: endedReason,
|
|
4747
|
-
});
|
|
4748
|
-
await sleep(delayMs);
|
|
4749
|
-
}
|
|
4750
|
-
}
|
|
4751
|
-
|
|
4752
4595
|
/** Get the exact original input retained for a run. This is intentionally separate from status. */
|
|
4753
4596
|
async getRunInput(runId: string): Promise<{
|
|
4754
4597
|
runId: string;
|
|
@@ -5822,10 +5665,10 @@ export class DeeplineClient {
|
|
|
5822
5665
|
// ——————————————————————————————————————————————————————————
|
|
5823
5666
|
|
|
5824
5667
|
/**
|
|
5825
|
-
* Run a play end-to-end: submit,
|
|
5668
|
+
* Run a play end-to-end: submit, poll progress until terminal, return result.
|
|
5826
5669
|
*
|
|
5827
5670
|
* This is the highest-level play execution method. It submits the play,
|
|
5828
|
-
* reads the canonical run
|
|
5671
|
+
* reads the canonical run snapshot for status updates, and returns a structured
|
|
5829
5672
|
* result with logs and timing. Supports cancellation via `AbortSignal`.
|
|
5830
5673
|
*
|
|
5831
5674
|
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
@@ -5902,6 +5745,7 @@ export class DeeplineClient {
|
|
|
5902
5745
|
lastLogSeq: 0,
|
|
5903
5746
|
latest: null,
|
|
5904
5747
|
};
|
|
5748
|
+
let terminalObserved: PlayStatus | null = null;
|
|
5905
5749
|
|
|
5906
5750
|
if (options?.signal?.aborted) {
|
|
5907
5751
|
await this.cancelPlay(workflowId);
|
|
@@ -5914,8 +5758,7 @@ export class DeeplineClient {
|
|
|
5914
5758
|
};
|
|
5915
5759
|
}
|
|
5916
5760
|
|
|
5917
|
-
for await (const event of this.
|
|
5918
|
-
mode: 'cli',
|
|
5761
|
+
for await (const event of this.watchRunEvents(workflowId, {
|
|
5919
5762
|
signal: options?.signal,
|
|
5920
5763
|
})) {
|
|
5921
5764
|
if (options?.signal?.aborted) {
|
|
@@ -5929,22 +5772,42 @@ export class DeeplineClient {
|
|
|
5929
5772
|
};
|
|
5930
5773
|
}
|
|
5931
5774
|
|
|
5775
|
+
const previousStatus = state.latest?.status;
|
|
5932
5776
|
const status = updatePlayLiveStatusState(state, event);
|
|
5933
5777
|
if (!status) {
|
|
5934
5778
|
continue;
|
|
5935
5779
|
}
|
|
5936
|
-
|
|
5780
|
+
if (
|
|
5781
|
+
event.type !== 'play.run.status' ||
|
|
5782
|
+
status.status !== previousStatus
|
|
5783
|
+
) {
|
|
5784
|
+
options?.onProgress?.(status);
|
|
5785
|
+
}
|
|
5937
5786
|
|
|
5938
5787
|
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
5939
|
-
|
|
5940
|
-
status.runId || workflowId,
|
|
5941
|
-
).catch(() => status);
|
|
5942
|
-
return playRunResultFromStatus(finalStatus, start, workflowId);
|
|
5788
|
+
terminalObserved = status;
|
|
5943
5789
|
}
|
|
5944
5790
|
}
|
|
5945
5791
|
|
|
5792
|
+
if (options?.signal?.aborted) {
|
|
5793
|
+
await this.cancelPlay(workflowId);
|
|
5794
|
+
return {
|
|
5795
|
+
success: false,
|
|
5796
|
+
runId: workflowId,
|
|
5797
|
+
logs: state.logs,
|
|
5798
|
+
durationMs: Date.now() - start,
|
|
5799
|
+
error: 'Cancelled by user',
|
|
5800
|
+
};
|
|
5801
|
+
}
|
|
5802
|
+
if (terminalObserved) {
|
|
5803
|
+
const finalStatus = await this.getPlayStatus(
|
|
5804
|
+
terminalObserved.runId || workflowId,
|
|
5805
|
+
).catch(() => terminalObserved!);
|
|
5806
|
+
return playRunResultFromStatus(finalStatus, start, workflowId);
|
|
5807
|
+
}
|
|
5808
|
+
|
|
5946
5809
|
throw new DeeplineError(
|
|
5947
|
-
`Run
|
|
5810
|
+
`Run watch for ${workflowId} ended before the run reached a terminal state.`,
|
|
5948
5811
|
undefined,
|
|
5949
5812
|
'PLAY_RUN_STREAM_ENDED',
|
|
5950
5813
|
{ runId: workflowId, workflowId },
|
|
@@ -992,7 +992,7 @@ function isRetryableApiErrorResponse(status: number): boolean {
|
|
|
992
992
|
* to CLI users — it lands verbatim in `DeeplineError.message`/`details`. When
|
|
993
993
|
* detected, we synthesize a structured message (status + extracted <title>) and
|
|
994
994
|
* preserve the literal token `Worker threw exception` when present, since SDK
|
|
995
|
-
* retry classifiers (`
|
|
995
|
+
* retry classifiers (`isTransientPlayRunReadError`) regex-match on it.
|
|
996
996
|
*/
|
|
997
997
|
function detectHtmlErrorBody(
|
|
998
998
|
body: string,
|
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.239',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|