deepline 0.1.90 → 0.1.93
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 +1356 -225
- package/dist/cli/index.mjs +1356 -225
- package/dist/index.d.mts +74 -5
- package/dist/index.d.ts +74 -5
- package/dist/index.js +1018 -62
- package/dist/index.mjs +1007 -62
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +87 -20
- package/dist/repo/apps/play-runner-workers/src/entry.ts +52 -14
- package/dist/repo/sdk/src/client.ts +289 -40
- package/dist/repo/sdk/src/index.ts +1 -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/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/package.json +5 -2
package/dist/cli/index.js
CHANGED
|
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
|
|
|
229
229
|
|
|
230
230
|
// src/release.ts
|
|
231
231
|
var SDK_RELEASE = {
|
|
232
|
-
version: "0.1.
|
|
232
|
+
version: "0.1.93",
|
|
233
233
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
234
234
|
supportPolicy: {
|
|
235
|
-
latest: "0.1.
|
|
235
|
+
latest: "0.1.93",
|
|
236
236
|
minimumSupported: "0.1.53",
|
|
237
237
|
deprecatedBelow: "0.1.53"
|
|
238
238
|
}
|
|
@@ -587,6 +587,807 @@ function sleep(ms) {
|
|
|
587
587
|
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
+
// src/stream-reconnect.ts
|
|
591
|
+
var STREAM_RECONNECT_BASE_DELAY_MS = 500;
|
|
592
|
+
var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
|
|
593
|
+
var STREAM_HEALTHY_CONNECTION_MS = 3e4;
|
|
594
|
+
function streamReconnectDelayMs(attempt) {
|
|
595
|
+
const cappedExponentialMs = Math.min(
|
|
596
|
+
STREAM_RECONNECT_MAX_DELAY_MS,
|
|
597
|
+
STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
|
|
598
|
+
);
|
|
599
|
+
return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
|
|
600
|
+
}
|
|
601
|
+
function isTransientPlayStreamError(error) {
|
|
602
|
+
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
603
|
+
return error.statusCode >= 500 && error.statusCode < 600;
|
|
604
|
+
}
|
|
605
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
606
|
+
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
607
|
+
text
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// ../shared_libs/play-runtime/live-events.ts
|
|
612
|
+
function resolveTimingWindow(input2) {
|
|
613
|
+
const startedAt = typeof input2.startedAt === "number" && Number.isFinite(input2.startedAt) ? input2.startedAt : null;
|
|
614
|
+
const completedAt = typeof input2.completedAt === "number" && Number.isFinite(input2.completedAt) ? input2.completedAt : null;
|
|
615
|
+
const updatedAt = typeof input2.updatedAt === "number" && Number.isFinite(input2.updatedAt) ? input2.updatedAt : null;
|
|
616
|
+
return {
|
|
617
|
+
startedAt,
|
|
618
|
+
completedAt,
|
|
619
|
+
updatedAt
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// ../shared_libs/play-runtime/run-ledger.ts
|
|
624
|
+
var LOG_TAIL_LIMIT = 100;
|
|
625
|
+
function isRecord(value) {
|
|
626
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
627
|
+
}
|
|
628
|
+
function finiteNumber(value) {
|
|
629
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
630
|
+
}
|
|
631
|
+
function optionalFiniteNumber(value) {
|
|
632
|
+
const normalized = finiteNumber(value);
|
|
633
|
+
return normalized === null ? void 0 : normalized;
|
|
634
|
+
}
|
|
635
|
+
function optionalString(value) {
|
|
636
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
637
|
+
}
|
|
638
|
+
function optionalNullableString(value) {
|
|
639
|
+
if (value === null) return null;
|
|
640
|
+
return optionalString(value);
|
|
641
|
+
}
|
|
642
|
+
function normalizePlayRunLedgerStatus(value) {
|
|
643
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
644
|
+
switch (normalized) {
|
|
645
|
+
case "queued":
|
|
646
|
+
case "pending":
|
|
647
|
+
return "queued";
|
|
648
|
+
case "running":
|
|
649
|
+
case "started":
|
|
650
|
+
return "running";
|
|
651
|
+
case "waiting":
|
|
652
|
+
return "waiting";
|
|
653
|
+
case "completed":
|
|
654
|
+
case "complete":
|
|
655
|
+
case "succeeded":
|
|
656
|
+
return "completed";
|
|
657
|
+
case "failed":
|
|
658
|
+
case "error":
|
|
659
|
+
return "failed";
|
|
660
|
+
case "cancelled":
|
|
661
|
+
case "canceled":
|
|
662
|
+
return "cancelled";
|
|
663
|
+
case "terminated":
|
|
664
|
+
return "terminated";
|
|
665
|
+
case "timed_out":
|
|
666
|
+
case "timeout":
|
|
667
|
+
return "timed_out";
|
|
668
|
+
default:
|
|
669
|
+
return "unknown";
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function createEmptyPlayRunLedgerSnapshot(input2) {
|
|
673
|
+
const status = normalizePlayRunLedgerStatus(input2.status ?? "unknown");
|
|
674
|
+
const startedAt = finiteNumber(input2.startedAt) ?? null;
|
|
675
|
+
const finishedAt = finiteNumber(input2.finishedAt) ?? null;
|
|
676
|
+
return {
|
|
677
|
+
runId: input2.runId,
|
|
678
|
+
playName: input2.playName ?? null,
|
|
679
|
+
status,
|
|
680
|
+
error: optionalNullableString(input2.error) ?? null,
|
|
681
|
+
createdAt: finiteNumber(input2.createdAt) ?? null,
|
|
682
|
+
startedAt,
|
|
683
|
+
updatedAt: finiteNumber(input2.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input2.createdAt) ?? null,
|
|
684
|
+
finishedAt,
|
|
685
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
|
|
686
|
+
orderedStepIds: [],
|
|
687
|
+
stepsById: {},
|
|
688
|
+
logTail: [],
|
|
689
|
+
totalLogCount: 0,
|
|
690
|
+
logsTruncated: false,
|
|
691
|
+
activeStepId: null,
|
|
692
|
+
activeArtifactTableNamespace: null,
|
|
693
|
+
resultTableNamespace: null
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function normalizePlayRunLedgerSnapshot(value, fallback) {
|
|
697
|
+
if (!isRecord(value)) {
|
|
698
|
+
return createEmptyPlayRunLedgerSnapshot(fallback);
|
|
699
|
+
}
|
|
700
|
+
const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
|
|
701
|
+
(entry) => typeof entry === "string" && Boolean(entry.trim())
|
|
702
|
+
) : [];
|
|
703
|
+
const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
|
|
704
|
+
const stepsById = {};
|
|
705
|
+
for (const [stepId, rawStep] of Object.entries(rawSteps)) {
|
|
706
|
+
if (!stepId.trim() || !isRecord(rawStep)) continue;
|
|
707
|
+
const rawStatus = normalizeStepStatus(rawStep.status);
|
|
708
|
+
if (!rawStatus) continue;
|
|
709
|
+
const completedAt = finiteNumber(rawStep.completedAt);
|
|
710
|
+
const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
|
|
711
|
+
const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
|
|
712
|
+
stepsById[stepId] = {
|
|
713
|
+
stepId,
|
|
714
|
+
label: optionalString(rawStep.label),
|
|
715
|
+
kind: optionalString(rawStep.kind),
|
|
716
|
+
status,
|
|
717
|
+
artifactTableNamespace: optionalNullableString(
|
|
718
|
+
rawStep.artifactTableNamespace
|
|
719
|
+
),
|
|
720
|
+
startedAt: finiteNumber(rawStep.startedAt),
|
|
721
|
+
completedAt,
|
|
722
|
+
updatedAt: finiteNumber(rawStep.updatedAt),
|
|
723
|
+
progress: rawProgress ? normalizeStepProgress(rawProgress) : null
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
|
|
727
|
+
const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
|
|
728
|
+
const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
|
|
729
|
+
const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
|
|
730
|
+
const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
|
|
731
|
+
const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
|
|
732
|
+
const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
|
|
733
|
+
return {
|
|
734
|
+
runId: optionalString(value.runId) ?? fallback.runId,
|
|
735
|
+
playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
|
|
736
|
+
status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
|
|
737
|
+
error,
|
|
738
|
+
createdAt,
|
|
739
|
+
startedAt,
|
|
740
|
+
updatedAt,
|
|
741
|
+
finishedAt,
|
|
742
|
+
durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
|
|
743
|
+
orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
|
|
744
|
+
stepsById,
|
|
745
|
+
logTail: logTail.slice(-LOG_TAIL_LIMIT),
|
|
746
|
+
// Snapshots persisted before totalLogCount existed only know the retained
|
|
747
|
+
// tail, so the best lower bound for the cumulative count is the tail size.
|
|
748
|
+
totalLogCount: Math.max(
|
|
749
|
+
finiteNumber(value.totalLogCount) ?? logTail.length,
|
|
750
|
+
logTail.length
|
|
751
|
+
),
|
|
752
|
+
logsTruncated: value.logsTruncated === true,
|
|
753
|
+
activeStepId: optionalNullableString(value.activeStepId),
|
|
754
|
+
activeArtifactTableNamespace: optionalNullableString(
|
|
755
|
+
value.activeArtifactTableNamespace
|
|
756
|
+
),
|
|
757
|
+
resultTableNamespace: optionalNullableString(value.resultTableNamespace),
|
|
758
|
+
resultSummary: value.resultSummary,
|
|
759
|
+
result: value.result
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
function normalizeStepStatus(value) {
|
|
763
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
764
|
+
if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
|
|
765
|
+
return normalized;
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
function normalizeStepProgress(value) {
|
|
770
|
+
return {
|
|
771
|
+
...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
|
|
772
|
+
...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
|
|
773
|
+
...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
|
|
774
|
+
...optionalString(value.message) ? { message: optionalString(value.message) } : {},
|
|
775
|
+
...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
|
|
776
|
+
artifactTableNamespace: optionalNullableString(
|
|
777
|
+
value.artifactTableNamespace
|
|
778
|
+
)
|
|
779
|
+
} : {},
|
|
780
|
+
...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// ../shared_libs/play-runtime/run-snapshot-stream.ts
|
|
785
|
+
function normalizePlayRunLiveStatus(value) {
|
|
786
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
787
|
+
switch (normalized) {
|
|
788
|
+
case "queued":
|
|
789
|
+
case "pending":
|
|
790
|
+
return "running";
|
|
791
|
+
case "running":
|
|
792
|
+
case "started":
|
|
793
|
+
return "running";
|
|
794
|
+
case "completed":
|
|
795
|
+
case "complete":
|
|
796
|
+
case "succeeded":
|
|
797
|
+
return "completed";
|
|
798
|
+
case "failed":
|
|
799
|
+
case "error":
|
|
800
|
+
return "failed";
|
|
801
|
+
case "cancelled":
|
|
802
|
+
case "canceled":
|
|
803
|
+
return "cancelled";
|
|
804
|
+
case "terminated":
|
|
805
|
+
return "terminated";
|
|
806
|
+
case "timed_out":
|
|
807
|
+
case "timeout":
|
|
808
|
+
return "timed_out";
|
|
809
|
+
default:
|
|
810
|
+
return "unknown";
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function isTerminalPlayRunLiveStatus(status) {
|
|
814
|
+
return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
|
|
815
|
+
}
|
|
816
|
+
function buildSnapshotFromLedger(snapshot) {
|
|
817
|
+
const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
|
|
818
|
+
nodeId: step.stepId,
|
|
819
|
+
status: step.status,
|
|
820
|
+
artifactTableNamespace: step.artifactTableNamespace ?? null,
|
|
821
|
+
progress: step.progress ? {
|
|
822
|
+
completed: step.progress.completed,
|
|
823
|
+
total: step.progress.total,
|
|
824
|
+
failed: step.progress.failed,
|
|
825
|
+
message: step.progress.message,
|
|
826
|
+
artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
|
|
827
|
+
startedAt: step.startedAt ?? null,
|
|
828
|
+
completedAt: step.completedAt ?? null,
|
|
829
|
+
updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
|
|
830
|
+
} : null,
|
|
831
|
+
startedAt: step.startedAt ?? null,
|
|
832
|
+
completedAt: step.completedAt ?? null,
|
|
833
|
+
updatedAt: step.updatedAt ?? null
|
|
834
|
+
}));
|
|
835
|
+
return {
|
|
836
|
+
runId: snapshot.runId,
|
|
837
|
+
status: normalizePlayRunLiveStatus(snapshot.status),
|
|
838
|
+
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
839
|
+
logs: snapshot.logTail,
|
|
840
|
+
totalLogCount: snapshot.totalLogCount,
|
|
841
|
+
...snapshot.logsTruncated ? { logsTruncated: true } : {},
|
|
842
|
+
activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
|
|
843
|
+
resultTableNamespace: snapshot.resultTableNamespace ?? null,
|
|
844
|
+
nodeStates,
|
|
845
|
+
activeNodeId: snapshot.activeStepId ?? null
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
function buildPlayRunStatusSnapshot(input2) {
|
|
849
|
+
const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input2.run.runSnapshot, {
|
|
850
|
+
runId: input2.run.workflowId,
|
|
851
|
+
playName: input2.run.name ?? null,
|
|
852
|
+
status: input2.run.status,
|
|
853
|
+
createdAt: input2.run.createdAt ?? null,
|
|
854
|
+
startedAt: input2.run.startedAt ?? null,
|
|
855
|
+
updatedAt: input2.run.updatedAt ?? null,
|
|
856
|
+
finishedAt: input2.run.finishedAt ?? null
|
|
857
|
+
});
|
|
858
|
+
return buildSnapshotFromLedger(ledgerSnapshot);
|
|
859
|
+
}
|
|
860
|
+
function makeRunStreamEvent(input2) {
|
|
861
|
+
return {
|
|
862
|
+
...input2,
|
|
863
|
+
scope: "play",
|
|
864
|
+
at: input2.at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
|
|
868
|
+
runSignature: "",
|
|
869
|
+
snapshotSignature: "",
|
|
870
|
+
stepStatusSignature: "",
|
|
871
|
+
stepProgressSignature: "",
|
|
872
|
+
lastLogSeq: 0
|
|
873
|
+
};
|
|
874
|
+
function getSnapshotCursor(snapshot) {
|
|
875
|
+
return String(snapshot.updatedAt ?? Date.now());
|
|
876
|
+
}
|
|
877
|
+
function getRunSignature(snapshot) {
|
|
878
|
+
return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
|
|
879
|
+
}
|
|
880
|
+
function getStepStatusSignature(snapshot) {
|
|
881
|
+
return snapshot.nodeStates.map(
|
|
882
|
+
(state) => [
|
|
883
|
+
state.nodeId,
|
|
884
|
+
state.status,
|
|
885
|
+
state.artifactTableNamespace ?? "",
|
|
886
|
+
state.startedAt ?? "",
|
|
887
|
+
state.completedAt ?? "",
|
|
888
|
+
state.progress?.startedAt ?? "",
|
|
889
|
+
state.progress?.completedAt ?? "",
|
|
890
|
+
state.updatedAt ?? "",
|
|
891
|
+
state.progress?.updatedAt ?? ""
|
|
892
|
+
].join(":")
|
|
893
|
+
).join("|");
|
|
894
|
+
}
|
|
895
|
+
function getStepProgressSignature(snapshot) {
|
|
896
|
+
return snapshot.nodeStates.map(
|
|
897
|
+
(state) => [
|
|
898
|
+
state.nodeId,
|
|
899
|
+
state.progress?.completed ?? "",
|
|
900
|
+
state.progress?.total ?? "",
|
|
901
|
+
state.progress?.failed ?? "",
|
|
902
|
+
state.progress?.artifactTableNamespace ?? "",
|
|
903
|
+
state.progress?.startedAt ?? "",
|
|
904
|
+
state.progress?.completedAt ?? "",
|
|
905
|
+
state.progress?.updatedAt ?? "",
|
|
906
|
+
state.progress?.message ?? ""
|
|
907
|
+
].join(":")
|
|
908
|
+
).join("|");
|
|
909
|
+
}
|
|
910
|
+
function getSnapshotSignature(snapshot) {
|
|
911
|
+
return JSON.stringify(snapshot);
|
|
912
|
+
}
|
|
913
|
+
function resolvePlayRunLogGap(snapshot, lastLogSeq) {
|
|
914
|
+
if (snapshot.totalLogCount <= lastLogSeq) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
|
|
918
|
+
if (lastLogSeq + 1 >= tailFirstSeq) {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
missingCount: tailFirstSeq - 1 - lastLogSeq,
|
|
923
|
+
tailFirstSeq
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
function diffLogLines(input2) {
|
|
927
|
+
const { logs, totalLogCount } = input2.snapshot;
|
|
928
|
+
if (totalLogCount <= input2.lastLogSeq) {
|
|
929
|
+
return { lines: [], lastLogSeq: input2.lastLogSeq, firstSeq: null };
|
|
930
|
+
}
|
|
931
|
+
const tailFirstSeq = totalLogCount - logs.length + 1;
|
|
932
|
+
if (input2.lastLogSeq + 1 >= tailFirstSeq) {
|
|
933
|
+
return {
|
|
934
|
+
lines: logs.slice(input2.lastLogSeq + 1 - tailFirstSeq),
|
|
935
|
+
lastLogSeq: totalLogCount,
|
|
936
|
+
firstSeq: input2.lastLogSeq + 1
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
const missingCount = tailFirstSeq - 1 - input2.lastLogSeq;
|
|
940
|
+
return {
|
|
941
|
+
lines: [
|
|
942
|
+
`[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
|
|
943
|
+
...logs
|
|
944
|
+
],
|
|
945
|
+
lastLogSeq: totalLogCount,
|
|
946
|
+
firstSeq: null
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function diffPlayRunStreamEvents(input2) {
|
|
950
|
+
const { snapshot, streamId, previous } = input2;
|
|
951
|
+
const cursor = getSnapshotCursor(snapshot);
|
|
952
|
+
const logDiff = diffLogLines({
|
|
953
|
+
snapshot,
|
|
954
|
+
lastLogSeq: previous.lastLogSeq
|
|
955
|
+
});
|
|
956
|
+
const next = {
|
|
957
|
+
runSignature: getRunSignature(snapshot),
|
|
958
|
+
stepStatusSignature: getStepStatusSignature(snapshot),
|
|
959
|
+
stepProgressSignature: getStepProgressSignature(snapshot),
|
|
960
|
+
snapshotSignature: getSnapshotSignature(snapshot),
|
|
961
|
+
lastLogSeq: logDiff.lastLogSeq
|
|
962
|
+
};
|
|
963
|
+
const events = [];
|
|
964
|
+
if (next.stepStatusSignature !== previous.stepStatusSignature) {
|
|
965
|
+
for (const state of snapshot.nodeStates) {
|
|
966
|
+
if (state.status === "idle") {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
|
|
970
|
+
const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
|
|
971
|
+
events.push(
|
|
972
|
+
makeRunStreamEvent({
|
|
973
|
+
cursor,
|
|
974
|
+
streamId,
|
|
975
|
+
type: "play.step.status",
|
|
976
|
+
payload: {
|
|
977
|
+
runId: snapshot.runId,
|
|
978
|
+
stepId: state.nodeId,
|
|
979
|
+
status: state.status,
|
|
980
|
+
artifactTableNamespace: state.artifactTableNamespace ?? null,
|
|
981
|
+
...resolveTimingWindow({
|
|
982
|
+
startedAt: persistedStartedAt,
|
|
983
|
+
completedAt: persistedCompletedAt,
|
|
984
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
985
|
+
})
|
|
986
|
+
}
|
|
987
|
+
})
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (next.stepProgressSignature !== previous.stepProgressSignature) {
|
|
992
|
+
for (const state of snapshot.nodeStates) {
|
|
993
|
+
if (!state.progress) {
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
events.push(
|
|
997
|
+
makeRunStreamEvent({
|
|
998
|
+
cursor: String(
|
|
999
|
+
state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
|
|
1000
|
+
),
|
|
1001
|
+
streamId,
|
|
1002
|
+
type: "play.step.progress",
|
|
1003
|
+
payload: {
|
|
1004
|
+
runId: snapshot.runId,
|
|
1005
|
+
stepId: state.nodeId,
|
|
1006
|
+
completed: state.progress.completed,
|
|
1007
|
+
total: state.progress.total,
|
|
1008
|
+
failed: state.progress.failed,
|
|
1009
|
+
message: state.progress.message,
|
|
1010
|
+
artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
|
|
1011
|
+
...resolveTimingWindow({
|
|
1012
|
+
startedAt: state.startedAt ?? state.progress.startedAt ?? null,
|
|
1013
|
+
completedAt: state.completedAt ?? state.progress.completedAt ?? null,
|
|
1014
|
+
updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
|
|
1015
|
+
})
|
|
1016
|
+
}
|
|
1017
|
+
})
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (logDiff.lines.length > 0) {
|
|
1022
|
+
events.push(
|
|
1023
|
+
makeRunStreamEvent({
|
|
1024
|
+
cursor,
|
|
1025
|
+
streamId,
|
|
1026
|
+
type: "play.run.log",
|
|
1027
|
+
payload: {
|
|
1028
|
+
runId: snapshot.runId,
|
|
1029
|
+
lines: logDiff.lines,
|
|
1030
|
+
source: "worker",
|
|
1031
|
+
...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
|
|
1032
|
+
totalLogCount: snapshot.totalLogCount
|
|
1033
|
+
}
|
|
1034
|
+
})
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
if (next.snapshotSignature !== previous.snapshotSignature) {
|
|
1038
|
+
const enrichedNodeStates = snapshot.nodeStates.map((state) => {
|
|
1039
|
+
const timing = resolveTimingWindow({
|
|
1040
|
+
startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
|
|
1041
|
+
completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
|
|
1042
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
1043
|
+
});
|
|
1044
|
+
return {
|
|
1045
|
+
...state,
|
|
1046
|
+
...timing,
|
|
1047
|
+
progress: state.progress ? {
|
|
1048
|
+
...state.progress,
|
|
1049
|
+
...resolveTimingWindow({
|
|
1050
|
+
startedAt: state.progress.startedAt ?? state.startedAt ?? null,
|
|
1051
|
+
completedAt: state.progress.completedAt ?? state.completedAt ?? null,
|
|
1052
|
+
updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
|
|
1053
|
+
})
|
|
1054
|
+
} : state.progress
|
|
1055
|
+
};
|
|
1056
|
+
});
|
|
1057
|
+
events.push(
|
|
1058
|
+
makeRunStreamEvent({
|
|
1059
|
+
cursor,
|
|
1060
|
+
streamId,
|
|
1061
|
+
type: "play.run.snapshot",
|
|
1062
|
+
payload: { ...snapshot, nodeStates: enrichedNodeStates }
|
|
1063
|
+
})
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
if (next.runSignature !== previous.runSignature) {
|
|
1067
|
+
events.push(
|
|
1068
|
+
makeRunStreamEvent({
|
|
1069
|
+
cursor,
|
|
1070
|
+
streamId,
|
|
1071
|
+
type: "play.run.status",
|
|
1072
|
+
payload: {
|
|
1073
|
+
runId: snapshot.runId,
|
|
1074
|
+
status: snapshot.status,
|
|
1075
|
+
updatedAt: snapshot.updatedAt
|
|
1076
|
+
}
|
|
1077
|
+
})
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
return { events, next };
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/runs/observe-transport.ts
|
|
1084
|
+
var RunObserveTransportUnavailableError = class extends Error {
|
|
1085
|
+
constructor(message, reason) {
|
|
1086
|
+
super(message);
|
|
1087
|
+
this.reason = reason;
|
|
1088
|
+
this.name = "RunObserveTransportUnavailableError";
|
|
1089
|
+
}
|
|
1090
|
+
reason;
|
|
1091
|
+
};
|
|
1092
|
+
var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
|
|
1093
|
+
var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
|
|
1094
|
+
var OBSERVE_STALE_WARNING_MS = 12e4;
|
|
1095
|
+
var OBSERVE_WATCHDOG_TICK_MS = 5e3;
|
|
1096
|
+
var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
|
|
1097
|
+
var BACKFILL_PAGE_LIMIT = 1e3;
|
|
1098
|
+
var BACKFILL_MAX_PAGES = 30;
|
|
1099
|
+
var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
|
|
1100
|
+
var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
|
|
1101
|
+
function errorText(error) {
|
|
1102
|
+
return error instanceof Error ? error.message : String(error);
|
|
1103
|
+
}
|
|
1104
|
+
async function mintRunObserveGrant(http, runId) {
|
|
1105
|
+
let response;
|
|
1106
|
+
try {
|
|
1107
|
+
response = await http.post(
|
|
1108
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
|
|
1109
|
+
{}
|
|
1110
|
+
);
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
if (error instanceof DeeplineError) {
|
|
1113
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
throw new RunObserveTransportUnavailableError(
|
|
1117
|
+
`observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
|
|
1118
|
+
"grant_endpoint_unavailable"
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
throw new RunObserveTransportUnavailableError(
|
|
1122
|
+
`observe-grant request failed: ${errorText(error)}`,
|
|
1123
|
+
"grant_request_failed"
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
const grant = response;
|
|
1127
|
+
if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
|
|
1128
|
+
throw new RunObserveTransportUnavailableError(
|
|
1129
|
+
"observe-grant endpoint returned an invalid grant payload.",
|
|
1130
|
+
"grant_payload_invalid"
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
return grant;
|
|
1134
|
+
}
|
|
1135
|
+
async function backfillLogGap(input2) {
|
|
1136
|
+
const lines = [];
|
|
1137
|
+
let cursor = input2.lastLogSeq;
|
|
1138
|
+
for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
|
|
1139
|
+
if (cursor >= input2.tailFirstSeq - 1) {
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
let logPage;
|
|
1143
|
+
try {
|
|
1144
|
+
logPage = await input2.queryLogPage(
|
|
1145
|
+
cursor,
|
|
1146
|
+
Math.min(BACKFILL_PAGE_LIMIT, input2.tailFirstSeq - 1 - cursor)
|
|
1147
|
+
);
|
|
1148
|
+
} catch {
|
|
1149
|
+
return null;
|
|
1150
|
+
}
|
|
1151
|
+
const entries = (logPage?.entries ?? []).filter(
|
|
1152
|
+
(entry) => entry.seq > cursor && entry.seq < input2.tailFirstSeq
|
|
1153
|
+
);
|
|
1154
|
+
if (entries.length === 0) {
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
for (const entry of entries) {
|
|
1158
|
+
if (entry.seq !== cursor + 1) {
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
lines.push(entry.line);
|
|
1162
|
+
cursor = entry.seq;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (cursor < input2.tailFirstSeq - 1) {
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
return lines;
|
|
1169
|
+
}
|
|
1170
|
+
async function* observeRunEvents(options) {
|
|
1171
|
+
const { http, runId } = options;
|
|
1172
|
+
let grant = await mintRunObserveGrant(http, runId);
|
|
1173
|
+
let convexBrowser;
|
|
1174
|
+
let convexServer;
|
|
1175
|
+
try {
|
|
1176
|
+
convexBrowser = await import("convex/browser");
|
|
1177
|
+
convexServer = await import("convex/server");
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
throw new RunObserveTransportUnavailableError(
|
|
1180
|
+
`convex client module unavailable: ${errorText(error)}`,
|
|
1181
|
+
"convex_module_unavailable"
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
let webSocketConstructor;
|
|
1185
|
+
if (typeof WebSocket === "undefined") {
|
|
1186
|
+
try {
|
|
1187
|
+
const wsModuleName = "ws";
|
|
1188
|
+
const ws = await import(wsModuleName);
|
|
1189
|
+
webSocketConstructor = ws.default;
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
throw new RunObserveTransportUnavailableError(
|
|
1192
|
+
`no WebSocket implementation available: ${errorText(error)}`,
|
|
1193
|
+
"websocket_unavailable"
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
const snapshotQuery = convexServer.makeFunctionReference(
|
|
1198
|
+
OBSERVER_SNAPSHOT_QUERY
|
|
1199
|
+
);
|
|
1200
|
+
const logPageQuery = convexServer.makeFunctionReference(
|
|
1201
|
+
OBSERVER_LOG_PAGE_QUERY
|
|
1202
|
+
);
|
|
1203
|
+
const client = new convexBrowser.ConvexClient(grant.convexUrl, {
|
|
1204
|
+
...webSocketConstructor ? { webSocketConstructor } : {},
|
|
1205
|
+
unsavedChangesWarning: false
|
|
1206
|
+
});
|
|
1207
|
+
const queue = [];
|
|
1208
|
+
let wake = null;
|
|
1209
|
+
const push = (item) => {
|
|
1210
|
+
queue.push(item);
|
|
1211
|
+
wake?.();
|
|
1212
|
+
wake = null;
|
|
1213
|
+
};
|
|
1214
|
+
let lastForcedRefreshAt = 0;
|
|
1215
|
+
client.setAuth(async ({ forceRefreshToken }) => {
|
|
1216
|
+
const now = Date.now();
|
|
1217
|
+
if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
|
|
1218
|
+
return grant.token;
|
|
1219
|
+
}
|
|
1220
|
+
if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
|
|
1221
|
+
push({
|
|
1222
|
+
kind: "error",
|
|
1223
|
+
error: new DeeplineError(
|
|
1224
|
+
`Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
|
|
1225
|
+
401,
|
|
1226
|
+
"RUN_OBSERVE_GRANT_REJECTED"
|
|
1227
|
+
)
|
|
1228
|
+
});
|
|
1229
|
+
return null;
|
|
1230
|
+
}
|
|
1231
|
+
if (forceRefreshToken) {
|
|
1232
|
+
lastForcedRefreshAt = now;
|
|
1233
|
+
}
|
|
1234
|
+
try {
|
|
1235
|
+
grant = await mintRunObserveGrant(http, runId);
|
|
1236
|
+
return grant.token;
|
|
1237
|
+
} catch (error) {
|
|
1238
|
+
push({ kind: "error", error });
|
|
1239
|
+
return null;
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
const unsubscribe = client.onUpdate(
|
|
1243
|
+
snapshotQuery,
|
|
1244
|
+
{ workflowId: runId },
|
|
1245
|
+
(run) => push({ kind: "run", run: run ?? null }),
|
|
1246
|
+
(error) => push({ kind: "error", error })
|
|
1247
|
+
);
|
|
1248
|
+
let lastUpdateAt = Date.now();
|
|
1249
|
+
let lastStatusTerminal = false;
|
|
1250
|
+
let disconnectedSince = null;
|
|
1251
|
+
let warnedReconnecting = false;
|
|
1252
|
+
let warnedStale = false;
|
|
1253
|
+
const watchdog = setInterval(() => {
|
|
1254
|
+
const now = Date.now();
|
|
1255
|
+
try {
|
|
1256
|
+
const connectionState = client.connectionState();
|
|
1257
|
+
if (connectionState.isWebSocketConnected) {
|
|
1258
|
+
disconnectedSince = null;
|
|
1259
|
+
warnedReconnecting = false;
|
|
1260
|
+
} else {
|
|
1261
|
+
disconnectedSince ??= now;
|
|
1262
|
+
if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
|
|
1263
|
+
warnedReconnecting = true;
|
|
1264
|
+
options.onNotice?.(
|
|
1265
|
+
`[observe] connection lost; reconnecting to live run ${runId}\u2026`
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1271
|
+
if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
|
|
1272
|
+
warnedStale = true;
|
|
1273
|
+
options.onNotice?.(
|
|
1274
|
+
`[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
}, OBSERVE_WATCHDOG_TICK_MS);
|
|
1278
|
+
watchdog.unref?.();
|
|
1279
|
+
const abortListener = () => push({
|
|
1280
|
+
kind: "error",
|
|
1281
|
+
error: new DeeplineError(
|
|
1282
|
+
"Run observation aborted.",
|
|
1283
|
+
void 0,
|
|
1284
|
+
"ABORTED"
|
|
1285
|
+
)
|
|
1286
|
+
});
|
|
1287
|
+
options.signal?.addEventListener("abort", abortListener, { once: true });
|
|
1288
|
+
let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
|
|
1289
|
+
const streamId = ["observe", runId].join(":");
|
|
1290
|
+
let sawFirstSnapshot = false;
|
|
1291
|
+
try {
|
|
1292
|
+
for (; ; ) {
|
|
1293
|
+
if (queue.length === 0) {
|
|
1294
|
+
const waitForItem = new Promise((resolve14) => {
|
|
1295
|
+
wake = resolve14;
|
|
1296
|
+
});
|
|
1297
|
+
if (!sawFirstSnapshot) {
|
|
1298
|
+
const timedOut = await Promise.race([
|
|
1299
|
+
waitForItem.then(() => false),
|
|
1300
|
+
new Promise(
|
|
1301
|
+
(resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
|
|
1302
|
+
)
|
|
1303
|
+
]);
|
|
1304
|
+
if (timedOut && queue.length === 0) {
|
|
1305
|
+
throw new RunObserveTransportUnavailableError(
|
|
1306
|
+
`no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
|
|
1307
|
+
"convex_unreachable"
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
} else {
|
|
1311
|
+
await waitForItem;
|
|
1312
|
+
}
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
const item = queue.shift();
|
|
1316
|
+
if (item.kind === "error") {
|
|
1317
|
+
if (options.signal?.aborted) {
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
throw item.error;
|
|
1321
|
+
}
|
|
1322
|
+
sawFirstSnapshot = true;
|
|
1323
|
+
lastUpdateAt = Date.now();
|
|
1324
|
+
warnedStale = false;
|
|
1325
|
+
if (item.run === null) {
|
|
1326
|
+
throw new DeeplineError(
|
|
1327
|
+
`Run ${runId} was not found (or is not visible to this grant).`,
|
|
1328
|
+
404,
|
|
1329
|
+
"RUN_NOT_FOUND"
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
|
|
1333
|
+
lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
|
|
1334
|
+
const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
|
|
1335
|
+
if (gap && diffState.lastLogSeq > 0) {
|
|
1336
|
+
const backfilled = await backfillLogGap({
|
|
1337
|
+
queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
|
|
1338
|
+
workflowId: runId,
|
|
1339
|
+
afterSeq,
|
|
1340
|
+
limit
|
|
1341
|
+
}),
|
|
1342
|
+
lastLogSeq: diffState.lastLogSeq,
|
|
1343
|
+
tailFirstSeq: gap.tailFirstSeq
|
|
1344
|
+
});
|
|
1345
|
+
if (backfilled && backfilled.length > 0) {
|
|
1346
|
+
yield {
|
|
1347
|
+
cursor: String(snapshot.updatedAt ?? Date.now()),
|
|
1348
|
+
streamId,
|
|
1349
|
+
scope: "play",
|
|
1350
|
+
type: "play.run.log",
|
|
1351
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1352
|
+
payload: {
|
|
1353
|
+
runId: snapshot.runId,
|
|
1354
|
+
lines: backfilled,
|
|
1355
|
+
source: "worker",
|
|
1356
|
+
firstSeq: diffState.lastLogSeq + 1,
|
|
1357
|
+
totalLogCount: snapshot.totalLogCount
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
const { events, next } = diffPlayRunStreamEvents({
|
|
1364
|
+
streamId,
|
|
1365
|
+
snapshot,
|
|
1366
|
+
previous: diffState
|
|
1367
|
+
});
|
|
1368
|
+
diffState = next;
|
|
1369
|
+
const ordered = [
|
|
1370
|
+
...events.filter((event) => event.type === "play.run.snapshot"),
|
|
1371
|
+
...events.filter((event) => event.type !== "play.run.snapshot")
|
|
1372
|
+
];
|
|
1373
|
+
for (const event of ordered) {
|
|
1374
|
+
yield event;
|
|
1375
|
+
}
|
|
1376
|
+
if (lastStatusTerminal) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
} finally {
|
|
1381
|
+
clearInterval(watchdog);
|
|
1382
|
+
options.signal?.removeEventListener("abort", abortListener);
|
|
1383
|
+
try {
|
|
1384
|
+
unsubscribe();
|
|
1385
|
+
} catch {
|
|
1386
|
+
}
|
|
1387
|
+
await client.close().catch(() => void 0);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
590
1391
|
// src/client.ts
|
|
591
1392
|
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
592
1393
|
var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
@@ -605,7 +1406,8 @@ function isTransientCompileManifestError(error) {
|
|
|
605
1406
|
message
|
|
606
1407
|
);
|
|
607
1408
|
}
|
|
608
|
-
|
|
1409
|
+
var RUN_LOGS_PAGE_LIMIT = 1e3;
|
|
1410
|
+
function isRecord2(value) {
|
|
609
1411
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
610
1412
|
}
|
|
611
1413
|
function isPrebuiltPlayDescription(play) {
|
|
@@ -675,10 +1477,31 @@ function normalizeLiveStatus(value) {
|
|
|
675
1477
|
}
|
|
676
1478
|
return null;
|
|
677
1479
|
}
|
|
1480
|
+
function appendPlayLiveLogLines(state, payload) {
|
|
1481
|
+
const lines = readStringArray(payload.lines);
|
|
1482
|
+
if (lines.length === 0) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
|
|
1486
|
+
if (firstSeq === null) {
|
|
1487
|
+
state.logs.push(...lines);
|
|
1488
|
+
const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
|
|
1489
|
+
if (totalLogCount !== null) {
|
|
1490
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
|
|
1491
|
+
}
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
|
|
1495
|
+
if (skip >= lines.length) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
state.logs.push(...lines.slice(skip));
|
|
1499
|
+
state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
|
|
1500
|
+
}
|
|
678
1501
|
function updatePlayLiveStatusState(state, event) {
|
|
679
1502
|
const payload = getPlayLiveEventPayload(event);
|
|
680
1503
|
if (event.type === "play.run.log") {
|
|
681
|
-
state
|
|
1504
|
+
appendPlayLiveLogLines(state, payload);
|
|
682
1505
|
return null;
|
|
683
1506
|
}
|
|
684
1507
|
if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
|
|
@@ -686,12 +1509,14 @@ function updatePlayLiveStatusState(state, event) {
|
|
|
686
1509
|
}
|
|
687
1510
|
const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
|
|
688
1511
|
const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
|
|
689
|
-
const progressPayload =
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1512
|
+
const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
|
|
1513
|
+
if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
|
|
1514
|
+
const payloadLogs = readStringArray(payload.logs);
|
|
1515
|
+
const progressLogs = readStringArray(progressPayload.logs);
|
|
1516
|
+
const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
|
|
1517
|
+
if (seedLogs.length > 0) {
|
|
1518
|
+
state.logs = seedLogs;
|
|
1519
|
+
}
|
|
695
1520
|
}
|
|
696
1521
|
if ("result" in payload) {
|
|
697
1522
|
state.result = payload.result;
|
|
@@ -785,9 +1610,9 @@ var DeeplineClient = class {
|
|
|
785
1610
|
return fields.length > 0 ? { fields } : schema;
|
|
786
1611
|
}
|
|
787
1612
|
schemaMetadata(schema, key) {
|
|
788
|
-
if (!
|
|
1613
|
+
if (!isRecord2(schema)) return null;
|
|
789
1614
|
const value = schema[key];
|
|
790
|
-
return
|
|
1615
|
+
return isRecord2(value) ? value : null;
|
|
791
1616
|
}
|
|
792
1617
|
playRunCommand(play, options) {
|
|
793
1618
|
const target = play.reference || play.name;
|
|
@@ -834,7 +1659,7 @@ var DeeplineClient = class {
|
|
|
834
1659
|
aliases,
|
|
835
1660
|
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
836
1661
|
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
837
|
-
staticPipeline:
|
|
1662
|
+
staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
|
|
838
1663
|
...csvInput ? { csvInput } : {},
|
|
839
1664
|
...rowOutputSchema ? { rowOutputSchema } : {},
|
|
840
1665
|
runCommand: runCommand2,
|
|
@@ -1518,43 +2343,140 @@ var DeeplineClient = class {
|
|
|
1518
2343
|
);
|
|
1519
2344
|
return response.runs ?? [];
|
|
1520
2345
|
}
|
|
1521
|
-
/**
|
|
1522
|
-
|
|
2346
|
+
/**
|
|
2347
|
+
* Observe one run's live events through the Convex Run Snapshot
|
|
2348
|
+
* subscription transport (ADR-0008). Yields the same `play.*` event
|
|
2349
|
+
* envelopes as {@link streamPlayRunEvents} and ends after the terminal
|
|
2350
|
+
* snapshot. Throws {@link RunObserveTransportUnavailableError} when this
|
|
2351
|
+
* server cannot serve the transport (older server, unconfigured grants, or
|
|
2352
|
+
* unreachable Convex) — callers fall back to the SSE stream with a notice.
|
|
2353
|
+
*/
|
|
2354
|
+
observeRunEvents(runId, options) {
|
|
2355
|
+
return observeRunEvents({
|
|
2356
|
+
http: this.http,
|
|
2357
|
+
runId,
|
|
2358
|
+
signal: options?.signal,
|
|
2359
|
+
onNotice: options?.onNotice
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Tail one run through the subscription transport until terminal, then
|
|
2364
|
+
* return one durable REST status read (the final Run Response Package).
|
|
2365
|
+
*/
|
|
2366
|
+
async tailRunViaObserveTransport(runId, options) {
|
|
1523
2367
|
const state = {
|
|
1524
2368
|
runId,
|
|
1525
2369
|
status: "running",
|
|
1526
2370
|
logs: [],
|
|
2371
|
+
lastLogSeq: 0,
|
|
1527
2372
|
latest: null
|
|
1528
2373
|
};
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
signal: options?.signal
|
|
2374
|
+
for await (const event of this.observeRunEvents(runId, {
|
|
2375
|
+
signal: options?.signal,
|
|
2376
|
+
onNotice: options?.onNotice
|
|
1533
2377
|
})) {
|
|
1534
2378
|
const status = updatePlayLiveStatusState(state, event);
|
|
1535
|
-
if (!status) {
|
|
2379
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
1536
2380
|
continue;
|
|
1537
2381
|
}
|
|
1538
|
-
|
|
1539
|
-
if (terminal) {
|
|
1540
|
-
break;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
if (terminal && state.latest) {
|
|
1544
|
-
return await this.getRunStatus(state.latest.runId || runId).catch(
|
|
2382
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
1545
2383
|
() => state.latest ?? playRunStatusFromState(state)
|
|
1546
2384
|
);
|
|
1547
2385
|
}
|
|
1548
|
-
if (
|
|
1549
|
-
|
|
2386
|
+
if (options?.signal?.aborted) {
|
|
2387
|
+
throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
|
|
2388
|
+
}
|
|
2389
|
+
const refreshed = await this.getRunStatus(runId);
|
|
2390
|
+
if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2391
|
+
return refreshed;
|
|
1550
2392
|
}
|
|
1551
2393
|
throw new DeeplineError(
|
|
1552
|
-
`Run
|
|
2394
|
+
`Run observation for ${runId} ended before a terminal status.`,
|
|
1553
2395
|
void 0,
|
|
1554
|
-
"
|
|
1555
|
-
{ runId }
|
|
2396
|
+
"PLAY_LIVE_STREAM_ENDED"
|
|
1556
2397
|
);
|
|
1557
2398
|
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Read the canonical run stream until a terminal run status is observed.
|
|
2401
|
+
*
|
|
2402
|
+
* Tries the Convex Run Snapshot subscription transport first (ADR-0008);
|
|
2403
|
+
* when the server cannot serve it (grant endpoint missing/unconfigured or
|
|
2404
|
+
* Convex unreachable) it falls back — with one `onNotice` message — to the
|
|
2405
|
+
* support-window SSE stream below.
|
|
2406
|
+
*
|
|
2407
|
+
* Server stream windows are finite: they end cleanly at the function
|
|
2408
|
+
* ceiling even while the run keeps executing. A window that ends (cleanly
|
|
2409
|
+
* or via transient network error) without a terminal event triggers one
|
|
2410
|
+
* durable-status re-check followed by a backed-off reconnect, so long runs
|
|
2411
|
+
* tail to completion. Abort via `options.signal` to stop waiting.
|
|
2412
|
+
*/
|
|
2413
|
+
async tailRun(runId, options) {
|
|
2414
|
+
try {
|
|
2415
|
+
return await this.tailRunViaObserveTransport(runId, options);
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
if (!(error instanceof RunObserveTransportUnavailableError)) {
|
|
2418
|
+
throw error;
|
|
2419
|
+
}
|
|
2420
|
+
options?.onNotice?.(
|
|
2421
|
+
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
const state = {
|
|
2425
|
+
runId,
|
|
2426
|
+
status: "running",
|
|
2427
|
+
logs: [],
|
|
2428
|
+
lastLogSeq: 0,
|
|
2429
|
+
latest: null
|
|
2430
|
+
};
|
|
2431
|
+
let reconnectAttempt = 0;
|
|
2432
|
+
for (; ; ) {
|
|
2433
|
+
const connectedAt = Date.now();
|
|
2434
|
+
let sawEvent = false;
|
|
2435
|
+
let endedReason = "stream window ended before a terminal event";
|
|
2436
|
+
try {
|
|
2437
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
2438
|
+
mode: "cli",
|
|
2439
|
+
signal: options?.signal
|
|
2440
|
+
})) {
|
|
2441
|
+
sawEvent = true;
|
|
2442
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
2443
|
+
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
return await this.getRunStatus(status.runId || runId).catch(
|
|
2447
|
+
() => state.latest ?? playRunStatusFromState(state)
|
|
2448
|
+
);
|
|
2449
|
+
}
|
|
2450
|
+
} catch (error) {
|
|
2451
|
+
if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
|
|
2452
|
+
throw error;
|
|
2453
|
+
}
|
|
2454
|
+
endedReason = error instanceof Error ? error.message : String(error);
|
|
2455
|
+
}
|
|
2456
|
+
let refreshed = null;
|
|
2457
|
+
try {
|
|
2458
|
+
refreshed = await this.getRunStatus(runId);
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
if (!isTransientPlayStreamError(error)) {
|
|
2461
|
+
throw error;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2465
|
+
return refreshed;
|
|
2466
|
+
}
|
|
2467
|
+
if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
|
|
2468
|
+
reconnectAttempt = 0;
|
|
2469
|
+
}
|
|
2470
|
+
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
2471
|
+
reconnectAttempt += 1;
|
|
2472
|
+
options?.onReconnect?.({
|
|
2473
|
+
attempt: reconnectAttempt,
|
|
2474
|
+
delayMs,
|
|
2475
|
+
reason: endedReason
|
|
2476
|
+
});
|
|
2477
|
+
await sleep2(delayMs);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
1558
2480
|
/**
|
|
1559
2481
|
* Fetch persisted logs for a run using the public runs resource model.
|
|
1560
2482
|
*
|
|
@@ -1565,19 +2487,40 @@ var DeeplineClient = class {
|
|
|
1565
2487
|
* ```
|
|
1566
2488
|
*/
|
|
1567
2489
|
async getRunLogs(runId, options) {
|
|
1568
|
-
const
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1571
|
-
|
|
2490
|
+
const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
|
|
2491
|
+
const fetchPage = (afterSeq2, pageLimit) => this.http.get(
|
|
2492
|
+
`/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
|
|
2493
|
+
);
|
|
2494
|
+
const probe = await fetchPage(0, 1);
|
|
2495
|
+
const lastStoredSeq = probe.lastStoredSeq;
|
|
2496
|
+
let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
|
|
2497
|
+
const entries = [];
|
|
2498
|
+
while (entries.length < limit) {
|
|
2499
|
+
const page = await fetchPage(
|
|
2500
|
+
afterSeq,
|
|
2501
|
+
Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
|
|
2502
|
+
);
|
|
2503
|
+
if (page.entries.length === 0) {
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
entries.push(...page.entries);
|
|
2507
|
+
afterSeq = page.entries[page.entries.length - 1].seq;
|
|
2508
|
+
if (!page.hasMore) {
|
|
2509
|
+
break;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
const firstSequence = entries.length > 0 ? entries[0].seq : null;
|
|
2513
|
+
const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
|
|
1572
2514
|
return {
|
|
1573
|
-
runId:
|
|
1574
|
-
totalCount:
|
|
2515
|
+
runId: probe.runId,
|
|
2516
|
+
totalCount: probe.totalLogCount,
|
|
1575
2517
|
returnedCount: entries.length,
|
|
1576
|
-
firstSequence
|
|
1577
|
-
lastSequence
|
|
1578
|
-
truncated:
|
|
1579
|
-
hasMore:
|
|
1580
|
-
entries
|
|
2518
|
+
firstSequence,
|
|
2519
|
+
lastSequence,
|
|
2520
|
+
truncated: entries.length < probe.totalLogCount,
|
|
2521
|
+
hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
|
|
2522
|
+
entries: entries.map((entry) => entry.line),
|
|
2523
|
+
...probe.logsTruncated ? { logsTruncated: true } : {}
|
|
1581
2524
|
};
|
|
1582
2525
|
}
|
|
1583
2526
|
/**
|
|
@@ -1868,6 +2811,7 @@ var DeeplineClient = class {
|
|
|
1868
2811
|
runId: workflowId,
|
|
1869
2812
|
status: "running",
|
|
1870
2813
|
logs: [],
|
|
2814
|
+
lastLogSeq: 0,
|
|
1871
2815
|
latest: null
|
|
1872
2816
|
};
|
|
1873
2817
|
if (options?.signal?.aborted) {
|
|
@@ -3550,11 +4494,11 @@ function sanitizeCsvProjectionInfo(input2) {
|
|
|
3550
4494
|
const rows = input2.rows.map(stripCsvProjectionFields);
|
|
3551
4495
|
return { rows, columns };
|
|
3552
4496
|
}
|
|
3553
|
-
function
|
|
4497
|
+
function isRecord3(value) {
|
|
3554
4498
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
3555
4499
|
}
|
|
3556
4500
|
function isSerializedDataset(value) {
|
|
3557
|
-
return
|
|
4501
|
+
return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
|
|
3558
4502
|
}
|
|
3559
4503
|
function pathParts(path) {
|
|
3560
4504
|
return path.split(".").map((part) => part.trim()).filter(Boolean);
|
|
@@ -3562,7 +4506,7 @@ function pathParts(path) {
|
|
|
3562
4506
|
function valueAtPath(root, path) {
|
|
3563
4507
|
let cursor = root;
|
|
3564
4508
|
for (const part of pathParts(path)) {
|
|
3565
|
-
if (!
|
|
4509
|
+
if (!isRecord3(cursor)) {
|
|
3566
4510
|
return void 0;
|
|
3567
4511
|
}
|
|
3568
4512
|
cursor = cursor[part];
|
|
@@ -3570,17 +4514,17 @@ function valueAtPath(root, path) {
|
|
|
3570
4514
|
return cursor;
|
|
3571
4515
|
}
|
|
3572
4516
|
function totalRowsForDataset(result, datasetPath) {
|
|
3573
|
-
const metadata =
|
|
4517
|
+
const metadata = isRecord3(result._metadata) ? result._metadata : null;
|
|
3574
4518
|
const parentPath = datasetPath.split(".").slice(0, -1).join(".");
|
|
3575
4519
|
const parent = parentPath ? valueAtPath({ result }, parentPath) : result;
|
|
3576
|
-
return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (
|
|
4520
|
+
return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord3(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
|
|
3577
4521
|
}
|
|
3578
4522
|
function rowArray(value) {
|
|
3579
4523
|
if (!Array.isArray(value)) {
|
|
3580
4524
|
return null;
|
|
3581
4525
|
}
|
|
3582
4526
|
const rows = value.filter(
|
|
3583
|
-
(row) =>
|
|
4527
|
+
(row) => isRecord3(row)
|
|
3584
4528
|
);
|
|
3585
4529
|
return rows.length === value.length ? rows : null;
|
|
3586
4530
|
}
|
|
@@ -3657,7 +4601,7 @@ function collectDatasetCandidates(input2) {
|
|
|
3657
4601
|
});
|
|
3658
4602
|
return;
|
|
3659
4603
|
}
|
|
3660
|
-
if (!
|
|
4604
|
+
if (!isRecord3(input2.value)) {
|
|
3661
4605
|
return;
|
|
3662
4606
|
}
|
|
3663
4607
|
for (const [key, child] of Object.entries(input2.value)) {
|
|
@@ -3674,12 +4618,12 @@ function collectDatasetCandidates(input2) {
|
|
|
3674
4618
|
}
|
|
3675
4619
|
}
|
|
3676
4620
|
function collectCanonicalRowsInfos(statusOrResult) {
|
|
3677
|
-
const root =
|
|
3678
|
-
const result =
|
|
4621
|
+
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4622
|
+
const result = isRecord3(root?.result) ? root.result : root;
|
|
3679
4623
|
if (!result) {
|
|
3680
4624
|
return [];
|
|
3681
4625
|
}
|
|
3682
|
-
const metadata =
|
|
4626
|
+
const metadata = isRecord3(result._metadata) ? result._metadata : null;
|
|
3683
4627
|
const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
|
|
3684
4628
|
const candidates = [
|
|
3685
4629
|
{
|
|
@@ -3703,8 +4647,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
3703
4647
|
total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count
|
|
3704
4648
|
}
|
|
3705
4649
|
];
|
|
3706
|
-
if (
|
|
3707
|
-
const outputMetadata =
|
|
4650
|
+
if (isRecord3(result.output)) {
|
|
4651
|
+
const outputMetadata = isRecord3(result.output._metadata) ? result.output._metadata : null;
|
|
3708
4652
|
const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
|
|
3709
4653
|
candidates.push(
|
|
3710
4654
|
{
|
|
@@ -3750,8 +4694,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
3750
4694
|
return infos;
|
|
3751
4695
|
}
|
|
3752
4696
|
function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
3753
|
-
const root =
|
|
3754
|
-
const result =
|
|
4697
|
+
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4698
|
+
const result = isRecord3(root?.result) ? root.result : root;
|
|
3755
4699
|
if (!result) {
|
|
3756
4700
|
return [];
|
|
3757
4701
|
}
|
|
@@ -3785,17 +4729,17 @@ function percentText(numerator, denominator) {
|
|
|
3785
4729
|
return datasetSummaryPercentText(numerator, denominator);
|
|
3786
4730
|
}
|
|
3787
4731
|
function isDatasetExecutionStatsInput(value) {
|
|
3788
|
-
return
|
|
4732
|
+
return isRecord3(value) && isRecord3(value.columnStats) && Object.values(value.columnStats).every(isRecord3);
|
|
3789
4733
|
}
|
|
3790
4734
|
function extractDatasetExecutionStats(statusOrResult) {
|
|
3791
|
-
if (!
|
|
4735
|
+
if (!isRecord3(statusOrResult)) {
|
|
3792
4736
|
return null;
|
|
3793
4737
|
}
|
|
3794
4738
|
const direct = statusOrResult.dataset_execution_stats;
|
|
3795
4739
|
if (isDatasetExecutionStatsInput(direct)) {
|
|
3796
4740
|
return direct;
|
|
3797
4741
|
}
|
|
3798
|
-
const nested =
|
|
4742
|
+
const nested = isRecord3(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
|
|
3799
4743
|
return isDatasetExecutionStatsInput(nested) ? nested : null;
|
|
3800
4744
|
}
|
|
3801
4745
|
function countPercentText(count, denominator) {
|
|
@@ -3836,13 +4780,13 @@ function summarizeSampleValue(value, depth = 0) {
|
|
|
3836
4780
|
if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
|
|
3837
4781
|
if (depth >= 3) {
|
|
3838
4782
|
if (Array.isArray(parsed)) return [];
|
|
3839
|
-
if (
|
|
4783
|
+
if (isRecord3(parsed)) return {};
|
|
3840
4784
|
return compactScalar(parsed);
|
|
3841
4785
|
}
|
|
3842
4786
|
if (Array.isArray(parsed)) {
|
|
3843
4787
|
return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
|
|
3844
4788
|
}
|
|
3845
|
-
if (
|
|
4789
|
+
if (isRecord3(parsed)) {
|
|
3846
4790
|
const out = {};
|
|
3847
4791
|
for (const [key, nested] of Object.entries(parsed)) {
|
|
3848
4792
|
if (["__dl", "meta", "metadata"].includes(key)) {
|
|
@@ -3873,7 +4817,7 @@ function compactCell(value) {
|
|
|
3873
4817
|
}
|
|
3874
4818
|
return `[${parsed.length} items]`;
|
|
3875
4819
|
}
|
|
3876
|
-
if (
|
|
4820
|
+
if (isRecord3(parsed)) {
|
|
3877
4821
|
for (const key of ["matched_result", "output"]) {
|
|
3878
4822
|
if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
|
|
3879
4823
|
return compactCell(parsed[key]);
|
|
@@ -6192,17 +7136,17 @@ function parsePositiveInteger2(value, flagName) {
|
|
|
6192
7136
|
}
|
|
6193
7137
|
return parsed;
|
|
6194
7138
|
}
|
|
6195
|
-
function
|
|
7139
|
+
function isRecord4(value) {
|
|
6196
7140
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6197
7141
|
}
|
|
6198
7142
|
function stringValue(value) {
|
|
6199
7143
|
return typeof value === "string" ? value.trim() : "";
|
|
6200
7144
|
}
|
|
6201
7145
|
function extractionEntries(value) {
|
|
6202
|
-
if (Array.isArray(value)) return value.filter(
|
|
6203
|
-
if (!
|
|
7146
|
+
if (Array.isArray(value)) return value.filter(isRecord4);
|
|
7147
|
+
if (!isRecord4(value)) return [];
|
|
6204
7148
|
return Object.entries(value).map(
|
|
6205
|
-
([name, entry]) =>
|
|
7149
|
+
([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
|
|
6206
7150
|
);
|
|
6207
7151
|
}
|
|
6208
7152
|
var PlayBootstrapError = class extends Error {
|
|
@@ -6622,7 +7566,7 @@ function readCsvSampleRows(sample) {
|
|
|
6622
7566
|
relax_column_count: true,
|
|
6623
7567
|
trim: true
|
|
6624
7568
|
});
|
|
6625
|
-
return Array.isArray(parsedRows) ? parsedRows.filter(
|
|
7569
|
+
return Array.isArray(parsedRows) ? parsedRows.filter(isRecord4) : [];
|
|
6626
7570
|
}
|
|
6627
7571
|
function readSourceCsvColumnSpecs(csvPath) {
|
|
6628
7572
|
const sample = readCsvSample(csvPath);
|
|
@@ -6655,16 +7599,16 @@ function packagedCsvPathForPlay(csvPath) {
|
|
|
6655
7599
|
return portablePath.startsWith(".") ? portablePath : `./${portablePath}`;
|
|
6656
7600
|
}
|
|
6657
7601
|
function getterNamesFromTool(tool, kind) {
|
|
6658
|
-
const usageGuidance =
|
|
6659
|
-
const resultGuidance =
|
|
7602
|
+
const usageGuidance = isRecord4(tool?.usageGuidance) ? tool.usageGuidance : {};
|
|
7603
|
+
const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
|
|
6660
7604
|
const key = kind === "list" ? "extractedLists" : "extractedValues";
|
|
6661
7605
|
const snakeKey = kind === "list" ? "extracted_lists" : "extracted_values";
|
|
6662
7606
|
return extractionEntries(resultGuidance[key] ?? resultGuidance[snakeKey]).map((entry) => stringValue(entry.name)).filter(Boolean);
|
|
6663
7607
|
}
|
|
6664
7608
|
function targetGettersFromTool(tool) {
|
|
6665
|
-
const record =
|
|
7609
|
+
const record = isRecord4(tool) ? tool : {};
|
|
6666
7610
|
const raw = record.targetGetters ?? record.target_getters;
|
|
6667
|
-
if (!
|
|
7611
|
+
if (!isRecord4(raw)) return {};
|
|
6668
7612
|
const entries = [];
|
|
6669
7613
|
for (const [target, value] of Object.entries(raw)) {
|
|
6670
7614
|
const paths = Array.isArray(value) ? value.map((path) => typeof path === "string" ? path.trim() : "").filter(Boolean) : [];
|
|
@@ -6685,10 +7629,10 @@ function listRowCandidateKeysFromTool(tool) {
|
|
|
6685
7629
|
return [...keys].sort();
|
|
6686
7630
|
}
|
|
6687
7631
|
function inputPropertyNames(schema) {
|
|
6688
|
-
if (!
|
|
6689
|
-
if (
|
|
7632
|
+
if (!isRecord4(schema)) return [];
|
|
7633
|
+
if (isRecord4(schema.properties)) return Object.keys(schema.properties);
|
|
6690
7634
|
if (Array.isArray(schema.fields)) {
|
|
6691
|
-
return schema.fields.filter(
|
|
7635
|
+
return schema.fields.filter(isRecord4).map((field) => stringValue(field.name)).filter(Boolean);
|
|
6692
7636
|
}
|
|
6693
7637
|
return [];
|
|
6694
7638
|
}
|
|
@@ -6702,7 +7646,7 @@ function schemaFieldDetails(schema) {
|
|
|
6702
7646
|
return { required, optional };
|
|
6703
7647
|
}
|
|
6704
7648
|
function jsonSchemaTypeExpression(schema) {
|
|
6705
|
-
if (!
|
|
7649
|
+
if (!isRecord4(schema)) return "unknown";
|
|
6706
7650
|
const type = schema.type;
|
|
6707
7651
|
if (Array.isArray(type)) {
|
|
6708
7652
|
return type.map((entry) => jsonSchemaTypeExpression({ ...schema, type: entry })).join(" | ");
|
|
@@ -6732,7 +7676,7 @@ function jsonSchemaTypeExpression(schema) {
|
|
|
6732
7676
|
}
|
|
6733
7677
|
}
|
|
6734
7678
|
function objectPropertySchema(schema, property) {
|
|
6735
|
-
return
|
|
7679
|
+
return isRecord4(schema) && isRecord4(schema.properties) ? schema.properties[property] : null;
|
|
6736
7680
|
}
|
|
6737
7681
|
function playOutputHasField(schema, field) {
|
|
6738
7682
|
return objectPropertySchema(schema, field) != null;
|
|
@@ -6908,14 +7852,14 @@ ${indent2.slice(2)}}`;
|
|
|
6908
7852
|
}
|
|
6909
7853
|
function requiredPlayInputFields(play) {
|
|
6910
7854
|
const schema = play?.inputSchema;
|
|
6911
|
-
if (!
|
|
7855
|
+
if (!isRecord4(schema)) return [];
|
|
6912
7856
|
if (Array.isArray(schema.required)) {
|
|
6913
7857
|
return schema.required.filter(
|
|
6914
7858
|
(value) => typeof value === "string"
|
|
6915
7859
|
);
|
|
6916
7860
|
}
|
|
6917
7861
|
if (Array.isArray(schema.fields)) {
|
|
6918
|
-
return schema.fields.filter(
|
|
7862
|
+
return schema.fields.filter(isRecord4).filter(
|
|
6919
7863
|
(field) => field.required === true && typeof field.name === "string"
|
|
6920
7864
|
).map((field) => String(field.name));
|
|
6921
7865
|
}
|
|
@@ -7096,7 +8040,7 @@ function validateBootstrapRoutes(input2) {
|
|
|
7096
8040
|
}
|
|
7097
8041
|
}
|
|
7098
8042
|
function staticPipelineSubsteps(pipeline) {
|
|
7099
|
-
if (!
|
|
8043
|
+
if (!isRecord4(pipeline)) return [];
|
|
7100
8044
|
return [
|
|
7101
8045
|
...extractionEntries(pipeline.stages),
|
|
7102
8046
|
...extractionEntries(pipeline.substeps)
|
|
@@ -7104,7 +8048,7 @@ function staticPipelineSubsteps(pipeline) {
|
|
|
7104
8048
|
}
|
|
7105
8049
|
function playUsesMapBackedRuntime(play) {
|
|
7106
8050
|
const pipeline = play?.staticPipeline;
|
|
7107
|
-
if (!
|
|
8051
|
+
if (!isRecord4(pipeline)) return false;
|
|
7108
8052
|
if (stringValue(pipeline.tableNamespace)) return true;
|
|
7109
8053
|
return staticPipelineSubsteps(pipeline).some((substep) => {
|
|
7110
8054
|
if (stringValue(substep.type) === "map") return true;
|
|
@@ -7113,7 +8057,7 @@ function playUsesMapBackedRuntime(play) {
|
|
|
7113
8057
|
aliases: [],
|
|
7114
8058
|
runCommand: "",
|
|
7115
8059
|
examples: [],
|
|
7116
|
-
staticPipeline:
|
|
8060
|
+
staticPipeline: isRecord4(substep.pipeline) ? substep.pipeline : null
|
|
7117
8061
|
});
|
|
7118
8062
|
});
|
|
7119
8063
|
}
|
|
@@ -8438,15 +9382,6 @@ function formatRunLine(run) {
|
|
|
8438
9382
|
const credits = typeof run.billingTotalCredits === "number" && Number.isFinite(run.billingTotalCredits) ? `${formatCreditAmount(run.billingTotalCredits)} credits` : "\u2014";
|
|
8439
9383
|
return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)} ${credits}`;
|
|
8440
9384
|
}
|
|
8441
|
-
function isTransientPlayStreamError(error) {
|
|
8442
|
-
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
8443
|
-
return error.statusCode >= 500 && error.statusCode < 600;
|
|
8444
|
-
}
|
|
8445
|
-
const text = error instanceof Error ? error.message : String(error);
|
|
8446
|
-
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
8447
|
-
text
|
|
8448
|
-
);
|
|
8449
|
-
}
|
|
8450
9385
|
function playStatusErrorText(status) {
|
|
8451
9386
|
const chunks = [];
|
|
8452
9387
|
const progressError = status.progress?.error;
|
|
@@ -8784,97 +9719,227 @@ function assertPlayWaitNotTimedOut(input2) {
|
|
|
8784
9719
|
}
|
|
8785
9720
|
}
|
|
8786
9721
|
async function waitForPlayCompletionByStream(input2) {
|
|
8787
|
-
const controller = new AbortController();
|
|
8788
|
-
let timedOut = false;
|
|
8789
9722
|
let lastPhase = null;
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
for await (const event of input2.client.streamPlayRunEvents(
|
|
8799
|
-
input2.workflowId,
|
|
8800
|
-
{ signal: controller.signal }
|
|
8801
|
-
)) {
|
|
8802
|
-
assertPlayWaitNotTimedOut({ ...input2, lastPhase });
|
|
8803
|
-
const phase = describeLiveEventPhase(event);
|
|
8804
|
-
if (phase) {
|
|
8805
|
-
lastPhase = phase;
|
|
8806
|
-
input2.progress.phase(phase);
|
|
8807
|
-
}
|
|
8808
|
-
emitLiveDebugTableHints({
|
|
8809
|
-
event,
|
|
8810
|
-
playName: input2.playName,
|
|
8811
|
-
runId: input2.workflowId,
|
|
8812
|
-
jsonOutput: input2.jsonOutput,
|
|
8813
|
-
state: input2.state,
|
|
8814
|
-
progress: input2.progress
|
|
9723
|
+
let reconnectAttempt = 0;
|
|
9724
|
+
const withDashboardUrl = (status) => input2.dashboardUrl ? { ...status, dashboardUrl: input2.dashboardUrl } : status;
|
|
9725
|
+
const remainingWaitMs = () => input2.waitTimeoutMs === null ? null : input2.waitTimeoutMs - (Date.now() - input2.startedAt);
|
|
9726
|
+
const fetchTerminalStatus = async () => {
|
|
9727
|
+
let refreshed;
|
|
9728
|
+
try {
|
|
9729
|
+
refreshed = await input2.client.getPlayStatus(input2.workflowId, {
|
|
9730
|
+
billing: false
|
|
8815
9731
|
});
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
9732
|
+
} catch (error) {
|
|
9733
|
+
if (isTransientPlayStreamError(error)) {
|
|
9734
|
+
return null;
|
|
9735
|
+
}
|
|
9736
|
+
throw error;
|
|
9737
|
+
}
|
|
9738
|
+
return TERMINAL_PLAY_STATUSES2.has(refreshed.status) ? withDashboardUrl(refreshed) : null;
|
|
9739
|
+
};
|
|
9740
|
+
const handleLiveEvent = async (event) => {
|
|
9741
|
+
assertPlayWaitNotTimedOut({ ...input2, lastPhase });
|
|
9742
|
+
const phase = describeLiveEventPhase(event);
|
|
9743
|
+
if (phase) {
|
|
9744
|
+
lastPhase = phase;
|
|
9745
|
+
input2.progress.phase(phase);
|
|
9746
|
+
}
|
|
9747
|
+
emitLiveDebugTableHints({
|
|
9748
|
+
event,
|
|
9749
|
+
playName: input2.playName,
|
|
9750
|
+
runId: input2.workflowId,
|
|
9751
|
+
jsonOutput: input2.jsonOutput,
|
|
9752
|
+
state: input2.state,
|
|
9753
|
+
progress: input2.progress
|
|
9754
|
+
});
|
|
9755
|
+
printPlayLogLines({
|
|
9756
|
+
lines: getLogLinesFromLiveEvent(event),
|
|
9757
|
+
status: null,
|
|
9758
|
+
jsonOutput: input2.jsonOutput,
|
|
9759
|
+
emitLogs: input2.emitLogs,
|
|
9760
|
+
state: input2.state,
|
|
9761
|
+
progress: input2.progress
|
|
9762
|
+
});
|
|
9763
|
+
if (!input2.jsonOutput) {
|
|
9764
|
+
const progressLines = getProgressLinesFromLiveEvent(event);
|
|
9765
|
+
printPlayProgressLines({
|
|
9766
|
+
lines: progressLines,
|
|
8821
9767
|
state: input2.state,
|
|
8822
9768
|
progress: input2.progress
|
|
8823
9769
|
});
|
|
8824
|
-
if (
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
9770
|
+
if (progressLines.length === 0) {
|
|
9771
|
+
printPlayStatusHeartbeat({
|
|
9772
|
+
event,
|
|
9773
|
+
playName: input2.playName,
|
|
8828
9774
|
state: input2.state,
|
|
8829
9775
|
progress: input2.progress
|
|
8830
9776
|
});
|
|
8831
|
-
if (progressLines.length === 0) {
|
|
8832
|
-
printPlayStatusHeartbeat({
|
|
8833
|
-
event,
|
|
8834
|
-
playName: input2.playName,
|
|
8835
|
-
state: input2.state,
|
|
8836
|
-
progress: input2.progress
|
|
8837
|
-
});
|
|
8838
|
-
}
|
|
8839
9777
|
}
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
9778
|
+
}
|
|
9779
|
+
const finalStatus = getFinalStatusFromLiveEvent(event);
|
|
9780
|
+
if (finalStatus) {
|
|
9781
|
+
return withDashboardUrl(finalStatus);
|
|
9782
|
+
}
|
|
9783
|
+
const status = getStatusFromLiveEvent(event);
|
|
9784
|
+
if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
|
|
9785
|
+
const refreshedStatus = await input2.client.getPlayStatus(
|
|
9786
|
+
input2.workflowId,
|
|
9787
|
+
{
|
|
9788
|
+
billing: false
|
|
9789
|
+
}
|
|
9790
|
+
);
|
|
9791
|
+
if (TERMINAL_PLAY_STATUSES2.has(refreshedStatus.status)) {
|
|
9792
|
+
return withDashboardUrl(refreshedStatus);
|
|
8843
9793
|
}
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
9794
|
+
}
|
|
9795
|
+
return null;
|
|
9796
|
+
};
|
|
9797
|
+
const watchViaObserveTransport = async () => {
|
|
9798
|
+
const controller = new AbortController();
|
|
9799
|
+
let timedOut = false;
|
|
9800
|
+
const remaining = remainingWaitMs();
|
|
9801
|
+
const timeout = remaining === null ? null : setTimeout(
|
|
9802
|
+
() => {
|
|
9803
|
+
timedOut = true;
|
|
9804
|
+
controller.abort();
|
|
9805
|
+
},
|
|
9806
|
+
Math.max(1, remaining)
|
|
9807
|
+
);
|
|
9808
|
+
try {
|
|
9809
|
+
for await (const event of input2.client.observeRunEvents(
|
|
9810
|
+
input2.workflowId,
|
|
9811
|
+
{
|
|
9812
|
+
signal: controller.signal,
|
|
9813
|
+
onNotice: input2.jsonOutput ? void 0 : (message) => input2.progress.writeLine(message)
|
|
9814
|
+
}
|
|
9815
|
+
)) {
|
|
9816
|
+
const terminal2 = await handleLiveEvent(event);
|
|
9817
|
+
if (terminal2) {
|
|
9818
|
+
return terminal2;
|
|
8854
9819
|
}
|
|
8855
9820
|
}
|
|
9821
|
+
} finally {
|
|
9822
|
+
if (timeout) {
|
|
9823
|
+
clearTimeout(timeout);
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
const terminal = await fetchTerminalStatus();
|
|
9827
|
+
if (terminal) {
|
|
9828
|
+
return terminal;
|
|
8856
9829
|
}
|
|
8857
|
-
} catch (error) {
|
|
8858
9830
|
if (timedOut) {
|
|
8859
9831
|
assertPlayWaitNotTimedOut({ ...input2, lastPhase });
|
|
8860
9832
|
}
|
|
8861
|
-
throw
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
9833
|
+
throw new DeeplineError(
|
|
9834
|
+
`Run observation for ${input2.workflowId} ended before a terminal status.`,
|
|
9835
|
+
void 0,
|
|
9836
|
+
"PLAY_LIVE_STREAM_ENDED",
|
|
9837
|
+
{ runId: input2.workflowId, workflowId: input2.workflowId }
|
|
9838
|
+
);
|
|
9839
|
+
};
|
|
9840
|
+
try {
|
|
9841
|
+
return await watchViaObserveTransport();
|
|
9842
|
+
} catch (error) {
|
|
9843
|
+
if (!(error instanceof RunObserveTransportUnavailableError)) {
|
|
9844
|
+
throw error;
|
|
8865
9845
|
}
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
9846
|
+
if (!input2.jsonOutput) {
|
|
9847
|
+
input2.progress.writeLine(
|
|
9848
|
+
`[play watch] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
|
|
9849
|
+
);
|
|
9850
|
+
}
|
|
9851
|
+
recordCliTrace({
|
|
9852
|
+
phase: "cli.play_observe_transport_fallback",
|
|
9853
|
+
ms: Date.now() - input2.startedAt,
|
|
9854
|
+
ok: true,
|
|
9855
|
+
playName: input2.playName,
|
|
8874
9856
|
workflowId: input2.workflowId,
|
|
8875
|
-
|
|
9857
|
+
reason: error.reason
|
|
9858
|
+
});
|
|
9859
|
+
}
|
|
9860
|
+
const streamOneWindow = async () => {
|
|
9861
|
+
const controller = new AbortController();
|
|
9862
|
+
let timedOut = false;
|
|
9863
|
+
let sawEvent = false;
|
|
9864
|
+
const remaining = remainingWaitMs();
|
|
9865
|
+
const timeout = remaining === null ? null : setTimeout(
|
|
9866
|
+
() => {
|
|
9867
|
+
timedOut = true;
|
|
9868
|
+
controller.abort();
|
|
9869
|
+
},
|
|
9870
|
+
Math.max(1, remaining)
|
|
9871
|
+
);
|
|
9872
|
+
try {
|
|
9873
|
+
for await (const event of input2.client.streamPlayRunEvents(
|
|
9874
|
+
input2.workflowId,
|
|
9875
|
+
{ signal: controller.signal }
|
|
9876
|
+
)) {
|
|
9877
|
+
sawEvent = true;
|
|
9878
|
+
const terminal = await handleLiveEvent(event);
|
|
9879
|
+
if (terminal) {
|
|
9880
|
+
return { kind: "terminal", status: terminal };
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
} catch (error) {
|
|
9884
|
+
if (timedOut) {
|
|
9885
|
+
return { kind: "timed_out", sawEvent };
|
|
9886
|
+
}
|
|
9887
|
+
if (!isTransientPlayStreamError(error)) {
|
|
9888
|
+
throw error;
|
|
9889
|
+
}
|
|
9890
|
+
return {
|
|
9891
|
+
kind: "ended",
|
|
9892
|
+
sawEvent,
|
|
9893
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
9894
|
+
};
|
|
9895
|
+
} finally {
|
|
9896
|
+
if (timeout) {
|
|
9897
|
+
clearTimeout(timeout);
|
|
9898
|
+
}
|
|
8876
9899
|
}
|
|
8877
|
-
|
|
9900
|
+
return {
|
|
9901
|
+
kind: "ended",
|
|
9902
|
+
sawEvent,
|
|
9903
|
+
reason: "stream window ended before a terminal event"
|
|
9904
|
+
};
|
|
9905
|
+
};
|
|
9906
|
+
for (; ; ) {
|
|
9907
|
+
const connectedAt = Date.now();
|
|
9908
|
+
const window = await streamOneWindow();
|
|
9909
|
+
if (window.kind === "terminal") {
|
|
9910
|
+
return window.status;
|
|
9911
|
+
}
|
|
9912
|
+
const terminal = await fetchTerminalStatus();
|
|
9913
|
+
if (terminal) {
|
|
9914
|
+
return terminal;
|
|
9915
|
+
}
|
|
9916
|
+
assertPlayWaitNotTimedOut({ ...input2, lastPhase });
|
|
9917
|
+
if (window.sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
|
|
9918
|
+
reconnectAttempt = 0;
|
|
9919
|
+
}
|
|
9920
|
+
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
9921
|
+
reconnectAttempt += 1;
|
|
9922
|
+
const reason = window.kind === "ended" ? window.reason : "wait timeout raced the stream";
|
|
9923
|
+
if (!input2.jsonOutput) {
|
|
9924
|
+
input2.progress.writeLine(
|
|
9925
|
+
`[play watch] stream ended without a terminal status; reconnecting to run ${input2.workflowId} (${reason})`
|
|
9926
|
+
);
|
|
9927
|
+
}
|
|
9928
|
+
recordCliTrace({
|
|
9929
|
+
phase: "cli.play_tail_stream_reconnect",
|
|
9930
|
+
ms: Date.now() - input2.startedAt,
|
|
9931
|
+
ok: true,
|
|
9932
|
+
playName: input2.playName,
|
|
9933
|
+
workflowId: input2.workflowId,
|
|
9934
|
+
attempt: reconnectAttempt,
|
|
9935
|
+
delayMs,
|
|
9936
|
+
reason
|
|
9937
|
+
});
|
|
9938
|
+
const remainingBeforeSleep = remainingWaitMs();
|
|
9939
|
+
await sleep4(
|
|
9940
|
+
remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
|
|
9941
|
+
);
|
|
9942
|
+
}
|
|
8878
9943
|
}
|
|
8879
9944
|
async function startAndWaitForPlayCompletionByStream(input2) {
|
|
8880
9945
|
for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
@@ -9819,6 +10884,10 @@ function buildRunPackageTextLines(packaged) {
|
|
|
9819
10884
|
const lines = [
|
|
9820
10885
|
`${status === "completed" ? "\u2713" : status === "failed" ? "\u2717" : "\u2022"} ${status} ${runId}`
|
|
9821
10886
|
];
|
|
10887
|
+
const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
|
|
10888
|
+
if (runError && (status === "failed" || status === "cancelled")) {
|
|
10889
|
+
lines.push(` error: ${runError.slice(0, 200)}`);
|
|
10890
|
+
}
|
|
9822
10891
|
if (playName) {
|
|
9823
10892
|
lines.push(` play: ${playName}`);
|
|
9824
10893
|
}
|
|
@@ -9920,18 +10989,33 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
9920
10989
|
` }
|
|
9921
10990
|
);
|
|
9922
10991
|
}
|
|
10992
|
+
function playRunPackageStepCount(pkg) {
|
|
10993
|
+
if (!pkg) return 0;
|
|
10994
|
+
const steps = pkg.steps;
|
|
10995
|
+
return Array.isArray(steps) ? steps.length : 0;
|
|
10996
|
+
}
|
|
9923
10997
|
async function resolvePlayRunOutputStatus(input2) {
|
|
9924
|
-
if (!input2.fullJson || !getPlayRunPackage(input2.status)) {
|
|
9925
|
-
return input2.status;
|
|
9926
|
-
}
|
|
9927
10998
|
const runId = input2.status.runId;
|
|
9928
10999
|
if (!runId) {
|
|
9929
11000
|
return input2.status;
|
|
9930
11001
|
}
|
|
9931
|
-
const
|
|
11002
|
+
const streamedPackage = getPlayRunPackage(input2.status);
|
|
11003
|
+
const refreshForFullJson = input2.fullJson && streamedPackage !== null;
|
|
11004
|
+
const streamedTextPackageIncomplete = !input2.jsonOutput && input2.status.status === "completed" && playRunPackageStepCount(streamedPackage) === 0;
|
|
11005
|
+
if (!refreshForFullJson && !streamedTextPackageIncomplete) {
|
|
11006
|
+
return input2.status;
|
|
11007
|
+
}
|
|
11008
|
+
let refreshedStatus = await input2.client.getPlayStatus(runId, {
|
|
9932
11009
|
billing: false,
|
|
9933
11010
|
full: input2.fullJson
|
|
9934
11011
|
});
|
|
11012
|
+
for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
|
|
11013
|
+
await sleep4(250);
|
|
11014
|
+
refreshedStatus = await input2.client.getPlayStatus(runId, {
|
|
11015
|
+
billing: false,
|
|
11016
|
+
full: input2.fullJson
|
|
11017
|
+
});
|
|
11018
|
+
}
|
|
9935
11019
|
const dashboardUrl = input2.status.dashboardUrl;
|
|
9936
11020
|
return typeof dashboardUrl === "string" ? { ...refreshedStatus, dashboardUrl } : refreshedStatus;
|
|
9937
11021
|
}
|
|
@@ -10185,7 +11269,7 @@ function extractPlayValidationErrors(value) {
|
|
|
10185
11269
|
if (value instanceof DeeplineError) {
|
|
10186
11270
|
return extractPlayValidationErrors(value.details);
|
|
10187
11271
|
}
|
|
10188
|
-
if (!
|
|
11272
|
+
if (!isRecord5(value)) {
|
|
10189
11273
|
return [];
|
|
10190
11274
|
}
|
|
10191
11275
|
const directErrors = stringArrayField(value, "errors");
|
|
@@ -10543,7 +11627,7 @@ function shouldUseLocalOnlyPlayCheck() {
|
|
|
10543
11627
|
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
10544
11628
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
10545
11629
|
}
|
|
10546
|
-
function
|
|
11630
|
+
function isRecord5(value) {
|
|
10547
11631
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
10548
11632
|
}
|
|
10549
11633
|
function stringValue2(value) {
|
|
@@ -10553,14 +11637,14 @@ function asArray(value) {
|
|
|
10553
11637
|
return Array.isArray(value) ? value : [];
|
|
10554
11638
|
}
|
|
10555
11639
|
function extractionEntries2(value) {
|
|
10556
|
-
if (Array.isArray(value)) return value.filter(
|
|
10557
|
-
if (!
|
|
11640
|
+
if (Array.isArray(value)) return value.filter(isRecord5);
|
|
11641
|
+
if (!isRecord5(value)) return [];
|
|
10558
11642
|
return Object.entries(value).map(
|
|
10559
|
-
([name, entry]) =>
|
|
11643
|
+
([name, entry]) => isRecord5(entry) ? { name, ...entry } : { name }
|
|
10560
11644
|
);
|
|
10561
11645
|
}
|
|
10562
11646
|
function firstRawPath(entry) {
|
|
10563
|
-
const details =
|
|
11647
|
+
const details = isRecord5(entry.details) ? entry.details : {};
|
|
10564
11648
|
const paths = [
|
|
10565
11649
|
...asArray(details.rawToolOutputPaths),
|
|
10566
11650
|
...asArray(details.raw_tool_output_paths),
|
|
@@ -10578,12 +11662,12 @@ function checkHintRawPath(value) {
|
|
|
10578
11662
|
function collectStaticPipelineToolIds(staticPipeline) {
|
|
10579
11663
|
const seen = /* @__PURE__ */ new Set();
|
|
10580
11664
|
const visitPipeline = (pipeline) => {
|
|
10581
|
-
if (!
|
|
11665
|
+
if (!isRecord5(pipeline)) return;
|
|
10582
11666
|
for (const step of [
|
|
10583
11667
|
...asArray(pipeline.stages),
|
|
10584
11668
|
...asArray(pipeline.substeps)
|
|
10585
11669
|
]) {
|
|
10586
|
-
if (!
|
|
11670
|
+
if (!isRecord5(step)) continue;
|
|
10587
11671
|
if (step.type === "tool") {
|
|
10588
11672
|
const toolId = stringValue2(step.toolId) || stringValue2(step.tool);
|
|
10589
11673
|
if (toolId) seen.add(toolId);
|
|
@@ -10597,9 +11681,9 @@ function collectStaticPipelineToolIds(staticPipeline) {
|
|
|
10597
11681
|
return [...seen].sort();
|
|
10598
11682
|
}
|
|
10599
11683
|
function toolGetterHintFromMetadata(toolId, tool) {
|
|
10600
|
-
const usageGuidance =
|
|
10601
|
-
const resultGuidance =
|
|
10602
|
-
const toolResponse =
|
|
11684
|
+
const usageGuidance = isRecord5(tool.usageGuidance) ? tool.usageGuidance : {};
|
|
11685
|
+
const resultGuidance = isRecord5(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord5(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
|
|
11686
|
+
const toolResponse = isRecord5(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord5(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
|
|
10603
11687
|
const lists = extractionEntries2(
|
|
10604
11688
|
resultGuidance.extractedLists ?? resultGuidance.extracted_lists
|
|
10605
11689
|
).map((entry) => ({
|
|
@@ -10895,7 +11979,8 @@ async function handleFileBackedRun(options) {
|
|
|
10895
11979
|
await resolvePlayRunOutputStatus({
|
|
10896
11980
|
client,
|
|
10897
11981
|
status: finalStatus,
|
|
10898
|
-
fullJson: options.fullJson
|
|
11982
|
+
fullJson: options.fullJson,
|
|
11983
|
+
jsonOutput: options.jsonOutput
|
|
10899
11984
|
}),
|
|
10900
11985
|
playName
|
|
10901
11986
|
);
|
|
@@ -11045,7 +12130,8 @@ async function handleNamedRun(options) {
|
|
|
11045
12130
|
await resolvePlayRunOutputStatus({
|
|
11046
12131
|
client,
|
|
11047
12132
|
status: finalStatus,
|
|
11048
|
-
fullJson: options.fullJson
|
|
12133
|
+
fullJson: options.fullJson,
|
|
12134
|
+
jsonOutput: options.jsonOutput
|
|
11049
12135
|
}),
|
|
11050
12136
|
playName
|
|
11051
12137
|
);
|
|
@@ -11226,8 +12312,17 @@ async function handleRunTail(args) {
|
|
|
11226
12312
|
}
|
|
11227
12313
|
}
|
|
11228
12314
|
const client = new DeeplineClient();
|
|
11229
|
-
const
|
|
11230
|
-
|
|
12315
|
+
const jsonOutput = argsWantJson(args);
|
|
12316
|
+
const status = await client.runs.tail(runId, {
|
|
12317
|
+
// Human mode only: in --json mode emit nothing non-protocol.
|
|
12318
|
+
onReconnect: jsonOutput ? void 0 : ({ reason }) => {
|
|
12319
|
+
process.stderr.write(
|
|
12320
|
+
`[runs tail] stream ended without a terminal status; reconnecting to run ${runId} (${reason})
|
|
12321
|
+
`
|
|
12322
|
+
);
|
|
12323
|
+
}
|
|
12324
|
+
});
|
|
12325
|
+
writePlayResult(status, jsonOutput);
|
|
11231
12326
|
return status.status === "failed" ? 1 : 0;
|
|
11232
12327
|
}
|
|
11233
12328
|
async function handleRunLogs(args) {
|
|
@@ -11252,21 +12347,28 @@ async function handleRunLogs(args) {
|
|
|
11252
12347
|
}
|
|
11253
12348
|
}
|
|
11254
12349
|
const client = new DeeplineClient();
|
|
11255
|
-
const status = await client.runs.get(runId, { full: true });
|
|
11256
|
-
const logs = status.progress?.logs ?? [];
|
|
11257
12350
|
if (outPath) {
|
|
12351
|
+
const result2 = await client.runs.logs(runId, { all: true });
|
|
12352
|
+
const logs = result2.entries;
|
|
11258
12353
|
(0, import_node_fs10.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
11259
12354
|
printCommandEnvelope(
|
|
11260
12355
|
{
|
|
11261
|
-
runId:
|
|
12356
|
+
runId: result2.runId,
|
|
11262
12357
|
log_path: outPath,
|
|
11263
12358
|
lineCount: logs.length,
|
|
12359
|
+
totalCount: result2.totalCount,
|
|
12360
|
+
...result2.logsTruncated ? { logsTruncated: true } : {},
|
|
11264
12361
|
local: { log_path: outPath },
|
|
11265
12362
|
render: {
|
|
11266
12363
|
sections: [
|
|
11267
12364
|
{
|
|
11268
12365
|
title: "run logs",
|
|
11269
|
-
lines: [
|
|
12366
|
+
lines: [
|
|
12367
|
+
`Wrote ${logs.length} log lines to ${outPath}`,
|
|
12368
|
+
...result2.logsTruncated ? [
|
|
12369
|
+
`Run crossed the log retention cap: ${result2.totalCount} lines were emitted, stored bodies end at the truncation marker.`
|
|
12370
|
+
] : []
|
|
12371
|
+
]
|
|
11270
12372
|
}
|
|
11271
12373
|
]
|
|
11272
12374
|
}
|
|
@@ -11275,25 +12377,26 @@ async function handleRunLogs(args) {
|
|
|
11275
12377
|
);
|
|
11276
12378
|
return 0;
|
|
11277
12379
|
}
|
|
11278
|
-
const
|
|
12380
|
+
const result = await client.runs.logs(runId, { limit });
|
|
11279
12381
|
printCommandEnvelope(
|
|
11280
12382
|
{
|
|
11281
|
-
runId:
|
|
11282
|
-
totalCount:
|
|
11283
|
-
returnedCount:
|
|
11284
|
-
firstSequence:
|
|
11285
|
-
lastSequence:
|
|
11286
|
-
truncated:
|
|
11287
|
-
hasMore:
|
|
11288
|
-
|
|
12383
|
+
runId: result.runId,
|
|
12384
|
+
totalCount: result.totalCount,
|
|
12385
|
+
returnedCount: result.returnedCount,
|
|
12386
|
+
firstSequence: result.firstSequence,
|
|
12387
|
+
lastSequence: result.lastSequence,
|
|
12388
|
+
truncated: result.truncated,
|
|
12389
|
+
hasMore: result.hasMore,
|
|
12390
|
+
...result.logsTruncated ? { logsTruncated: true } : {},
|
|
12391
|
+
entries: result.entries,
|
|
11289
12392
|
next: {
|
|
11290
|
-
export: `deepline runs logs ${
|
|
12393
|
+
export: `deepline runs logs ${result.runId} --out run.log --json`
|
|
11291
12394
|
},
|
|
11292
|
-
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
12395
|
+
render: { sections: [{ title: "run logs", lines: result.entries }] }
|
|
11293
12396
|
},
|
|
11294
12397
|
{
|
|
11295
12398
|
json: argsWantJson(args),
|
|
11296
|
-
text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`
|
|
12399
|
+
text: `${result.entries.join("\n")}${result.entries.length > 0 ? "\n" : ""}`
|
|
11297
12400
|
}
|
|
11298
12401
|
);
|
|
11299
12402
|
return 0;
|
|
@@ -11316,6 +12419,34 @@ async function handleRunStop(args) {
|
|
|
11316
12419
|
}
|
|
11317
12420
|
const client = new DeeplineClient();
|
|
11318
12421
|
const result = await client.runs.stop(runId, { reason });
|
|
12422
|
+
const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
|
|
12423
|
+
if (stopNotConfirmed) {
|
|
12424
|
+
const detail = typeof result.error === "string" && result.error.trim() ? result.error.trim() : result.staleSchedulerState === true ? "scheduler state for the run is stale" : "the server did not confirm the stop";
|
|
12425
|
+
process.stderr.write(
|
|
12426
|
+
`Failed to stop run ${runId}: ${detail}. Retry 'deepline runs stop ${runId}' or inspect the run with 'deepline runs get ${runId} --full --json'.
|
|
12427
|
+
`
|
|
12428
|
+
);
|
|
12429
|
+
printCommandEnvelope(
|
|
12430
|
+
{
|
|
12431
|
+
...result,
|
|
12432
|
+
render: {
|
|
12433
|
+
sections: [
|
|
12434
|
+
{
|
|
12435
|
+
title: "run stop",
|
|
12436
|
+
lines: [
|
|
12437
|
+
`\u2717 stop not confirmed for ${result.runId || runId}`,
|
|
12438
|
+
` ${detail}`,
|
|
12439
|
+
` retry: deepline runs stop ${runId}`,
|
|
12440
|
+
` inspect: deepline runs get ${runId} --full --json`
|
|
12441
|
+
]
|
|
12442
|
+
}
|
|
12443
|
+
]
|
|
12444
|
+
}
|
|
12445
|
+
},
|
|
12446
|
+
{ json: argsWantJson(args) }
|
|
12447
|
+
);
|
|
12448
|
+
return 1;
|
|
12449
|
+
}
|
|
11319
12450
|
const lines = [
|
|
11320
12451
|
`Stopped ${result.runId}`,
|
|
11321
12452
|
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
@@ -14957,7 +16088,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
14957
16088
|
}
|
|
14958
16089
|
function extractionContractEntries(entries) {
|
|
14959
16090
|
return entries.flatMap((entry) => {
|
|
14960
|
-
if (!
|
|
16091
|
+
if (!isRecord6(entry)) return [];
|
|
14961
16092
|
const name = stringField(entry, "name");
|
|
14962
16093
|
const expression = stringField(entry, "expression");
|
|
14963
16094
|
return name && expression ? [{ name, expression }] : [];
|
|
@@ -14965,8 +16096,8 @@ function extractionContractEntries(entries) {
|
|
|
14965
16096
|
}
|
|
14966
16097
|
function printCompactToolContract(tool, requestedToolId) {
|
|
14967
16098
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
14968
|
-
const cost =
|
|
14969
|
-
const getters =
|
|
16099
|
+
const cost = isRecord6(contract.cost) ? contract.cost : {};
|
|
16100
|
+
const getters = isRecord6(contract.getters) ? contract.getters : {};
|
|
14970
16101
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
14971
16102
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
14972
16103
|
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
@@ -14983,7 +16114,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
14983
16114
|
console.log("");
|
|
14984
16115
|
console.log("Inputs:");
|
|
14985
16116
|
for (const field of inputFields) {
|
|
14986
|
-
if (!
|
|
16117
|
+
if (!isRecord6(field)) continue;
|
|
14987
16118
|
const name = stringField(field, "name");
|
|
14988
16119
|
if (!name) continue;
|
|
14989
16120
|
const required = field.required ? "*" : "";
|
|
@@ -14996,7 +16127,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
14996
16127
|
}
|
|
14997
16128
|
console.log("");
|
|
14998
16129
|
printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
|
|
14999
|
-
const starterScript =
|
|
16130
|
+
const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
|
|
15000
16131
|
const starterPath = stringField(starterScript, "path");
|
|
15001
16132
|
if (starterPath) {
|
|
15002
16133
|
console.log("");
|
|
@@ -15010,14 +16141,14 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
15010
16141
|
console.log("Getters:");
|
|
15011
16142
|
if (listGetters.length) console.log("Lists:");
|
|
15012
16143
|
for (const entry of listGetters) {
|
|
15013
|
-
if (
|
|
16144
|
+
if (isRecord6(entry))
|
|
15014
16145
|
console.log(
|
|
15015
16146
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
15016
16147
|
);
|
|
15017
16148
|
}
|
|
15018
16149
|
if (valueGetters.length) console.log("Values:");
|
|
15019
16150
|
for (const entry of valueGetters) {
|
|
15020
|
-
if (
|
|
16151
|
+
if (isRecord6(entry))
|
|
15021
16152
|
console.log(
|
|
15022
16153
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
15023
16154
|
);
|
|
@@ -15030,7 +16161,7 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
15030
16161
|
}
|
|
15031
16162
|
function printToolPricingOnly(tool, requestedToolId, options = {}) {
|
|
15032
16163
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
15033
|
-
const cost =
|
|
16164
|
+
const cost = isRecord6(contract.cost) ? contract.cost : {};
|
|
15034
16165
|
const pricingModel = stringField(cost, "pricingModel") || "unknown";
|
|
15035
16166
|
const billingMode = stringField(cost, "billingMode") || "unknown";
|
|
15036
16167
|
const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
|
|
@@ -15050,7 +16181,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
|
|
|
15050
16181
|
}
|
|
15051
16182
|
console.log("Inputs:");
|
|
15052
16183
|
for (const field of inputFields) {
|
|
15053
|
-
if (!
|
|
16184
|
+
if (!isRecord6(field)) continue;
|
|
15054
16185
|
const name = stringField(field, "name");
|
|
15055
16186
|
if (!name) continue;
|
|
15056
16187
|
const required = field.required ? "*" : "";
|
|
@@ -15076,10 +16207,10 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
15076
16207
|
` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
|
|
15077
16208
|
);
|
|
15078
16209
|
console.log("});");
|
|
15079
|
-
const getters =
|
|
16210
|
+
const getters = isRecord6(contract.getters) ? contract.getters : {};
|
|
15080
16211
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
15081
16212
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
15082
|
-
const firstGetter = [...valueGetters, ...listGetters].find(
|
|
16213
|
+
const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
|
|
15083
16214
|
if (firstGetter) {
|
|
15084
16215
|
const name = stringField(firstGetter, "name") || "value";
|
|
15085
16216
|
const expression = stringField(firstGetter, "expression");
|
|
@@ -15096,7 +16227,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
15096
16227
|
}
|
|
15097
16228
|
function printToolGettersOnly(tool, requestedToolId) {
|
|
15098
16229
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
15099
|
-
const getters =
|
|
16230
|
+
const getters = isRecord6(contract.getters) ? contract.getters : {};
|
|
15100
16231
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
15101
16232
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
15102
16233
|
console.log(`Getters: ${contract.toolId}`);
|
|
@@ -15109,7 +16240,7 @@ function printToolGettersOnly(tool, requestedToolId) {
|
|
|
15109
16240
|
if (listGetters.length) {
|
|
15110
16241
|
console.log("Lists:");
|
|
15111
16242
|
for (const entry of listGetters) {
|
|
15112
|
-
if (
|
|
16243
|
+
if (isRecord6(entry))
|
|
15113
16244
|
console.log(
|
|
15114
16245
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
15115
16246
|
);
|
|
@@ -15118,7 +16249,7 @@ function printToolGettersOnly(tool, requestedToolId) {
|
|
|
15118
16249
|
if (valueGetters.length) {
|
|
15119
16250
|
console.log("Values:");
|
|
15120
16251
|
for (const entry of valueGetters) {
|
|
15121
|
-
if (
|
|
16252
|
+
if (isRecord6(entry))
|
|
15122
16253
|
console.log(
|
|
15123
16254
|
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
15124
16255
|
);
|
|
@@ -15144,7 +16275,7 @@ function sampleValueForField(field) {
|
|
|
15144
16275
|
function samplePayloadForInputFields(fields) {
|
|
15145
16276
|
return Object.fromEntries(
|
|
15146
16277
|
fields.slice(0, 4).flatMap((field) => {
|
|
15147
|
-
if (!
|
|
16278
|
+
if (!isRecord6(field)) return [];
|
|
15148
16279
|
const name = stringField(field, "name");
|
|
15149
16280
|
if (!name) return [];
|
|
15150
16281
|
return [[name, sampleValueForField(field)]];
|
|
@@ -15258,12 +16389,12 @@ function formatListedToolCost(tool) {
|
|
|
15258
16389
|
}
|
|
15259
16390
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
15260
16391
|
if (Array.isArray(inputSchema.fields))
|
|
15261
|
-
return inputSchema.fields.filter(
|
|
15262
|
-
const jsonSchema =
|
|
15263
|
-
const properties =
|
|
16392
|
+
return inputSchema.fields.filter(isRecord6);
|
|
16393
|
+
const jsonSchema = isRecord6(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
16394
|
+
const properties = isRecord6(jsonSchema.properties) ? jsonSchema.properties : {};
|
|
15264
16395
|
const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
|
|
15265
16396
|
return Object.entries(properties).map(([name, value]) => {
|
|
15266
|
-
const property =
|
|
16397
|
+
const property = isRecord6(value) ? value : {};
|
|
15267
16398
|
return {
|
|
15268
16399
|
name,
|
|
15269
16400
|
type: typeof property.type === "string" ? property.type : "unknown",
|
|
@@ -15292,15 +16423,15 @@ function printJsonPreview(label, payload) {
|
|
|
15292
16423
|
}
|
|
15293
16424
|
function samplePayload(samples, key) {
|
|
15294
16425
|
const entry = samples[key];
|
|
15295
|
-
if (!
|
|
16426
|
+
if (!isRecord6(entry)) return void 0;
|
|
15296
16427
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
15297
16428
|
}
|
|
15298
16429
|
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
15299
|
-
return
|
|
16430
|
+
return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
15300
16431
|
}
|
|
15301
16432
|
function listExtractorPathsFromUsageGuidance(tool) {
|
|
15302
16433
|
const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
|
|
15303
|
-
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists :
|
|
16434
|
+
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord6(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
|
|
15304
16435
|
return extractedLists.flatMap((entry) => {
|
|
15305
16436
|
const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
|
|
15306
16437
|
if (!Array.isArray(paths)) return [];
|
|
@@ -15316,7 +16447,7 @@ function formatDecimal(value) {
|
|
|
15316
16447
|
function formatUsd(value) {
|
|
15317
16448
|
return `$${formatDecimal(value)}`;
|
|
15318
16449
|
}
|
|
15319
|
-
function
|
|
16450
|
+
function isRecord6(value) {
|
|
15320
16451
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
15321
16452
|
}
|
|
15322
16453
|
function stringField(source, ...keys) {
|
|
@@ -15343,7 +16474,7 @@ function arrayField(source, ...keys) {
|
|
|
15343
16474
|
function recordField(source, ...keys) {
|
|
15344
16475
|
for (const key of keys) {
|
|
15345
16476
|
const value = source[key];
|
|
15346
|
-
if (
|
|
16477
|
+
if (isRecord6(value)) return value;
|
|
15347
16478
|
}
|
|
15348
16479
|
return {};
|
|
15349
16480
|
}
|
|
@@ -15409,7 +16540,7 @@ function parseJsonObjectArgument(raw, flagName) {
|
|
|
15409
16540
|
}
|
|
15410
16541
|
throw invalidJsonError(flagName, message);
|
|
15411
16542
|
}
|
|
15412
|
-
if (!
|
|
16543
|
+
if (!isRecord6(parsed)) {
|
|
15413
16544
|
throw invalidJsonError(flagName, "expected an object.");
|
|
15414
16545
|
}
|
|
15415
16546
|
return parsed;
|
|
@@ -15532,7 +16663,7 @@ function buildToolExecuteBaseEnvelope(input2) {
|
|
|
15532
16663
|
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
15533
16664
|
summary: input2.summary
|
|
15534
16665
|
};
|
|
15535
|
-
const envelopeHasCanonicalOutput =
|
|
16666
|
+
const envelopeHasCanonicalOutput = isRecord6(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
|
|
15536
16667
|
const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
|
|
15537
16668
|
const actions = input2.listConversion ? [
|
|
15538
16669
|
{
|
|
@@ -15641,7 +16772,7 @@ async function executeTool(args) {
|
|
|
15641
16772
|
{
|
|
15642
16773
|
...baseEnvelope,
|
|
15643
16774
|
local: {
|
|
15644
|
-
...
|
|
16775
|
+
...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
15645
16776
|
payload_file: jsonPath
|
|
15646
16777
|
}
|
|
15647
16778
|
},
|