deepline 0.1.91 → 0.1.94
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 +4012 -705
- package/dist/cli/index.mjs +4028 -714
- package/dist/index.d.mts +232 -108
- package/dist/index.d.ts +232 -108
- package/dist/index.js +1145 -99
- package/dist/index.mjs +1134 -99
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +87 -20
- package/dist/repo/apps/play-runner-workers/src/entry.ts +75 -22
- package/dist/repo/sdk/src/client.ts +412 -40
- package/dist/repo/sdk/src/index.ts +1 -0
- package/dist/repo/sdk/src/play.ts +51 -0
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/sdk/src/runs/observe-transport.ts +481 -0
- package/dist/repo/sdk/src/stream-reconnect.ts +44 -0
- package/dist/repo/sdk/src/types.ts +10 -3
- package/dist/repo/shared_libs/play-runtime/email-status.ts +10 -36
- package/dist/repo/shared_libs/play-runtime/extractor-targets.ts +3 -3
- package/dist/repo/shared_libs/play-runtime/live-events.ts +217 -0
- package/dist/repo/shared_libs/play-runtime/run-ledger.ts +1074 -0
- package/dist/repo/shared_libs/play-runtime/run-snapshot-stream.ts +581 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +44 -0
- package/dist/repo/shared_libs/plays/secret-guardrails.ts +22 -11
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -43,6 +53,7 @@ __export(src_exports, {
|
|
|
43
53
|
PLAY_BOOTSTRAP_TEMPLATES: () => PLAY_BOOTSTRAP_TEMPLATES,
|
|
44
54
|
PROD_URL: () => PROD_URL,
|
|
45
55
|
RateLimitError: () => RateLimitError,
|
|
56
|
+
RunObserveTransportUnavailableError: () => RunObserveTransportUnavailableError,
|
|
46
57
|
SDK_API_CONTRACT: () => SDK_API_CONTRACT,
|
|
47
58
|
SDK_VERSION: () => SDK_VERSION,
|
|
48
59
|
defineInput: () => defineInput,
|
|
@@ -246,10 +257,10 @@ var import_node_path2 = require("path");
|
|
|
246
257
|
|
|
247
258
|
// src/release.ts
|
|
248
259
|
var SDK_RELEASE = {
|
|
249
|
-
version: "0.1.
|
|
260
|
+
version: "0.1.94",
|
|
250
261
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
251
262
|
supportPolicy: {
|
|
252
|
-
latest: "0.1.
|
|
263
|
+
latest: "0.1.94",
|
|
253
264
|
minimumSupported: "0.1.53",
|
|
254
265
|
deprecatedBelow: "0.1.53"
|
|
255
266
|
}
|
|
@@ -604,6 +615,807 @@ function sleep(ms) {
|
|
|
604
615
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
605
616
|
}
|
|
606
617
|
|
|
618
|
+
// src/stream-reconnect.ts
|
|
619
|
+
var STREAM_RECONNECT_BASE_DELAY_MS = 500;
|
|
620
|
+
var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
|
|
621
|
+
var STREAM_HEALTHY_CONNECTION_MS = 3e4;
|
|
622
|
+
function streamReconnectDelayMs(attempt) {
|
|
623
|
+
const cappedExponentialMs = Math.min(
|
|
624
|
+
STREAM_RECONNECT_MAX_DELAY_MS,
|
|
625
|
+
STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
|
|
626
|
+
);
|
|
627
|
+
return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
|
|
628
|
+
}
|
|
629
|
+
function isTransientPlayStreamError(error) {
|
|
630
|
+
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
631
|
+
return error.statusCode >= 500 && error.statusCode < 600;
|
|
632
|
+
}
|
|
633
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
634
|
+
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
635
|
+
text
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// ../shared_libs/play-runtime/live-events.ts
|
|
640
|
+
function resolveTimingWindow(input) {
|
|
641
|
+
const startedAt = typeof input.startedAt === "number" && Number.isFinite(input.startedAt) ? input.startedAt : null;
|
|
642
|
+
const completedAt = typeof input.completedAt === "number" && Number.isFinite(input.completedAt) ? input.completedAt : null;
|
|
643
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : null;
|
|
644
|
+
return {
|
|
645
|
+
startedAt,
|
|
646
|
+
completedAt,
|
|
647
|
+
updatedAt
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// ../shared_libs/play-runtime/run-ledger.ts
|
|
652
|
+
var LOG_TAIL_LIMIT = 100;
|
|
653
|
+
function isRecord(value) {
|
|
654
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
655
|
+
}
|
|
656
|
+
function finiteNumber(value) {
|
|
657
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
658
|
+
}
|
|
659
|
+
function optionalFiniteNumber(value) {
|
|
660
|
+
const normalized = finiteNumber(value);
|
|
661
|
+
return normalized === null ? void 0 : normalized;
|
|
662
|
+
}
|
|
663
|
+
function optionalString(value) {
|
|
664
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
665
|
+
}
|
|
666
|
+
function optionalNullableString(value) {
|
|
667
|
+
if (value === null) return null;
|
|
668
|
+
return optionalString(value);
|
|
669
|
+
}
|
|
670
|
+
function normalizePlayRunLedgerStatus(value) {
|
|
671
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
672
|
+
switch (normalized) {
|
|
673
|
+
case "queued":
|
|
674
|
+
case "pending":
|
|
675
|
+
return "queued";
|
|
676
|
+
case "running":
|
|
677
|
+
case "started":
|
|
678
|
+
return "running";
|
|
679
|
+
case "waiting":
|
|
680
|
+
return "waiting";
|
|
681
|
+
case "completed":
|
|
682
|
+
case "complete":
|
|
683
|
+
case "succeeded":
|
|
684
|
+
return "completed";
|
|
685
|
+
case "failed":
|
|
686
|
+
case "error":
|
|
687
|
+
return "failed";
|
|
688
|
+
case "cancelled":
|
|
689
|
+
case "canceled":
|
|
690
|
+
return "cancelled";
|
|
691
|
+
case "terminated":
|
|
692
|
+
return "terminated";
|
|
693
|
+
case "timed_out":
|
|
694
|
+
case "timeout":
|
|
695
|
+
return "timed_out";
|
|
696
|
+
default:
|
|
697
|
+
return "unknown";
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function createEmptyPlayRunLedgerSnapshot(input) {
|
|
701
|
+
const status = normalizePlayRunLedgerStatus(input.status ?? "unknown");
|
|
702
|
+
const startedAt = finiteNumber(input.startedAt) ?? null;
|
|
703
|
+
const finishedAt = finiteNumber(input.finishedAt) ?? null;
|
|
704
|
+
return {
|
|
705
|
+
runId: input.runId,
|
|
706
|
+
playName: input.playName ?? null,
|
|
707
|
+
status,
|
|
708
|
+
error: optionalNullableString(input.error) ?? null,
|
|
709
|
+
createdAt: finiteNumber(input.createdAt) ?? null,
|
|
710
|
+
startedAt,
|
|
711
|
+
updatedAt: finiteNumber(input.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input.createdAt) ?? null,
|
|
712
|
+
finishedAt,
|
|
713
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
|
|
714
|
+
orderedStepIds: [],
|
|
715
|
+
stepsById: {},
|
|
716
|
+
logTail: [],
|
|
717
|
+
totalLogCount: 0,
|
|
718
|
+
logsTruncated: false,
|
|
719
|
+
activeStepId: null,
|
|
720
|
+
activeArtifactTableNamespace: null,
|
|
721
|
+
resultTableNamespace: null
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function normalizePlayRunLedgerSnapshot(value, fallback) {
|
|
725
|
+
if (!isRecord(value)) {
|
|
726
|
+
return createEmptyPlayRunLedgerSnapshot(fallback);
|
|
727
|
+
}
|
|
728
|
+
const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
|
|
729
|
+
(entry) => typeof entry === "string" && Boolean(entry.trim())
|
|
730
|
+
) : [];
|
|
731
|
+
const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
|
|
732
|
+
const stepsById = {};
|
|
733
|
+
for (const [stepId, rawStep] of Object.entries(rawSteps)) {
|
|
734
|
+
if (!stepId.trim() || !isRecord(rawStep)) continue;
|
|
735
|
+
const rawStatus = normalizeStepStatus(rawStep.status);
|
|
736
|
+
if (!rawStatus) continue;
|
|
737
|
+
const completedAt = finiteNumber(rawStep.completedAt);
|
|
738
|
+
const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
|
|
739
|
+
const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
|
|
740
|
+
stepsById[stepId] = {
|
|
741
|
+
stepId,
|
|
742
|
+
label: optionalString(rawStep.label),
|
|
743
|
+
kind: optionalString(rawStep.kind),
|
|
744
|
+
status,
|
|
745
|
+
artifactTableNamespace: optionalNullableString(
|
|
746
|
+
rawStep.artifactTableNamespace
|
|
747
|
+
),
|
|
748
|
+
startedAt: finiteNumber(rawStep.startedAt),
|
|
749
|
+
completedAt,
|
|
750
|
+
updatedAt: finiteNumber(rawStep.updatedAt),
|
|
751
|
+
progress: rawProgress ? normalizeStepProgress(rawProgress) : null
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
|
|
755
|
+
const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
|
|
756
|
+
const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
|
|
757
|
+
const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
|
|
758
|
+
const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
|
|
759
|
+
const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
|
|
760
|
+
const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
|
|
761
|
+
return {
|
|
762
|
+
runId: optionalString(value.runId) ?? fallback.runId,
|
|
763
|
+
playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
|
|
764
|
+
status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
|
|
765
|
+
error,
|
|
766
|
+
createdAt,
|
|
767
|
+
startedAt,
|
|
768
|
+
updatedAt,
|
|
769
|
+
finishedAt,
|
|
770
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
|
|
771
|
+
orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
|
|
772
|
+
stepsById,
|
|
773
|
+
logTail: logTail.slice(-LOG_TAIL_LIMIT),
|
|
774
|
+
// Snapshots persisted before totalLogCount existed only know the retained
|
|
775
|
+
// tail, so the best lower bound for the cumulative count is the tail size.
|
|
776
|
+
totalLogCount: Math.max(
|
|
777
|
+
finiteNumber(value.totalLogCount) ?? logTail.length,
|
|
778
|
+
logTail.length
|
|
779
|
+
),
|
|
780
|
+
logsTruncated: value.logsTruncated === true,
|
|
781
|
+
activeStepId: optionalNullableString(value.activeStepId),
|
|
782
|
+
activeArtifactTableNamespace: optionalNullableString(
|
|
783
|
+
value.activeArtifactTableNamespace
|
|
784
|
+
),
|
|
785
|
+
resultTableNamespace: optionalNullableString(value.resultTableNamespace),
|
|
786
|
+
resultSummary: value.resultSummary,
|
|
787
|
+
result: value.result
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
function normalizeStepStatus(value) {
|
|
791
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
792
|
+
if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
|
|
793
|
+
return normalized;
|
|
794
|
+
}
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
function normalizeStepProgress(value) {
|
|
798
|
+
return {
|
|
799
|
+
...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
|
|
800
|
+
...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
|
|
801
|
+
...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
|
|
802
|
+
...optionalString(value.message) ? { message: optionalString(value.message) } : {},
|
|
803
|
+
...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
|
|
804
|
+
artifactTableNamespace: optionalNullableString(
|
|
805
|
+
value.artifactTableNamespace
|
|
806
|
+
)
|
|
807
|
+
} : {},
|
|
808
|
+
...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ../shared_libs/play-runtime/run-snapshot-stream.ts
|
|
813
|
+
function normalizePlayRunLiveStatus(value) {
|
|
814
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
815
|
+
switch (normalized) {
|
|
816
|
+
case "queued":
|
|
817
|
+
case "pending":
|
|
818
|
+
return "running";
|
|
819
|
+
case "running":
|
|
820
|
+
case "started":
|
|
821
|
+
return "running";
|
|
822
|
+
case "completed":
|
|
823
|
+
case "complete":
|
|
824
|
+
case "succeeded":
|
|
825
|
+
return "completed";
|
|
826
|
+
case "failed":
|
|
827
|
+
case "error":
|
|
828
|
+
return "failed";
|
|
829
|
+
case "cancelled":
|
|
830
|
+
case "canceled":
|
|
831
|
+
return "cancelled";
|
|
832
|
+
case "terminated":
|
|
833
|
+
return "terminated";
|
|
834
|
+
case "timed_out":
|
|
835
|
+
case "timeout":
|
|
836
|
+
return "timed_out";
|
|
837
|
+
default:
|
|
838
|
+
return "unknown";
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function isTerminalPlayRunLiveStatus(status) {
|
|
842
|
+
return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
|
|
843
|
+
}
|
|
844
|
+
function buildSnapshotFromLedger(snapshot) {
|
|
845
|
+
const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
|
|
846
|
+
nodeId: step.stepId,
|
|
847
|
+
status: step.status,
|
|
848
|
+
artifactTableNamespace: step.artifactTableNamespace ?? null,
|
|
849
|
+
progress: step.progress ? {
|
|
850
|
+
completed: step.progress.completed,
|
|
851
|
+
total: step.progress.total,
|
|
852
|
+
failed: step.progress.failed,
|
|
853
|
+
message: step.progress.message,
|
|
854
|
+
artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
|
|
855
|
+
startedAt: step.startedAt ?? null,
|
|
856
|
+
completedAt: step.completedAt ?? null,
|
|
857
|
+
updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
|
|
858
|
+
} : null,
|
|
859
|
+
startedAt: step.startedAt ?? null,
|
|
860
|
+
completedAt: step.completedAt ?? null,
|
|
861
|
+
updatedAt: step.updatedAt ?? null
|
|
862
|
+
}));
|
|
863
|
+
return {
|
|
864
|
+
runId: snapshot.runId,
|
|
865
|
+
status: normalizePlayRunLiveStatus(snapshot.status),
|
|
866
|
+
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
867
|
+
logs: snapshot.logTail,
|
|
868
|
+
totalLogCount: snapshot.totalLogCount,
|
|
869
|
+
...snapshot.logsTruncated ? { logsTruncated: true } : {},
|
|
870
|
+
activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
|
|
871
|
+
resultTableNamespace: snapshot.resultTableNamespace ?? null,
|
|
872
|
+
nodeStates,
|
|
873
|
+
activeNodeId: snapshot.activeStepId ?? null
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function buildPlayRunStatusSnapshot(input) {
|
|
877
|
+
const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input.run.runSnapshot, {
|
|
878
|
+
runId: input.run.workflowId,
|
|
879
|
+
playName: input.run.name ?? null,
|
|
880
|
+
status: input.run.status,
|
|
881
|
+
createdAt: input.run.createdAt ?? null,
|
|
882
|
+
startedAt: input.run.startedAt ?? null,
|
|
883
|
+
updatedAt: input.run.updatedAt ?? null,
|
|
884
|
+
finishedAt: input.run.finishedAt ?? null
|
|
885
|
+
});
|
|
886
|
+
return buildSnapshotFromLedger(ledgerSnapshot);
|
|
887
|
+
}
|
|
888
|
+
function makeRunStreamEvent(input) {
|
|
889
|
+
return {
|
|
890
|
+
...input,
|
|
891
|
+
scope: "play",
|
|
892
|
+
at: input.at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
|
|
896
|
+
runSignature: "",
|
|
897
|
+
snapshotSignature: "",
|
|
898
|
+
stepStatusSignature: "",
|
|
899
|
+
stepProgressSignature: "",
|
|
900
|
+
lastLogSeq: 0
|
|
901
|
+
};
|
|
902
|
+
function getSnapshotCursor(snapshot) {
|
|
903
|
+
return String(snapshot.updatedAt ?? Date.now());
|
|
904
|
+
}
|
|
905
|
+
function getRunSignature(snapshot) {
|
|
906
|
+
return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
|
|
907
|
+
}
|
|
908
|
+
function getStepStatusSignature(snapshot) {
|
|
909
|
+
return snapshot.nodeStates.map(
|
|
910
|
+
(state) => [
|
|
911
|
+
state.nodeId,
|
|
912
|
+
state.status,
|
|
913
|
+
state.artifactTableNamespace ?? "",
|
|
914
|
+
state.startedAt ?? "",
|
|
915
|
+
state.completedAt ?? "",
|
|
916
|
+
state.progress?.startedAt ?? "",
|
|
917
|
+
state.progress?.completedAt ?? "",
|
|
918
|
+
state.updatedAt ?? "",
|
|
919
|
+
state.progress?.updatedAt ?? ""
|
|
920
|
+
].join(":")
|
|
921
|
+
).join("|");
|
|
922
|
+
}
|
|
923
|
+
function getStepProgressSignature(snapshot) {
|
|
924
|
+
return snapshot.nodeStates.map(
|
|
925
|
+
(state) => [
|
|
926
|
+
state.nodeId,
|
|
927
|
+
state.progress?.completed ?? "",
|
|
928
|
+
state.progress?.total ?? "",
|
|
929
|
+
state.progress?.failed ?? "",
|
|
930
|
+
state.progress?.artifactTableNamespace ?? "",
|
|
931
|
+
state.progress?.startedAt ?? "",
|
|
932
|
+
state.progress?.completedAt ?? "",
|
|
933
|
+
state.progress?.updatedAt ?? "",
|
|
934
|
+
state.progress?.message ?? ""
|
|
935
|
+
].join(":")
|
|
936
|
+
).join("|");
|
|
937
|
+
}
|
|
938
|
+
function getSnapshotSignature(snapshot) {
|
|
939
|
+
return JSON.stringify(snapshot);
|
|
940
|
+
}
|
|
941
|
+
function resolvePlayRunLogGap(snapshot, lastLogSeq) {
|
|
942
|
+
if (snapshot.totalLogCount <= lastLogSeq) {
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
|
|
946
|
+
if (lastLogSeq + 1 >= tailFirstSeq) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
missingCount: tailFirstSeq - 1 - lastLogSeq,
|
|
951
|
+
tailFirstSeq
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function diffLogLines(input) {
|
|
955
|
+
const { logs, totalLogCount } = input.snapshot;
|
|
956
|
+
if (totalLogCount <= input.lastLogSeq) {
|
|
957
|
+
return { lines: [], lastLogSeq: input.lastLogSeq, firstSeq: null };
|
|
958
|
+
}
|
|
959
|
+
const tailFirstSeq = totalLogCount - logs.length + 1;
|
|
960
|
+
if (input.lastLogSeq + 1 >= tailFirstSeq) {
|
|
961
|
+
return {
|
|
962
|
+
lines: logs.slice(input.lastLogSeq + 1 - tailFirstSeq),
|
|
963
|
+
lastLogSeq: totalLogCount,
|
|
964
|
+
firstSeq: input.lastLogSeq + 1
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
const missingCount = tailFirstSeq - 1 - input.lastLogSeq;
|
|
968
|
+
return {
|
|
969
|
+
lines: [
|
|
970
|
+
`[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
|
|
971
|
+
...logs
|
|
972
|
+
],
|
|
973
|
+
lastLogSeq: totalLogCount,
|
|
974
|
+
firstSeq: null
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function diffPlayRunStreamEvents(input) {
|
|
978
|
+
const { snapshot, streamId, previous } = input;
|
|
979
|
+
const cursor = getSnapshotCursor(snapshot);
|
|
980
|
+
const logDiff = diffLogLines({
|
|
981
|
+
snapshot,
|
|
982
|
+
lastLogSeq: previous.lastLogSeq
|
|
983
|
+
});
|
|
984
|
+
const next = {
|
|
985
|
+
runSignature: getRunSignature(snapshot),
|
|
986
|
+
stepStatusSignature: getStepStatusSignature(snapshot),
|
|
987
|
+
stepProgressSignature: getStepProgressSignature(snapshot),
|
|
988
|
+
snapshotSignature: getSnapshotSignature(snapshot),
|
|
989
|
+
lastLogSeq: logDiff.lastLogSeq
|
|
990
|
+
};
|
|
991
|
+
const events = [];
|
|
992
|
+
if (next.stepStatusSignature !== previous.stepStatusSignature) {
|
|
993
|
+
for (const state of snapshot.nodeStates) {
|
|
994
|
+
if (state.status === "idle") {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
|
|
998
|
+
const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
|
|
999
|
+
events.push(
|
|
1000
|
+
makeRunStreamEvent({
|
|
1001
|
+
cursor,
|
|
1002
|
+
streamId,
|
|
1003
|
+
type: "play.step.status",
|
|
1004
|
+
payload: {
|
|
1005
|
+
runId: snapshot.runId,
|
|
1006
|
+
stepId: state.nodeId,
|
|
1007
|
+
status: state.status,
|
|
1008
|
+
artifactTableNamespace: state.artifactTableNamespace ?? null,
|
|
1009
|
+
...resolveTimingWindow({
|
|
1010
|
+
startedAt: persistedStartedAt,
|
|
1011
|
+
completedAt: persistedCompletedAt,
|
|
1012
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
1013
|
+
})
|
|
1014
|
+
}
|
|
1015
|
+
})
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (next.stepProgressSignature !== previous.stepProgressSignature) {
|
|
1020
|
+
for (const state of snapshot.nodeStates) {
|
|
1021
|
+
if (!state.progress) {
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
events.push(
|
|
1025
|
+
makeRunStreamEvent({
|
|
1026
|
+
cursor: String(
|
|
1027
|
+
state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
|
|
1028
|
+
),
|
|
1029
|
+
streamId,
|
|
1030
|
+
type: "play.step.progress",
|
|
1031
|
+
payload: {
|
|
1032
|
+
runId: snapshot.runId,
|
|
1033
|
+
stepId: state.nodeId,
|
|
1034
|
+
completed: state.progress.completed,
|
|
1035
|
+
total: state.progress.total,
|
|
1036
|
+
failed: state.progress.failed,
|
|
1037
|
+
message: state.progress.message,
|
|
1038
|
+
artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
|
|
1039
|
+
...resolveTimingWindow({
|
|
1040
|
+
startedAt: state.startedAt ?? state.progress.startedAt ?? null,
|
|
1041
|
+
completedAt: state.completedAt ?? state.progress.completedAt ?? null,
|
|
1042
|
+
updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
|
|
1043
|
+
})
|
|
1044
|
+
}
|
|
1045
|
+
})
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (logDiff.lines.length > 0) {
|
|
1050
|
+
events.push(
|
|
1051
|
+
makeRunStreamEvent({
|
|
1052
|
+
cursor,
|
|
1053
|
+
streamId,
|
|
1054
|
+
type: "play.run.log",
|
|
1055
|
+
payload: {
|
|
1056
|
+
runId: snapshot.runId,
|
|
1057
|
+
lines: logDiff.lines,
|
|
1058
|
+
source: "worker",
|
|
1059
|
+
...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
|
|
1060
|
+
totalLogCount: snapshot.totalLogCount
|
|
1061
|
+
}
|
|
1062
|
+
})
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
if (next.snapshotSignature !== previous.snapshotSignature) {
|
|
1066
|
+
const enrichedNodeStates = snapshot.nodeStates.map((state) => {
|
|
1067
|
+
const timing = resolveTimingWindow({
|
|
1068
|
+
startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
|
|
1069
|
+
completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
|
|
1070
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
1071
|
+
});
|
|
1072
|
+
return {
|
|
1073
|
+
...state,
|
|
1074
|
+
...timing,
|
|
1075
|
+
progress: state.progress ? {
|
|
1076
|
+
...state.progress,
|
|
1077
|
+
...resolveTimingWindow({
|
|
1078
|
+
startedAt: state.progress.startedAt ?? state.startedAt ?? null,
|
|
1079
|
+
completedAt: state.progress.completedAt ?? state.completedAt ?? null,
|
|
1080
|
+
updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
|
|
1081
|
+
})
|
|
1082
|
+
} : state.progress
|
|
1083
|
+
};
|
|
1084
|
+
});
|
|
1085
|
+
events.push(
|
|
1086
|
+
makeRunStreamEvent({
|
|
1087
|
+
cursor,
|
|
1088
|
+
streamId,
|
|
1089
|
+
type: "play.run.snapshot",
|
|
1090
|
+
payload: { ...snapshot, nodeStates: enrichedNodeStates }
|
|
1091
|
+
})
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
if (next.runSignature !== previous.runSignature) {
|
|
1095
|
+
events.push(
|
|
1096
|
+
makeRunStreamEvent({
|
|
1097
|
+
cursor,
|
|
1098
|
+
streamId,
|
|
1099
|
+
type: "play.run.status",
|
|
1100
|
+
payload: {
|
|
1101
|
+
runId: snapshot.runId,
|
|
1102
|
+
status: snapshot.status,
|
|
1103
|
+
updatedAt: snapshot.updatedAt
|
|
1104
|
+
}
|
|
1105
|
+
})
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
return { events, next };
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/runs/observe-transport.ts
|
|
1112
|
+
var RunObserveTransportUnavailableError = class extends Error {
|
|
1113
|
+
constructor(message, reason) {
|
|
1114
|
+
super(message);
|
|
1115
|
+
this.reason = reason;
|
|
1116
|
+
this.name = "RunObserveTransportUnavailableError";
|
|
1117
|
+
}
|
|
1118
|
+
reason;
|
|
1119
|
+
};
|
|
1120
|
+
var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
|
|
1121
|
+
var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
|
|
1122
|
+
var OBSERVE_STALE_WARNING_MS = 12e4;
|
|
1123
|
+
var OBSERVE_WATCHDOG_TICK_MS = 5e3;
|
|
1124
|
+
var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
|
|
1125
|
+
var BACKFILL_PAGE_LIMIT = 1e3;
|
|
1126
|
+
var BACKFILL_MAX_PAGES = 30;
|
|
1127
|
+
var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
|
|
1128
|
+
var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
|
|
1129
|
+
function errorText(error) {
|
|
1130
|
+
return error instanceof Error ? error.message : String(error);
|
|
1131
|
+
}
|
|
1132
|
+
async function mintRunObserveGrant(http, runId) {
|
|
1133
|
+
let response;
|
|
1134
|
+
try {
|
|
1135
|
+
response = await http.post(
|
|
1136
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
|
|
1137
|
+
{}
|
|
1138
|
+
);
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
if (error instanceof DeeplineError) {
|
|
1141
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
1142
|
+
throw error;
|
|
1143
|
+
}
|
|
1144
|
+
throw new RunObserveTransportUnavailableError(
|
|
1145
|
+
`observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
|
|
1146
|
+
"grant_endpoint_unavailable"
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
throw new RunObserveTransportUnavailableError(
|
|
1150
|
+
`observe-grant request failed: ${errorText(error)}`,
|
|
1151
|
+
"grant_request_failed"
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
const grant = response;
|
|
1155
|
+
if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
|
|
1156
|
+
throw new RunObserveTransportUnavailableError(
|
|
1157
|
+
"observe-grant endpoint returned an invalid grant payload.",
|
|
1158
|
+
"grant_payload_invalid"
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
return grant;
|
|
1162
|
+
}
|
|
1163
|
+
async function backfillLogGap(input) {
|
|
1164
|
+
const lines = [];
|
|
1165
|
+
let cursor = input.lastLogSeq;
|
|
1166
|
+
for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
|
|
1167
|
+
if (cursor >= input.tailFirstSeq - 1) {
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
let logPage;
|
|
1171
|
+
try {
|
|
1172
|
+
logPage = await input.queryLogPage(
|
|
1173
|
+
cursor,
|
|
1174
|
+
Math.min(BACKFILL_PAGE_LIMIT, input.tailFirstSeq - 1 - cursor)
|
|
1175
|
+
);
|
|
1176
|
+
} catch {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
const entries = (logPage?.entries ?? []).filter(
|
|
1180
|
+
(entry) => entry.seq > cursor && entry.seq < input.tailFirstSeq
|
|
1181
|
+
);
|
|
1182
|
+
if (entries.length === 0) {
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
for (const entry of entries) {
|
|
1186
|
+
if (entry.seq !== cursor + 1) {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
lines.push(entry.line);
|
|
1190
|
+
cursor = entry.seq;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (cursor < input.tailFirstSeq - 1) {
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
return lines;
|
|
1197
|
+
}
|
|
1198
|
+
async function* observeRunEvents(options) {
|
|
1199
|
+
const { http, runId } = options;
|
|
1200
|
+
let grant = await mintRunObserveGrant(http, runId);
|
|
1201
|
+
let convexBrowser;
|
|
1202
|
+
let convexServer;
|
|
1203
|
+
try {
|
|
1204
|
+
convexBrowser = await import("convex/browser");
|
|
1205
|
+
convexServer = await import("convex/server");
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
throw new RunObserveTransportUnavailableError(
|
|
1208
|
+
`convex client module unavailable: ${errorText(error)}`,
|
|
1209
|
+
"convex_module_unavailable"
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
let webSocketConstructor;
|
|
1213
|
+
if (typeof WebSocket === "undefined") {
|
|
1214
|
+
try {
|
|
1215
|
+
const wsModuleName = "ws";
|
|
1216
|
+
const ws = await import(wsModuleName);
|
|
1217
|
+
webSocketConstructor = ws.default;
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
throw new RunObserveTransportUnavailableError(
|
|
1220
|
+
`no WebSocket implementation available: ${errorText(error)}`,
|
|
1221
|
+
"websocket_unavailable"
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const snapshotQuery = convexServer.makeFunctionReference(
|
|
1226
|
+
OBSERVER_SNAPSHOT_QUERY
|
|
1227
|
+
);
|
|
1228
|
+
const logPageQuery = convexServer.makeFunctionReference(
|
|
1229
|
+
OBSERVER_LOG_PAGE_QUERY
|
|
1230
|
+
);
|
|
1231
|
+
const client = new convexBrowser.ConvexClient(grant.convexUrl, {
|
|
1232
|
+
...webSocketConstructor ? { webSocketConstructor } : {},
|
|
1233
|
+
unsavedChangesWarning: false
|
|
1234
|
+
});
|
|
1235
|
+
const queue = [];
|
|
1236
|
+
let wake = null;
|
|
1237
|
+
const push = (item) => {
|
|
1238
|
+
queue.push(item);
|
|
1239
|
+
wake?.();
|
|
1240
|
+
wake = null;
|
|
1241
|
+
};
|
|
1242
|
+
let lastForcedRefreshAt = 0;
|
|
1243
|
+
client.setAuth(async ({ forceRefreshToken }) => {
|
|
1244
|
+
const now = Date.now();
|
|
1245
|
+
if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
|
|
1246
|
+
return grant.token;
|
|
1247
|
+
}
|
|
1248
|
+
if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
|
|
1249
|
+
push({
|
|
1250
|
+
kind: "error",
|
|
1251
|
+
error: new DeeplineError(
|
|
1252
|
+
`Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
|
|
1253
|
+
401,
|
|
1254
|
+
"RUN_OBSERVE_GRANT_REJECTED"
|
|
1255
|
+
)
|
|
1256
|
+
});
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
if (forceRefreshToken) {
|
|
1260
|
+
lastForcedRefreshAt = now;
|
|
1261
|
+
}
|
|
1262
|
+
try {
|
|
1263
|
+
grant = await mintRunObserveGrant(http, runId);
|
|
1264
|
+
return grant.token;
|
|
1265
|
+
} catch (error) {
|
|
1266
|
+
push({ kind: "error", error });
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
const unsubscribe = client.onUpdate(
|
|
1271
|
+
snapshotQuery,
|
|
1272
|
+
{ workflowId: runId },
|
|
1273
|
+
(run) => push({ kind: "run", run: run ?? null }),
|
|
1274
|
+
(error) => push({ kind: "error", error })
|
|
1275
|
+
);
|
|
1276
|
+
let lastUpdateAt = Date.now();
|
|
1277
|
+
let lastStatusTerminal = false;
|
|
1278
|
+
let disconnectedSince = null;
|
|
1279
|
+
let warnedReconnecting = false;
|
|
1280
|
+
let warnedStale = false;
|
|
1281
|
+
const watchdog = setInterval(() => {
|
|
1282
|
+
const now = Date.now();
|
|
1283
|
+
try {
|
|
1284
|
+
const connectionState = client.connectionState();
|
|
1285
|
+
if (connectionState.isWebSocketConnected) {
|
|
1286
|
+
disconnectedSince = null;
|
|
1287
|
+
warnedReconnecting = false;
|
|
1288
|
+
} else {
|
|
1289
|
+
disconnectedSince ??= now;
|
|
1290
|
+
if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
|
|
1291
|
+
warnedReconnecting = true;
|
|
1292
|
+
options.onNotice?.(
|
|
1293
|
+
`[observe] connection lost; reconnecting to live run ${runId}\u2026`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
} catch {
|
|
1298
|
+
}
|
|
1299
|
+
if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
|
|
1300
|
+
warnedStale = true;
|
|
1301
|
+
options.onNotice?.(
|
|
1302
|
+
`[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
}, OBSERVE_WATCHDOG_TICK_MS);
|
|
1306
|
+
watchdog.unref?.();
|
|
1307
|
+
const abortListener = () => push({
|
|
1308
|
+
kind: "error",
|
|
1309
|
+
error: new DeeplineError(
|
|
1310
|
+
"Run observation aborted.",
|
|
1311
|
+
void 0,
|
|
1312
|
+
"ABORTED"
|
|
1313
|
+
)
|
|
1314
|
+
});
|
|
1315
|
+
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
1316
|
+
let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
|
|
1317
|
+
const streamId = ["observe", runId].join(":");
|
|
1318
|
+
let sawFirstSnapshot = false;
|
|
1319
|
+
try {
|
|
1320
|
+
for (; ; ) {
|
|
1321
|
+
if (queue.length === 0) {
|
|
1322
|
+
const waitForItem = new Promise((resolve2) => {
|
|
1323
|
+
wake = resolve2;
|
|
1324
|
+
});
|
|
1325
|
+
if (!sawFirstSnapshot) {
|
|
1326
|
+
const timedOut = await Promise.race([
|
|
1327
|
+
waitForItem.then(() => false),
|
|
1328
|
+
new Promise(
|
|
1329
|
+
(resolve2) => setTimeout(() => resolve2(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
|
|
1330
|
+
)
|
|
1331
|
+
]);
|
|
1332
|
+
if (timedOut && queue.length === 0) {
|
|
1333
|
+
throw new RunObserveTransportUnavailableError(
|
|
1334
|
+
`no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
|
|
1335
|
+
"convex_unreachable"
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
} else {
|
|
1339
|
+
await waitForItem;
|
|
1340
|
+
}
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
const item = queue.shift();
|
|
1344
|
+
if (item.kind === "error") {
|
|
1345
|
+
if (options.signal?.aborted) {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
throw item.error;
|
|
1349
|
+
}
|
|
1350
|
+
sawFirstSnapshot = true;
|
|
1351
|
+
lastUpdateAt = Date.now();
|
|
1352
|
+
warnedStale = false;
|
|
1353
|
+
if (item.run === null) {
|
|
1354
|
+
throw new DeeplineError(
|
|
1355
|
+
`Run ${runId} was not found (or is not visible to this grant).`,
|
|
1356
|
+
404,
|
|
1357
|
+
"RUN_NOT_FOUND"
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
|
|
1361
|
+
lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
|
|
1362
|
+
const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
|
|
1363
|
+
if (gap && diffState.lastLogSeq > 0) {
|
|
1364
|
+
const backfilled = await backfillLogGap({
|
|
1365
|
+
queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
|
|
1366
|
+
workflowId: runId,
|
|
1367
|
+
afterSeq,
|
|
1368
|
+
limit
|
|
1369
|
+
}),
|
|
1370
|
+
lastLogSeq: diffState.lastLogSeq,
|
|
1371
|
+
tailFirstSeq: gap.tailFirstSeq
|
|
1372
|
+
});
|
|
1373
|
+
if (backfilled && backfilled.length > 0) {
|
|
1374
|
+
yield {
|
|
1375
|
+
cursor: String(snapshot.updatedAt ?? Date.now()),
|
|
1376
|
+
streamId,
|
|
1377
|
+
scope: "play",
|
|
1378
|
+
type: "play.run.log",
|
|
1379
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1380
|
+
payload: {
|
|
1381
|
+
runId: snapshot.runId,
|
|
1382
|
+
lines: backfilled,
|
|
1383
|
+
source: "worker",
|
|
1384
|
+
firstSeq: diffState.lastLogSeq + 1,
|
|
1385
|
+
totalLogCount: snapshot.totalLogCount
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
const { events, next } = diffPlayRunStreamEvents({
|
|
1392
|
+
streamId,
|
|
1393
|
+
snapshot,
|
|
1394
|
+
previous: diffState
|
|
1395
|
+
});
|
|
1396
|
+
diffState = next;
|
|
1397
|
+
const ordered = [
|
|
1398
|
+
...events.filter((event) => event.type === "play.run.snapshot"),
|
|
1399
|
+
...events.filter((event) => event.type !== "play.run.snapshot")
|
|
1400
|
+
];
|
|
1401
|
+
for (const event of ordered) {
|
|
1402
|
+
yield event;
|
|
1403
|
+
}
|
|
1404
|
+
if (lastStatusTerminal) {
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
} finally {
|
|
1409
|
+
clearInterval(watchdog);
|
|
1410
|
+
options.signal?.removeEventListener("abort", abortListener);
|
|
1411
|
+
try {
|
|
1412
|
+
unsubscribe();
|
|
1413
|
+
} catch {
|
|
1414
|
+
}
|
|
1415
|
+
await client.close().catch(() => void 0);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
607
1419
|
// src/client.ts
|
|
608
1420
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
609
1421
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
@@ -622,7 +1434,8 @@ function isTransientCompileManifestError(error) {
|
|
|
622
1434
|
message
|
|
623
1435
|
);
|
|
624
1436
|
}
|
|
625
|
-
|
|
1437
|
+
var RUN_LOGS_PAGE_LIMIT = 1e3;
|
|
1438
|
+
function isRecord2(value) {
|
|
626
1439
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
627
1440
|
}
|
|
628
1441
|
function isPrebuiltPlayDescription(play) {
|
|
@@ -692,10 +1505,31 @@ function normalizeLiveStatus(value) {
|
|
|
692
1505
|
}
|
|
693
1506
|
return null;
|
|
694
1507
|
}
|
|
1508
|
+
function appendPlayLiveLogLines(state, payload) {
|
|
1509
|
+
const lines = readStringArray(payload.lines);
|
|
1510
|
+
if (lines.length === 0) {
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
|
|
1514
|
+
if (firstSeq === null) {
|
|
1515
|
+
state.logs.push(...lines);
|
|
1516
|
+
const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
|
|
1517
|
+
if (totalLogCount !== null) {
|
|
1518
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
|
|
1519
|
+
}
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
|
|
1523
|
+
if (skip >= lines.length) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
state.logs.push(...lines.slice(skip));
|
|
1527
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
|
|
1528
|
+
}
|
|
695
1529
|
function updatePlayLiveStatusState(state, event) {
|
|
696
1530
|
const payload = getPlayLiveEventPayload(event);
|
|
697
1531
|
if (event.type === "play.run.log") {
|
|
698
|
-
state
|
|
1532
|
+
appendPlayLiveLogLines(state, payload);
|
|
699
1533
|
return null;
|
|
700
1534
|
}
|
|
701
1535
|
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
@@ -703,12 +1537,14 @@ function updatePlayLiveStatusState(state, event) {
|
|
|
703
1537
|
}
|
|
704
1538
|
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
|
|
705
1539
|
const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
|
|
706
|
-
const progressPayload =
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
1540
|
+
const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
|
|
1541
|
+
if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
|
|
1542
|
+
const payloadLogs = readStringArray(payload.logs);
|
|
1543
|
+
const progressLogs = readStringArray(progressPayload.logs);
|
|
1544
|
+
const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
|
|
1545
|
+
if (seedLogs.length > 0) {
|
|
1546
|
+
state.logs = seedLogs;
|
|
1547
|
+
}
|
|
712
1548
|
}
|
|
713
1549
|
if ("result" in payload) {
|
|
714
1550
|
state.result = payload.result;
|
|
@@ -802,9 +1638,9 @@ var DeeplineClient = class {
|
|
|
802
1638
|
return fields.length > 0 ? { fields } : schema;
|
|
803
1639
|
}
|
|
804
1640
|
schemaMetadata(schema, key) {
|
|
805
|
-
if (!
|
|
1641
|
+
if (!isRecord2(schema)) return null;
|
|
806
1642
|
const value = schema[key];
|
|
807
|
-
return
|
|
1643
|
+
return isRecord2(value) ? value : null;
|
|
808
1644
|
}
|
|
809
1645
|
playRunCommand(play, options) {
|
|
810
1646
|
const target = play.reference || play.name;
|
|
@@ -851,7 +1687,7 @@ var DeeplineClient = class {
|
|
|
851
1687
|
aliases,
|
|
852
1688
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
853
1689
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
854
|
-
staticPipeline:
|
|
1690
|
+
staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
|
|
855
1691
|
...csvInput ? { csvInput } : {},
|
|
856
1692
|
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
857
1693
|
runCommand,
|
|
@@ -1490,6 +2326,93 @@ var DeeplineClient = class {
|
|
|
1490
2326
|
);
|
|
1491
2327
|
return response.runs ?? [];
|
|
1492
2328
|
}
|
|
2329
|
+
// ---------------------------------------------------------------------------
|
|
2330
|
+
// Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
|
|
2331
|
+
// `/api/v2/workflows/*` API so the SDK CLI keeps existing cloud workflows
|
|
2332
|
+
// working while users migrate them to plays via `workflows transform`. Kept
|
|
2333
|
+
// intentionally minimal — workflows are a deprecated surface.
|
|
2334
|
+
// ---------------------------------------------------------------------------
|
|
2335
|
+
/** List the org's workflows. `GET /api/v2/workflows`. */
|
|
2336
|
+
async listWorkflows(options) {
|
|
2337
|
+
const params = new URLSearchParams();
|
|
2338
|
+
if (typeof options?.limit === "number") {
|
|
2339
|
+
params.set("limit", String(options.limit));
|
|
2340
|
+
}
|
|
2341
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2342
|
+
return this.http.get(`/api/v2/workflows${query}`);
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Fetch a single workflow (including its published-revision config — the
|
|
2346
|
+
* input to `compileWorkflowConfigToPlay`). `GET /api/v2/workflows/:id`.
|
|
2347
|
+
*/
|
|
2348
|
+
async getWorkflow(id) {
|
|
2349
|
+
return this.http.get(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2350
|
+
}
|
|
2351
|
+
/** Delete a workflow. `DELETE /api/v2/workflows/:id`. */
|
|
2352
|
+
async deleteWorkflow(id) {
|
|
2353
|
+
return this.http.delete(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2354
|
+
}
|
|
2355
|
+
/** Turn a workflow off. `POST /api/v2/workflows/:id/disable`. */
|
|
2356
|
+
async disableWorkflow(id) {
|
|
2357
|
+
return this.http.post(
|
|
2358
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/disable`,
|
|
2359
|
+
{}
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
/** Turn a workflow back on. `POST /api/v2/workflows/:id/enable`. */
|
|
2363
|
+
async enableWorkflow(id) {
|
|
2364
|
+
return this.http.post(
|
|
2365
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/enable`,
|
|
2366
|
+
{}
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
/** Create/update a workflow from config. `POST /api/v2/workflows/apply`. */
|
|
2370
|
+
async applyWorkflow(body) {
|
|
2371
|
+
return this.http.post("/api/v2/workflows/apply", body);
|
|
2372
|
+
}
|
|
2373
|
+
/** Validate a workflow config without saving. `POST /api/v2/workflows/lint`. */
|
|
2374
|
+
async lintWorkflow(body) {
|
|
2375
|
+
return this.http.post("/api/v2/workflows/lint", body);
|
|
2376
|
+
}
|
|
2377
|
+
/** Fetch live workflow request schemas. `GET /api/v2/workflows/schema`. */
|
|
2378
|
+
async getWorkflowSchema(subject) {
|
|
2379
|
+
const params = new URLSearchParams();
|
|
2380
|
+
if (subject) params.set("subject", subject);
|
|
2381
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2382
|
+
return this.http.get(`/api/v2/workflows/schema${query}`);
|
|
2383
|
+
}
|
|
2384
|
+
/** Queue a workflow run. `POST /api/v2/workflows/call`. */
|
|
2385
|
+
async callWorkflow(body) {
|
|
2386
|
+
return this.http.post("/api/v2/workflows/call", body);
|
|
2387
|
+
}
|
|
2388
|
+
/** List a workflow's runs. `GET /api/v2/workflows/:id/runs`. */
|
|
2389
|
+
async listWorkflowRuns(id, options) {
|
|
2390
|
+
const params = new URLSearchParams();
|
|
2391
|
+
if (typeof options?.limit === "number") {
|
|
2392
|
+
params.set("limit", String(options.limit));
|
|
2393
|
+
}
|
|
2394
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2395
|
+
return this.http.get(
|
|
2396
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs${query}`
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
/** Fetch one workflow run. `GET /api/v2/workflows/:id/runs/:runId`. */
|
|
2400
|
+
async getWorkflowRun(id, runId) {
|
|
2401
|
+
return this.http.get(
|
|
2402
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2403
|
+
runId
|
|
2404
|
+
)}`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
/** Cancel a workflow run. `POST /api/v2/workflows/:id/runs/:runId/cancel`. */
|
|
2408
|
+
async cancelWorkflowRun(id, runId) {
|
|
2409
|
+
return this.http.post(
|
|
2410
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2411
|
+
runId
|
|
2412
|
+
)}/cancel`,
|
|
2413
|
+
{}
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
1493
2416
|
/**
|
|
1494
2417
|
* Get a run by id using the public runs resource model.
|
|
1495
2418
|
*
|
|
@@ -1535,43 +2458,140 @@ var DeeplineClient = class {
|
|
|
1535
2458
|
);
|
|
1536
2459
|
return response.runs ?? [];
|
|
1537
2460
|
}
|
|
1538
|
-
/**
|
|
1539
|
-
|
|
2461
|
+
/**
|
|
2462
|
+
* Observe one run's live events through the Convex Run Snapshot
|
|
2463
|
+
* subscription transport (ADR-0008). Yields the same `play.*` event
|
|
2464
|
+
* envelopes as {@link streamPlayRunEvents} and ends after the terminal
|
|
2465
|
+
* snapshot. Throws {@link RunObserveTransportUnavailableError} when this
|
|
2466
|
+
* server cannot serve the transport (older server, unconfigured grants, or
|
|
2467
|
+
* unreachable Convex) — callers fall back to the SSE stream with a notice.
|
|
2468
|
+
*/
|
|
2469
|
+
observeRunEvents(runId, options) {
|
|
2470
|
+
return observeRunEvents({
|
|
2471
|
+
http: this.http,
|
|
2472
|
+
runId,
|
|
2473
|
+
signal: options?.signal,
|
|
2474
|
+
onNotice: options?.onNotice
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Tail one run through the subscription transport until terminal, then
|
|
2479
|
+
* return one durable REST status read (the final Run Response Package).
|
|
2480
|
+
*/
|
|
2481
|
+
async tailRunViaObserveTransport(runId, options) {
|
|
1540
2482
|
const state = {
|
|
1541
2483
|
runId,
|
|
1542
2484
|
status: "running",
|
|
1543
2485
|
logs: [],
|
|
2486
|
+
lastLogSeq: 0,
|
|
1544
2487
|
latest: null
|
|
1545
2488
|
};
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
signal: options?.signal
|
|
2489
|
+
for await (const event of this.observeRunEvents(runId, {
|
|
2490
|
+
signal: options?.signal,
|
|
2491
|
+
onNotice: options?.onNotice
|
|
1550
2492
|
})) {
|
|
1551
2493
|
const status = updatePlayLiveStatusState(state, event);
|
|
1552
|
-
if (!status) {
|
|
2494
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1553
2495
|
continue;
|
|
1554
2496
|
}
|
|
1555
|
-
|
|
1556
|
-
if (terminal) {
|
|
1557
|
-
break;
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
if (terminal && state.latest) {
|
|
1561
|
-
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
2497
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
1562
2498
|
() => state.latest ?? playRunStatusFromState(state)
|
|
1563
2499
|
);
|
|
1564
2500
|
}
|
|
1565
|
-
if (
|
|
1566
|
-
|
|
2501
|
+
if (options?.signal?.aborted) {
|
|
2502
|
+
throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
|
|
2503
|
+
}
|
|
2504
|
+
const refreshed = await this.getRunStatus(runId);
|
|
2505
|
+
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2506
|
+
return refreshed;
|
|
1567
2507
|
}
|
|
1568
2508
|
throw new DeeplineError(
|
|
1569
|
-
`Run
|
|
2509
|
+
`Run observation for ${runId} ended before a terminal status.`,
|
|
1570
2510
|
void 0,
|
|
1571
|
-
"
|
|
1572
|
-
{ runId }
|
|
2511
|
+
"PLAY_LIVE_STREAM_ENDED"
|
|
1573
2512
|
);
|
|
1574
2513
|
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Read the canonical run stream until a terminal run status is observed.
|
|
2516
|
+
*
|
|
2517
|
+
* Tries the Convex Run Snapshot subscription transport first (ADR-0008);
|
|
2518
|
+
* when the server cannot serve it (grant endpoint missing/unconfigured or
|
|
2519
|
+
* Convex unreachable) it falls back — with one `onNotice` message — to the
|
|
2520
|
+
* support-window SSE stream below.
|
|
2521
|
+
*
|
|
2522
|
+
* Server stream windows are finite: they end cleanly at the function
|
|
2523
|
+
* ceiling even while the run keeps executing. A window that ends (cleanly
|
|
2524
|
+
* or via transient network error) without a terminal event triggers one
|
|
2525
|
+
* durable-status re-check followed by a backed-off reconnect, so long runs
|
|
2526
|
+
* tail to completion. Abort via `options.signal` to stop waiting.
|
|
2527
|
+
*/
|
|
2528
|
+
async tailRun(runId, options) {
|
|
2529
|
+
try {
|
|
2530
|
+
return await this.tailRunViaObserveTransport(runId, options);
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
if (!(error instanceof RunObserveTransportUnavailableError)) {
|
|
2533
|
+
throw error;
|
|
2534
|
+
}
|
|
2535
|
+
options?.onNotice?.(
|
|
2536
|
+
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
|
|
2537
|
+
);
|
|
2538
|
+
}
|
|
2539
|
+
const state = {
|
|
2540
|
+
runId,
|
|
2541
|
+
status: "running",
|
|
2542
|
+
logs: [],
|
|
2543
|
+
lastLogSeq: 0,
|
|
2544
|
+
latest: null
|
|
2545
|
+
};
|
|
2546
|
+
let reconnectAttempt = 0;
|
|
2547
|
+
for (; ; ) {
|
|
2548
|
+
const connectedAt = Date.now();
|
|
2549
|
+
let sawEvent = false;
|
|
2550
|
+
let endedReason = "stream window ended before a terminal event";
|
|
2551
|
+
try {
|
|
2552
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
2553
|
+
mode: "cli",
|
|
2554
|
+
signal: options?.signal
|
|
2555
|
+
})) {
|
|
2556
|
+
sawEvent = true;
|
|
2557
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
2558
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2561
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
2562
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
} catch (error) {
|
|
2566
|
+
if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
|
|
2567
|
+
throw error;
|
|
2568
|
+
}
|
|
2569
|
+
endedReason = error instanceof Error ? error.message : String(error);
|
|
2570
|
+
}
|
|
2571
|
+
let refreshed = null;
|
|
2572
|
+
try {
|
|
2573
|
+
refreshed = await this.getRunStatus(runId);
|
|
2574
|
+
} catch (error) {
|
|
2575
|
+
if (!isTransientPlayStreamError(error)) {
|
|
2576
|
+
throw error;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2580
|
+
return refreshed;
|
|
2581
|
+
}
|
|
2582
|
+
if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
|
|
2583
|
+
reconnectAttempt = 0;
|
|
2584
|
+
}
|
|
2585
|
+
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
2586
|
+
reconnectAttempt += 1;
|
|
2587
|
+
options?.onReconnect?.({
|
|
2588
|
+
attempt: reconnectAttempt,
|
|
2589
|
+
delayMs,
|
|
2590
|
+
reason: endedReason
|
|
2591
|
+
});
|
|
2592
|
+
await sleep2(delayMs);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
1575
2595
|
/**
|
|
1576
2596
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
1577
2597
|
*
|
|
@@ -1582,19 +2602,40 @@ var DeeplineClient = class {
|
|
|
1582
2602
|
* ```
|
|
1583
2603
|
*/
|
|
1584
2604
|
async getRunLogs(runId, options) {
|
|
1585
|
-
const
|
|
1586
|
-
const
|
|
1587
|
-
|
|
1588
|
-
|
|
2605
|
+
const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
|
|
2606
|
+
const fetchPage = (afterSeq2, pageLimit) => this.http.get(
|
|
2607
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
|
|
2608
|
+
);
|
|
2609
|
+
const probe = await fetchPage(0, 1);
|
|
2610
|
+
const lastStoredSeq = probe.lastStoredSeq;
|
|
2611
|
+
let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
|
|
2612
|
+
const entries = [];
|
|
2613
|
+
while (entries.length < limit) {
|
|
2614
|
+
const page = await fetchPage(
|
|
2615
|
+
afterSeq,
|
|
2616
|
+
Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
|
|
2617
|
+
);
|
|
2618
|
+
if (page.entries.length === 0) {
|
|
2619
|
+
break;
|
|
2620
|
+
}
|
|
2621
|
+
entries.push(...page.entries);
|
|
2622
|
+
afterSeq = page.entries[page.entries.length - 1].seq;
|
|
2623
|
+
if (!page.hasMore) {
|
|
2624
|
+
break;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
const firstSequence = entries.length > 0 ? entries[0].seq : null;
|
|
2628
|
+
const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
|
|
1589
2629
|
return {
|
|
1590
|
-
runId:
|
|
1591
|
-
totalCount:
|
|
2630
|
+
runId: probe.runId,
|
|
2631
|
+
totalCount: probe.totalLogCount,
|
|
1592
2632
|
returnedCount: entries.length,
|
|
1593
|
-
firstSequence
|
|
1594
|
-
lastSequence
|
|
1595
|
-
truncated:
|
|
1596
|
-
hasMore:
|
|
1597
|
-
entries
|
|
2633
|
+
firstSequence,
|
|
2634
|
+
lastSequence,
|
|
2635
|
+
truncated: entries.length < probe.totalLogCount,
|
|
2636
|
+
hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
|
|
2637
|
+
entries: entries.map((entry) => entry.line),
|
|
2638
|
+
...probe.logsTruncated ? { logsTruncated: true } : {}
|
|
1598
2639
|
};
|
|
1599
2640
|
}
|
|
1600
2641
|
/**
|
|
@@ -1885,6 +2926,7 @@ var DeeplineClient = class {
|
|
|
1885
2926
|
runId: workflowId,
|
|
1886
2927
|
status: "running",
|
|
1887
2928
|
logs: [],
|
|
2929
|
+
lastLogSeq: 0,
|
|
1888
2930
|
latest: null
|
|
1889
2931
|
};
|
|
1890
2932
|
if (options?.signal?.aborted) {
|
|
@@ -2038,41 +3080,6 @@ function formatPlayBootstrapFinderKindsForSentence() {
|
|
|
2038
3080
|
}
|
|
2039
3081
|
|
|
2040
3082
|
// ../shared_libs/play-runtime/email-status.ts
|
|
2041
|
-
var DEFAULT_STATUS_MAP = {
|
|
2042
|
-
verified: { status: "valid", verdict: "send", verified: true },
|
|
2043
|
-
valid: { status: "valid", verdict: "send", verified: true },
|
|
2044
|
-
deliverable: { status: "valid", verdict: "send", verified: true },
|
|
2045
|
-
true: { status: "valid", verdict: "send", verified: true },
|
|
2046
|
-
invalid: { status: "invalid", verdict: "drop", verified: false },
|
|
2047
|
-
undeliverable: { status: "invalid", verdict: "drop", verified: false },
|
|
2048
|
-
false: { status: "invalid", verdict: "drop", verified: false },
|
|
2049
|
-
"catch-all": {
|
|
2050
|
-
status: "catch_all",
|
|
2051
|
-
verdict: "verify_next",
|
|
2052
|
-
verified: false
|
|
2053
|
-
},
|
|
2054
|
-
catch_all: {
|
|
2055
|
-
status: "catch_all",
|
|
2056
|
-
verdict: "verify_next",
|
|
2057
|
-
verified: false
|
|
2058
|
-
},
|
|
2059
|
-
valid_catch_all: {
|
|
2060
|
-
status: "valid_catch_all",
|
|
2061
|
-
verdict: "send_with_caution",
|
|
2062
|
-
verified: true
|
|
2063
|
-
},
|
|
2064
|
-
accept_all: {
|
|
2065
|
-
status: "catch_all",
|
|
2066
|
-
verdict: "verify_next",
|
|
2067
|
-
verified: false
|
|
2068
|
-
},
|
|
2069
|
-
unknown: { status: "unknown", verdict: "hold", verified: false },
|
|
2070
|
-
unavailable: { status: "unknown", verdict: "hold", verified: false },
|
|
2071
|
-
do_not_mail: { status: "do_not_mail", verdict: "drop", verified: false },
|
|
2072
|
-
spamtrap: { status: "spamtrap", verdict: "drop", verified: false },
|
|
2073
|
-
abuse: { status: "abuse", verdict: "drop", verified: false },
|
|
2074
|
-
disposable: { status: "disposable", verdict: "drop", verified: false }
|
|
2075
|
-
};
|
|
2076
3083
|
function normalizeKey(value) {
|
|
2077
3084
|
if (value == null) return null;
|
|
2078
3085
|
if (typeof value === "boolean") return String(value);
|
|
@@ -2121,7 +3128,7 @@ function mxClass(mxProvider, mxRecord) {
|
|
|
2121
3128
|
}
|
|
2122
3129
|
function entryForStatus(key, map) {
|
|
2123
3130
|
if (!key) return null;
|
|
2124
|
-
return map?.[key] ??
|
|
3131
|
+
return map?.[key] ?? null;
|
|
2125
3132
|
}
|
|
2126
3133
|
function read(values, name) {
|
|
2127
3134
|
return values[name];
|
|
@@ -2255,7 +3262,7 @@ var TARGET_FALLBACK_KEYS = {
|
|
|
2255
3262
|
status: [/^email_status$/i, /^status$/i],
|
|
2256
3263
|
email_status: [/^email_status$/i, /^status$/i]
|
|
2257
3264
|
};
|
|
2258
|
-
function
|
|
3265
|
+
function isRecord3(value) {
|
|
2259
3266
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2260
3267
|
}
|
|
2261
3268
|
function toV2RawToolOutputPath(path) {
|
|
@@ -2315,7 +3322,7 @@ function valuesAtSegments(current, segments, path = []) {
|
|
|
2315
3322
|
if (!Array.isArray(current)) return [];
|
|
2316
3323
|
return valuesAtSegments(current[segment], rest, [...path, segment]);
|
|
2317
3324
|
}
|
|
2318
|
-
if (!
|
|
3325
|
+
if (!isRecord3(current)) return [];
|
|
2319
3326
|
const directMatches = valuesAtSegments(current[segment], rest, [
|
|
2320
3327
|
...path,
|
|
2321
3328
|
segment
|
|
@@ -2345,7 +3352,7 @@ function getValuesAtPath(root, path) {
|
|
|
2345
3352
|
return valuesAtSegments(root, parsePath(path)).map((entry) => entry.value);
|
|
2346
3353
|
}
|
|
2347
3354
|
function toResultEnvelope(value) {
|
|
2348
|
-
if (
|
|
3355
|
+
if (isRecord3(value) && "data" in value) {
|
|
2349
3356
|
const envelope = { data: value.data };
|
|
2350
3357
|
if ("meta" in value) envelope.meta = value.meta;
|
|
2351
3358
|
return envelope;
|
|
@@ -2398,7 +3405,7 @@ function getAtPath(root, path) {
|
|
|
2398
3405
|
current = current[segment];
|
|
2399
3406
|
continue;
|
|
2400
3407
|
}
|
|
2401
|
-
if (!
|
|
3408
|
+
if (!isRecord3(current)) return void 0;
|
|
2402
3409
|
current = current[segment];
|
|
2403
3410
|
}
|
|
2404
3411
|
return current;
|
|
@@ -2415,7 +3422,7 @@ function normalizeString(value) {
|
|
|
2415
3422
|
}
|
|
2416
3423
|
function normalizeRows(value) {
|
|
2417
3424
|
if (!Array.isArray(value)) return null;
|
|
2418
|
-
return value.map((entry) =>
|
|
3425
|
+
return value.map((entry) => isRecord3(entry) ? entry : { value: entry });
|
|
2419
3426
|
}
|
|
2420
3427
|
function findFirstTargetByPath(result, paths) {
|
|
2421
3428
|
for (const path of paths ?? []) {
|
|
@@ -2474,7 +3481,7 @@ function findFirstTargetByKey(result, target, depth = 0, path = []) {
|
|
|
2474
3481
|
}
|
|
2475
3482
|
return null;
|
|
2476
3483
|
}
|
|
2477
|
-
if (!
|
|
3484
|
+
if (!isRecord3(result)) return null;
|
|
2478
3485
|
const patterns = TARGET_FALLBACK_KEYS[target] ?? [
|
|
2479
3486
|
new RegExp(`^${target}$`, "i")
|
|
2480
3487
|
];
|
|
@@ -2534,7 +3541,7 @@ function normalizeJobChangeStatus(value) {
|
|
|
2534
3541
|
function firstExperienceDate(value) {
|
|
2535
3542
|
if (!Array.isArray(value)) return null;
|
|
2536
3543
|
for (const entry of value) {
|
|
2537
|
-
if (!
|
|
3544
|
+
if (!isRecord3(entry)) continue;
|
|
2538
3545
|
const date = normalizeString(
|
|
2539
3546
|
entry.start_date ?? entry.started_at ?? entry.startDate
|
|
2540
3547
|
);
|
|
@@ -2543,10 +3550,10 @@ function firstExperienceDate(value) {
|
|
|
2543
3550
|
return null;
|
|
2544
3551
|
}
|
|
2545
3552
|
function normalizeJobChange(value) {
|
|
2546
|
-
const record =
|
|
2547
|
-
const nested =
|
|
2548
|
-
const output =
|
|
2549
|
-
const person =
|
|
3553
|
+
const record = isRecord3(value) ? value : {};
|
|
3554
|
+
const nested = isRecord3(record.job_change) ? record.job_change : record;
|
|
3555
|
+
const output = isRecord3(nested.output) ? nested.output : nested;
|
|
3556
|
+
const person = isRecord3(output.person) ? output.person : {};
|
|
2550
3557
|
const status = normalizeJobChangeStatus(
|
|
2551
3558
|
output.status ?? output.job_change_status ?? output.job_changed ?? output.changed
|
|
2552
3559
|
);
|
|
@@ -3114,11 +4121,11 @@ var Deepline = class {
|
|
|
3114
4121
|
return new DeeplineContext(options);
|
|
3115
4122
|
}
|
|
3116
4123
|
};
|
|
3117
|
-
function
|
|
4124
|
+
function isRecord4(value) {
|
|
3118
4125
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3119
4126
|
}
|
|
3120
4127
|
function stringArrayRecord(value) {
|
|
3121
|
-
if (!
|
|
4128
|
+
if (!isRecord4(value)) return {};
|
|
3122
4129
|
return Object.fromEntries(
|
|
3123
4130
|
Object.entries(value).map(([key, paths]) => [
|
|
3124
4131
|
key,
|
|
@@ -3129,22 +4136,60 @@ function stringArrayRecord(value) {
|
|
|
3129
4136
|
function stringArray(value) {
|
|
3130
4137
|
return Array.isArray(value) ? value.map(String) : [];
|
|
3131
4138
|
}
|
|
4139
|
+
function emailStatusExtractorConfig(value) {
|
|
4140
|
+
if (!isRecord4(value)) return void 0;
|
|
4141
|
+
const readPaths = (key) => {
|
|
4142
|
+
const paths = stringArray(value[key]).map((path) => path.trim()).filter(Boolean);
|
|
4143
|
+
return paths.length > 0 ? paths : void 0;
|
|
4144
|
+
};
|
|
4145
|
+
const provider = typeof value.provider === "string" && value.provider.trim() ? value.provider.trim() : null;
|
|
4146
|
+
if (!provider) return void 0;
|
|
4147
|
+
const config = { provider };
|
|
4148
|
+
for (const key of [
|
|
4149
|
+
"rawStatus",
|
|
4150
|
+
"rawScore",
|
|
4151
|
+
"valid",
|
|
4152
|
+
"deliverability",
|
|
4153
|
+
"catchAll",
|
|
4154
|
+
"mxProvider",
|
|
4155
|
+
"mxRecord",
|
|
4156
|
+
"fraudScore",
|
|
4157
|
+
"disposable",
|
|
4158
|
+
"roleBased",
|
|
4159
|
+
"freeEmail",
|
|
4160
|
+
"abuse",
|
|
4161
|
+
"spamtrap",
|
|
4162
|
+
"suspect"
|
|
4163
|
+
]) {
|
|
4164
|
+
const paths = readPaths(key);
|
|
4165
|
+
if (paths) config[key] = paths;
|
|
4166
|
+
}
|
|
4167
|
+
if (isRecord4(value.statusMap)) {
|
|
4168
|
+
config.statusMap = value.statusMap;
|
|
4169
|
+
}
|
|
4170
|
+
if (Array.isArray(value.rules)) {
|
|
4171
|
+
config.rules = value.rules;
|
|
4172
|
+
}
|
|
4173
|
+
return config;
|
|
4174
|
+
}
|
|
3132
4175
|
function extractorDescriptorRecord(value) {
|
|
3133
|
-
if (!
|
|
4176
|
+
if (!isRecord4(value)) return {};
|
|
3134
4177
|
return Object.fromEntries(
|
|
3135
4178
|
Object.entries(value).flatMap(([key, descriptor]) => {
|
|
3136
|
-
if (!
|
|
4179
|
+
if (!isRecord4(descriptor)) return [];
|
|
3137
4180
|
const paths = stringArray(descriptor.paths).map((path) => path.trim()).filter(Boolean);
|
|
3138
4181
|
if (paths.length === 0) return [];
|
|
3139
4182
|
const transforms = stringArray(descriptor.transforms).map((transform) => transform.trim()).filter(Boolean);
|
|
3140
4183
|
const enumValues = stringArray(descriptor.enum).map((entry) => entry.trim()).filter(Boolean);
|
|
4184
|
+
const emailStatus = emailStatusExtractorConfig(descriptor.emailStatus);
|
|
3141
4185
|
return [
|
|
3142
4186
|
[
|
|
3143
4187
|
key,
|
|
3144
4188
|
{
|
|
3145
4189
|
paths,
|
|
3146
4190
|
...transforms.length > 0 ? { transforms } : {},
|
|
3147
|
-
...enumValues.length > 0 ? { enum: enumValues } : {}
|
|
4191
|
+
...enumValues.length > 0 ? { enum: enumValues } : {},
|
|
4192
|
+
...emailStatus ? { emailStatus } : {}
|
|
3148
4193
|
}
|
|
3149
4194
|
]
|
|
3150
4195
|
];
|
|
@@ -3154,14 +4199,14 @@ function extractorDescriptorRecord(value) {
|
|
|
3154
4199
|
function toolExecutionEnvelopeToResult(fallbackToolId, response) {
|
|
3155
4200
|
const raw = response.toolResponse?.raw ?? null;
|
|
3156
4201
|
const meta = response.toolResponse?.meta;
|
|
3157
|
-
const metadata =
|
|
3158
|
-
const toolMetadata =
|
|
4202
|
+
const metadata = isRecord4(response._metadata) ? response._metadata.tool : null;
|
|
4203
|
+
const toolMetadata = isRecord4(metadata) ? metadata : {};
|
|
3159
4204
|
return createToolExecuteResult({
|
|
3160
4205
|
status: typeof response.status === "string" ? response.status : "completed",
|
|
3161
4206
|
jobId: typeof response.job_id === "string" ? response.job_id : void 0,
|
|
3162
4207
|
result: {
|
|
3163
4208
|
data: raw,
|
|
3164
|
-
...
|
|
4209
|
+
...isRecord4(meta) ? { meta } : {}
|
|
3165
4210
|
},
|
|
3166
4211
|
metadata: {
|
|
3167
4212
|
toolId: typeof toolMetadata.toolId === "string" ? toolMetadata.toolId : fallbackToolId,
|
|
@@ -3175,7 +4220,7 @@ function toolExecutionEnvelopeToResult(fallbackToolId, response) {
|
|
|
3175
4220
|
cached: false,
|
|
3176
4221
|
source: "live"
|
|
3177
4222
|
},
|
|
3178
|
-
meta:
|
|
4223
|
+
meta: isRecord4(response.meta) ? response.meta : void 0
|
|
3179
4224
|
});
|
|
3180
4225
|
}
|
|
3181
4226
|
function defineInput(schema) {
|
|
@@ -3477,6 +4522,7 @@ function extractSummaryFields(payload) {
|
|
|
3477
4522
|
PLAY_BOOTSTRAP_TEMPLATES,
|
|
3478
4523
|
PROD_URL,
|
|
3479
4524
|
RateLimitError,
|
|
4525
|
+
RunObserveTransportUnavailableError,
|
|
3480
4526
|
SDK_API_CONTRACT,
|
|
3481
4527
|
SDK_VERSION,
|
|
3482
4528
|
defineInput,
|