deepline 0.3.274 → 0.3.276
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 +148 -19
- package/dist/bundling-sources/sdk/src/index.ts +3 -0
- package/dist/bundling-sources/sdk/src/play.ts +4 -10
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/log-cursor.ts +35 -0
- package/dist/bundling-sources/sdk/src/types.ts +10 -3
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +12 -26
- package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +7 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +15 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +18 -18
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +7 -4
- package/dist/bundling-sources/shared_libs/plays/table-csv.ts +152 -0
- package/dist/cli/index.js +411 -284
- package/dist/cli/index.mjs +411 -284
- package/dist/{client-2rIHKJiq.d.mts → client-C0EhEIQO.d.mts} +78 -13
- package/dist/{client-D6fRwhnE.d.ts → client-HxPrGPbv.d.ts} +78 -13
- package/dist/index.d.mts +6 -12
- package/dist/index.d.ts +6 -12
- package/dist/index.js +87 -9
- package/dist/index.mjs +87 -9
- package/dist/install-integrity.json +4 -2
- package/dist/plays/run-request.d.mts +1 -1
- package/dist/plays/run-request.d.ts +1 -1
- 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
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
type ProductNotificationGroupDraft,
|
|
75
75
|
} from '../../shared_libs/product-notifications/configuration.js';
|
|
76
76
|
import { watchRunEvents as pollRunSnapshotEvents } from './runs/watch-polling.js';
|
|
77
|
+
import { encodeRunLogsCursor, parseRunLogsCursor } from './runs/log-cursor.js';
|
|
77
78
|
import type {
|
|
78
79
|
DeeplineClientOptions,
|
|
79
80
|
ResolvedConfig,
|
|
@@ -626,6 +627,49 @@ export type RunsListPage = {
|
|
|
626
627
|
hasMore?: boolean;
|
|
627
628
|
};
|
|
628
629
|
|
|
630
|
+
/** Cursor-paged workspace run history, including runs of removed Plays. */
|
|
631
|
+
export type RunsHistoryOptions = {
|
|
632
|
+
/** Case-insensitive Play/workflow name, run ID, or status substring. */
|
|
633
|
+
search?: string;
|
|
634
|
+
/** Search classification from recorded trigger metadata; requires search. */
|
|
635
|
+
type?: 'all' | 'plays' | 'monitors';
|
|
636
|
+
cursor?: string;
|
|
637
|
+
limit?: number;
|
|
638
|
+
/** Must match the workspace authorized by the credential. */
|
|
639
|
+
orgId?: string;
|
|
640
|
+
signal?: AbortSignal;
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
export type RunsHistoryItem = {
|
|
644
|
+
runId: string;
|
|
645
|
+
workflowId: string;
|
|
646
|
+
workflowName: string;
|
|
647
|
+
playScope: 'org' | 'system' | null;
|
|
648
|
+
status: string;
|
|
649
|
+
createdAt: number;
|
|
650
|
+
updatedAt: number;
|
|
651
|
+
callerName: string | null;
|
|
652
|
+
callerEmail: string | null;
|
|
653
|
+
authorName: string | null;
|
|
654
|
+
authorEmail: string | null;
|
|
655
|
+
callerAgent: string | null;
|
|
656
|
+
executionMode: string | null;
|
|
657
|
+
bindingId: string | null;
|
|
658
|
+
parentWorkflowName: string | null;
|
|
659
|
+
outputRowCount: number | null;
|
|
660
|
+
hasOutput: boolean;
|
|
661
|
+
billingTotalCredits?: number;
|
|
662
|
+
triggerSource?: string;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
/** An empty search page is incomplete while hasMore is true. */
|
|
666
|
+
export type RunsHistoryPage = {
|
|
667
|
+
runs: RunsHistoryItem[];
|
|
668
|
+
nextCursor: string | null;
|
|
669
|
+
hasMore: boolean;
|
|
670
|
+
avatarsByName?: Record<string, string>;
|
|
671
|
+
};
|
|
672
|
+
|
|
629
673
|
export type PlaysListOptions = {
|
|
630
674
|
origin?: 'prebuilt' | 'owned';
|
|
631
675
|
grep?: string;
|
|
@@ -657,6 +701,8 @@ export type PlaysListPage = {
|
|
|
657
701
|
export type RunsGetOptions = {
|
|
658
702
|
/** Return the raw status payload instead of the compact package. */
|
|
659
703
|
full?: boolean;
|
|
704
|
+
/** Explicitly read this run's recorded graph and source, without full results. */
|
|
705
|
+
configuration?: boolean;
|
|
660
706
|
/** Attach a bounded end-of-stream window for a failed run. */
|
|
661
707
|
failedLogs?: boolean;
|
|
662
708
|
/** Requested failed-log lines to attach (default 20, hard-capped at 20). */
|
|
@@ -748,6 +794,12 @@ export type RunsLogsOptions = {
|
|
|
748
794
|
all?: boolean;
|
|
749
795
|
/** Read a terminal-failed run's last 20 retained lines, or fewer with `limit`. Retention loss returns an explicit association and warning. */
|
|
750
796
|
failed?: boolean;
|
|
797
|
+
/**
|
|
798
|
+
* Continue with the next OLDER page: pass a prior result's `nextCursor`.
|
|
799
|
+
* The page holds the last `limit` stored lines before that cursor (with
|
|
800
|
+
* `all`, every stored line before it).
|
|
801
|
+
*/
|
|
802
|
+
cursor?: string;
|
|
751
803
|
};
|
|
752
804
|
|
|
753
805
|
/** Retained text for one run. Counts and retention flags describe the stored stream, not every original console call. */
|
|
@@ -764,8 +816,14 @@ export type RunsLogsResult = {
|
|
|
764
816
|
lastSequence: number | null;
|
|
765
817
|
/** Tail/all: returned count is below total count. Failed: the stream lost bodies to retention. Check `logsTruncated` to distinguish retention loss from a selected tail. */
|
|
766
818
|
truncated: boolean;
|
|
767
|
-
/**
|
|
819
|
+
/** Older stored lines exist before this page; `nextCursor` reads them. */
|
|
768
820
|
hasMore: boolean;
|
|
821
|
+
/**
|
|
822
|
+
* Opaque cursor for the next older page (`runs.logs(id, { cursor })`),
|
|
823
|
+
* null when this page reaches the oldest stored line. Absent for the failed
|
|
824
|
+
* view, which is a complete designed window rather than a pageable slice.
|
|
825
|
+
*/
|
|
826
|
+
nextCursor?: string | null;
|
|
769
827
|
/** Formatted text lines in stored sequence order; structured context is available through event search. */
|
|
770
828
|
entries: string[];
|
|
771
829
|
/** Selected public log view. */
|
|
@@ -783,7 +841,7 @@ export type RunsLogsResult = {
|
|
|
783
841
|
logsTruncated?: boolean;
|
|
784
842
|
};
|
|
785
843
|
|
|
786
|
-
/**
|
|
844
|
+
/** Search existing events in your organization, newest first. Discover kinds and filters with `deepline runs logs --help`. */
|
|
787
845
|
export type RunsEventSearchOptions = {
|
|
788
846
|
/** Inclusive UTC timestamp; defaults to one hour before the request. */
|
|
789
847
|
since?: string;
|
|
@@ -795,9 +853,9 @@ export type RunsEventSearchOptions = {
|
|
|
795
853
|
playName?: string;
|
|
796
854
|
/** Exact dotted event kind, such as `step.failed`. The catalog is discoverable with `runs logs --help`; future stored kinds are accepted too. */
|
|
797
855
|
kind?: string;
|
|
798
|
-
/**
|
|
856
|
+
/** JSONPath equality joined with `&&`, at most eight predicates and 512 bytes. See `runs logs --help` for fields and examples. Missing fields do not match; status is not inferred from kind. HTTP `jsonpath`, CLI `--where`. */
|
|
799
857
|
jsonpath?: string;
|
|
800
|
-
/**
|
|
858
|
+
/** Include available `eventPayload` details and receipt `payload` results; maximum ten events and 16 MiB. HTTP `payloads=true`, CLI `--payloads`. */
|
|
801
859
|
includePayloads?: boolean;
|
|
802
860
|
/** Page size: default 100, maximum 200; with payloads, default and maximum ten. */
|
|
803
861
|
limit?: number;
|
|
@@ -807,7 +865,7 @@ export type RunsEventSearchOptions = {
|
|
|
807
865
|
runtime?: PlayRuntimeSelection;
|
|
808
866
|
};
|
|
809
867
|
|
|
810
|
-
/** One
|
|
868
|
+
/** One matching event. Optional fields and details are present only when recorded. */
|
|
811
869
|
export type RunsEventSearchEntry = {
|
|
812
870
|
/** Stable event identity; also breaks ties in newest-first pagination. */
|
|
813
871
|
eventId: string;
|
|
@@ -819,15 +877,15 @@ export type RunsEventSearchEntry = {
|
|
|
819
877
|
runId: string;
|
|
820
878
|
/** Recorded Play name. */
|
|
821
879
|
playName: string;
|
|
822
|
-
/** Exact
|
|
880
|
+
/** Exact dotted event kind, such as `step.failed`. */
|
|
823
881
|
kind: string;
|
|
824
882
|
/** Recorded log severity, or null; not a JSONPath filter field. */
|
|
825
883
|
level: string | null;
|
|
826
884
|
/** Log message preview of at most 2,048 characters, or null. Use text logs/export for the full retained line. */
|
|
827
885
|
message: string | null;
|
|
828
|
-
/**
|
|
886
|
+
/** Recorded filter fields. SDK/API keep this nested; the CLI places these fields directly on each event. */
|
|
829
887
|
searchDoc: Record<string, unknown>;
|
|
830
|
-
/**
|
|
888
|
+
/** Available event details when requested with `includePayloads`; dataset events contain metadata, not dataset rows. */
|
|
831
889
|
eventPayload?: Record<string, unknown>;
|
|
832
890
|
/** Retained work result for a receipt match when payloads are requested; null if unavailable. Other events need not carry this field. */
|
|
833
891
|
payload?: RunsEventPayload | null;
|
|
@@ -845,7 +903,7 @@ export type RunsEventSearchResult = {
|
|
|
845
903
|
until: string;
|
|
846
904
|
};
|
|
847
905
|
|
|
848
|
-
/**
|
|
906
|
+
/** Saved work result returned for a receipt event. Reading it does not execute the work again. */
|
|
849
907
|
export type RunsEventPayload = {
|
|
850
908
|
/** Recorded terminal work outcome. */
|
|
851
909
|
status: 'completed' | 'failed' | 'skipped';
|
|
@@ -965,6 +1023,8 @@ export type RunsNamespace = {
|
|
|
965
1023
|
list: (options: RunsListOptions) => Promise<PlayRunListItem[]>;
|
|
966
1024
|
/** Read one run page with total, offset, limit and completeness metadata. */
|
|
967
1025
|
listPage: (options: RunsListOptions) => Promise<RunsListPage>;
|
|
1026
|
+
/** Search/page workspace run history newest first; follow empty continuing pages. */
|
|
1027
|
+
history: (options?: RunsHistoryOptions) => Promise<RunsHistoryPage>;
|
|
968
1028
|
/** Poll run snapshots every two seconds until terminal status. */
|
|
969
1029
|
watch: (runId: string, options?: RunsWatchOptions) => Promise<PlayStatus>;
|
|
970
1030
|
/** @deprecated Use `watch`; retained as a source-compatible alias. */
|
|
@@ -2709,6 +2769,7 @@ export class DeeplineClient {
|
|
|
2709
2769
|
status: (runId) => this.getRunLifecycleStatus(runId),
|
|
2710
2770
|
list: (options) => this.listRuns(options),
|
|
2711
2771
|
listPage: (options) => this.listRunsPage(options),
|
|
2772
|
+
history: (options) => this.getRunsHistory(options),
|
|
2712
2773
|
watch: (runId, options) => this.watchRunToTerminal(runId, options),
|
|
2713
2774
|
tail: (runId, options) => this.tailRun(runId, options),
|
|
2714
2775
|
logs: (runId, options) => this.getRunLogs(runId, options),
|
|
@@ -4222,11 +4283,24 @@ export class DeeplineClient {
|
|
|
4222
4283
|
if (options?.full === true) {
|
|
4223
4284
|
params.set('full', 'true');
|
|
4224
4285
|
}
|
|
4286
|
+
if (options?.configuration === true) {
|
|
4287
|
+
params.set('include', 'configuration');
|
|
4288
|
+
}
|
|
4225
4289
|
const query = params.size > 0 ? `?${params.toString()}` : '';
|
|
4226
4290
|
const response = await this.http.get<Record<string, unknown>>(
|
|
4227
4291
|
`/api/v2/runs/${encodeURIComponent(runId)}${query}`,
|
|
4228
4292
|
{ headers: { 'x-deepline-run-status': '1' } },
|
|
4229
4293
|
);
|
|
4294
|
+
if (
|
|
4295
|
+
options?.configuration === true &&
|
|
4296
|
+
!Object.hasOwn(response, 'contract')
|
|
4297
|
+
) {
|
|
4298
|
+
throw new DeeplineError(
|
|
4299
|
+
'This server does not support recorded configuration reads.',
|
|
4300
|
+
undefined,
|
|
4301
|
+
'RUN_CONFIGURATION_UNAVAILABLE',
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
4230
4304
|
const status = normalizePlayStatus(response);
|
|
4231
4305
|
if (options?.failedLogs !== true || status.status !== 'failed') {
|
|
4232
4306
|
return status;
|
|
@@ -4335,6 +4409,36 @@ export class DeeplineClient {
|
|
|
4335
4409
|
return (await this.listRunsPage(options)).runs;
|
|
4336
4410
|
}
|
|
4337
4411
|
|
|
4412
|
+
/** The same tenant-scoped run history searched by the dashboard and CLI. */
|
|
4413
|
+
async getRunsHistory(
|
|
4414
|
+
options: RunsHistoryOptions = {},
|
|
4415
|
+
): Promise<RunsHistoryPage> {
|
|
4416
|
+
const params = new URLSearchParams();
|
|
4417
|
+
const search = options.search?.trim();
|
|
4418
|
+
if (search && search.length > 200) {
|
|
4419
|
+
throw new Error('Run search must be at most 200 characters.');
|
|
4420
|
+
}
|
|
4421
|
+
if (options.type && options.type !== 'all' && !search) {
|
|
4422
|
+
throw new Error('runs.history options.type requires options.search.');
|
|
4423
|
+
}
|
|
4424
|
+
if (search) params.set('search', search);
|
|
4425
|
+
if (search && options.type) params.set('type', options.type);
|
|
4426
|
+
if (options.cursor) params.set('cursor', options.cursor);
|
|
4427
|
+
if (options.orgId) params.set('orgId', options.orgId);
|
|
4428
|
+
if (options.limit !== undefined) {
|
|
4429
|
+
if (!Number.isSafeInteger(options.limit) || options.limit < 1) {
|
|
4430
|
+
throw new Error(
|
|
4431
|
+
'runs.history options.limit must be a positive integer.',
|
|
4432
|
+
);
|
|
4433
|
+
}
|
|
4434
|
+
params.set('limit', String(options.limit));
|
|
4435
|
+
}
|
|
4436
|
+
return this.http.get<RunsHistoryPage>(
|
|
4437
|
+
`/api/v2/dashboard/runs-history?${params.toString()}`,
|
|
4438
|
+
{ signal: options.signal },
|
|
4439
|
+
);
|
|
4440
|
+
}
|
|
4441
|
+
|
|
4338
4442
|
/** Read one run page without discarding server pagination metadata. */
|
|
4339
4443
|
async listRunsPage(options: RunsListOptions): Promise<RunsListPage> {
|
|
4340
4444
|
const playName = options.play?.trim();
|
|
@@ -4477,6 +4581,13 @@ export class DeeplineClient {
|
|
|
4477
4581
|
'INVALID_RUN_LOG_VIEW',
|
|
4478
4582
|
);
|
|
4479
4583
|
}
|
|
4584
|
+
if (options?.cursor !== undefined && options.failed === true) {
|
|
4585
|
+
throw new DeeplineError(
|
|
4586
|
+
'runs.logs cannot combine failed with cursor.',
|
|
4587
|
+
undefined,
|
|
4588
|
+
'INVALID_RUN_LOG_VIEW',
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4480
4591
|
if (options?.failed === true) {
|
|
4481
4592
|
const requestedLimit =
|
|
4482
4593
|
typeof options.limit === 'number' &&
|
|
@@ -4507,6 +4618,8 @@ export class DeeplineClient {
|
|
|
4507
4618
|
...(page.logsTruncated ? { logsTruncated: true } : {}),
|
|
4508
4619
|
};
|
|
4509
4620
|
}
|
|
4621
|
+
const beforeSequence =
|
|
4622
|
+
options?.cursor !== undefined ? parseRunLogsCursor(options.cursor) : null;
|
|
4510
4623
|
const limit = options?.all
|
|
4511
4624
|
? Number.MAX_SAFE_INTEGER
|
|
4512
4625
|
: typeof options?.limit === 'number' &&
|
|
@@ -4520,29 +4633,44 @@ export class DeeplineClient {
|
|
|
4520
4633
|
{ retryApiErrors: true },
|
|
4521
4634
|
);
|
|
4522
4635
|
// Probe for the run's stored extent, then read the LAST `limit` stored
|
|
4523
|
-
// lines (matching the historical tail-slice semantics)
|
|
4524
|
-
// server-capped pages until the window is exhausted.
|
|
4636
|
+
// lines (matching the historical tail-slice semantics) ending before the
|
|
4637
|
+
// cursor, paginating in server-capped pages until the window is exhausted.
|
|
4525
4638
|
const probe = await fetchPage(0, 1);
|
|
4526
4639
|
const lastStoredSeq = probe.lastStoredSeq;
|
|
4527
|
-
|
|
4640
|
+
const oldestStoredSeq = probe.entries[0]?.seq ?? null;
|
|
4641
|
+
const upperSeq =
|
|
4642
|
+
beforeSequence === null
|
|
4643
|
+
? Number.POSITIVE_INFINITY
|
|
4644
|
+
: Math.min(beforeSequence - 1, lastStoredSeq);
|
|
4645
|
+
const windowEndSeq = Math.min(upperSeq, lastStoredSeq);
|
|
4646
|
+
let afterSeq = options?.all ? 0 : Math.max(0, windowEndSeq - limit);
|
|
4528
4647
|
const entries: Array<{ seq: number; line: string }> = [];
|
|
4529
|
-
while (entries.length < limit) {
|
|
4648
|
+
while (entries.length < limit && afterSeq < upperSeq) {
|
|
4530
4649
|
const page = await fetchPage(
|
|
4531
4650
|
afterSeq,
|
|
4532
|
-
Math.min(
|
|
4651
|
+
Math.min(
|
|
4652
|
+
RUN_LOGS_PAGE_LIMIT,
|
|
4653
|
+
limit - entries.length,
|
|
4654
|
+
upperSeq - afterSeq,
|
|
4655
|
+
),
|
|
4533
4656
|
);
|
|
4534
|
-
|
|
4657
|
+
const inWindow = page.entries.filter((entry) => entry.seq <= upperSeq);
|
|
4658
|
+
if (inWindow.length === 0) {
|
|
4535
4659
|
break;
|
|
4536
4660
|
}
|
|
4537
|
-
entries.push(...
|
|
4538
|
-
afterSeq =
|
|
4539
|
-
if (!page.hasMore) {
|
|
4661
|
+
entries.push(...inWindow);
|
|
4662
|
+
afterSeq = inWindow[inWindow.length - 1]!.seq;
|
|
4663
|
+
if (!page.hasMore || inWindow.length < page.entries.length) {
|
|
4540
4664
|
break;
|
|
4541
4665
|
}
|
|
4542
4666
|
}
|
|
4543
4667
|
const firstSequence = entries.length > 0 ? entries[0]!.seq : null;
|
|
4544
4668
|
const lastSequence =
|
|
4545
4669
|
entries.length > 0 ? entries[entries.length - 1]!.seq : null;
|
|
4670
|
+
const olderLinesExist =
|
|
4671
|
+
firstSequence !== null &&
|
|
4672
|
+
oldestStoredSeq !== null &&
|
|
4673
|
+
firstSequence > oldestStoredSeq;
|
|
4546
4674
|
return {
|
|
4547
4675
|
runId: probe.runId,
|
|
4548
4676
|
totalCount: probe.totalLogCount,
|
|
@@ -4550,7 +4678,8 @@ export class DeeplineClient {
|
|
|
4550
4678
|
firstSequence,
|
|
4551
4679
|
lastSequence,
|
|
4552
4680
|
truncated: entries.length < probe.totalLogCount,
|
|
4553
|
-
hasMore:
|
|
4681
|
+
hasMore: olderLinesExist,
|
|
4682
|
+
nextCursor: olderLinesExist ? encodeRunLogsCursor(firstSequence) : null,
|
|
4554
4683
|
entries: entries.map((entry) => entry.line),
|
|
4555
4684
|
view: options?.all ? 'all' : 'tail',
|
|
4556
4685
|
...(probe.logsTruncated ? { logsTruncated: true } : {}),
|
|
@@ -481,16 +481,10 @@ export type CsvOptions = PlayAuthoringCsvOptions;
|
|
|
481
481
|
export type DeeplinePlayRuntimeContext = PlayAuthoringRuntimeContext;
|
|
482
482
|
|
|
483
483
|
/**
|
|
484
|
-
* Handle to a
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* This handle is the SDK-context equivalent of `deepline plays run` and
|
|
490
|
-
* `POST /api/v2/plays/run`: every surface returns a run id first, then exposes
|
|
491
|
-
* the completed user output through `PlayJob.get()` or the status endpoint's
|
|
492
|
-
* `result` field. Runtime logs are available from `status().progress.logs` and
|
|
493
|
-
* are intentionally separate from the returned output object.
|
|
484
|
+
* Handle to a Play execution: inspect status, stream logs, await output, or cancel.
|
|
485
|
+
* Like `deepline plays run` and `POST /api/v2/plays/run`, it returns a run id first.
|
|
486
|
+
* Read completed output through `PlayJob.get()` or the status endpoint's `result` field;
|
|
487
|
+
* runtime logs in `status().progress.logs` are separate from that output.
|
|
494
488
|
*
|
|
495
489
|
* @typeParam TOutput - The play's return type
|
|
496
490
|
*
|
|
@@ -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.276',
|
|
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: {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuation cursor for the bounded run-log tail.
|
|
3
|
+
*
|
|
4
|
+
* `runs logs` returns the LAST `limit` stored lines, so the lines a caller has
|
|
5
|
+
* not seen yet are the OLDER ones. The cursor names the exclusive upper
|
|
6
|
+
* sequence bound of the next (older) page. Treat it as opaque; the `before:`
|
|
7
|
+
* prefix keeps it distinct from runtime event-search cursors, which share the
|
|
8
|
+
* `--cursor` flag on `deepline runs logs`.
|
|
9
|
+
*/
|
|
10
|
+
const RUN_LOGS_CURSOR_PREFIX = 'before:';
|
|
11
|
+
|
|
12
|
+
export function encodeRunLogsCursor(beforeSequence: number): string {
|
|
13
|
+
return `${RUN_LOGS_CURSOR_PREFIX}${beforeSequence}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isRunLogsCursor(value: string): boolean {
|
|
17
|
+
return value.startsWith(RUN_LOGS_CURSOR_PREFIX);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Returns the exclusive upper sequence bound, or throws on a foreign token. */
|
|
21
|
+
export function parseRunLogsCursor(value: string): number {
|
|
22
|
+
const raw = isRunLogsCursor(value)
|
|
23
|
+
? value.slice(RUN_LOGS_CURSOR_PREFIX.length)
|
|
24
|
+
: '';
|
|
25
|
+
const parsed = /^[1-9]\d*$/.test(raw) ? Number(raw) : NaN;
|
|
26
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
27
|
+
throw Object.assign(
|
|
28
|
+
new Error(
|
|
29
|
+
`Invalid run log cursor ${JSON.stringify(value)}. Pass the nextCursor value from a previous deepline runs logs result.`,
|
|
30
|
+
),
|
|
31
|
+
{ exitCode: 2 },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
@@ -1075,6 +1075,12 @@ export type PlayLiveEvent = LiveEventEnvelope<unknown> & {
|
|
|
1075
1075
|
export interface StopPlayRunResult {
|
|
1076
1076
|
/** Public play-run identifier the stop request targeted. */
|
|
1077
1077
|
runId: string;
|
|
1078
|
+
/**
|
|
1079
|
+
* Typed stop outcome: `stopped` (terminal), `cancelling` (cancellation
|
|
1080
|
+
* durably queued; cleanup and the final run update are pending, not an
|
|
1081
|
+
* error), or `failed` (the stop was not confirmed). Absent on older servers.
|
|
1082
|
+
*/
|
|
1083
|
+
state?: 'stopped' | 'cancelling' | 'failed';
|
|
1078
1084
|
/** Whether the server confirmed the run was stopped. */
|
|
1079
1085
|
stopped: boolean;
|
|
1080
1086
|
/** True when the durable cancellation lane accepted the request and is draining. */
|
|
@@ -1084,11 +1090,12 @@ export interface StopPlayRunResult {
|
|
|
1084
1090
|
/** True if HITL state or its same-message Slack card still needs retrying. */
|
|
1085
1091
|
hitlCancellationPending?: boolean;
|
|
1086
1092
|
/**
|
|
1087
|
-
* True when the scheduler
|
|
1088
|
-
*
|
|
1093
|
+
* True only when the scheduler and the canonical run ledger disagree (for
|
|
1094
|
+
* example a terminal scheduler run whose ledger projection is pending), so
|
|
1095
|
+
* the stop could not be confirmed. Never set for a queued cancellation.
|
|
1089
1096
|
*/
|
|
1090
1097
|
staleSchedulerState?: boolean;
|
|
1091
|
-
/** Server-side error detail when the stop
|
|
1098
|
+
/** Server-side error detail when the stop failed (`state: 'failed'`). */
|
|
1092
1099
|
error?: string;
|
|
1093
1100
|
}
|
|
1094
1101
|
|
|
@@ -125,24 +125,6 @@
|
|
|
125
125
|
"failureAlert": "github_workflow_run",
|
|
126
126
|
"domainReportRequired": true
|
|
127
127
|
},
|
|
128
|
-
{
|
|
129
|
-
"id": "github.convex-preview-usage-limit-reset",
|
|
130
|
-
"name": "Reset Convex Preview usage limits",
|
|
131
|
-
"scheduler": "github_actions",
|
|
132
|
-
"cadence": "0 0 1 * *",
|
|
133
|
-
"timezone": "UTC",
|
|
134
|
-
"source": ".github/workflows/apply-preview-convex-usage-limits.yml",
|
|
135
|
-
"locator": "schedule:",
|
|
136
|
-
"owner": "platform",
|
|
137
|
-
"criticality": "operational",
|
|
138
|
-
"purpose": "Restore the strict per-deployment usage thresholds for current-project Convex Previews after their monthly UTC usage windows reset.",
|
|
139
|
-
"successDefinition": "Every current-project Preview has all eight daily and monthly limits active at the strict baseline; only explicitly retired inventory entries are skipped.",
|
|
140
|
-
"failureDefinition": "Preview inventory is unavailable, a deployment cannot be addressed, or any usage threshold cannot be set and verified.",
|
|
141
|
-
"startGraceMs": 900000,
|
|
142
|
-
"runTimeoutMs": 2700000,
|
|
143
|
-
"failureAlert": "github_workflow_run",
|
|
144
|
-
"domainReportRequired": true
|
|
145
|
-
},
|
|
146
128
|
{
|
|
147
129
|
"id": "github.daily-slack-report",
|
|
148
130
|
"name": "Daily Slack report",
|
|
@@ -325,15 +307,18 @@
|
|
|
325
307
|
},
|
|
326
308
|
{
|
|
327
309
|
"id": "github.runtime-lifecycle",
|
|
328
|
-
"name": "
|
|
310
|
+
"name": "On-Demand Runtime Lifecycle Load Check",
|
|
311
|
+
"legacyNames": [
|
|
312
|
+
"Scheduled Runtime Lifecycle Load Monitor"
|
|
313
|
+
],
|
|
329
314
|
"scheduler": "github_actions",
|
|
330
|
-
"cadence": "
|
|
315
|
+
"cadence": "runAfter explicit workflow dispatch or PR label",
|
|
331
316
|
"timezone": "UTC",
|
|
332
317
|
"source": ".github/workflows/runtime-daytona-lifecycle-scheduled.yml",
|
|
333
|
-
"locator": "
|
|
318
|
+
"locator": "workflow_dispatch:",
|
|
334
319
|
"owner": "runtime",
|
|
335
320
|
"criticality": "operational",
|
|
336
|
-
"purpose": "Exercise the canonical Daytona/Play runtime lifecycle against the exact healthy Preview target and retain load evidence.",
|
|
321
|
+
"purpose": "Exercise the canonical Daytona/Play runtime lifecycle against the exact healthy Preview target when explicitly requested and retain load evidence.",
|
|
337
322
|
"successDefinition": "Target preparation, lifecycle bursts, correctness/latency checks, cleanup, and reporting complete.",
|
|
338
323
|
"failureDefinition": "Target resolution, lifecycle execution, evaluation, cleanup, or reporting fails.",
|
|
339
324
|
"startGraceMs": 600000,
|
|
@@ -361,19 +346,20 @@
|
|
|
361
346
|
},
|
|
362
347
|
{
|
|
363
348
|
"id": "github.runtime-shape",
|
|
364
|
-
"name": "
|
|
349
|
+
"name": "On-Demand Play Runtime Dataset Matrix",
|
|
365
350
|
"legacyNames": [
|
|
351
|
+
"Twice-Daily Play Runtime Dataset Matrix",
|
|
366
352
|
"Every-Six-Hours Play Runtime Dataset Matrix",
|
|
367
353
|
"Every-Two-Hours Play Runtime Dataset Matrix"
|
|
368
354
|
],
|
|
369
355
|
"scheduler": "github_actions",
|
|
370
|
-
"cadence": "
|
|
356
|
+
"cadence": "runAfter explicit workflow dispatch",
|
|
371
357
|
"timezone": "UTC",
|
|
372
358
|
"source": ".github/workflows/runtime-shape-hourly.yml",
|
|
373
|
-
"locator": "
|
|
359
|
+
"locator": "workflow_dispatch:",
|
|
374
360
|
"owner": "runtime",
|
|
375
361
|
"criticality": "operational",
|
|
376
|
-
"purpose": "Run the exact-SHA Play runtime dataset-shape matrix across isolated Preview lanes.",
|
|
362
|
+
"purpose": "Run the exact-SHA Play runtime dataset-shape matrix across isolated Preview lanes when explicitly requested.",
|
|
377
363
|
"successDefinition": "All required lanes pass and every lane is cleaned up with detailed results retained.",
|
|
378
364
|
"failureDefinition": "A lane cannot prepare, violates a shape invariant, or cleanup/evidence is incomplete.",
|
|
379
365
|
"startGraceMs": 600000,
|
|
@@ -44,10 +44,14 @@ export function isManuallyRunnableConvexCronJob(
|
|
|
44
44
|
return manuallyRunnableConvexCronJobIds.has(jobId);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/**
|
|
48
|
-
export function
|
|
47
|
+
/** Non-production cron roots run only when explicitly requested. */
|
|
48
|
+
export function isNonProductionOnDemandScheduledWork(
|
|
49
49
|
jobId: string,
|
|
50
50
|
environment: string,
|
|
51
51
|
): boolean {
|
|
52
|
-
|
|
52
|
+
const normalizedEnvironment = environment.trim().toLowerCase();
|
|
53
|
+
return (
|
|
54
|
+
['dev', 'development', 'preview'].includes(normalizedEnvironment) &&
|
|
55
|
+
isManuallyRunnableConvexCronJob(jobId)
|
|
56
|
+
);
|
|
53
57
|
}
|
|
@@ -261,8 +261,11 @@ export type PlaySchedulerRunHandle = {
|
|
|
261
261
|
observe(options?: {
|
|
262
262
|
signal?: AbortSignal;
|
|
263
263
|
}): AsyncIterable<PlaySchedulerProgressEvent>;
|
|
264
|
-
/**
|
|
265
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Cooperatively cancel the run. Resolves with the scheduler's typed
|
|
266
|
+
* outcome; a queued cancellation is an expected state, never an error.
|
|
267
|
+
*/
|
|
268
|
+
cancel(reason?: string): Promise<PlaySchedulerCancelOutcome>;
|
|
266
269
|
/** Inject an external event (HITL, webhook). */
|
|
267
270
|
signal(payload: PlaySchedulerSignalPayload): Promise<void>;
|
|
268
271
|
/** Block until terminal state and return final envelope. */
|
|
@@ -270,12 +273,17 @@ export type PlaySchedulerRunHandle = {
|
|
|
270
273
|
};
|
|
271
274
|
|
|
272
275
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
+
* Outcome of a scheduler cancel request.
|
|
277
|
+
*
|
|
278
|
+
* - `stopped`: the scheduler run is terminal (cancelled now, or it already
|
|
279
|
+
* reached a terminal state before the request).
|
|
280
|
+
* - `cancelling`: the durable engine cancellation lane accepted the request,
|
|
281
|
+
* but sandbox cleanup and the final run update are still pending. Callers
|
|
282
|
+
* surface this as an in-progress cancellation, never as success or failure.
|
|
276
283
|
*/
|
|
277
|
-
export
|
|
278
|
-
'
|
|
284
|
+
export type PlaySchedulerCancelOutcome =
|
|
285
|
+
| { state: 'stopped' }
|
|
286
|
+
| { state: 'cancelling' };
|
|
279
287
|
|
|
280
288
|
export type PlaySchedulerSignalPayload = {
|
|
281
289
|
kind: 'integration_event' | 'cancel' | 'custom';
|
|
@@ -25,6 +25,7 @@ import { PLAY_ADMISSION_MAX_ACTIVE_LIFECYCLES } from '../admission-transport-pol
|
|
|
25
25
|
import {
|
|
26
26
|
PLAY_SCHEDULER_BACKENDS,
|
|
27
27
|
type PlaySchedulerBackend,
|
|
28
|
+
type PlaySchedulerCancelOutcome,
|
|
28
29
|
type PlaySchedulerRunHandle,
|
|
29
30
|
type PlaySchedulerSubmitInput,
|
|
30
31
|
} from '../scheduler-backend';
|
|
@@ -46,7 +47,6 @@ import {
|
|
|
46
47
|
withRuntimeSchedulerClient,
|
|
47
48
|
withRuntimeSchedulerTransaction,
|
|
48
49
|
} from './postgres-pool';
|
|
49
|
-
import { RUNTIME_SCHEDULER_CANCELLATION_PENDING_CODE } from '../scheduler-backend';
|
|
50
50
|
import { resolveRuntimeSchedulerSchemaEnv } from './runtime-scheduler-schema';
|
|
51
51
|
import { withRuntimeSchedulerSubmitAdmission } from './submit-admission';
|
|
52
52
|
import {
|
|
@@ -75,7 +75,10 @@ export type AbsurdSchedulerBackendOptions = AbsurdSchedulerOptions & {
|
|
|
75
75
|
queueSuffix?: string | null;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
const cancellationFlights = new Map<
|
|
78
|
+
const cancellationFlights = new Map<
|
|
79
|
+
string,
|
|
80
|
+
Promise<PlaySchedulerCancelOutcome>
|
|
81
|
+
>();
|
|
79
82
|
const ABSURD_CANCEL_TASK_SAVEPOINT = 'deepline_cancel_absurd_task';
|
|
80
83
|
|
|
81
84
|
function quotePostgresIdentifier(identifier: string): string {
|
|
@@ -127,8 +130,8 @@ async function cancelAbsurdTaskIfPresent(
|
|
|
127
130
|
|
|
128
131
|
function runCancellationSingleFlight(
|
|
129
132
|
key: string,
|
|
130
|
-
operation: () => Promise<
|
|
131
|
-
): Promise<
|
|
133
|
+
operation: () => Promise<PlaySchedulerCancelOutcome>,
|
|
134
|
+
): Promise<PlaySchedulerCancelOutcome> {
|
|
132
135
|
const existing = cancellationFlights.get(key);
|
|
133
136
|
if (existing) return existing;
|
|
134
137
|
const flight = operation().finally(() => {
|
|
@@ -285,20 +288,17 @@ function absurdRunHandle(input: {
|
|
|
285
288
|
{ maxAttempts: 1 },
|
|
286
289
|
);
|
|
287
290
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
};
|
|
291
|
+
// A queued cancellation is the normal Absurd outcome: the worker owns
|
|
292
|
+
// sandbox cleanup and the final run update. Report it as a typed state, not
|
|
293
|
+
// an error; `stopped` means the scheduler result was already terminal.
|
|
294
|
+
const requestCancellation = (
|
|
295
|
+
reason?: string,
|
|
296
|
+
): Promise<PlaySchedulerCancelOutcome> =>
|
|
297
|
+
runCancellationSingleFlight(cancellationKey, async () =>
|
|
298
|
+
(await queueCancellation(reason))
|
|
299
|
+
? { state: 'cancelling' }
|
|
300
|
+
: { state: 'stopped' },
|
|
301
|
+
);
|
|
302
302
|
|
|
303
303
|
return {
|
|
304
304
|
...delegate,
|
|
@@ -20098,6 +20098,7 @@ export function postgresSchedulerRunHandle(
|
|
|
20098
20098
|
priority: 'critical',
|
|
20099
20099
|
},
|
|
20100
20100
|
);
|
|
20101
|
+
return { state: 'stopped' };
|
|
20101
20102
|
},
|
|
20102
20103
|
signal: async (payload) => {
|
|
20103
20104
|
if (payload.kind === 'cancel') {
|