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.mjs
CHANGED
|
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
|
|
|
179
179
|
|
|
180
180
|
// src/release.ts
|
|
181
181
|
var SDK_RELEASE = {
|
|
182
|
-
version: "0.1.
|
|
182
|
+
version: "0.1.94",
|
|
183
183
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
184
184
|
supportPolicy: {
|
|
185
|
-
latest: "0.1.
|
|
185
|
+
latest: "0.1.94",
|
|
186
186
|
minimumSupported: "0.1.53",
|
|
187
187
|
deprecatedBelow: "0.1.53"
|
|
188
188
|
}
|
|
@@ -537,6 +537,807 @@ function sleep(ms) {
|
|
|
537
537
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
538
538
|
}
|
|
539
539
|
|
|
540
|
+
// src/stream-reconnect.ts
|
|
541
|
+
var STREAM_RECONNECT_BASE_DELAY_MS = 500;
|
|
542
|
+
var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
|
|
543
|
+
var STREAM_HEALTHY_CONNECTION_MS = 3e4;
|
|
544
|
+
function streamReconnectDelayMs(attempt) {
|
|
545
|
+
const cappedExponentialMs = Math.min(
|
|
546
|
+
STREAM_RECONNECT_MAX_DELAY_MS,
|
|
547
|
+
STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
|
|
548
|
+
);
|
|
549
|
+
return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
|
|
550
|
+
}
|
|
551
|
+
function isTransientPlayStreamError(error) {
|
|
552
|
+
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
553
|
+
return error.statusCode >= 500 && error.statusCode < 600;
|
|
554
|
+
}
|
|
555
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
556
|
+
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
557
|
+
text
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ../shared_libs/play-runtime/live-events.ts
|
|
562
|
+
function resolveTimingWindow(input) {
|
|
563
|
+
const startedAt = typeof input.startedAt === "number" && Number.isFinite(input.startedAt) ? input.startedAt : null;
|
|
564
|
+
const completedAt = typeof input.completedAt === "number" && Number.isFinite(input.completedAt) ? input.completedAt : null;
|
|
565
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : null;
|
|
566
|
+
return {
|
|
567
|
+
startedAt,
|
|
568
|
+
completedAt,
|
|
569
|
+
updatedAt
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ../shared_libs/play-runtime/run-ledger.ts
|
|
574
|
+
var LOG_TAIL_LIMIT = 100;
|
|
575
|
+
function isRecord(value) {
|
|
576
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
577
|
+
}
|
|
578
|
+
function finiteNumber(value) {
|
|
579
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
580
|
+
}
|
|
581
|
+
function optionalFiniteNumber(value) {
|
|
582
|
+
const normalized = finiteNumber(value);
|
|
583
|
+
return normalized === null ? void 0 : normalized;
|
|
584
|
+
}
|
|
585
|
+
function optionalString(value) {
|
|
586
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
587
|
+
}
|
|
588
|
+
function optionalNullableString(value) {
|
|
589
|
+
if (value === null) return null;
|
|
590
|
+
return optionalString(value);
|
|
591
|
+
}
|
|
592
|
+
function normalizePlayRunLedgerStatus(value) {
|
|
593
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
594
|
+
switch (normalized) {
|
|
595
|
+
case "queued":
|
|
596
|
+
case "pending":
|
|
597
|
+
return "queued";
|
|
598
|
+
case "running":
|
|
599
|
+
case "started":
|
|
600
|
+
return "running";
|
|
601
|
+
case "waiting":
|
|
602
|
+
return "waiting";
|
|
603
|
+
case "completed":
|
|
604
|
+
case "complete":
|
|
605
|
+
case "succeeded":
|
|
606
|
+
return "completed";
|
|
607
|
+
case "failed":
|
|
608
|
+
case "error":
|
|
609
|
+
return "failed";
|
|
610
|
+
case "cancelled":
|
|
611
|
+
case "canceled":
|
|
612
|
+
return "cancelled";
|
|
613
|
+
case "terminated":
|
|
614
|
+
return "terminated";
|
|
615
|
+
case "timed_out":
|
|
616
|
+
case "timeout":
|
|
617
|
+
return "timed_out";
|
|
618
|
+
default:
|
|
619
|
+
return "unknown";
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function createEmptyPlayRunLedgerSnapshot(input) {
|
|
623
|
+
const status = normalizePlayRunLedgerStatus(input.status ?? "unknown");
|
|
624
|
+
const startedAt = finiteNumber(input.startedAt) ?? null;
|
|
625
|
+
const finishedAt = finiteNumber(input.finishedAt) ?? null;
|
|
626
|
+
return {
|
|
627
|
+
runId: input.runId,
|
|
628
|
+
playName: input.playName ?? null,
|
|
629
|
+
status,
|
|
630
|
+
error: optionalNullableString(input.error) ?? null,
|
|
631
|
+
createdAt: finiteNumber(input.createdAt) ?? null,
|
|
632
|
+
startedAt,
|
|
633
|
+
updatedAt: finiteNumber(input.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input.createdAt) ?? null,
|
|
634
|
+
finishedAt,
|
|
635
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
|
|
636
|
+
orderedStepIds: [],
|
|
637
|
+
stepsById: {},
|
|
638
|
+
logTail: [],
|
|
639
|
+
totalLogCount: 0,
|
|
640
|
+
logsTruncated: false,
|
|
641
|
+
activeStepId: null,
|
|
642
|
+
activeArtifactTableNamespace: null,
|
|
643
|
+
resultTableNamespace: null
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function normalizePlayRunLedgerSnapshot(value, fallback) {
|
|
647
|
+
if (!isRecord(value)) {
|
|
648
|
+
return createEmptyPlayRunLedgerSnapshot(fallback);
|
|
649
|
+
}
|
|
650
|
+
const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
|
|
651
|
+
(entry) => typeof entry === "string" && Boolean(entry.trim())
|
|
652
|
+
) : [];
|
|
653
|
+
const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
|
|
654
|
+
const stepsById = {};
|
|
655
|
+
for (const [stepId, rawStep] of Object.entries(rawSteps)) {
|
|
656
|
+
if (!stepId.trim() || !isRecord(rawStep)) continue;
|
|
657
|
+
const rawStatus = normalizeStepStatus(rawStep.status);
|
|
658
|
+
if (!rawStatus) continue;
|
|
659
|
+
const completedAt = finiteNumber(rawStep.completedAt);
|
|
660
|
+
const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
|
|
661
|
+
const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
|
|
662
|
+
stepsById[stepId] = {
|
|
663
|
+
stepId,
|
|
664
|
+
label: optionalString(rawStep.label),
|
|
665
|
+
kind: optionalString(rawStep.kind),
|
|
666
|
+
status,
|
|
667
|
+
artifactTableNamespace: optionalNullableString(
|
|
668
|
+
rawStep.artifactTableNamespace
|
|
669
|
+
),
|
|
670
|
+
startedAt: finiteNumber(rawStep.startedAt),
|
|
671
|
+
completedAt,
|
|
672
|
+
updatedAt: finiteNumber(rawStep.updatedAt),
|
|
673
|
+
progress: rawProgress ? normalizeStepProgress(rawProgress) : null
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
|
|
677
|
+
const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
|
|
678
|
+
const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
|
|
679
|
+
const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
|
|
680
|
+
const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
|
|
681
|
+
const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
|
|
682
|
+
const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
|
|
683
|
+
return {
|
|
684
|
+
runId: optionalString(value.runId) ?? fallback.runId,
|
|
685
|
+
playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
|
|
686
|
+
status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
|
|
687
|
+
error,
|
|
688
|
+
createdAt,
|
|
689
|
+
startedAt,
|
|
690
|
+
updatedAt,
|
|
691
|
+
finishedAt,
|
|
692
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
|
|
693
|
+
orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
|
|
694
|
+
stepsById,
|
|
695
|
+
logTail: logTail.slice(-LOG_TAIL_LIMIT),
|
|
696
|
+
// Snapshots persisted before totalLogCount existed only know the retained
|
|
697
|
+
// tail, so the best lower bound for the cumulative count is the tail size.
|
|
698
|
+
totalLogCount: Math.max(
|
|
699
|
+
finiteNumber(value.totalLogCount) ?? logTail.length,
|
|
700
|
+
logTail.length
|
|
701
|
+
),
|
|
702
|
+
logsTruncated: value.logsTruncated === true,
|
|
703
|
+
activeStepId: optionalNullableString(value.activeStepId),
|
|
704
|
+
activeArtifactTableNamespace: optionalNullableString(
|
|
705
|
+
value.activeArtifactTableNamespace
|
|
706
|
+
),
|
|
707
|
+
resultTableNamespace: optionalNullableString(value.resultTableNamespace),
|
|
708
|
+
resultSummary: value.resultSummary,
|
|
709
|
+
result: value.result
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function normalizeStepStatus(value) {
|
|
713
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
714
|
+
if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
|
|
715
|
+
return normalized;
|
|
716
|
+
}
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
function normalizeStepProgress(value) {
|
|
720
|
+
return {
|
|
721
|
+
...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
|
|
722
|
+
...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
|
|
723
|
+
...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
|
|
724
|
+
...optionalString(value.message) ? { message: optionalString(value.message) } : {},
|
|
725
|
+
...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
|
|
726
|
+
artifactTableNamespace: optionalNullableString(
|
|
727
|
+
value.artifactTableNamespace
|
|
728
|
+
)
|
|
729
|
+
} : {},
|
|
730
|
+
...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// ../shared_libs/play-runtime/run-snapshot-stream.ts
|
|
735
|
+
function normalizePlayRunLiveStatus(value) {
|
|
736
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
737
|
+
switch (normalized) {
|
|
738
|
+
case "queued":
|
|
739
|
+
case "pending":
|
|
740
|
+
return "running";
|
|
741
|
+
case "running":
|
|
742
|
+
case "started":
|
|
743
|
+
return "running";
|
|
744
|
+
case "completed":
|
|
745
|
+
case "complete":
|
|
746
|
+
case "succeeded":
|
|
747
|
+
return "completed";
|
|
748
|
+
case "failed":
|
|
749
|
+
case "error":
|
|
750
|
+
return "failed";
|
|
751
|
+
case "cancelled":
|
|
752
|
+
case "canceled":
|
|
753
|
+
return "cancelled";
|
|
754
|
+
case "terminated":
|
|
755
|
+
return "terminated";
|
|
756
|
+
case "timed_out":
|
|
757
|
+
case "timeout":
|
|
758
|
+
return "timed_out";
|
|
759
|
+
default:
|
|
760
|
+
return "unknown";
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
function isTerminalPlayRunLiveStatus(status) {
|
|
764
|
+
return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
|
|
765
|
+
}
|
|
766
|
+
function buildSnapshotFromLedger(snapshot) {
|
|
767
|
+
const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
|
|
768
|
+
nodeId: step.stepId,
|
|
769
|
+
status: step.status,
|
|
770
|
+
artifactTableNamespace: step.artifactTableNamespace ?? null,
|
|
771
|
+
progress: step.progress ? {
|
|
772
|
+
completed: step.progress.completed,
|
|
773
|
+
total: step.progress.total,
|
|
774
|
+
failed: step.progress.failed,
|
|
775
|
+
message: step.progress.message,
|
|
776
|
+
artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
|
|
777
|
+
startedAt: step.startedAt ?? null,
|
|
778
|
+
completedAt: step.completedAt ?? null,
|
|
779
|
+
updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
|
|
780
|
+
} : null,
|
|
781
|
+
startedAt: step.startedAt ?? null,
|
|
782
|
+
completedAt: step.completedAt ?? null,
|
|
783
|
+
updatedAt: step.updatedAt ?? null
|
|
784
|
+
}));
|
|
785
|
+
return {
|
|
786
|
+
runId: snapshot.runId,
|
|
787
|
+
status: normalizePlayRunLiveStatus(snapshot.status),
|
|
788
|
+
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
789
|
+
logs: snapshot.logTail,
|
|
790
|
+
totalLogCount: snapshot.totalLogCount,
|
|
791
|
+
...snapshot.logsTruncated ? { logsTruncated: true } : {},
|
|
792
|
+
activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
|
|
793
|
+
resultTableNamespace: snapshot.resultTableNamespace ?? null,
|
|
794
|
+
nodeStates,
|
|
795
|
+
activeNodeId: snapshot.activeStepId ?? null
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function buildPlayRunStatusSnapshot(input) {
|
|
799
|
+
const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input.run.runSnapshot, {
|
|
800
|
+
runId: input.run.workflowId,
|
|
801
|
+
playName: input.run.name ?? null,
|
|
802
|
+
status: input.run.status,
|
|
803
|
+
createdAt: input.run.createdAt ?? null,
|
|
804
|
+
startedAt: input.run.startedAt ?? null,
|
|
805
|
+
updatedAt: input.run.updatedAt ?? null,
|
|
806
|
+
finishedAt: input.run.finishedAt ?? null
|
|
807
|
+
});
|
|
808
|
+
return buildSnapshotFromLedger(ledgerSnapshot);
|
|
809
|
+
}
|
|
810
|
+
function makeRunStreamEvent(input) {
|
|
811
|
+
return {
|
|
812
|
+
...input,
|
|
813
|
+
scope: "play",
|
|
814
|
+
at: input.at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
|
|
818
|
+
runSignature: "",
|
|
819
|
+
snapshotSignature: "",
|
|
820
|
+
stepStatusSignature: "",
|
|
821
|
+
stepProgressSignature: "",
|
|
822
|
+
lastLogSeq: 0
|
|
823
|
+
};
|
|
824
|
+
function getSnapshotCursor(snapshot) {
|
|
825
|
+
return String(snapshot.updatedAt ?? Date.now());
|
|
826
|
+
}
|
|
827
|
+
function getRunSignature(snapshot) {
|
|
828
|
+
return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
|
|
829
|
+
}
|
|
830
|
+
function getStepStatusSignature(snapshot) {
|
|
831
|
+
return snapshot.nodeStates.map(
|
|
832
|
+
(state) => [
|
|
833
|
+
state.nodeId,
|
|
834
|
+
state.status,
|
|
835
|
+
state.artifactTableNamespace ?? "",
|
|
836
|
+
state.startedAt ?? "",
|
|
837
|
+
state.completedAt ?? "",
|
|
838
|
+
state.progress?.startedAt ?? "",
|
|
839
|
+
state.progress?.completedAt ?? "",
|
|
840
|
+
state.updatedAt ?? "",
|
|
841
|
+
state.progress?.updatedAt ?? ""
|
|
842
|
+
].join(":")
|
|
843
|
+
).join("|");
|
|
844
|
+
}
|
|
845
|
+
function getStepProgressSignature(snapshot) {
|
|
846
|
+
return snapshot.nodeStates.map(
|
|
847
|
+
(state) => [
|
|
848
|
+
state.nodeId,
|
|
849
|
+
state.progress?.completed ?? "",
|
|
850
|
+
state.progress?.total ?? "",
|
|
851
|
+
state.progress?.failed ?? "",
|
|
852
|
+
state.progress?.artifactTableNamespace ?? "",
|
|
853
|
+
state.progress?.startedAt ?? "",
|
|
854
|
+
state.progress?.completedAt ?? "",
|
|
855
|
+
state.progress?.updatedAt ?? "",
|
|
856
|
+
state.progress?.message ?? ""
|
|
857
|
+
].join(":")
|
|
858
|
+
).join("|");
|
|
859
|
+
}
|
|
860
|
+
function getSnapshotSignature(snapshot) {
|
|
861
|
+
return JSON.stringify(snapshot);
|
|
862
|
+
}
|
|
863
|
+
function resolvePlayRunLogGap(snapshot, lastLogSeq) {
|
|
864
|
+
if (snapshot.totalLogCount <= lastLogSeq) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
|
|
868
|
+
if (lastLogSeq + 1 >= tailFirstSeq) {
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
missingCount: tailFirstSeq - 1 - lastLogSeq,
|
|
873
|
+
tailFirstSeq
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function diffLogLines(input) {
|
|
877
|
+
const { logs, totalLogCount } = input.snapshot;
|
|
878
|
+
if (totalLogCount <= input.lastLogSeq) {
|
|
879
|
+
return { lines: [], lastLogSeq: input.lastLogSeq, firstSeq: null };
|
|
880
|
+
}
|
|
881
|
+
const tailFirstSeq = totalLogCount - logs.length + 1;
|
|
882
|
+
if (input.lastLogSeq + 1 >= tailFirstSeq) {
|
|
883
|
+
return {
|
|
884
|
+
lines: logs.slice(input.lastLogSeq + 1 - tailFirstSeq),
|
|
885
|
+
lastLogSeq: totalLogCount,
|
|
886
|
+
firstSeq: input.lastLogSeq + 1
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
const missingCount = tailFirstSeq - 1 - input.lastLogSeq;
|
|
890
|
+
return {
|
|
891
|
+
lines: [
|
|
892
|
+
`[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
|
|
893
|
+
...logs
|
|
894
|
+
],
|
|
895
|
+
lastLogSeq: totalLogCount,
|
|
896
|
+
firstSeq: null
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function diffPlayRunStreamEvents(input) {
|
|
900
|
+
const { snapshot, streamId, previous } = input;
|
|
901
|
+
const cursor = getSnapshotCursor(snapshot);
|
|
902
|
+
const logDiff = diffLogLines({
|
|
903
|
+
snapshot,
|
|
904
|
+
lastLogSeq: previous.lastLogSeq
|
|
905
|
+
});
|
|
906
|
+
const next = {
|
|
907
|
+
runSignature: getRunSignature(snapshot),
|
|
908
|
+
stepStatusSignature: getStepStatusSignature(snapshot),
|
|
909
|
+
stepProgressSignature: getStepProgressSignature(snapshot),
|
|
910
|
+
snapshotSignature: getSnapshotSignature(snapshot),
|
|
911
|
+
lastLogSeq: logDiff.lastLogSeq
|
|
912
|
+
};
|
|
913
|
+
const events = [];
|
|
914
|
+
if (next.stepStatusSignature !== previous.stepStatusSignature) {
|
|
915
|
+
for (const state of snapshot.nodeStates) {
|
|
916
|
+
if (state.status === "idle") {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
|
|
920
|
+
const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
|
|
921
|
+
events.push(
|
|
922
|
+
makeRunStreamEvent({
|
|
923
|
+
cursor,
|
|
924
|
+
streamId,
|
|
925
|
+
type: "play.step.status",
|
|
926
|
+
payload: {
|
|
927
|
+
runId: snapshot.runId,
|
|
928
|
+
stepId: state.nodeId,
|
|
929
|
+
status: state.status,
|
|
930
|
+
artifactTableNamespace: state.artifactTableNamespace ?? null,
|
|
931
|
+
...resolveTimingWindow({
|
|
932
|
+
startedAt: persistedStartedAt,
|
|
933
|
+
completedAt: persistedCompletedAt,
|
|
934
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
935
|
+
})
|
|
936
|
+
}
|
|
937
|
+
})
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (next.stepProgressSignature !== previous.stepProgressSignature) {
|
|
942
|
+
for (const state of snapshot.nodeStates) {
|
|
943
|
+
if (!state.progress) {
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
events.push(
|
|
947
|
+
makeRunStreamEvent({
|
|
948
|
+
cursor: String(
|
|
949
|
+
state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
|
|
950
|
+
),
|
|
951
|
+
streamId,
|
|
952
|
+
type: "play.step.progress",
|
|
953
|
+
payload: {
|
|
954
|
+
runId: snapshot.runId,
|
|
955
|
+
stepId: state.nodeId,
|
|
956
|
+
completed: state.progress.completed,
|
|
957
|
+
total: state.progress.total,
|
|
958
|
+
failed: state.progress.failed,
|
|
959
|
+
message: state.progress.message,
|
|
960
|
+
artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
|
|
961
|
+
...resolveTimingWindow({
|
|
962
|
+
startedAt: state.startedAt ?? state.progress.startedAt ?? null,
|
|
963
|
+
completedAt: state.completedAt ?? state.progress.completedAt ?? null,
|
|
964
|
+
updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
|
|
965
|
+
})
|
|
966
|
+
}
|
|
967
|
+
})
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (logDiff.lines.length > 0) {
|
|
972
|
+
events.push(
|
|
973
|
+
makeRunStreamEvent({
|
|
974
|
+
cursor,
|
|
975
|
+
streamId,
|
|
976
|
+
type: "play.run.log",
|
|
977
|
+
payload: {
|
|
978
|
+
runId: snapshot.runId,
|
|
979
|
+
lines: logDiff.lines,
|
|
980
|
+
source: "worker",
|
|
981
|
+
...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
|
|
982
|
+
totalLogCount: snapshot.totalLogCount
|
|
983
|
+
}
|
|
984
|
+
})
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
if (next.snapshotSignature !== previous.snapshotSignature) {
|
|
988
|
+
const enrichedNodeStates = snapshot.nodeStates.map((state) => {
|
|
989
|
+
const timing = resolveTimingWindow({
|
|
990
|
+
startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
|
|
991
|
+
completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
|
|
992
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
993
|
+
});
|
|
994
|
+
return {
|
|
995
|
+
...state,
|
|
996
|
+
...timing,
|
|
997
|
+
progress: state.progress ? {
|
|
998
|
+
...state.progress,
|
|
999
|
+
...resolveTimingWindow({
|
|
1000
|
+
startedAt: state.progress.startedAt ?? state.startedAt ?? null,
|
|
1001
|
+
completedAt: state.progress.completedAt ?? state.completedAt ?? null,
|
|
1002
|
+
updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
|
|
1003
|
+
})
|
|
1004
|
+
} : state.progress
|
|
1005
|
+
};
|
|
1006
|
+
});
|
|
1007
|
+
events.push(
|
|
1008
|
+
makeRunStreamEvent({
|
|
1009
|
+
cursor,
|
|
1010
|
+
streamId,
|
|
1011
|
+
type: "play.run.snapshot",
|
|
1012
|
+
payload: { ...snapshot, nodeStates: enrichedNodeStates }
|
|
1013
|
+
})
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
if (next.runSignature !== previous.runSignature) {
|
|
1017
|
+
events.push(
|
|
1018
|
+
makeRunStreamEvent({
|
|
1019
|
+
cursor,
|
|
1020
|
+
streamId,
|
|
1021
|
+
type: "play.run.status",
|
|
1022
|
+
payload: {
|
|
1023
|
+
runId: snapshot.runId,
|
|
1024
|
+
status: snapshot.status,
|
|
1025
|
+
updatedAt: snapshot.updatedAt
|
|
1026
|
+
}
|
|
1027
|
+
})
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
return { events, next };
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/runs/observe-transport.ts
|
|
1034
|
+
var RunObserveTransportUnavailableError = class extends Error {
|
|
1035
|
+
constructor(message, reason) {
|
|
1036
|
+
super(message);
|
|
1037
|
+
this.reason = reason;
|
|
1038
|
+
this.name = "RunObserveTransportUnavailableError";
|
|
1039
|
+
}
|
|
1040
|
+
reason;
|
|
1041
|
+
};
|
|
1042
|
+
var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
|
|
1043
|
+
var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
|
|
1044
|
+
var OBSERVE_STALE_WARNING_MS = 12e4;
|
|
1045
|
+
var OBSERVE_WATCHDOG_TICK_MS = 5e3;
|
|
1046
|
+
var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
|
|
1047
|
+
var BACKFILL_PAGE_LIMIT = 1e3;
|
|
1048
|
+
var BACKFILL_MAX_PAGES = 30;
|
|
1049
|
+
var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
|
|
1050
|
+
var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
|
|
1051
|
+
function errorText(error) {
|
|
1052
|
+
return error instanceof Error ? error.message : String(error);
|
|
1053
|
+
}
|
|
1054
|
+
async function mintRunObserveGrant(http, runId) {
|
|
1055
|
+
let response;
|
|
1056
|
+
try {
|
|
1057
|
+
response = await http.post(
|
|
1058
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
|
|
1059
|
+
{}
|
|
1060
|
+
);
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
if (error instanceof DeeplineError) {
|
|
1063
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
1064
|
+
throw error;
|
|
1065
|
+
}
|
|
1066
|
+
throw new RunObserveTransportUnavailableError(
|
|
1067
|
+
`observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
|
|
1068
|
+
"grant_endpoint_unavailable"
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
throw new RunObserveTransportUnavailableError(
|
|
1072
|
+
`observe-grant request failed: ${errorText(error)}`,
|
|
1073
|
+
"grant_request_failed"
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
const grant = response;
|
|
1077
|
+
if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
|
|
1078
|
+
throw new RunObserveTransportUnavailableError(
|
|
1079
|
+
"observe-grant endpoint returned an invalid grant payload.",
|
|
1080
|
+
"grant_payload_invalid"
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
return grant;
|
|
1084
|
+
}
|
|
1085
|
+
async function backfillLogGap(input) {
|
|
1086
|
+
const lines = [];
|
|
1087
|
+
let cursor = input.lastLogSeq;
|
|
1088
|
+
for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
|
|
1089
|
+
if (cursor >= input.tailFirstSeq - 1) {
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1092
|
+
let logPage;
|
|
1093
|
+
try {
|
|
1094
|
+
logPage = await input.queryLogPage(
|
|
1095
|
+
cursor,
|
|
1096
|
+
Math.min(BACKFILL_PAGE_LIMIT, input.tailFirstSeq - 1 - cursor)
|
|
1097
|
+
);
|
|
1098
|
+
} catch {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
const entries = (logPage?.entries ?? []).filter(
|
|
1102
|
+
(entry) => entry.seq > cursor && entry.seq < input.tailFirstSeq
|
|
1103
|
+
);
|
|
1104
|
+
if (entries.length === 0) {
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
for (const entry of entries) {
|
|
1108
|
+
if (entry.seq !== cursor + 1) {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
lines.push(entry.line);
|
|
1112
|
+
cursor = entry.seq;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
if (cursor < input.tailFirstSeq - 1) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
return lines;
|
|
1119
|
+
}
|
|
1120
|
+
async function* observeRunEvents(options) {
|
|
1121
|
+
const { http, runId } = options;
|
|
1122
|
+
let grant = await mintRunObserveGrant(http, runId);
|
|
1123
|
+
let convexBrowser;
|
|
1124
|
+
let convexServer;
|
|
1125
|
+
try {
|
|
1126
|
+
convexBrowser = await import("convex/browser");
|
|
1127
|
+
convexServer = await import("convex/server");
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
throw new RunObserveTransportUnavailableError(
|
|
1130
|
+
`convex client module unavailable: ${errorText(error)}`,
|
|
1131
|
+
"convex_module_unavailable"
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
let webSocketConstructor;
|
|
1135
|
+
if (typeof WebSocket === "undefined") {
|
|
1136
|
+
try {
|
|
1137
|
+
const wsModuleName = "ws";
|
|
1138
|
+
const ws = await import(wsModuleName);
|
|
1139
|
+
webSocketConstructor = ws.default;
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
throw new RunObserveTransportUnavailableError(
|
|
1142
|
+
`no WebSocket implementation available: ${errorText(error)}`,
|
|
1143
|
+
"websocket_unavailable"
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const snapshotQuery = convexServer.makeFunctionReference(
|
|
1148
|
+
OBSERVER_SNAPSHOT_QUERY
|
|
1149
|
+
);
|
|
1150
|
+
const logPageQuery = convexServer.makeFunctionReference(
|
|
1151
|
+
OBSERVER_LOG_PAGE_QUERY
|
|
1152
|
+
);
|
|
1153
|
+
const client = new convexBrowser.ConvexClient(grant.convexUrl, {
|
|
1154
|
+
...webSocketConstructor ? { webSocketConstructor } : {},
|
|
1155
|
+
unsavedChangesWarning: false
|
|
1156
|
+
});
|
|
1157
|
+
const queue = [];
|
|
1158
|
+
let wake = null;
|
|
1159
|
+
const push = (item) => {
|
|
1160
|
+
queue.push(item);
|
|
1161
|
+
wake?.();
|
|
1162
|
+
wake = null;
|
|
1163
|
+
};
|
|
1164
|
+
let lastForcedRefreshAt = 0;
|
|
1165
|
+
client.setAuth(async ({ forceRefreshToken }) => {
|
|
1166
|
+
const now = Date.now();
|
|
1167
|
+
if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
|
|
1168
|
+
return grant.token;
|
|
1169
|
+
}
|
|
1170
|
+
if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
|
|
1171
|
+
push({
|
|
1172
|
+
kind: "error",
|
|
1173
|
+
error: new DeeplineError(
|
|
1174
|
+
`Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
|
|
1175
|
+
401,
|
|
1176
|
+
"RUN_OBSERVE_GRANT_REJECTED"
|
|
1177
|
+
)
|
|
1178
|
+
});
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
if (forceRefreshToken) {
|
|
1182
|
+
lastForcedRefreshAt = now;
|
|
1183
|
+
}
|
|
1184
|
+
try {
|
|
1185
|
+
grant = await mintRunObserveGrant(http, runId);
|
|
1186
|
+
return grant.token;
|
|
1187
|
+
} catch (error) {
|
|
1188
|
+
push({ kind: "error", error });
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
const unsubscribe = client.onUpdate(
|
|
1193
|
+
snapshotQuery,
|
|
1194
|
+
{ workflowId: runId },
|
|
1195
|
+
(run) => push({ kind: "run", run: run ?? null }),
|
|
1196
|
+
(error) => push({ kind: "error", error })
|
|
1197
|
+
);
|
|
1198
|
+
let lastUpdateAt = Date.now();
|
|
1199
|
+
let lastStatusTerminal = false;
|
|
1200
|
+
let disconnectedSince = null;
|
|
1201
|
+
let warnedReconnecting = false;
|
|
1202
|
+
let warnedStale = false;
|
|
1203
|
+
const watchdog = setInterval(() => {
|
|
1204
|
+
const now = Date.now();
|
|
1205
|
+
try {
|
|
1206
|
+
const connectionState = client.connectionState();
|
|
1207
|
+
if (connectionState.isWebSocketConnected) {
|
|
1208
|
+
disconnectedSince = null;
|
|
1209
|
+
warnedReconnecting = false;
|
|
1210
|
+
} else {
|
|
1211
|
+
disconnectedSince ??= now;
|
|
1212
|
+
if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
|
|
1213
|
+
warnedReconnecting = true;
|
|
1214
|
+
options.onNotice?.(
|
|
1215
|
+
`[observe] connection lost; reconnecting to live run ${runId}\u2026`
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
1221
|
+
if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
|
|
1222
|
+
warnedStale = true;
|
|
1223
|
+
options.onNotice?.(
|
|
1224
|
+
`[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
}, OBSERVE_WATCHDOG_TICK_MS);
|
|
1228
|
+
watchdog.unref?.();
|
|
1229
|
+
const abortListener = () => push({
|
|
1230
|
+
kind: "error",
|
|
1231
|
+
error: new DeeplineError(
|
|
1232
|
+
"Run observation aborted.",
|
|
1233
|
+
void 0,
|
|
1234
|
+
"ABORTED"
|
|
1235
|
+
)
|
|
1236
|
+
});
|
|
1237
|
+
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
1238
|
+
let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
|
|
1239
|
+
const streamId = ["observe", runId].join(":");
|
|
1240
|
+
let sawFirstSnapshot = false;
|
|
1241
|
+
try {
|
|
1242
|
+
for (; ; ) {
|
|
1243
|
+
if (queue.length === 0) {
|
|
1244
|
+
const waitForItem = new Promise((resolve2) => {
|
|
1245
|
+
wake = resolve2;
|
|
1246
|
+
});
|
|
1247
|
+
if (!sawFirstSnapshot) {
|
|
1248
|
+
const timedOut = await Promise.race([
|
|
1249
|
+
waitForItem.then(() => false),
|
|
1250
|
+
new Promise(
|
|
1251
|
+
(resolve2) => setTimeout(() => resolve2(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
|
|
1252
|
+
)
|
|
1253
|
+
]);
|
|
1254
|
+
if (timedOut && queue.length === 0) {
|
|
1255
|
+
throw new RunObserveTransportUnavailableError(
|
|
1256
|
+
`no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
|
|
1257
|
+
"convex_unreachable"
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
} else {
|
|
1261
|
+
await waitForItem;
|
|
1262
|
+
}
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
const item = queue.shift();
|
|
1266
|
+
if (item.kind === "error") {
|
|
1267
|
+
if (options.signal?.aborted) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
throw item.error;
|
|
1271
|
+
}
|
|
1272
|
+
sawFirstSnapshot = true;
|
|
1273
|
+
lastUpdateAt = Date.now();
|
|
1274
|
+
warnedStale = false;
|
|
1275
|
+
if (item.run === null) {
|
|
1276
|
+
throw new DeeplineError(
|
|
1277
|
+
`Run ${runId} was not found (or is not visible to this grant).`,
|
|
1278
|
+
404,
|
|
1279
|
+
"RUN_NOT_FOUND"
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
|
|
1283
|
+
lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
|
|
1284
|
+
const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
|
|
1285
|
+
if (gap && diffState.lastLogSeq > 0) {
|
|
1286
|
+
const backfilled = await backfillLogGap({
|
|
1287
|
+
queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
|
|
1288
|
+
workflowId: runId,
|
|
1289
|
+
afterSeq,
|
|
1290
|
+
limit
|
|
1291
|
+
}),
|
|
1292
|
+
lastLogSeq: diffState.lastLogSeq,
|
|
1293
|
+
tailFirstSeq: gap.tailFirstSeq
|
|
1294
|
+
});
|
|
1295
|
+
if (backfilled && backfilled.length > 0) {
|
|
1296
|
+
yield {
|
|
1297
|
+
cursor: String(snapshot.updatedAt ?? Date.now()),
|
|
1298
|
+
streamId,
|
|
1299
|
+
scope: "play",
|
|
1300
|
+
type: "play.run.log",
|
|
1301
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1302
|
+
payload: {
|
|
1303
|
+
runId: snapshot.runId,
|
|
1304
|
+
lines: backfilled,
|
|
1305
|
+
source: "worker",
|
|
1306
|
+
firstSeq: diffState.lastLogSeq + 1,
|
|
1307
|
+
totalLogCount: snapshot.totalLogCount
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
const { events, next } = diffPlayRunStreamEvents({
|
|
1314
|
+
streamId,
|
|
1315
|
+
snapshot,
|
|
1316
|
+
previous: diffState
|
|
1317
|
+
});
|
|
1318
|
+
diffState = next;
|
|
1319
|
+
const ordered = [
|
|
1320
|
+
...events.filter((event) => event.type === "play.run.snapshot"),
|
|
1321
|
+
...events.filter((event) => event.type !== "play.run.snapshot")
|
|
1322
|
+
];
|
|
1323
|
+
for (const event of ordered) {
|
|
1324
|
+
yield event;
|
|
1325
|
+
}
|
|
1326
|
+
if (lastStatusTerminal) {
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
} finally {
|
|
1331
|
+
clearInterval(watchdog);
|
|
1332
|
+
options.signal?.removeEventListener("abort", abortListener);
|
|
1333
|
+
try {
|
|
1334
|
+
unsubscribe();
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
await client.close().catch(() => void 0);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
540
1341
|
// src/client.ts
|
|
541
1342
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
542
1343
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
@@ -555,7 +1356,8 @@ function isTransientCompileManifestError(error) {
|
|
|
555
1356
|
message
|
|
556
1357
|
);
|
|
557
1358
|
}
|
|
558
|
-
|
|
1359
|
+
var RUN_LOGS_PAGE_LIMIT = 1e3;
|
|
1360
|
+
function isRecord2(value) {
|
|
559
1361
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
560
1362
|
}
|
|
561
1363
|
function isPrebuiltPlayDescription(play) {
|
|
@@ -625,10 +1427,31 @@ function normalizeLiveStatus(value) {
|
|
|
625
1427
|
}
|
|
626
1428
|
return null;
|
|
627
1429
|
}
|
|
1430
|
+
function appendPlayLiveLogLines(state, payload) {
|
|
1431
|
+
const lines = readStringArray(payload.lines);
|
|
1432
|
+
if (lines.length === 0) {
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
|
|
1436
|
+
if (firstSeq === null) {
|
|
1437
|
+
state.logs.push(...lines);
|
|
1438
|
+
const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
|
|
1439
|
+
if (totalLogCount !== null) {
|
|
1440
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
|
|
1441
|
+
}
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
|
|
1445
|
+
if (skip >= lines.length) {
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
state.logs.push(...lines.slice(skip));
|
|
1449
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
|
|
1450
|
+
}
|
|
628
1451
|
function updatePlayLiveStatusState(state, event) {
|
|
629
1452
|
const payload = getPlayLiveEventPayload(event);
|
|
630
1453
|
if (event.type === "play.run.log") {
|
|
631
|
-
state
|
|
1454
|
+
appendPlayLiveLogLines(state, payload);
|
|
632
1455
|
return null;
|
|
633
1456
|
}
|
|
634
1457
|
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
@@ -636,12 +1459,14 @@ function updatePlayLiveStatusState(state, event) {
|
|
|
636
1459
|
}
|
|
637
1460
|
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
|
|
638
1461
|
const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
|
|
639
|
-
const progressPayload =
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1462
|
+
const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
|
|
1463
|
+
if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
|
|
1464
|
+
const payloadLogs = readStringArray(payload.logs);
|
|
1465
|
+
const progressLogs = readStringArray(progressPayload.logs);
|
|
1466
|
+
const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
|
|
1467
|
+
if (seedLogs.length > 0) {
|
|
1468
|
+
state.logs = seedLogs;
|
|
1469
|
+
}
|
|
645
1470
|
}
|
|
646
1471
|
if ("result" in payload) {
|
|
647
1472
|
state.result = payload.result;
|
|
@@ -735,9 +1560,9 @@ var DeeplineClient = class {
|
|
|
735
1560
|
return fields.length > 0 ? { fields } : schema;
|
|
736
1561
|
}
|
|
737
1562
|
schemaMetadata(schema, key) {
|
|
738
|
-
if (!
|
|
1563
|
+
if (!isRecord2(schema)) return null;
|
|
739
1564
|
const value = schema[key];
|
|
740
|
-
return
|
|
1565
|
+
return isRecord2(value) ? value : null;
|
|
741
1566
|
}
|
|
742
1567
|
playRunCommand(play, options) {
|
|
743
1568
|
const target = play.reference || play.name;
|
|
@@ -784,7 +1609,7 @@ var DeeplineClient = class {
|
|
|
784
1609
|
aliases,
|
|
785
1610
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
786
1611
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
787
|
-
staticPipeline:
|
|
1612
|
+
staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
|
|
788
1613
|
...csvInput ? { csvInput } : {},
|
|
789
1614
|
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
790
1615
|
runCommand,
|
|
@@ -1423,6 +2248,93 @@ var DeeplineClient = class {
|
|
|
1423
2248
|
);
|
|
1424
2249
|
return response.runs ?? [];
|
|
1425
2250
|
}
|
|
2251
|
+
// ---------------------------------------------------------------------------
|
|
2252
|
+
// Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
|
|
2253
|
+
// `/api/v2/workflows/*` API so the SDK CLI keeps existing cloud workflows
|
|
2254
|
+
// working while users migrate them to plays via `workflows transform`. Kept
|
|
2255
|
+
// intentionally minimal — workflows are a deprecated surface.
|
|
2256
|
+
// ---------------------------------------------------------------------------
|
|
2257
|
+
/** List the org's workflows. `GET /api/v2/workflows`. */
|
|
2258
|
+
async listWorkflows(options) {
|
|
2259
|
+
const params = new URLSearchParams();
|
|
2260
|
+
if (typeof options?.limit === "number") {
|
|
2261
|
+
params.set("limit", String(options.limit));
|
|
2262
|
+
}
|
|
2263
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2264
|
+
return this.http.get(`/api/v2/workflows${query}`);
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Fetch a single workflow (including its published-revision config — the
|
|
2268
|
+
* input to `compileWorkflowConfigToPlay`). `GET /api/v2/workflows/:id`.
|
|
2269
|
+
*/
|
|
2270
|
+
async getWorkflow(id) {
|
|
2271
|
+
return this.http.get(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2272
|
+
}
|
|
2273
|
+
/** Delete a workflow. `DELETE /api/v2/workflows/:id`. */
|
|
2274
|
+
async deleteWorkflow(id) {
|
|
2275
|
+
return this.http.delete(`/api/v2/workflows/${encodeURIComponent(id)}`);
|
|
2276
|
+
}
|
|
2277
|
+
/** Turn a workflow off. `POST /api/v2/workflows/:id/disable`. */
|
|
2278
|
+
async disableWorkflow(id) {
|
|
2279
|
+
return this.http.post(
|
|
2280
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/disable`,
|
|
2281
|
+
{}
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
/** Turn a workflow back on. `POST /api/v2/workflows/:id/enable`. */
|
|
2285
|
+
async enableWorkflow(id) {
|
|
2286
|
+
return this.http.post(
|
|
2287
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/enable`,
|
|
2288
|
+
{}
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
2291
|
+
/** Create/update a workflow from config. `POST /api/v2/workflows/apply`. */
|
|
2292
|
+
async applyWorkflow(body) {
|
|
2293
|
+
return this.http.post("/api/v2/workflows/apply", body);
|
|
2294
|
+
}
|
|
2295
|
+
/** Validate a workflow config without saving. `POST /api/v2/workflows/lint`. */
|
|
2296
|
+
async lintWorkflow(body) {
|
|
2297
|
+
return this.http.post("/api/v2/workflows/lint", body);
|
|
2298
|
+
}
|
|
2299
|
+
/** Fetch live workflow request schemas. `GET /api/v2/workflows/schema`. */
|
|
2300
|
+
async getWorkflowSchema(subject) {
|
|
2301
|
+
const params = new URLSearchParams();
|
|
2302
|
+
if (subject) params.set("subject", subject);
|
|
2303
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2304
|
+
return this.http.get(`/api/v2/workflows/schema${query}`);
|
|
2305
|
+
}
|
|
2306
|
+
/** Queue a workflow run. `POST /api/v2/workflows/call`. */
|
|
2307
|
+
async callWorkflow(body) {
|
|
2308
|
+
return this.http.post("/api/v2/workflows/call", body);
|
|
2309
|
+
}
|
|
2310
|
+
/** List a workflow's runs. `GET /api/v2/workflows/:id/runs`. */
|
|
2311
|
+
async listWorkflowRuns(id, options) {
|
|
2312
|
+
const params = new URLSearchParams();
|
|
2313
|
+
if (typeof options?.limit === "number") {
|
|
2314
|
+
params.set("limit", String(options.limit));
|
|
2315
|
+
}
|
|
2316
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2317
|
+
return this.http.get(
|
|
2318
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs${query}`
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
/** Fetch one workflow run. `GET /api/v2/workflows/:id/runs/:runId`. */
|
|
2322
|
+
async getWorkflowRun(id, runId) {
|
|
2323
|
+
return this.http.get(
|
|
2324
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2325
|
+
runId
|
|
2326
|
+
)}`
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
/** Cancel a workflow run. `POST /api/v2/workflows/:id/runs/:runId/cancel`. */
|
|
2330
|
+
async cancelWorkflowRun(id, runId) {
|
|
2331
|
+
return this.http.post(
|
|
2332
|
+
`/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
|
|
2333
|
+
runId
|
|
2334
|
+
)}/cancel`,
|
|
2335
|
+
{}
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
1426
2338
|
/**
|
|
1427
2339
|
* Get a run by id using the public runs resource model.
|
|
1428
2340
|
*
|
|
@@ -1468,43 +2380,140 @@ var DeeplineClient = class {
|
|
|
1468
2380
|
);
|
|
1469
2381
|
return response.runs ?? [];
|
|
1470
2382
|
}
|
|
1471
|
-
/**
|
|
1472
|
-
|
|
2383
|
+
/**
|
|
2384
|
+
* Observe one run's live events through the Convex Run Snapshot
|
|
2385
|
+
* subscription transport (ADR-0008). Yields the same `play.*` event
|
|
2386
|
+
* envelopes as {@link streamPlayRunEvents} and ends after the terminal
|
|
2387
|
+
* snapshot. Throws {@link RunObserveTransportUnavailableError} when this
|
|
2388
|
+
* server cannot serve the transport (older server, unconfigured grants, or
|
|
2389
|
+
* unreachable Convex) — callers fall back to the SSE stream with a notice.
|
|
2390
|
+
*/
|
|
2391
|
+
observeRunEvents(runId, options) {
|
|
2392
|
+
return observeRunEvents({
|
|
2393
|
+
http: this.http,
|
|
2394
|
+
runId,
|
|
2395
|
+
signal: options?.signal,
|
|
2396
|
+
onNotice: options?.onNotice
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Tail one run through the subscription transport until terminal, then
|
|
2401
|
+
* return one durable REST status read (the final Run Response Package).
|
|
2402
|
+
*/
|
|
2403
|
+
async tailRunViaObserveTransport(runId, options) {
|
|
1473
2404
|
const state = {
|
|
1474
2405
|
runId,
|
|
1475
2406
|
status: "running",
|
|
1476
2407
|
logs: [],
|
|
2408
|
+
lastLogSeq: 0,
|
|
1477
2409
|
latest: null
|
|
1478
2410
|
};
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
signal: options?.signal
|
|
2411
|
+
for await (const event of this.observeRunEvents(runId, {
|
|
2412
|
+
signal: options?.signal,
|
|
2413
|
+
onNotice: options?.onNotice
|
|
1483
2414
|
})) {
|
|
1484
2415
|
const status = updatePlayLiveStatusState(state, event);
|
|
1485
|
-
if (!status) {
|
|
2416
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1486
2417
|
continue;
|
|
1487
2418
|
}
|
|
1488
|
-
|
|
1489
|
-
if (terminal) {
|
|
1490
|
-
break;
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
if (terminal && state.latest) {
|
|
1494
|
-
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
2419
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
1495
2420
|
() => state.latest ?? playRunStatusFromState(state)
|
|
1496
2421
|
);
|
|
1497
2422
|
}
|
|
1498
|
-
if (
|
|
1499
|
-
|
|
2423
|
+
if (options?.signal?.aborted) {
|
|
2424
|
+
throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
|
|
2425
|
+
}
|
|
2426
|
+
const refreshed = await this.getRunStatus(runId);
|
|
2427
|
+
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2428
|
+
return refreshed;
|
|
1500
2429
|
}
|
|
1501
2430
|
throw new DeeplineError(
|
|
1502
|
-
`Run
|
|
2431
|
+
`Run observation for ${runId} ended before a terminal status.`,
|
|
1503
2432
|
void 0,
|
|
1504
|
-
"
|
|
1505
|
-
{ runId }
|
|
2433
|
+
"PLAY_LIVE_STREAM_ENDED"
|
|
1506
2434
|
);
|
|
1507
2435
|
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Read the canonical run stream until a terminal run status is observed.
|
|
2438
|
+
*
|
|
2439
|
+
* Tries the Convex Run Snapshot subscription transport first (ADR-0008);
|
|
2440
|
+
* when the server cannot serve it (grant endpoint missing/unconfigured or
|
|
2441
|
+
* Convex unreachable) it falls back — with one `onNotice` message — to the
|
|
2442
|
+
* support-window SSE stream below.
|
|
2443
|
+
*
|
|
2444
|
+
* Server stream windows are finite: they end cleanly at the function
|
|
2445
|
+
* ceiling even while the run keeps executing. A window that ends (cleanly
|
|
2446
|
+
* or via transient network error) without a terminal event triggers one
|
|
2447
|
+
* durable-status re-check followed by a backed-off reconnect, so long runs
|
|
2448
|
+
* tail to completion. Abort via `options.signal` to stop waiting.
|
|
2449
|
+
*/
|
|
2450
|
+
async tailRun(runId, options) {
|
|
2451
|
+
try {
|
|
2452
|
+
return await this.tailRunViaObserveTransport(runId, options);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
if (!(error instanceof RunObserveTransportUnavailableError)) {
|
|
2455
|
+
throw error;
|
|
2456
|
+
}
|
|
2457
|
+
options?.onNotice?.(
|
|
2458
|
+
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
const state = {
|
|
2462
|
+
runId,
|
|
2463
|
+
status: "running",
|
|
2464
|
+
logs: [],
|
|
2465
|
+
lastLogSeq: 0,
|
|
2466
|
+
latest: null
|
|
2467
|
+
};
|
|
2468
|
+
let reconnectAttempt = 0;
|
|
2469
|
+
for (; ; ) {
|
|
2470
|
+
const connectedAt = Date.now();
|
|
2471
|
+
let sawEvent = false;
|
|
2472
|
+
let endedReason = "stream window ended before a terminal event";
|
|
2473
|
+
try {
|
|
2474
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
2475
|
+
mode: "cli",
|
|
2476
|
+
signal: options?.signal
|
|
2477
|
+
})) {
|
|
2478
|
+
sawEvent = true;
|
|
2479
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
2480
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
2484
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2487
|
+
} catch (error) {
|
|
2488
|
+
if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
|
|
2489
|
+
throw error;
|
|
2490
|
+
}
|
|
2491
|
+
endedReason = error instanceof Error ? error.message : String(error);
|
|
2492
|
+
}
|
|
2493
|
+
let refreshed = null;
|
|
2494
|
+
try {
|
|
2495
|
+
refreshed = await this.getRunStatus(runId);
|
|
2496
|
+
} catch (error) {
|
|
2497
|
+
if (!isTransientPlayStreamError(error)) {
|
|
2498
|
+
throw error;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2502
|
+
return refreshed;
|
|
2503
|
+
}
|
|
2504
|
+
if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
|
|
2505
|
+
reconnectAttempt = 0;
|
|
2506
|
+
}
|
|
2507
|
+
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
2508
|
+
reconnectAttempt += 1;
|
|
2509
|
+
options?.onReconnect?.({
|
|
2510
|
+
attempt: reconnectAttempt,
|
|
2511
|
+
delayMs,
|
|
2512
|
+
reason: endedReason
|
|
2513
|
+
});
|
|
2514
|
+
await sleep2(delayMs);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
1508
2517
|
/**
|
|
1509
2518
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
1510
2519
|
*
|
|
@@ -1515,19 +2524,40 @@ var DeeplineClient = class {
|
|
|
1515
2524
|
* ```
|
|
1516
2525
|
*/
|
|
1517
2526
|
async getRunLogs(runId, options) {
|
|
1518
|
-
const
|
|
1519
|
-
const
|
|
1520
|
-
|
|
1521
|
-
|
|
2527
|
+
const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
|
|
2528
|
+
const fetchPage = (afterSeq2, pageLimit) => this.http.get(
|
|
2529
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
|
|
2530
|
+
);
|
|
2531
|
+
const probe = await fetchPage(0, 1);
|
|
2532
|
+
const lastStoredSeq = probe.lastStoredSeq;
|
|
2533
|
+
let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
|
|
2534
|
+
const entries = [];
|
|
2535
|
+
while (entries.length < limit) {
|
|
2536
|
+
const page = await fetchPage(
|
|
2537
|
+
afterSeq,
|
|
2538
|
+
Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
|
|
2539
|
+
);
|
|
2540
|
+
if (page.entries.length === 0) {
|
|
2541
|
+
break;
|
|
2542
|
+
}
|
|
2543
|
+
entries.push(...page.entries);
|
|
2544
|
+
afterSeq = page.entries[page.entries.length - 1].seq;
|
|
2545
|
+
if (!page.hasMore) {
|
|
2546
|
+
break;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
const firstSequence = entries.length > 0 ? entries[0].seq : null;
|
|
2550
|
+
const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
|
|
1522
2551
|
return {
|
|
1523
|
-
runId:
|
|
1524
|
-
totalCount:
|
|
2552
|
+
runId: probe.runId,
|
|
2553
|
+
totalCount: probe.totalLogCount,
|
|
1525
2554
|
returnedCount: entries.length,
|
|
1526
|
-
firstSequence
|
|
1527
|
-
lastSequence
|
|
1528
|
-
truncated:
|
|
1529
|
-
hasMore:
|
|
1530
|
-
entries
|
|
2555
|
+
firstSequence,
|
|
2556
|
+
lastSequence,
|
|
2557
|
+
truncated: entries.length < probe.totalLogCount,
|
|
2558
|
+
hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
|
|
2559
|
+
entries: entries.map((entry) => entry.line),
|
|
2560
|
+
...probe.logsTruncated ? { logsTruncated: true } : {}
|
|
1531
2561
|
};
|
|
1532
2562
|
}
|
|
1533
2563
|
/**
|
|
@@ -1818,6 +2848,7 @@ var DeeplineClient = class {
|
|
|
1818
2848
|
runId: workflowId,
|
|
1819
2849
|
status: "running",
|
|
1820
2850
|
logs: [],
|
|
2851
|
+
lastLogSeq: 0,
|
|
1821
2852
|
latest: null
|
|
1822
2853
|
};
|
|
1823
2854
|
if (options?.signal?.aborted) {
|
|
@@ -1971,41 +3002,6 @@ function formatPlayBootstrapFinderKindsForSentence() {
|
|
|
1971
3002
|
}
|
|
1972
3003
|
|
|
1973
3004
|
// ../shared_libs/play-runtime/email-status.ts
|
|
1974
|
-
var DEFAULT_STATUS_MAP = {
|
|
1975
|
-
verified: { status: "valid", verdict: "send", verified: true },
|
|
1976
|
-
valid: { status: "valid", verdict: "send", verified: true },
|
|
1977
|
-
deliverable: { status: "valid", verdict: "send", verified: true },
|
|
1978
|
-
true: { status: "valid", verdict: "send", verified: true },
|
|
1979
|
-
invalid: { status: "invalid", verdict: "drop", verified: false },
|
|
1980
|
-
undeliverable: { status: "invalid", verdict: "drop", verified: false },
|
|
1981
|
-
false: { status: "invalid", verdict: "drop", verified: false },
|
|
1982
|
-
"catch-all": {
|
|
1983
|
-
status: "catch_all",
|
|
1984
|
-
verdict: "verify_next",
|
|
1985
|
-
verified: false
|
|
1986
|
-
},
|
|
1987
|
-
catch_all: {
|
|
1988
|
-
status: "catch_all",
|
|
1989
|
-
verdict: "verify_next",
|
|
1990
|
-
verified: false
|
|
1991
|
-
},
|
|
1992
|
-
valid_catch_all: {
|
|
1993
|
-
status: "valid_catch_all",
|
|
1994
|
-
verdict: "send_with_caution",
|
|
1995
|
-
verified: true
|
|
1996
|
-
},
|
|
1997
|
-
accept_all: {
|
|
1998
|
-
status: "catch_all",
|
|
1999
|
-
verdict: "verify_next",
|
|
2000
|
-
verified: false
|
|
2001
|
-
},
|
|
2002
|
-
unknown: { status: "unknown", verdict: "hold", verified: false },
|
|
2003
|
-
unavailable: { status: "unknown", verdict: "hold", verified: false },
|
|
2004
|
-
do_not_mail: { status: "do_not_mail", verdict: "drop", verified: false },
|
|
2005
|
-
spamtrap: { status: "spamtrap", verdict: "drop", verified: false },
|
|
2006
|
-
abuse: { status: "abuse", verdict: "drop", verified: false },
|
|
2007
|
-
disposable: { status: "disposable", verdict: "drop", verified: false }
|
|
2008
|
-
};
|
|
2009
3005
|
function normalizeKey(value) {
|
|
2010
3006
|
if (value == null) return null;
|
|
2011
3007
|
if (typeof value === "boolean") return String(value);
|
|
@@ -2054,7 +3050,7 @@ function mxClass(mxProvider, mxRecord) {
|
|
|
2054
3050
|
}
|
|
2055
3051
|
function entryForStatus(key, map) {
|
|
2056
3052
|
if (!key) return null;
|
|
2057
|
-
return map?.[key] ??
|
|
3053
|
+
return map?.[key] ?? null;
|
|
2058
3054
|
}
|
|
2059
3055
|
function read(values, name) {
|
|
2060
3056
|
return values[name];
|
|
@@ -2188,7 +3184,7 @@ var TARGET_FALLBACK_KEYS = {
|
|
|
2188
3184
|
status: [/^email_status$/i, /^status$/i],
|
|
2189
3185
|
email_status: [/^email_status$/i, /^status$/i]
|
|
2190
3186
|
};
|
|
2191
|
-
function
|
|
3187
|
+
function isRecord3(value) {
|
|
2192
3188
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2193
3189
|
}
|
|
2194
3190
|
function toV2RawToolOutputPath(path) {
|
|
@@ -2248,7 +3244,7 @@ function valuesAtSegments(current, segments, path = []) {
|
|
|
2248
3244
|
if (!Array.isArray(current)) return [];
|
|
2249
3245
|
return valuesAtSegments(current[segment], rest, [...path, segment]);
|
|
2250
3246
|
}
|
|
2251
|
-
if (!
|
|
3247
|
+
if (!isRecord3(current)) return [];
|
|
2252
3248
|
const directMatches = valuesAtSegments(current[segment], rest, [
|
|
2253
3249
|
...path,
|
|
2254
3250
|
segment
|
|
@@ -2278,7 +3274,7 @@ function getValuesAtPath(root, path) {
|
|
|
2278
3274
|
return valuesAtSegments(root, parsePath(path)).map((entry) => entry.value);
|
|
2279
3275
|
}
|
|
2280
3276
|
function toResultEnvelope(value) {
|
|
2281
|
-
if (
|
|
3277
|
+
if (isRecord3(value) && "data" in value) {
|
|
2282
3278
|
const envelope = { data: value.data };
|
|
2283
3279
|
if ("meta" in value) envelope.meta = value.meta;
|
|
2284
3280
|
return envelope;
|
|
@@ -2331,7 +3327,7 @@ function getAtPath(root, path) {
|
|
|
2331
3327
|
current = current[segment];
|
|
2332
3328
|
continue;
|
|
2333
3329
|
}
|
|
2334
|
-
if (!
|
|
3330
|
+
if (!isRecord3(current)) return void 0;
|
|
2335
3331
|
current = current[segment];
|
|
2336
3332
|
}
|
|
2337
3333
|
return current;
|
|
@@ -2348,7 +3344,7 @@ function normalizeString(value) {
|
|
|
2348
3344
|
}
|
|
2349
3345
|
function normalizeRows(value) {
|
|
2350
3346
|
if (!Array.isArray(value)) return null;
|
|
2351
|
-
return value.map((entry) =>
|
|
3347
|
+
return value.map((entry) => isRecord3(entry) ? entry : { value: entry });
|
|
2352
3348
|
}
|
|
2353
3349
|
function findFirstTargetByPath(result, paths) {
|
|
2354
3350
|
for (const path of paths ?? []) {
|
|
@@ -2407,7 +3403,7 @@ function findFirstTargetByKey(result, target, depth = 0, path = []) {
|
|
|
2407
3403
|
}
|
|
2408
3404
|
return null;
|
|
2409
3405
|
}
|
|
2410
|
-
if (!
|
|
3406
|
+
if (!isRecord3(result)) return null;
|
|
2411
3407
|
const patterns = TARGET_FALLBACK_KEYS[target] ?? [
|
|
2412
3408
|
new RegExp(`^${target}$`, "i")
|
|
2413
3409
|
];
|
|
@@ -2467,7 +3463,7 @@ function normalizeJobChangeStatus(value) {
|
|
|
2467
3463
|
function firstExperienceDate(value) {
|
|
2468
3464
|
if (!Array.isArray(value)) return null;
|
|
2469
3465
|
for (const entry of value) {
|
|
2470
|
-
if (!
|
|
3466
|
+
if (!isRecord3(entry)) continue;
|
|
2471
3467
|
const date = normalizeString(
|
|
2472
3468
|
entry.start_date ?? entry.started_at ?? entry.startDate
|
|
2473
3469
|
);
|
|
@@ -2476,10 +3472,10 @@ function firstExperienceDate(value) {
|
|
|
2476
3472
|
return null;
|
|
2477
3473
|
}
|
|
2478
3474
|
function normalizeJobChange(value) {
|
|
2479
|
-
const record =
|
|
2480
|
-
const nested =
|
|
2481
|
-
const output =
|
|
2482
|
-
const person =
|
|
3475
|
+
const record = isRecord3(value) ? value : {};
|
|
3476
|
+
const nested = isRecord3(record.job_change) ? record.job_change : record;
|
|
3477
|
+
const output = isRecord3(nested.output) ? nested.output : nested;
|
|
3478
|
+
const person = isRecord3(output.person) ? output.person : {};
|
|
2483
3479
|
const status = normalizeJobChangeStatus(
|
|
2484
3480
|
output.status ?? output.job_change_status ?? output.job_changed ?? output.changed
|
|
2485
3481
|
);
|
|
@@ -3047,11 +4043,11 @@ var Deepline = class {
|
|
|
3047
4043
|
return new DeeplineContext(options);
|
|
3048
4044
|
}
|
|
3049
4045
|
};
|
|
3050
|
-
function
|
|
4046
|
+
function isRecord4(value) {
|
|
3051
4047
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3052
4048
|
}
|
|
3053
4049
|
function stringArrayRecord(value) {
|
|
3054
|
-
if (!
|
|
4050
|
+
if (!isRecord4(value)) return {};
|
|
3055
4051
|
return Object.fromEntries(
|
|
3056
4052
|
Object.entries(value).map(([key, paths]) => [
|
|
3057
4053
|
key,
|
|
@@ -3062,22 +4058,60 @@ function stringArrayRecord(value) {
|
|
|
3062
4058
|
function stringArray(value) {
|
|
3063
4059
|
return Array.isArray(value) ? value.map(String) : [];
|
|
3064
4060
|
}
|
|
4061
|
+
function emailStatusExtractorConfig(value) {
|
|
4062
|
+
if (!isRecord4(value)) return void 0;
|
|
4063
|
+
const readPaths = (key) => {
|
|
4064
|
+
const paths = stringArray(value[key]).map((path) => path.trim()).filter(Boolean);
|
|
4065
|
+
return paths.length > 0 ? paths : void 0;
|
|
4066
|
+
};
|
|
4067
|
+
const provider = typeof value.provider === "string" && value.provider.trim() ? value.provider.trim() : null;
|
|
4068
|
+
if (!provider) return void 0;
|
|
4069
|
+
const config = { provider };
|
|
4070
|
+
for (const key of [
|
|
4071
|
+
"rawStatus",
|
|
4072
|
+
"rawScore",
|
|
4073
|
+
"valid",
|
|
4074
|
+
"deliverability",
|
|
4075
|
+
"catchAll",
|
|
4076
|
+
"mxProvider",
|
|
4077
|
+
"mxRecord",
|
|
4078
|
+
"fraudScore",
|
|
4079
|
+
"disposable",
|
|
4080
|
+
"roleBased",
|
|
4081
|
+
"freeEmail",
|
|
4082
|
+
"abuse",
|
|
4083
|
+
"spamtrap",
|
|
4084
|
+
"suspect"
|
|
4085
|
+
]) {
|
|
4086
|
+
const paths = readPaths(key);
|
|
4087
|
+
if (paths) config[key] = paths;
|
|
4088
|
+
}
|
|
4089
|
+
if (isRecord4(value.statusMap)) {
|
|
4090
|
+
config.statusMap = value.statusMap;
|
|
4091
|
+
}
|
|
4092
|
+
if (Array.isArray(value.rules)) {
|
|
4093
|
+
config.rules = value.rules;
|
|
4094
|
+
}
|
|
4095
|
+
return config;
|
|
4096
|
+
}
|
|
3065
4097
|
function extractorDescriptorRecord(value) {
|
|
3066
|
-
if (!
|
|
4098
|
+
if (!isRecord4(value)) return {};
|
|
3067
4099
|
return Object.fromEntries(
|
|
3068
4100
|
Object.entries(value).flatMap(([key, descriptor]) => {
|
|
3069
|
-
if (!
|
|
4101
|
+
if (!isRecord4(descriptor)) return [];
|
|
3070
4102
|
const paths = stringArray(descriptor.paths).map((path) => path.trim()).filter(Boolean);
|
|
3071
4103
|
if (paths.length === 0) return [];
|
|
3072
4104
|
const transforms = stringArray(descriptor.transforms).map((transform) => transform.trim()).filter(Boolean);
|
|
3073
4105
|
const enumValues = stringArray(descriptor.enum).map((entry) => entry.trim()).filter(Boolean);
|
|
4106
|
+
const emailStatus = emailStatusExtractorConfig(descriptor.emailStatus);
|
|
3074
4107
|
return [
|
|
3075
4108
|
[
|
|
3076
4109
|
key,
|
|
3077
4110
|
{
|
|
3078
4111
|
paths,
|
|
3079
4112
|
...transforms.length > 0 ? { transforms } : {},
|
|
3080
|
-
...enumValues.length > 0 ? { enum: enumValues } : {}
|
|
4113
|
+
...enumValues.length > 0 ? { enum: enumValues } : {},
|
|
4114
|
+
...emailStatus ? { emailStatus } : {}
|
|
3081
4115
|
}
|
|
3082
4116
|
]
|
|
3083
4117
|
];
|
|
@@ -3087,14 +4121,14 @@ function extractorDescriptorRecord(value) {
|
|
|
3087
4121
|
function toolExecutionEnvelopeToResult(fallbackToolId, response) {
|
|
3088
4122
|
const raw = response.toolResponse?.raw ?? null;
|
|
3089
4123
|
const meta = response.toolResponse?.meta;
|
|
3090
|
-
const metadata =
|
|
3091
|
-
const toolMetadata =
|
|
4124
|
+
const metadata = isRecord4(response._metadata) ? response._metadata.tool : null;
|
|
4125
|
+
const toolMetadata = isRecord4(metadata) ? metadata : {};
|
|
3092
4126
|
return createToolExecuteResult({
|
|
3093
4127
|
status: typeof response.status === "string" ? response.status : "completed",
|
|
3094
4128
|
jobId: typeof response.job_id === "string" ? response.job_id : void 0,
|
|
3095
4129
|
result: {
|
|
3096
4130
|
data: raw,
|
|
3097
|
-
...
|
|
4131
|
+
...isRecord4(meta) ? { meta } : {}
|
|
3098
4132
|
},
|
|
3099
4133
|
metadata: {
|
|
3100
4134
|
toolId: typeof toolMetadata.toolId === "string" ? toolMetadata.toolId : fallbackToolId,
|
|
@@ -3108,7 +4142,7 @@ function toolExecutionEnvelopeToResult(fallbackToolId, response) {
|
|
|
3108
4142
|
cached: false,
|
|
3109
4143
|
source: "live"
|
|
3110
4144
|
},
|
|
3111
|
-
meta:
|
|
4145
|
+
meta: isRecord4(response.meta) ? response.meta : void 0
|
|
3112
4146
|
});
|
|
3113
4147
|
}
|
|
3114
4148
|
function defineInput(schema) {
|
|
@@ -3409,6 +4443,7 @@ export {
|
|
|
3409
4443
|
PLAY_BOOTSTRAP_TEMPLATES,
|
|
3410
4444
|
PROD_URL,
|
|
3411
4445
|
RateLimitError,
|
|
4446
|
+
RunObserveTransportUnavailableError,
|
|
3412
4447
|
SDK_API_CONTRACT,
|
|
3413
4448
|
SDK_VERSION,
|
|
3414
4449
|
defineInput,
|