deepline 0.3.374 → 0.3.376
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +56 -4
- package/dist/cli/auth-BTFXJQLT.js +35 -0
- package/dist/cli/{auth-YGCDA4WG.mjs → auth-MLCDUTJY.mjs} +6 -4
- package/dist/cli/{billing-ZXRKPD5D.js → billing-IONI4V7P.js} +73 -73
- package/dist/cli/{billing-3ZLR3CJU.mjs → billing-OUGNDBL4.mjs} +4 -4
- package/dist/cli/{chunk-TZTGK5JQ.js → chunk-34KUUN3Z.js} +52 -51
- package/dist/cli/{chunk-MOBOAZMA.js → chunk-47X6MQ7J.js} +60 -60
- package/dist/cli/{chunk-O7KJYX7R.js → chunk-4V2Q3JO6.js} +15 -15
- package/dist/cli/{chunk-JSK6GUY6.js → chunk-4WINCNNH.js} +13 -9
- package/dist/cli/{chunk-T35LCTC7.js → chunk-5HDB7XXT.js} +235 -235
- package/dist/cli/{chunk-62IBVKWB.mjs → chunk-5HJHTZI6.mjs} +1 -1
- package/dist/cli/{chunk-RT5ICJ5V.js → chunk-6EYTKUDI.js} +2 -2
- package/dist/cli/{chunk-IZVGMVNX.mjs → chunk-7LX2G2PU.mjs} +1 -1
- package/dist/cli/{chunk-NOSIMGZV.js → chunk-7ZZSGV4A.js} +1 -1
- package/dist/cli/{chunk-YPVTQRUM.js → chunk-A6ZTYKYF.js} +33 -7
- package/dist/cli/{chunk-HC5L6KGE.js → chunk-ETUV6RRK.js} +1495 -1495
- package/dist/cli/{chunk-46JCJ7FT.mjs → chunk-FWF24ULX.mjs} +13 -9
- package/dist/cli/{chunk-GT6KGIWV.mjs → chunk-GMRWUGXB.mjs} +3 -3
- package/dist/cli/{chunk-A62HBMHY.mjs → chunk-H7SLESX6.mjs} +1467 -1467
- package/dist/cli/{chunk-VZSVYNZH.mjs → chunk-HDB4GAWY.mjs} +12 -12
- package/dist/cli/{chunk-RXR7A2MX.js → chunk-KHZ6WEVF.js} +26 -26
- package/dist/cli/{chunk-OS7GXARW.mjs → chunk-L6OSOKS6.mjs} +7 -7
- package/dist/cli/{chunk-NNYQ24O5.mjs → chunk-MHRDXS7J.mjs} +1 -1
- package/dist/cli/{chunk-JK547IDB.js → chunk-N6KIEYZV.js} +15 -15
- package/dist/cli/{chunk-3XKAOV74.js → chunk-QSH5CHRI.js} +6 -6
- package/dist/cli/{chunk-OYTPVM2R.mjs → chunk-SPZNLE2D.mjs} +2 -2
- package/dist/cli/{chunk-2DAWTVNN.mjs → chunk-UABI2T3Z.mjs} +2 -2
- package/dist/cli/{chunk-E6BZWURC.mjs → chunk-UE2UHYT3.mjs} +33 -7
- package/dist/cli/{chunk-MQ5UW5AZ.mjs → chunk-V7ZREPSD.mjs} +4 -4
- package/dist/cli/{chunk-SBB27CIJ.mjs → chunk-VL5ERG22.mjs} +2 -2
- package/dist/cli/{chunk-FT6O2ZVJ.mjs → chunk-VNBAOWN3.mjs} +13 -32
- package/dist/cli/{chunk-PCYCNZLR.js → chunk-Y4MXBE3V.js} +8 -8
- package/dist/cli/{chunk-X5V2QTDR.js → chunk-Y5W5IIA4.js} +65 -84
- package/dist/cli/{chunk-YFFH6NDU.mjs → chunk-YXQ7SRL3.mjs} +3 -2
- package/dist/cli/{chunk-53PZA4EP.js → chunk-ZXFGA2VK.js} +8 -8
- package/dist/cli/command-docs.js +2 -2
- package/dist/cli/command-docs.mjs +1 -1
- package/dist/cli/{csv-WSRTANXI.js → csv-LPC7BGZ2.js} +12 -12
- package/dist/cli/{csv-VEHHRUG4.mjs → csv-NITSP7QJ.mjs} +3 -3
- package/dist/cli/{db-BH25DX2G.mjs → db-JVDYEXNC.mjs} +4 -4
- package/dist/cli/{db-JXZNMFCE.js → db-WGD54UKM.js} +29 -29
- package/dist/cli/{describe-N2OBGVJM.js → describe-GA7LSJRY.js} +144 -34
- package/dist/cli/{describe-TMBECHTX.mjs → describe-SVMLA6SN.mjs} +130 -20
- package/dist/cli/{executions-Y4HQHYSO.mjs → executions-CXBJDGMU.mjs} +4 -4
- package/dist/cli/{executions-TX5CQF66.js → executions-P47HEI4N.js} +6 -6
- package/dist/cli/{feedback-W7SRXYSP.mjs → feedback-34OSXUQ2.mjs} +3 -3
- package/dist/cli/{feedback-ASEJ5A7I.js → feedback-GWKWE5CU.js} +7 -7
- package/dist/cli/git-MIPISCON.js +23 -0
- package/dist/cli/{git-2W7CZZRC.mjs → git-YAROF6D3.mjs} +4 -4
- package/dist/cli/index.js +106 -91
- package/dist/cli/index.mjs +63 -48
- package/dist/cli/{legacy-noops-TXMPY3P7.mjs → legacy-noops-2G55OKIU.mjs} +4 -4
- package/dist/cli/{legacy-noops-NJYAAGBY.js → legacy-noops-MDZQL3QH.js} +7 -7
- package/dist/cli/{monitors-G4L65QGQ.mjs → monitors-KUGKMABN.mjs} +4 -4
- package/dist/cli/{monitors-YLKTQ57D.js → monitors-X7QYWFSP.js} +85 -85
- package/dist/cli/{org-ENXO2A33.js → org-6XL3IBT2.js} +64 -64
- package/dist/cli/{org-BCHOWKIA.mjs → org-CNBBAN4W.mjs} +4 -4
- package/dist/cli/{play-JBY7HUYN.mjs → play-33ZMPAR5.mjs} +8 -8
- package/dist/cli/play-3CM3P2DS.js +155 -0
- package/dist/cli/{providers-JV2DZG43.mjs → providers-3OSQNFPE.mjs} +4 -4
- package/dist/cli/{providers-GWFX3E7C.js → providers-5POAURDT.js} +7 -7
- package/dist/cli/{quickstart-CASPK7F4.js → quickstart-LQGPDJTS.js} +5 -5
- package/dist/cli/{quickstart-AIXURXZQ.mjs → quickstart-QICTOOW6.mjs} +3 -3
- package/dist/cli/search-A2YLJ4I5.js +26 -0
- package/dist/cli/{search-DYQB7LHK.mjs → search-HBIX4SF4.mjs} +5 -5
- package/dist/cli/{secrets-RQZPIPO5.mjs → secrets-6MRGZPHT.mjs} +4 -4
- package/dist/cli/{secrets-AZQG2E4U.js → secrets-QKPBNQ3E.js} +13 -13
- package/dist/cli/{sessions-3FJJDMBB.mjs → sessions-NODY2OW4.mjs} +4 -4
- package/dist/cli/{sessions-ZY6RBYD4.js → sessions-NP7BLZD2.js} +5 -5
- package/dist/cli/{settings-GXKWNR5V.js → settings-42QHXUB2.js} +74 -74
- package/dist/cli/{settings-UNZDQGSE.mjs → settings-JCEIOTNL.mjs} +7 -7
- package/dist/cli/{setup-PIRXJ2KJ.mjs → setup-3QR4RGMG.mjs} +6 -6
- package/dist/cli/{setup-UURZD4YO.js → setup-5XXWP54Y.js} +41 -41
- package/dist/cli/skills-4JJEAA66.js +28 -0
- package/dist/cli/{skills-B4AQEIAF.mjs → skills-U4OMXLNS.mjs} +4 -4
- package/dist/cli/{tools-UMGQKUSW.mjs → tools-45OROEZQ.mjs} +7 -7
- package/dist/cli/tools-FDCFSA7W.js +40 -0
- package/dist/cli/{update-SFUTMEKL.mjs → update-2XCRMJ4R.mjs} +6 -6
- package/dist/cli/update-VYL4TXIE.js +28 -0
- package/dist/cli/{workflow-CDAJCMT7.js → workflow-47P4LQNC.js} +15 -15
- package/dist/cli/{workflow-ON6XVW36.mjs → workflow-7X3GCOPY.mjs} +5 -5
- package/dist/index.js +8 -8
- package/dist/index.mjs +7 -7
- package/dist/install-integrity.json +76 -78
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-IFXXXZP6.js +0 -33
- package/dist/cli/client-O6IEMURJ.js +0 -20
- package/dist/cli/client-TV254HC4.mjs +0 -20
- package/dist/cli/git-SSSOIQ2L.js +0 -23
- package/dist/cli/play-SSJ3PO4D.js +0 -155
- package/dist/cli/search-N7K6A2YV.js +0 -26
- package/dist/cli/skills-4OUZLKTW.js +0 -28
- package/dist/cli/tools-MIZS5UN6.js +0 -40
- package/dist/cli/update-3CO7LA4M.js +0 -28
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
RETAINED_PIPELINE_RECEIVERS_HEADER,
|
|
22
22
|
RUNTIME_ENVIRONMENT_TOKEN_HEADER,
|
|
23
23
|
resolveConfig
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-HDB4GAWY.mjs";
|
|
25
25
|
import {
|
|
26
26
|
DeeplineError,
|
|
27
27
|
TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
|
|
@@ -29,11 +29,6 @@ import {
|
|
|
29
29
|
ToolExecutionError
|
|
30
30
|
} from "./chunk-NXGCO4JB.mjs";
|
|
31
31
|
|
|
32
|
-
// ../plays/trigger-config-route.ts
|
|
33
|
-
function playTriggerConfigPath(playName) {
|
|
34
|
-
return `/api/v2/plays/${encodeURIComponent(playName)}/triggers`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
32
|
// src/csv-header-matching.ts
|
|
38
33
|
var CSV_HEADER_MATCHING_NOTE = 'A header matches a column when it equals the column name or its default header, ignoring case, spaces and punctuation: LINKEDIN_URL, linkedin-url and "Linkedin Url" all match linkedin_url. A capital inside a word starts a new word, so "LinkedIn URL" reads as linked_in_url and does not match. Map any other header in the input: {"csv":"<path>","columns":{"<column>":"<your header>"}}.';
|
|
39
34
|
|
|
@@ -57,31 +52,6 @@ async function* iterateCursorPages(fetchPage) {
|
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
// src/plays/input-example.ts
|
|
61
|
-
function playInputExampleArgs(schema) {
|
|
62
|
-
if (!schema) return " --input '@<input.json>'";
|
|
63
|
-
if (schema.$ref || schema.allOf) return " --input '@<input.json>'";
|
|
64
|
-
const csv = schema.csvInput;
|
|
65
|
-
const csvField = typeof csv?.inputField === "string" ? csv.inputField : null;
|
|
66
|
-
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
67
|
-
const csvOnlyRequired = required.every((key) => key === csvField);
|
|
68
|
-
const alternatives = schema.oneOf ?? schema.anyOf;
|
|
69
|
-
const hasCsvAlternative = Array.isArray(alternatives) && alternatives.some((branch) => {
|
|
70
|
-
if (!branch || typeof branch !== "object") return false;
|
|
71
|
-
const keys = branch.required;
|
|
72
|
-
return Array.isArray(keys) && keys.includes(csvField) && keys.every((key) => key === csvField);
|
|
73
|
-
});
|
|
74
|
-
if (csvField && csvOnlyRequired && (!alternatives || hasCsvAlternative)) {
|
|
75
|
-
if (csvField === "csv") return " --csv '<contacts.csv>'";
|
|
76
|
-
const value = JSON.stringify({ [csvField]: "<contacts.csv>" });
|
|
77
|
-
return ` --input '${value.replace(/'/g, "'\\''")}'`;
|
|
78
|
-
}
|
|
79
|
-
if (Array.isArray(schema.required) && !required.length && !csv && !alternatives) {
|
|
80
|
-
return "";
|
|
81
|
-
}
|
|
82
|
-
return " --input '@<input.json>'";
|
|
83
|
-
}
|
|
84
|
-
|
|
85
55
|
// src/billing-idempotency.ts
|
|
86
56
|
function requireBillingIdempotencyKey(value) {
|
|
87
57
|
const normalized = value.trim();
|
|
@@ -747,302 +717,166 @@ var PRODUCT_NOTIFICATION_DELIVERY_LEASE_MS = 2 * 6e4;
|
|
|
747
717
|
var PRODUCT_NOTIFICATION_SUCCESS_TTL_MS = 15 * 6e4;
|
|
748
718
|
var PRODUCT_NOTIFICATION_FAILURE_TTL_MS = 24 * 60 * 6e4;
|
|
749
719
|
|
|
750
|
-
// ../
|
|
751
|
-
var
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
720
|
+
// ../play-runtime/log-provenance.ts
|
|
721
|
+
var LOG_LEVELS = [
|
|
722
|
+
"debug",
|
|
723
|
+
"info",
|
|
724
|
+
"warn",
|
|
725
|
+
"error"
|
|
726
|
+
];
|
|
727
|
+
var LOG_LEVEL_RANK = {
|
|
728
|
+
debug: 0,
|
|
729
|
+
info: 1,
|
|
730
|
+
warn: 2,
|
|
731
|
+
error: 3
|
|
759
732
|
};
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
733
|
+
var LOG_PROVENANCE_CLASSES = [
|
|
734
|
+
"user",
|
|
735
|
+
"lifecycle",
|
|
736
|
+
"replay",
|
|
737
|
+
"infra",
|
|
738
|
+
"diagnostic",
|
|
739
|
+
"receipt"
|
|
740
|
+
];
|
|
741
|
+
var LOG_PROVENANCE_POLICY = {
|
|
742
|
+
user: { watch: true, ui: true, debug: true },
|
|
743
|
+
lifecycle: { watch: true, ui: true, debug: true },
|
|
744
|
+
replay: { watch: false, ui: false, debug: true },
|
|
745
|
+
infra: { watch: false, ui: false, debug: true },
|
|
746
|
+
diagnostic: { watch: true, ui: true, debug: true },
|
|
747
|
+
receipt: { watch: false, ui: false, debug: true }
|
|
748
|
+
};
|
|
749
|
+
function logProvenanceReaches(provenance, surface) {
|
|
750
|
+
return LOG_PROVENANCE_POLICY[provenance][surface];
|
|
773
751
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
752
|
+
var PROVENANCE_SENTINEL = "";
|
|
753
|
+
var PROVENANCE_PREFIX = `${PROVENANCE_SENTINEL}prov:`;
|
|
754
|
+
function logLevelReaches(level, minimum) {
|
|
755
|
+
return LOG_LEVEL_RANK[level] >= LOG_LEVEL_RANK[minimum];
|
|
756
|
+
}
|
|
757
|
+
function parseLogLevel(value) {
|
|
758
|
+
const normalized = value.trim().toLowerCase();
|
|
759
|
+
return LOG_LEVELS.includes(normalized) ? normalized : null;
|
|
760
|
+
}
|
|
761
|
+
function readProvenanceTag(line) {
|
|
762
|
+
if (!line.startsWith(PROVENANCE_PREFIX)) {
|
|
763
|
+
return { provenance: null, line };
|
|
764
|
+
}
|
|
765
|
+
const end = line.indexOf(PROVENANCE_SENTINEL, PROVENANCE_PREFIX.length);
|
|
766
|
+
if (end === -1) {
|
|
767
|
+
return { provenance: null, line };
|
|
768
|
+
}
|
|
769
|
+
const candidate = line.slice(PROVENANCE_PREFIX.length, end);
|
|
770
|
+
const provenance = LOG_PROVENANCE_CLASSES.includes(candidate) ? candidate : null;
|
|
771
|
+
return { provenance, line: line.slice(end + 1) };
|
|
772
|
+
}
|
|
773
|
+
function classifyLegacyLogLine(line) {
|
|
774
|
+
const message = stripLeadingTimestamp(line);
|
|
775
|
+
if (/recovered (?:from checkpoint|response from checkpoint)/i.test(message)) {
|
|
776
|
+
return "replay";
|
|
777
|
+
}
|
|
778
|
+
if (/^\[perf\] runtime receipt\b/i.test(message)) {
|
|
779
|
+
return "receipt";
|
|
780
|
+
}
|
|
781
|
+
if (/^\[perf\]/i.test(message) || /^\[runtime\.(?:phase_summary|logs|log_transport)\]/i.test(message) || /\[worker\] picked up run\b/.test(message) || /\[worker\] heartbeat\b/.test(message) || /\[worker\] progress completedRows=\d+ totalRows=\d+ rowUpdates=\d+/.test(
|
|
782
|
+
message
|
|
783
|
+
) || /\[worker\] step started\b/.test(message) || /\[worker\] Preparing run files\b/.test(message) || /\[worker\] Run files ready\b/.test(message) || /\[worker\] Runtime ready\b/.test(message) || /\[worker\] Sandbox (?:starting|create start|create done|workspace ready|upload start|runner uploaded)\b/.test(
|
|
784
|
+
message
|
|
785
|
+
) || /^\[event\] play\.step\.progress\b/.test(message) || /^\[event\] play\.run\.snapshot\b/.test(message) || /^\[event\] play\.sheet\.summary\b/.test(message)) {
|
|
786
|
+
return "infra";
|
|
787
|
+
}
|
|
788
|
+
if (/^\[warn\]/i.test(message) || /^\[error\]/i.test(message) || /^\[runtime\.[a-z_]*(?:failure|error)\]/i.test(message)) {
|
|
789
|
+
return "diagnostic";
|
|
790
|
+
}
|
|
791
|
+
return "user";
|
|
792
|
+
}
|
|
793
|
+
function classifyLogLine(line) {
|
|
794
|
+
const tagged = readProvenanceTag(line);
|
|
795
|
+
const provenance = tagged.provenance ?? classifyLegacyLogLine(tagged.line);
|
|
796
|
+
if (tagged.provenance !== null) {
|
|
797
|
+
return {
|
|
798
|
+
provenance,
|
|
799
|
+
level: inferLogLevel(provenance, tagged.line),
|
|
800
|
+
line: tagged.line
|
|
801
|
+
};
|
|
802
|
+
}
|
|
778
803
|
return {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
804
|
+
provenance,
|
|
805
|
+
level: inferLogLevel(provenance, tagged.line),
|
|
806
|
+
line: tagged.line
|
|
782
807
|
};
|
|
783
808
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
// ../play-runtime/log-context.ts
|
|
789
|
-
var utf8 = new TextEncoder();
|
|
790
|
-
|
|
791
|
-
// ../play-runtime/run-log-buffer.ts
|
|
792
|
-
var RUNNER_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
793
|
-
var RUNNER_LOG_CONTEXT_MAX_BYTES = 128 * 1024;
|
|
794
|
-
var TERMINAL_RESERVE_BYTES = 64 * 1024;
|
|
795
|
-
var encoder = new TextEncoder();
|
|
796
|
-
|
|
797
|
-
// ../play-runtime/secret-redaction.ts
|
|
798
|
-
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
799
|
-
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
800
|
-
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
801
|
-
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
802
|
-
var EMAIL_LOCAL_CHAR_RE = /[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]/;
|
|
803
|
-
var EMAIL_DOMAIN_RE = /^[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?\.[A-Za-z]{2,63}\b/;
|
|
804
|
-
function isEmailLocalPart(value, start, end) {
|
|
805
|
-
let localStart = start;
|
|
806
|
-
while (localStart > 0 && EMAIL_LOCAL_CHAR_RE.test(value[localStart - 1])) {
|
|
807
|
-
localStart -= 1;
|
|
809
|
+
function inferLogLevel(provenance, line) {
|
|
810
|
+
const message = stripLeadingTimestamp(line);
|
|
811
|
+
if (/^\[info\]/i.test(message)) {
|
|
812
|
+
return "info";
|
|
808
813
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
localEnd += 1;
|
|
814
|
+
if (/^\[debug\]/i.test(message)) {
|
|
815
|
+
return "debug";
|
|
812
816
|
}
|
|
813
|
-
|
|
817
|
+
if (/^\[console\.error\]/i.test(message) || /^\[error\]/i.test(message)) {
|
|
818
|
+
return "error";
|
|
819
|
+
}
|
|
820
|
+
if (/^\[console\.warn\]/i.test(message) || /^\[warn\]/i.test(message)) {
|
|
821
|
+
return "warn";
|
|
822
|
+
}
|
|
823
|
+
if (/^\[console\.debug\]/i.test(message)) {
|
|
824
|
+
return "debug";
|
|
825
|
+
}
|
|
826
|
+
if (provenance === "replay" || provenance === "infra" || provenance === "receipt") {
|
|
827
|
+
return "debug";
|
|
828
|
+
}
|
|
829
|
+
if (provenance === "diagnostic") {
|
|
830
|
+
return "warn";
|
|
831
|
+
}
|
|
832
|
+
return "info";
|
|
814
833
|
}
|
|
815
|
-
function
|
|
816
|
-
|
|
834
|
+
function stripLeadingTimestamp(line) {
|
|
835
|
+
const match = line.match(/^\[([^\]]+)\]\s*([\s\S]*)$/);
|
|
836
|
+
if (!match) {
|
|
837
|
+
return line;
|
|
838
|
+
}
|
|
839
|
+
const inner = match[1] ?? "";
|
|
840
|
+
const isTimestamp = !Number.isNaN(new Date(inner).getTime());
|
|
841
|
+
return isTimestamp ? match[2] ?? line : line;
|
|
817
842
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
var GENERIC_SECRET_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*["']?[^"',\s]{12,}["']?/gi;
|
|
824
|
-
var HIGH_ENTROPY_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password|authorization|(?:access|refresh)[_-]?token)\b\s*[:=]\s*["']?[^"',\s]{16,}["']?/gi;
|
|
825
|
-
var SENSITIVE_URL_PARAM_RE = /[?#&](?:\w*(?:key|token)|secret|password|authorization|credential|signature|sig)(?:=|$)/i;
|
|
826
|
-
var SENSITIVE_FIELD_NAME_RE = new RegExp(
|
|
827
|
-
`(^|[._-])(?:${SENSITIVE_FIELD_NAME})$`,
|
|
828
|
-
"i"
|
|
829
|
-
);
|
|
830
|
-
var SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES = 4096;
|
|
831
|
-
var sensitiveFieldNameCache = /* @__PURE__ */ new Map();
|
|
832
|
-
function isSensitiveFieldName(key) {
|
|
833
|
-
const cached = sensitiveFieldNameCache.get(key);
|
|
834
|
-
if (cached !== void 0) return cached;
|
|
835
|
-
const sensitive = SENSITIVE_FIELD_NAME_RE.test(key);
|
|
836
|
-
if (sensitiveFieldNameCache.size >= SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES) {
|
|
837
|
-
sensitiveFieldNameCache.clear();
|
|
843
|
+
|
|
844
|
+
// src/runs/read-errors.ts
|
|
845
|
+
function isTransientPlayRunReadError(error) {
|
|
846
|
+
if (error instanceof DeeplineError && error.code === "PLAY_STREAM_NETWORK_ABORTED") {
|
|
847
|
+
return true;
|
|
838
848
|
}
|
|
839
|
-
|
|
840
|
-
|
|
849
|
+
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
850
|
+
return error.statusCode >= 500 && error.statusCode < 600;
|
|
851
|
+
}
|
|
852
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
853
|
+
return /auth validation backend timed out|coordinator \/submit(?:\?[^ ]*)? 5\d\d|Worker threw exception|Internal Server Error|Service Unavailable|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
854
|
+
text
|
|
855
|
+
);
|
|
841
856
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
)
|
|
846
|
-
|
|
847
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
857
|
+
|
|
858
|
+
// src/runs/log-cursor.ts
|
|
859
|
+
var RUN_LOGS_CURSOR_PREFIX = "before:";
|
|
860
|
+
function encodeRunLogsCursor(beforeSequence) {
|
|
861
|
+
return `${RUN_LOGS_CURSOR_PREFIX}${beforeSequence}`;
|
|
848
862
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
// Sensitive field names, in object keys and in `name=`/`"name":` text.
|
|
852
|
-
"token",
|
|
853
|
-
"secret",
|
|
854
|
-
"password",
|
|
855
|
-
"authorization",
|
|
856
|
-
"cookie",
|
|
857
|
-
"credential",
|
|
858
|
-
// One `key` branch instead of `api[_-]?key`, `private[_-]?key` and `key_`.
|
|
859
|
-
// A regex alternation costs roughly one substring scan per branch, and a
|
|
860
|
-
// broader branch can only send more payloads down the exact path.
|
|
861
|
-
"key",
|
|
862
|
-
// Self-identifying credential prefixes.
|
|
863
|
-
"bearer",
|
|
864
|
-
"basic",
|
|
865
|
-
"-----BEGIN",
|
|
866
|
-
"xox[baprs]-",
|
|
867
|
-
"[prs]k_",
|
|
868
|
-
"pat_",
|
|
869
|
-
"ghp_",
|
|
870
|
-
// URL credentials: `//user:pass@host` userinfo and sensitive query params.
|
|
871
|
-
// The userinfo class excludes `/` exactly as the rule it guards does.
|
|
872
|
-
"//[^\\s/]{0,256}@",
|
|
873
|
-
"[?#&]sig",
|
|
874
|
-
// JWTs carry no keyword at all, so the shape itself is the marker. Anchor
|
|
875
|
-
// on the separating dot and drop JWT_RE's leading `{8,}` segment: a dot is
|
|
876
|
-
// rare enough for the engine to skip between candidates, which makes this
|
|
877
|
-
// gate roughly ten times cheaper than re-running JWT_RE itself, and
|
|
878
|
-
// dropping a required segment can only widen what it accepts.
|
|
879
|
-
"\\.[A-Za-z0-9_-]{1,16384}\\.[A-Za-z0-9_-]{8}"
|
|
880
|
-
].join("|"),
|
|
881
|
-
"i"
|
|
882
|
-
);
|
|
883
|
-
function mightContainRedactableSecret(value) {
|
|
884
|
-
return REDACTION_TRIGGER_RE.test(value);
|
|
885
|
-
}
|
|
886
|
-
function redactSecretLikeString(value) {
|
|
887
|
-
if (!mightContainRedactableSecret(value)) return value;
|
|
888
|
-
const serializedRedacted = value.replace(
|
|
889
|
-
SERIALIZED_SECRET_FIELD_RE,
|
|
890
|
-
`"$1":"${SECRET_REDACTION_PLACEHOLDER}"`
|
|
891
|
-
);
|
|
892
|
-
const trimmed = serializedRedacted.trim();
|
|
893
|
-
if (/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
894
|
-
if (/^https?:\/\/[^/?#@]+@/i.test(trimmed) || SENSITIVE_URL_PARAM_RE.test(trimmed)) {
|
|
895
|
-
return SECRET_REDACTION_PLACEHOLDER;
|
|
896
|
-
}
|
|
897
|
-
if (!URL_SECRET_VALUE_RE.test(serializedRedacted)) {
|
|
898
|
-
return serializedRedacted;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
return serializedRedacted.replace(PRIVATE_KEY_RE, SECRET_REDACTION_PLACEHOLDER).replace(BEARER_TOKEN_RE, `Bearer ${SECRET_REDACTION_PLACEHOLDER}`).replace(BASIC_CREDENTIAL_RE, `Basic ${SECRET_REDACTION_PLACEHOLDER}`).replace(JWT_RE, redactTokenUnlessEmail).replace(COMMON_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_PREFIXED_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_SECRET_ASSIGNMENT_RE, SECRET_REDACTION_PLACEHOLDER).replace(HIGH_ENTROPY_ASSIGNMENT_RE, (match) => {
|
|
902
|
-
const separator = match.includes("=") ? "=" : ":";
|
|
903
|
-
const [key] = match.split(separator, 1);
|
|
904
|
-
return `${key.trim()}${separator}${SECRET_REDACTION_PLACEHOLDER}`;
|
|
905
|
-
});
|
|
863
|
+
function isRunLogsCursor(value) {
|
|
864
|
+
return value.startsWith(RUN_LOGS_CURSOR_PREFIX);
|
|
906
865
|
}
|
|
907
|
-
function
|
|
908
|
-
const
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
try {
|
|
918
|
-
const encoded = encodeURIComponent(secret);
|
|
919
|
-
if (encoded !== secret) literals.push(encoded);
|
|
920
|
-
} catch {
|
|
921
|
-
}
|
|
922
|
-
const built = literals.map((literal) => ({
|
|
923
|
-
literal,
|
|
924
|
-
matcher: new RegExp(escapeRegExp(literal), "g")
|
|
925
|
-
}));
|
|
926
|
-
exactSecretMatchers.set(secret, built);
|
|
927
|
-
return built;
|
|
928
|
-
}
|
|
929
|
-
for (const value of initialValues) register(value);
|
|
930
|
-
function containsRegisteredSecret(value, {
|
|
931
|
-
includeEncoded = false,
|
|
932
|
-
minimumLength = 1
|
|
933
|
-
} = {}) {
|
|
934
|
-
for (const secret of exactSecrets) {
|
|
935
|
-
if (secret.length < minimumLength) continue;
|
|
936
|
-
if (value.includes(secret)) return true;
|
|
937
|
-
if (includeEncoded) {
|
|
938
|
-
try {
|
|
939
|
-
const encoded = encodeURIComponent(secret);
|
|
940
|
-
if (encoded !== secret && value.includes(encoded)) return true;
|
|
941
|
-
} catch {
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
return false;
|
|
946
|
-
}
|
|
947
|
-
function matchesRegisteredSecret(value) {
|
|
948
|
-
return exactSecrets.has(value);
|
|
949
|
-
}
|
|
950
|
-
function redactRegisteredSecrets(value) {
|
|
951
|
-
if (exactSecrets.size === 0) return value;
|
|
952
|
-
let output = value;
|
|
953
|
-
for (const secret of exactSecrets) {
|
|
954
|
-
for (const { literal, matcher } of matchersForSecret(secret)) {
|
|
955
|
-
if (!output.includes(literal)) continue;
|
|
956
|
-
matcher.lastIndex = 0;
|
|
957
|
-
output = output.replace(matcher, SECRET_REDACTION_PLACEHOLDER);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
return output;
|
|
961
|
-
}
|
|
962
|
-
function redactString(value) {
|
|
963
|
-
const output = redactRegisteredSecrets(value);
|
|
964
|
-
return redactSecretLikeString(output);
|
|
965
|
-
}
|
|
966
|
-
function redact(value) {
|
|
967
|
-
if (typeof value === "string") return redactString(value);
|
|
968
|
-
if (Array.isArray(value)) {
|
|
969
|
-
let changed = false;
|
|
970
|
-
const next = new Array(value.length);
|
|
971
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
972
|
-
const entry = value[index];
|
|
973
|
-
const redacted = redact(entry);
|
|
974
|
-
next[index] = redacted;
|
|
975
|
-
if (redacted !== entry) changed = true;
|
|
976
|
-
}
|
|
977
|
-
return changed ? next : value;
|
|
978
|
-
}
|
|
979
|
-
if (value && typeof value === "object") {
|
|
980
|
-
const prototype = Object.getPrototypeOf(value);
|
|
981
|
-
let changed = prototype !== Object.prototype && prototype !== null;
|
|
982
|
-
const source = value;
|
|
983
|
-
const keys = Object.keys(source);
|
|
984
|
-
const redactedValues = new Array(keys.length);
|
|
985
|
-
for (let index = 0; index < keys.length; index += 1) {
|
|
986
|
-
const key = keys[index];
|
|
987
|
-
const entry = source[key];
|
|
988
|
-
const redacted = isSensitiveFieldName(key) ? SECRET_REDACTION_PLACEHOLDER : redact(entry);
|
|
989
|
-
redactedValues[index] = redacted;
|
|
990
|
-
if (redacted !== entry) changed = true;
|
|
991
|
-
}
|
|
992
|
-
if (!changed) return value;
|
|
993
|
-
return Object.fromEntries(
|
|
994
|
-
keys.map((key, index) => [key, redactedValues[index]])
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
return value;
|
|
998
|
-
}
|
|
999
|
-
function redactKnownSecrets(value) {
|
|
1000
|
-
if (typeof value === "string") return redactRegisteredSecrets(value);
|
|
1001
|
-
if (Array.isArray(value)) {
|
|
1002
|
-
return value.map((entry) => redactKnownSecrets(entry));
|
|
1003
|
-
}
|
|
1004
|
-
if (value && typeof value === "object") {
|
|
1005
|
-
return Object.fromEntries(
|
|
1006
|
-
Object.entries(value).map(([key, entry]) => [
|
|
1007
|
-
key,
|
|
1008
|
-
redactKnownSecrets(entry)
|
|
1009
|
-
])
|
|
1010
|
-
);
|
|
1011
|
-
}
|
|
1012
|
-
return value;
|
|
866
|
+
function parseRunLogsCursor(value) {
|
|
867
|
+
const raw = isRunLogsCursor(value) ? value.slice(RUN_LOGS_CURSOR_PREFIX.length) : "";
|
|
868
|
+
const parsed = /^[1-9]\d*$/.test(raw) ? Number(raw) : NaN;
|
|
869
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
870
|
+
throw Object.assign(
|
|
871
|
+
new Error(
|
|
872
|
+
`Invalid run log cursor ${JSON.stringify(value)}. Pass the nextCursor value from a previous deepline runs logs result.`
|
|
873
|
+
),
|
|
874
|
+
{ exitCode: 2 }
|
|
875
|
+
);
|
|
1013
876
|
}
|
|
1014
|
-
return
|
|
1015
|
-
register,
|
|
1016
|
-
containsRegisteredSecret,
|
|
1017
|
-
matchesRegisteredSecret,
|
|
1018
|
-
redactRegisteredSecrets,
|
|
1019
|
-
redactString,
|
|
1020
|
-
redactKnownSecrets,
|
|
1021
|
-
redact
|
|
1022
|
-
};
|
|
877
|
+
return parsed;
|
|
1023
878
|
}
|
|
1024
879
|
|
|
1025
|
-
// ../play-runtime/output-size-limits.ts
|
|
1026
|
-
var TERMINAL_RUN_RESULT_MAX_BYTES = 6 * 1024 * 1024;
|
|
1027
|
-
var LEDGER_TERMINAL_RESULT_MAX_BYTES = 256 * 1024;
|
|
1028
|
-
var CUSTOMER_OUTPUT_VALUE_MAX_BYTES = 5 * 1024 * 1024;
|
|
1029
|
-
var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
|
|
1030
|
-
var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
|
|
1031
|
-
var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
|
|
1032
|
-
var RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES = 2 * 1024 * 1024;
|
|
1033
|
-
var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
|
1034
|
-
var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
|
|
1035
|
-
var RUNTIME_RECEIPT_WRITER_TARGET_BATCH_BYTES = 8 * 1024 * 1024;
|
|
1036
|
-
var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
|
|
1037
|
-
var JSON_SIZE_LIMIT_REACHED = Symbol("JSON_SIZE_LIMIT_REACHED");
|
|
1038
|
-
|
|
1039
|
-
// ../play-runtime/ledger-safe-payload.ts
|
|
1040
|
-
var ledgerIngressRedactor = createSecretRedactionContext();
|
|
1041
|
-
|
|
1042
|
-
// ../play-runtime/docflow-node-io.ts
|
|
1043
|
-
var utf8Encoder = new TextEncoder();
|
|
1044
|
-
var PLAY_DATASET_BRAND = Symbol.for("deepline.play.dataset");
|
|
1045
|
-
|
|
1046
880
|
// ../play-runtime/run-event-catalog.ts
|
|
1047
881
|
var RUN_EVENT_KIND = {
|
|
1048
882
|
runCreated: "run.created",
|
|
@@ -1458,1254 +1292,1420 @@ var RUN_EVENT_SEARCH_FALLBACK_KIND_SQL_LITERAL = sqlStringLiteral(
|
|
|
1458
1292
|
RUN_EVENT_SEARCH_FALLBACK_KIND
|
|
1459
1293
|
);
|
|
1460
1294
|
|
|
1461
|
-
// ../play-
|
|
1462
|
-
var
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1295
|
+
// ../play-runtime/run-event-query.ts
|
|
1296
|
+
var stringField = { type: "string", nullable: false };
|
|
1297
|
+
var optionalString = { type: "string", nullable: true };
|
|
1298
|
+
var RUN_EVENT_QUERY_FIELD_CATALOG = {
|
|
1299
|
+
eventId: stringField,
|
|
1300
|
+
orgId: stringField,
|
|
1301
|
+
runtimeScope: stringField,
|
|
1302
|
+
runId: stringField,
|
|
1303
|
+
playName: stringField,
|
|
1304
|
+
kind: stringField,
|
|
1305
|
+
source: optionalString,
|
|
1306
|
+
occurredAt: { type: "timestamp", nullable: false },
|
|
1307
|
+
status: optionalString,
|
|
1308
|
+
level: { type: "level", nullable: true },
|
|
1309
|
+
stepId: optionalString,
|
|
1310
|
+
stepKind: optionalString,
|
|
1311
|
+
datasetId: optionalString,
|
|
1312
|
+
phase: optionalString,
|
|
1313
|
+
provider: optionalString,
|
|
1314
|
+
operation: optionalString,
|
|
1315
|
+
producerAttempt: { type: "number", nullable: true }
|
|
1467
1316
|
};
|
|
1468
|
-
var
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
requiresDefault: false
|
|
1486
|
-
},
|
|
1487
|
-
{
|
|
1488
|
-
name: "_run_id",
|
|
1489
|
-
sqlType: "text",
|
|
1490
|
-
dataType: "text",
|
|
1491
|
-
nullable: "YES",
|
|
1492
|
-
defaultSql: null,
|
|
1493
|
-
repairable: true,
|
|
1494
|
-
requiresDefault: false
|
|
1495
|
-
},
|
|
1496
|
-
{
|
|
1497
|
-
name: "_error",
|
|
1498
|
-
sqlType: "text",
|
|
1499
|
-
dataType: "text",
|
|
1500
|
-
nullable: "YES",
|
|
1501
|
-
defaultSql: null,
|
|
1502
|
-
repairable: true,
|
|
1503
|
-
requiresDefault: false
|
|
1504
|
-
},
|
|
1505
|
-
{
|
|
1506
|
-
name: "_stage",
|
|
1507
|
-
sqlType: "text",
|
|
1508
|
-
dataType: "text",
|
|
1509
|
-
nullable: "YES",
|
|
1510
|
-
defaultSql: null,
|
|
1511
|
-
repairable: true,
|
|
1512
|
-
requiresDefault: false
|
|
1513
|
-
},
|
|
1514
|
-
{
|
|
1515
|
-
name: "_provider",
|
|
1516
|
-
sqlType: "text",
|
|
1517
|
-
dataType: "text",
|
|
1518
|
-
nullable: "YES",
|
|
1519
|
-
defaultSql: null,
|
|
1520
|
-
repairable: true,
|
|
1521
|
-
requiresDefault: false
|
|
1522
|
-
},
|
|
1523
|
-
{
|
|
1524
|
-
name: "_input_index",
|
|
1525
|
-
sqlType: "bigint",
|
|
1526
|
-
dataType: "bigint",
|
|
1527
|
-
nullable: "YES",
|
|
1528
|
-
defaultSql: null,
|
|
1529
|
-
repairable: true,
|
|
1530
|
-
requiresDefault: false
|
|
1531
|
-
},
|
|
1532
|
-
{
|
|
1533
|
-
name: "_attempt_id",
|
|
1534
|
-
sqlType: "text",
|
|
1535
|
-
dataType: "text",
|
|
1536
|
-
nullable: "YES",
|
|
1537
|
-
defaultSql: null,
|
|
1538
|
-
repairable: true,
|
|
1539
|
-
requiresDefault: false
|
|
1540
|
-
},
|
|
1541
|
-
{
|
|
1542
|
-
name: "_attempt_owner_run_id",
|
|
1543
|
-
sqlType: "text",
|
|
1544
|
-
dataType: "text",
|
|
1545
|
-
nullable: "YES",
|
|
1546
|
-
defaultSql: null,
|
|
1547
|
-
repairable: true,
|
|
1548
|
-
requiresDefault: false
|
|
1549
|
-
},
|
|
1550
|
-
{
|
|
1551
|
-
name: "_attempt_expires_at",
|
|
1552
|
-
sqlType: "timestamptz",
|
|
1553
|
-
dataType: "timestamp with time zone",
|
|
1554
|
-
nullable: "YES",
|
|
1555
|
-
defaultSql: null,
|
|
1556
|
-
repairable: true,
|
|
1557
|
-
requiresDefault: false
|
|
1558
|
-
},
|
|
1559
|
-
{
|
|
1560
|
-
name: "_attempt_seq",
|
|
1561
|
-
sqlType: "integer",
|
|
1562
|
-
dataType: "integer",
|
|
1563
|
-
nullable: "YES",
|
|
1564
|
-
defaultSql: null,
|
|
1565
|
-
repairable: true,
|
|
1566
|
-
requiresDefault: false
|
|
1567
|
-
},
|
|
1568
|
-
{
|
|
1569
|
-
name: "_write_version",
|
|
1570
|
-
sqlType: "bigint NOT NULL DEFAULT 0",
|
|
1571
|
-
dataType: "bigint",
|
|
1572
|
-
nullable: "NO",
|
|
1573
|
-
defaultSql: "0",
|
|
1574
|
-
repairable: true,
|
|
1575
|
-
requiresDefault: true
|
|
1576
|
-
},
|
|
1577
|
-
{
|
|
1578
|
-
name: "_writer_run_id",
|
|
1579
|
-
sqlType: "text",
|
|
1580
|
-
dataType: "text",
|
|
1581
|
-
nullable: "YES",
|
|
1582
|
-
defaultSql: null,
|
|
1583
|
-
repairable: true,
|
|
1584
|
-
requiresDefault: false
|
|
1585
|
-
},
|
|
1586
|
-
{
|
|
1587
|
-
name: "_created_at",
|
|
1588
|
-
sqlType: "timestamptz NOT NULL DEFAULT now()",
|
|
1589
|
-
dataType: "timestamp with time zone",
|
|
1590
|
-
nullable: "NO",
|
|
1591
|
-
defaultSql: "now()",
|
|
1592
|
-
repairable: true,
|
|
1593
|
-
requiresDefault: true
|
|
1594
|
-
},
|
|
1595
|
-
{
|
|
1596
|
-
name: "_updated_at",
|
|
1597
|
-
sqlType: "timestamptz NOT NULL DEFAULT now()",
|
|
1598
|
-
dataType: "timestamp with time zone",
|
|
1599
|
-
nullable: "NO",
|
|
1600
|
-
defaultSql: "now()",
|
|
1601
|
-
repairable: true,
|
|
1602
|
-
requiresDefault: true
|
|
1603
|
-
},
|
|
1604
|
-
{
|
|
1605
|
-
name: "_version",
|
|
1606
|
-
sqlType: "bigint NOT NULL",
|
|
1607
|
-
dataType: "bigint",
|
|
1608
|
-
nullable: "NO",
|
|
1609
|
-
defaultSql: null,
|
|
1610
|
-
defaultKind: "version_sequence",
|
|
1611
|
-
repairable: true,
|
|
1612
|
-
requiresDefault: true
|
|
1613
|
-
},
|
|
1614
|
-
{
|
|
1615
|
-
...RUNTIME_SHEET_CELL_META_COLUMN,
|
|
1616
|
-
sqlType: `jsonb NOT NULL DEFAULT ${RUNTIME_SHEET_CELL_META_COLUMN.defaultSql}`,
|
|
1617
|
-
repairable: true,
|
|
1618
|
-
requiresDefault: true
|
|
1619
|
-
}
|
|
1620
|
-
];
|
|
1621
|
-
var RUNTIME_SHEET_ATTEMPT_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
1622
|
-
(column) => column.name.startsWith("_attempt_")
|
|
1623
|
-
).map((column) => column.name);
|
|
1624
|
-
var RUNTIME_SHEET_WRITE_VERSION_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
1625
|
-
(column) => column.name === "_write_version" || column.name === "_writer_run_id"
|
|
1626
|
-
).map((column) => column.name);
|
|
1627
|
-
var RUNTIME_SHEET_TIMESTAMP_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
1628
|
-
(column) => column.name === "_created_at" || column.name === "_updated_at"
|
|
1629
|
-
).map((column) => column.name);
|
|
1630
|
-
var RUNTIME_SHEET_SYSTEM_COLUMNS = new Set(
|
|
1631
|
-
RUNTIME_SHEET_COLUMN_CONTRACTS.map((column) => column.name)
|
|
1632
|
-
);
|
|
1633
|
-
var RUNTIME_SHEET_REPAIRABLE_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
1634
|
-
(column) => column.repairable
|
|
1635
|
-
);
|
|
1636
|
-
|
|
1637
|
-
// ../play-runtime/run-ledger.ts
|
|
1638
|
-
var TERMINAL_STATUS_BY_EVENT_TYPE = {
|
|
1639
|
-
[RUN_EVENT_KIND.runCompleted]: "completed",
|
|
1640
|
-
[RUN_EVENT_KIND.runFailed]: "failed",
|
|
1641
|
-
[RUN_EVENT_KIND.runCancelled]: "cancelled"
|
|
1642
|
-
};
|
|
1643
|
-
|
|
1644
|
-
// ../play-runtime/log-provenance.ts
|
|
1645
|
-
var LOG_LEVELS = [
|
|
1646
|
-
"debug",
|
|
1647
|
-
"info",
|
|
1648
|
-
"warn",
|
|
1649
|
-
"error"
|
|
1650
|
-
];
|
|
1651
|
-
var LOG_LEVEL_RANK = {
|
|
1652
|
-
debug: 0,
|
|
1653
|
-
info: 1,
|
|
1654
|
-
warn: 2,
|
|
1655
|
-
error: 3
|
|
1656
|
-
};
|
|
1657
|
-
var LOG_PROVENANCE_CLASSES = [
|
|
1658
|
-
"user",
|
|
1659
|
-
"lifecycle",
|
|
1660
|
-
"replay",
|
|
1661
|
-
"infra",
|
|
1662
|
-
"diagnostic",
|
|
1663
|
-
"receipt"
|
|
1317
|
+
var RUN_EVENT_QUERY_RETENTION_MS = 7 * 864e5;
|
|
1318
|
+
var RUN_EVENT_QUERY_MAX_LIMIT = 200;
|
|
1319
|
+
var RUN_EVENT_QUERY_MAX_PAYLOAD_LIMIT = 10;
|
|
1320
|
+
var RUN_EVENT_QUERY_MAX_CURSOR_BYTES = 48 * 1024;
|
|
1321
|
+
var RUN_EVENT_QUERY_MAX_REQUEST_BYTES = 64 * 1024;
|
|
1322
|
+
var MAX_BYTES = 8192;
|
|
1323
|
+
var MAX_DEPTH = 8;
|
|
1324
|
+
var MAX_PREDICATES = 64;
|
|
1325
|
+
var OPERATORS = [
|
|
1326
|
+
"eq",
|
|
1327
|
+
"ne",
|
|
1328
|
+
"gt",
|
|
1329
|
+
"gte",
|
|
1330
|
+
"lt",
|
|
1331
|
+
"lte",
|
|
1332
|
+
"exists",
|
|
1333
|
+
"in"
|
|
1664
1334
|
];
|
|
1665
|
-
var
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
receipt: { watch: false, ui: false, debug: true }
|
|
1335
|
+
var RunEventQueryInputError = class extends TypeError {
|
|
1336
|
+
constructor(message, code = "invalid_run_event_query") {
|
|
1337
|
+
super(message);
|
|
1338
|
+
this.code = code;
|
|
1339
|
+
this.name = "RunEventQueryInputError";
|
|
1340
|
+
}
|
|
1672
1341
|
};
|
|
1673
|
-
function
|
|
1674
|
-
|
|
1675
|
-
}
|
|
1676
|
-
var PROVENANCE_SENTINEL = "";
|
|
1677
|
-
var PROVENANCE_PREFIX = `${PROVENANCE_SENTINEL}prov:`;
|
|
1678
|
-
function logLevelReaches(level, minimum) {
|
|
1679
|
-
return LOG_LEVEL_RANK[level] >= LOG_LEVEL_RANK[minimum];
|
|
1680
|
-
}
|
|
1681
|
-
function parseLogLevel(value) {
|
|
1682
|
-
const normalized = value.trim().toLowerCase();
|
|
1683
|
-
return LOG_LEVELS.includes(normalized) ? normalized : null;
|
|
1342
|
+
function fail(message) {
|
|
1343
|
+
throw new RunEventQueryInputError(message);
|
|
1684
1344
|
}
|
|
1685
|
-
function
|
|
1686
|
-
|
|
1687
|
-
return { provenance: null, line };
|
|
1688
|
-
}
|
|
1689
|
-
const end = line.indexOf(PROVENANCE_SENTINEL, PROVENANCE_PREFIX.length);
|
|
1690
|
-
if (end === -1) {
|
|
1691
|
-
return { provenance: null, line };
|
|
1692
|
-
}
|
|
1693
|
-
const candidate = line.slice(PROVENANCE_PREFIX.length, end);
|
|
1694
|
-
const provenance = LOG_PROVENANCE_CLASSES.includes(candidate) ? candidate : null;
|
|
1695
|
-
return { provenance, line: line.slice(end + 1) };
|
|
1345
|
+
function object(value) {
|
|
1346
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
1696
1347
|
}
|
|
1697
|
-
function
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
return "receipt";
|
|
1704
|
-
}
|
|
1705
|
-
if (/^\[perf\]/i.test(message) || /^\[runtime\.(?:phase_summary|logs|log_transport)\]/i.test(message) || /\[worker\] picked up run\b/.test(message) || /\[worker\] heartbeat\b/.test(message) || /\[worker\] progress completedRows=\d+ totalRows=\d+ rowUpdates=\d+/.test(
|
|
1706
|
-
message
|
|
1707
|
-
) || /\[worker\] step started\b/.test(message) || /\[worker\] Preparing run files\b/.test(message) || /\[worker\] Run files ready\b/.test(message) || /\[worker\] Runtime ready\b/.test(message) || /\[worker\] Sandbox (?:starting|create start|create done|workspace ready|upload start|runner uploaded)\b/.test(
|
|
1708
|
-
message
|
|
1709
|
-
) || /^\[event\] play\.step\.progress\b/.test(message) || /^\[event\] play\.run\.snapshot\b/.test(message) || /^\[event\] play\.sheet\.summary\b/.test(message)) {
|
|
1710
|
-
return "infra";
|
|
1711
|
-
}
|
|
1712
|
-
if (/^\[warn\]/i.test(message) || /^\[error\]/i.test(message) || /^\[runtime\.[a-z_]*(?:failure|error)\]/i.test(message)) {
|
|
1713
|
-
return "diagnostic";
|
|
1348
|
+
function bounded(value, maxBytes = MAX_BYTES) {
|
|
1349
|
+
let encoded;
|
|
1350
|
+
try {
|
|
1351
|
+
encoded = JSON.stringify(value);
|
|
1352
|
+
} catch {
|
|
1353
|
+
fail("Run event query must be bounded JSON without cycles.");
|
|
1714
1354
|
}
|
|
1715
|
-
|
|
1355
|
+
if (!encoded || new TextEncoder().encode(encoded).length > maxBytes)
|
|
1356
|
+
fail(`Run event query exceeds ${maxBytes} bytes.`);
|
|
1716
1357
|
}
|
|
1717
|
-
function
|
|
1718
|
-
|
|
1719
|
-
const
|
|
1720
|
-
if (
|
|
1721
|
-
return
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1358
|
+
function validateRunEventField(value) {
|
|
1359
|
+
if (typeof value !== "string") fail("Run event field must be a string.");
|
|
1360
|
+
const field = value.startsWith("$.") ? value.slice(2) : value;
|
|
1361
|
+
if (Object.prototype.hasOwnProperty.call(RUN_EVENT_QUERY_FIELD_CATALOG, field))
|
|
1362
|
+
return field;
|
|
1363
|
+
if (/^context\.[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(field) && !["__proto__", "prototype", "constructor"].includes(field.slice(8)))
|
|
1364
|
+
return field;
|
|
1365
|
+
const alternatives = {
|
|
1366
|
+
step: "stepId",
|
|
1367
|
+
severity: "level",
|
|
1368
|
+
timestamp: "occurredAt",
|
|
1369
|
+
play: "playName",
|
|
1370
|
+
run: "runId",
|
|
1371
|
+
attempt: "producerAttempt"
|
|
1731
1372
|
};
|
|
1373
|
+
fail(
|
|
1374
|
+
`Unsupported run event field: ${field}. Use ${alternatives[field] ?? "a catalog field or context.<key>"}; call fields() for supported fields.`
|
|
1375
|
+
);
|
|
1732
1376
|
}
|
|
1733
|
-
function
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
if (
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
if (
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
return "warn";
|
|
1377
|
+
function scalar(field, value, op) {
|
|
1378
|
+
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")
|
|
1379
|
+
fail(`Run event ${field} requires a JSON scalar.`);
|
|
1380
|
+
if (typeof value === "number" && !Number.isFinite(value))
|
|
1381
|
+
fail(`Run event ${field} requires a finite number.`);
|
|
1382
|
+
if (typeof value === "string" && new TextEncoder().encode(value).length > 1024)
|
|
1383
|
+
fail(`Run event ${field} value exceeds 1024 bytes.`);
|
|
1384
|
+
const descriptor = RUN_EVENT_QUERY_FIELD_CATALOG[field];
|
|
1385
|
+
if (descriptor) {
|
|
1386
|
+
if (value === null) {
|
|
1387
|
+
if (!descriptor.nullable) fail(`Run event ${field} cannot be null.`);
|
|
1388
|
+
} else if (descriptor.type === "number") {
|
|
1389
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
|
|
1390
|
+
fail(`Run event ${field} requires a positive integer.`);
|
|
1391
|
+
} else if (typeof value !== "string")
|
|
1392
|
+
fail(`Run event ${field} requires a string.`);
|
|
1393
|
+
else if (descriptor.type === "level" && !RUN_LOG_LEVELS.includes(value))
|
|
1394
|
+
fail("Run event level requires debug, info, warn or error.");
|
|
1395
|
+
else if (descriptor.type === "timestamp") {
|
|
1396
|
+
value = normalizeRunEventQueryTimestamp(value);
|
|
1397
|
+
}
|
|
1755
1398
|
}
|
|
1756
|
-
|
|
1399
|
+
if (["gt", "gte", "lt", "lte"].includes(op) && (value === null || typeof value === "boolean"))
|
|
1400
|
+
fail(`Run event ${op} requires a string or number.`);
|
|
1401
|
+
return value;
|
|
1757
1402
|
}
|
|
1758
|
-
function
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1403
|
+
function comparison(field, op, value) {
|
|
1404
|
+
if (!OPERATORS.includes(op))
|
|
1405
|
+
fail(`Unsupported run event operator: ${op}. Use ${OPERATORS.join(", ")}.`);
|
|
1406
|
+
if (op === "exists") {
|
|
1407
|
+
if (typeof value !== "boolean")
|
|
1408
|
+
fail("Run event exists requires a boolean.");
|
|
1409
|
+
return { field, op, value };
|
|
1410
|
+
}
|
|
1411
|
+
if (op === "in") {
|
|
1412
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 32)
|
|
1413
|
+
fail("Run event in requires between 1 and 32 scalars.");
|
|
1414
|
+
return { field, op, value: value.map((item) => scalar(field, item, "eq")) };
|
|
1762
1415
|
}
|
|
1763
|
-
const inner = match[1] ?? "";
|
|
1764
|
-
const isTimestamp = !Number.isNaN(new Date(inner).getTime());
|
|
1765
|
-
return isTimestamp ? match[2] ?? line : line;
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
// ../play-runtime/run-snapshot-stream.ts
|
|
1769
|
-
function isTerminalPlayRunLiveStatus(status) {
|
|
1770
|
-
return isTerminalPlayRunLifecycleStatus(status);
|
|
1771
|
-
}
|
|
1772
|
-
function makeRunStreamEvent(input) {
|
|
1773
1416
|
return {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1417
|
+
field,
|
|
1418
|
+
op,
|
|
1419
|
+
value: scalar(field, value, op)
|
|
1777
1420
|
};
|
|
1778
1421
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1422
|
+
function admitObject(input, depth = 0, budget = { count: 0 }) {
|
|
1423
|
+
if (depth > MAX_DEPTH) fail("Run event where exceeds depth 8.");
|
|
1424
|
+
if (!object(input) || !Object.keys(input).length)
|
|
1425
|
+
fail("Run event where requires a nonempty object.");
|
|
1426
|
+
const terms = [];
|
|
1427
|
+
const fieldTerms = (raw, filter) => {
|
|
1428
|
+
const field = validateRunEventField(raw);
|
|
1429
|
+
const operators = object(filter) ? Object.entries(filter) : [["eq", filter]];
|
|
1430
|
+
if (!operators.length)
|
|
1431
|
+
fail(`Run event ${field} has an empty operator object.`);
|
|
1432
|
+
for (const [op, value] of operators) {
|
|
1433
|
+
if (++budget.count > MAX_PREDICATES)
|
|
1434
|
+
fail("Run event where exceeds 64 predicates.");
|
|
1435
|
+
terms.push(comparison(field, op, value));
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
for (const [key, filter] of Object.entries(input)) {
|
|
1439
|
+
if (key === "and" || key === "or") {
|
|
1440
|
+
if (!Array.isArray(filter) || filter.length < 1 || filter.length > MAX_PREDICATES)
|
|
1441
|
+
fail(`Run event ${key} requires a nonempty bounded array.`);
|
|
1442
|
+
const children = filter.map(
|
|
1443
|
+
(item) => admitObject(item, depth + 1, budget)
|
|
1444
|
+
);
|
|
1445
|
+
terms.push(key === "and" ? { and: children } : { or: children });
|
|
1446
|
+
} else if (key === "context") {
|
|
1447
|
+
if (!object(filter) || !Object.keys(filter).length)
|
|
1448
|
+
fail("Run event context requires flat scalar fields.");
|
|
1449
|
+
for (const [contextKey, value] of Object.entries(filter))
|
|
1450
|
+
fieldTerms(`context.${contextKey}`, value);
|
|
1451
|
+
} else fieldTerms(key, filter);
|
|
1452
|
+
}
|
|
1453
|
+
return terms.length === 1 ? terms[0] : { and: terms };
|
|
1788
1454
|
}
|
|
1789
|
-
function
|
|
1790
|
-
|
|
1455
|
+
function compact(predicate) {
|
|
1456
|
+
if ("and" in predicate) {
|
|
1457
|
+
const merged = {};
|
|
1458
|
+
for (const term of predicate.and) {
|
|
1459
|
+
const child = compact(term);
|
|
1460
|
+
for (const [key, value] of Object.entries(child)) {
|
|
1461
|
+
if (key === "and") return { and: predicate.and.map(compact) };
|
|
1462
|
+
if (key === "context") {
|
|
1463
|
+
const context = merged.context ?? {};
|
|
1464
|
+
for (const [name, filter2] of Object.entries(value)) {
|
|
1465
|
+
if (Object.prototype.hasOwnProperty.call(context, name)) {
|
|
1466
|
+
const combined = combineFilters(context[name], filter2);
|
|
1467
|
+
if (!combined) return { and: predicate.and.map(compact) };
|
|
1468
|
+
context[name] = combined;
|
|
1469
|
+
} else context[name] = filter2;
|
|
1470
|
+
}
|
|
1471
|
+
merged.context = context;
|
|
1472
|
+
} else if (Object.prototype.hasOwnProperty.call(merged, key)) {
|
|
1473
|
+
const combined = key === "or" ? void 0 : combineFilters(merged[key], value);
|
|
1474
|
+
if (!combined) return { and: predicate.and.map(compact) };
|
|
1475
|
+
merged[key] = combined;
|
|
1476
|
+
} else merged[key] = value;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return merged;
|
|
1480
|
+
}
|
|
1481
|
+
if ("or" in predicate) return { or: predicate.or.map(compact) };
|
|
1482
|
+
const filter = predicate.op === "eq" ? predicate.value : { [predicate.op]: predicate.value };
|
|
1483
|
+
if (predicate.field.startsWith("context."))
|
|
1484
|
+
return { context: { [predicate.field.slice(8)]: filter } };
|
|
1485
|
+
return { [predicate.field]: filter };
|
|
1791
1486
|
}
|
|
1792
|
-
function
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
state.progress?.startedAt ?? "",
|
|
1801
|
-
state.progress?.completedAt ?? "",
|
|
1802
|
-
state.updatedAt ?? "",
|
|
1803
|
-
state.progress?.updatedAt ?? ""
|
|
1804
|
-
].join(":")
|
|
1805
|
-
).join("|");
|
|
1487
|
+
function combineFilters(left, right) {
|
|
1488
|
+
const first = object(left) ? left : { eq: left };
|
|
1489
|
+
const second = object(right) ? right : { eq: right };
|
|
1490
|
+
if (Object.keys(first).some(
|
|
1491
|
+
(key) => Object.prototype.hasOwnProperty.call(second, key)
|
|
1492
|
+
))
|
|
1493
|
+
return void 0;
|
|
1494
|
+
return { ...first, ...second };
|
|
1806
1495
|
}
|
|
1807
|
-
function
|
|
1808
|
-
return
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1496
|
+
function canonical(predicate) {
|
|
1497
|
+
if ("field" in predicate) return predicate;
|
|
1498
|
+
const operator = "and" in predicate ? "and" : "or";
|
|
1499
|
+
const source = "and" in predicate ? predicate.and : predicate.or;
|
|
1500
|
+
const terms = source.map(canonical).flatMap((term) => {
|
|
1501
|
+
if (operator === "and" && "and" in term) return term.and;
|
|
1502
|
+
if (operator === "or" && "or" in term) return term.or;
|
|
1503
|
+
return [term];
|
|
1504
|
+
});
|
|
1505
|
+
terms.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
1506
|
+
const unique = terms.filter(
|
|
1507
|
+
(term, i) => i === 0 || JSON.stringify(term) !== JSON.stringify(terms[i - 1])
|
|
1818
1508
|
);
|
|
1509
|
+
if (unique.length === 1) return unique[0];
|
|
1510
|
+
return operator === "and" ? { and: unique } : { or: unique };
|
|
1819
1511
|
}
|
|
1820
|
-
function
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
}
|
|
1827
|
-
const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
|
|
1828
|
-
if (lastLogSeq + 1 >= tailFirstSeq) {
|
|
1829
|
-
return null;
|
|
1830
|
-
}
|
|
1831
|
-
return {
|
|
1832
|
-
missingCount: tailFirstSeq - 1 - lastLogSeq,
|
|
1833
|
-
tailFirstSeq
|
|
1512
|
+
function parseDsl(input) {
|
|
1513
|
+
let position = 0;
|
|
1514
|
+
let count = 0;
|
|
1515
|
+
const skip = () => {
|
|
1516
|
+
while (/\s/.test(input[position] ?? "") && position < input.length)
|
|
1517
|
+
position++;
|
|
1834
1518
|
};
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
firstSeq: null
|
|
1843
|
-
};
|
|
1844
|
-
}
|
|
1845
|
-
if (totalLogCount <= input.lastLogSeq) {
|
|
1846
|
-
return { lines: [], lastLogSeq: input.lastLogSeq, firstSeq: null };
|
|
1847
|
-
}
|
|
1848
|
-
const tailFirstSeq = totalLogCount - logs.length + 1;
|
|
1849
|
-
if (input.lastLogSeq + 1 >= tailFirstSeq) {
|
|
1850
|
-
return {
|
|
1851
|
-
lines: logs.slice(input.lastLogSeq + 1 - tailFirstSeq),
|
|
1852
|
-
lastLogSeq: totalLogCount,
|
|
1853
|
-
firstSeq: input.lastLogSeq + 1
|
|
1854
|
-
};
|
|
1855
|
-
}
|
|
1856
|
-
const missingCount = tailFirstSeq - 1 - input.lastLogSeq;
|
|
1857
|
-
return {
|
|
1858
|
-
lines: [
|
|
1859
|
-
`[watch] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
|
|
1860
|
-
...logs
|
|
1861
|
-
],
|
|
1862
|
-
lastLogSeq: totalLogCount,
|
|
1863
|
-
firstSeq: null
|
|
1864
|
-
};
|
|
1865
|
-
}
|
|
1866
|
-
function diffPlayRunStreamEvents(input) {
|
|
1867
|
-
const { snapshot, streamId, previous } = input;
|
|
1868
|
-
const cursor = getSnapshotCursor(snapshot);
|
|
1869
|
-
const logDiff = diffLogLines({
|
|
1870
|
-
snapshot,
|
|
1871
|
-
lastLogSeq: previous.lastLogSeq
|
|
1872
|
-
});
|
|
1873
|
-
const next = {
|
|
1874
|
-
runSignature: getRunSignature(snapshot),
|
|
1875
|
-
stepStatusSignature: getStepStatusSignature(snapshot),
|
|
1876
|
-
stepProgressSignature: getStepProgressSignature(snapshot),
|
|
1877
|
-
snapshotSignature: getSnapshotSignature(snapshot),
|
|
1878
|
-
lastLogSeq: logDiff.lastLogSeq
|
|
1519
|
+
const take = (token) => {
|
|
1520
|
+
skip();
|
|
1521
|
+
if (input.startsWith(token, position)) {
|
|
1522
|
+
position += token.length;
|
|
1523
|
+
return true;
|
|
1524
|
+
}
|
|
1525
|
+
return false;
|
|
1879
1526
|
};
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1527
|
+
const json = () => {
|
|
1528
|
+
skip();
|
|
1529
|
+
const start = position;
|
|
1530
|
+
if (input[position] === '"') {
|
|
1531
|
+
position++;
|
|
1532
|
+
let closed = false;
|
|
1533
|
+
while (position < input.length) {
|
|
1534
|
+
const char = input[position++];
|
|
1535
|
+
if (char === "\\") position++;
|
|
1536
|
+
else if (char === '"') {
|
|
1537
|
+
closed = true;
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1885
1540
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
cursor,
|
|
1891
|
-
streamId,
|
|
1892
|
-
type: "play.step.status",
|
|
1893
|
-
payload: {
|
|
1894
|
-
runId: snapshot.runId,
|
|
1895
|
-
stepId: state.nodeId,
|
|
1896
|
-
status: state.status,
|
|
1897
|
-
artifactTableNamespace: state.artifactTableNamespace ?? null,
|
|
1898
|
-
...resolveTimingWindow({
|
|
1899
|
-
startedAt: persistedStartedAt,
|
|
1900
|
-
completedAt: persistedCompletedAt,
|
|
1901
|
-
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
1902
|
-
})
|
|
1903
|
-
}
|
|
1904
|
-
})
|
|
1541
|
+
if (!closed) fail("Run event DSL has an unterminated JSON string.");
|
|
1542
|
+
} else {
|
|
1543
|
+
const match = /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/.exec(
|
|
1544
|
+
input.slice(position)
|
|
1905
1545
|
);
|
|
1546
|
+
if (!match)
|
|
1547
|
+
fail("Run event DSL requires a JSON quoted string or scalar.");
|
|
1548
|
+
position += match[0].length;
|
|
1906
1549
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1550
|
+
try {
|
|
1551
|
+
return JSON.parse(input.slice(start, position));
|
|
1552
|
+
} catch {
|
|
1553
|
+
fail("Run event DSL requires valid JSON scalars.");
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
const term = (depth) => {
|
|
1557
|
+
if (depth > MAX_DEPTH) fail("Run event where exceeds depth 8.");
|
|
1558
|
+
if (take("(")) {
|
|
1559
|
+
const nested = or(depth + 1);
|
|
1560
|
+
if (!take(")")) fail("Run event DSL is missing a closing parenthesis.");
|
|
1561
|
+
return nested;
|
|
1562
|
+
}
|
|
1563
|
+
skip();
|
|
1564
|
+
const match = /^(?:\$\.)?[A-Za-z_][A-Za-z0-9_.]*/.exec(
|
|
1565
|
+
input.slice(position)
|
|
1566
|
+
);
|
|
1567
|
+
if (!match) fail("Run event DSL requires a supported field.");
|
|
1568
|
+
position += match[0].length;
|
|
1569
|
+
const field = validateRunEventField(match[0]);
|
|
1570
|
+
if (++count > MAX_PREDICATES)
|
|
1571
|
+
fail("Run event where exceeds 64 predicates.");
|
|
1572
|
+
skip();
|
|
1573
|
+
const operation = /^(==|!=|>=|<=|>|<|\b(?:eq|ne|gte|gt|lte|lt|in|exists)\b)/.exec(
|
|
1574
|
+
input.slice(position)
|
|
1575
|
+
);
|
|
1576
|
+
if (!operation) {
|
|
1577
|
+
const token = /^(?:[=!<>~]+|[A-Za-z_]+)/.exec(input.slice(position))?.[0] ?? input[position] ?? "<end>";
|
|
1578
|
+
if (token === "=") {
|
|
1579
|
+
const corrected = `${input.slice(0, position)}==${input.slice(position + 1)}`;
|
|
1580
|
+
fail(
|
|
1581
|
+
`Run event DSL does not support "=" at character ${position + 1}. For equality, replace this "=" with "==": ${JSON.stringify(corrected)}.`
|
|
1582
|
+
);
|
|
1912
1583
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
cursor: String(
|
|
1916
|
-
state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
|
|
1917
|
-
),
|
|
1918
|
-
streamId,
|
|
1919
|
-
type: "play.step.progress",
|
|
1920
|
-
payload: {
|
|
1921
|
-
runId: snapshot.runId,
|
|
1922
|
-
stepId: state.nodeId,
|
|
1923
|
-
...projectPlayStepProgressCounters(state.progress),
|
|
1924
|
-
message: state.progress.message,
|
|
1925
|
-
artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
|
|
1926
|
-
...resolveTimingWindow({
|
|
1927
|
-
startedAt: state.startedAt ?? state.progress.startedAt ?? null,
|
|
1928
|
-
completedAt: state.completedAt ?? state.progress.completedAt ?? null,
|
|
1929
|
-
updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
|
|
1930
|
-
})
|
|
1931
|
-
}
|
|
1932
|
-
})
|
|
1584
|
+
fail(
|
|
1585
|
+
`Run event DSL does not support ${JSON.stringify(token)} after ${JSON.stringify(match[0])} at character ${position + 1}. Use ==, !=, >, >=, <, <=, in or exists.`
|
|
1933
1586
|
);
|
|
1934
1587
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1588
|
+
position += operation[0].length;
|
|
1589
|
+
const symbols = {
|
|
1590
|
+
"==": "eq",
|
|
1591
|
+
"!=": "ne",
|
|
1592
|
+
">=": "gte",
|
|
1593
|
+
"<=": "lte",
|
|
1594
|
+
">": "gt",
|
|
1595
|
+
"<": "lt"
|
|
1596
|
+
};
|
|
1597
|
+
const op = symbols[operation[0]] ?? operation[0];
|
|
1598
|
+
if (op === "in") {
|
|
1599
|
+
if (!take("[")) fail("Run event DSL in requires a JSON scalar array.");
|
|
1600
|
+
const values = [];
|
|
1601
|
+
if (!take("]")) {
|
|
1602
|
+
do {
|
|
1603
|
+
values.push(json());
|
|
1604
|
+
if (values.length > 32)
|
|
1605
|
+
fail("Run event in requires between 1 and 32 scalars.");
|
|
1606
|
+
} while (take(","));
|
|
1607
|
+
if (!take("]")) fail("Run event DSL in requires a closing bracket.");
|
|
1608
|
+
}
|
|
1609
|
+
return comparison(field, op, values);
|
|
1610
|
+
}
|
|
1611
|
+
return comparison(field, op, json());
|
|
1612
|
+
};
|
|
1613
|
+
const and = (depth) => {
|
|
1614
|
+
const terms = [term(depth)];
|
|
1615
|
+
while (take("&&")) terms.push(term(depth));
|
|
1616
|
+
return terms.length === 1 ? terms[0] : { and: terms };
|
|
1617
|
+
};
|
|
1618
|
+
const or = (depth) => {
|
|
1619
|
+
const terms = [and(depth)];
|
|
1620
|
+
while (take("||")) terms.push(and(depth));
|
|
1621
|
+
return terms.length === 1 ? terms[0] : { or: terms };
|
|
1622
|
+
};
|
|
1623
|
+
const result = or(0);
|
|
1624
|
+
skip();
|
|
1625
|
+
if (position !== input.length)
|
|
1626
|
+
fail(`Run event DSL has unsupported input at position ${position}.`);
|
|
1627
|
+
return result;
|
|
1628
|
+
}
|
|
1629
|
+
function parseRunEventWhere(input) {
|
|
1630
|
+
bounded(input);
|
|
1631
|
+
const where = compact(
|
|
1632
|
+
canonical(typeof input === "string" ? parseDsl(input) : admitObject(input))
|
|
1633
|
+
);
|
|
1634
|
+
bounded(where);
|
|
1635
|
+
return where;
|
|
1636
|
+
}
|
|
1637
|
+
function normalizeRunEventQueryTimestamp(value) {
|
|
1638
|
+
let extraMicros = "";
|
|
1639
|
+
if (typeof value === "string") {
|
|
1640
|
+
const timestamp = /^[+-]?\d{4,6}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.(\d{1,6}))?(?:Z|[+-]\d{2}:\d{2})$/.exec(
|
|
1641
|
+
value
|
|
1950
1642
|
);
|
|
1643
|
+
if (!timestamp)
|
|
1644
|
+
fail(
|
|
1645
|
+
"Run event time bounds require an ISO timestamp with a timezone and at most six fractional digits."
|
|
1646
|
+
);
|
|
1647
|
+
extraMicros = (timestamp[1] ?? "").padEnd(6, "0").slice(3);
|
|
1951
1648
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1649
|
+
const date = new Date(value);
|
|
1650
|
+
if (!Number.isFinite(date.getTime()))
|
|
1651
|
+
fail("Run event time bound is invalid.");
|
|
1652
|
+
const canonical2 = date.toISOString();
|
|
1653
|
+
return /[1-9]/.test(extraMicros) ? canonical2.replace(/Z$/, `${extraMicros}Z`) : canonical2;
|
|
1654
|
+
}
|
|
1655
|
+
function timestampMicros(value) {
|
|
1656
|
+
const fraction = /\.(\d+)Z$/.exec(value)?.[1] ?? "";
|
|
1657
|
+
return BigInt(Date.parse(value)) * BigInt(1e3) + BigInt(fraction.padEnd(6, "0").slice(3));
|
|
1658
|
+
}
|
|
1659
|
+
function time(value, nowMs, fallback) {
|
|
1660
|
+
if (value === void 0) value = fallback;
|
|
1661
|
+
let timestamp;
|
|
1662
|
+
if (typeof value === "number") timestamp = value;
|
|
1663
|
+
else if (typeof value === "string") {
|
|
1664
|
+
const relative = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/.exec(value);
|
|
1665
|
+
if (relative) {
|
|
1666
|
+
const units = {
|
|
1667
|
+
ms: 1,
|
|
1668
|
+
s: 1e3,
|
|
1669
|
+
m: 6e4,
|
|
1670
|
+
h: 36e5,
|
|
1671
|
+
d: 864e5
|
|
1970
1672
|
};
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1673
|
+
timestamp = nowMs - Number(relative[1]) * units[relative[2]];
|
|
1674
|
+
} else {
|
|
1675
|
+
return normalizeRunEventQueryTimestamp(value);
|
|
1676
|
+
}
|
|
1677
|
+
} else fail("Run event time bounds require a timestamp or duration.");
|
|
1678
|
+
return normalizeRunEventQueryTimestamp(timestamp);
|
|
1679
|
+
}
|
|
1680
|
+
function exact(value, name) {
|
|
1681
|
+
if (typeof value !== "string" || !value.trim() || value.length > 256)
|
|
1682
|
+
fail(
|
|
1683
|
+
`Run event ${name} requires a nonempty string of at most 256 characters.`
|
|
1979
1684
|
);
|
|
1685
|
+
return value;
|
|
1686
|
+
}
|
|
1687
|
+
var CRITERIA_KEYS = [
|
|
1688
|
+
"since",
|
|
1689
|
+
"until",
|
|
1690
|
+
"where",
|
|
1691
|
+
"runId",
|
|
1692
|
+
"stepId",
|
|
1693
|
+
"minLevel",
|
|
1694
|
+
"text",
|
|
1695
|
+
"playName",
|
|
1696
|
+
"kind"
|
|
1697
|
+
];
|
|
1698
|
+
function normalizeRunEventQuery(criteria, nowMs = Date.now()) {
|
|
1699
|
+
if (!object(criteria)) fail("Run event criteria requires an object.");
|
|
1700
|
+
bounded(criteria, RUN_EVENT_QUERY_MAX_REQUEST_BYTES);
|
|
1701
|
+
for (const key of Object.keys(criteria)) {
|
|
1702
|
+
if (!CRITERIA_KEYS.includes(key))
|
|
1703
|
+
fail(`Unsupported run event criterion: ${key}.`);
|
|
1980
1704
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
updatedAt: snapshot.updatedAt,
|
|
1991
|
-
...snapshot.activity ? { activity: snapshot.activity } : {}
|
|
1992
|
-
}
|
|
1993
|
-
})
|
|
1994
|
-
);
|
|
1705
|
+
const until = time(criteria.until, nowMs, nowMs);
|
|
1706
|
+
const since = time(criteria.since, nowMs, Date.parse(until) - 36e5);
|
|
1707
|
+
if (timestampMicros(since) >= timestampMicros(until) || timestampMicros(until) - timestampMicros(since) > BigInt(RUN_EVENT_QUERY_RETENTION_MS) * BigInt(1e3))
|
|
1708
|
+
fail("Run event query requires a window of at most seven days.");
|
|
1709
|
+
const query = { since, until };
|
|
1710
|
+
if (criteria.where !== void 0)
|
|
1711
|
+
query.where = parseRunEventWhere(criteria.where);
|
|
1712
|
+
for (const name of ["runId", "stepId", "playName", "kind"]) {
|
|
1713
|
+
if (criteria[name] !== void 0) query[name] = exact(criteria[name], name);
|
|
1995
1714
|
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
function isTransientPlayRunReadError(error) {
|
|
2001
|
-
if (error instanceof DeeplineError && error.code === "PLAY_STREAM_NETWORK_ABORTED") {
|
|
2002
|
-
return true;
|
|
1715
|
+
if (criteria.minLevel !== void 0) {
|
|
1716
|
+
if (!RUN_LOG_LEVELS.includes(criteria.minLevel))
|
|
1717
|
+
fail("Run event minLevel requires debug, info, warn or error.");
|
|
1718
|
+
query.minLevel = criteria.minLevel;
|
|
2003
1719
|
}
|
|
2004
|
-
if (
|
|
2005
|
-
|
|
1720
|
+
if (criteria.text !== void 0) {
|
|
1721
|
+
if (typeof criteria.text !== "string" || !criteria.text.length || new TextEncoder().encode(criteria.text).length > 1024)
|
|
1722
|
+
fail("Run event text requires between 1 and 1024 bytes.");
|
|
1723
|
+
query.text = criteria.text;
|
|
2006
1724
|
}
|
|
2007
|
-
|
|
2008
|
-
return /auth validation backend timed out|coordinator \/submit(?:\?[^ ]*)? 5\d\d|Worker threw exception|Internal Server Error|Service Unavailable|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
2009
|
-
text
|
|
2010
|
-
);
|
|
1725
|
+
return query;
|
|
2011
1726
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
if (
|
|
2017
|
-
|
|
1727
|
+
function normalizeRunEventOperationRequest(input, nowMs = Date.now()) {
|
|
1728
|
+
bounded(input, RUN_EVENT_QUERY_MAX_REQUEST_BYTES);
|
|
1729
|
+
if (!object(input)) fail("Run event operation requires an object.");
|
|
1730
|
+
const operation = input.operation;
|
|
1731
|
+
if (!["list", "count", "summarize", "fields"].includes(String(operation)))
|
|
1732
|
+
fail("Run event operation requires list, count, summarize or fields.");
|
|
1733
|
+
if (input.resource !== "logs" && input.resource !== "events")
|
|
1734
|
+
fail("Run event resource requires logs or events.");
|
|
1735
|
+
const allowed = [
|
|
1736
|
+
"operation",
|
|
1737
|
+
"resource",
|
|
1738
|
+
"runId",
|
|
1739
|
+
...operation === "fields" ? [] : CRITERIA_KEYS,
|
|
1740
|
+
...operation === "list" ? ["limit", "cursor", "includePayloads"] : operation === "summarize" ? ["groupBy", "measures", "limit", "cursor"] : []
|
|
1741
|
+
];
|
|
1742
|
+
for (const key of Object.keys(input)) {
|
|
1743
|
+
if (!allowed.includes(key))
|
|
1744
|
+
fail(`Run event ${operation} does not support ${key}.`);
|
|
2018
1745
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
1746
|
+
const base = {
|
|
1747
|
+
operation,
|
|
1748
|
+
resource: input.resource,
|
|
1749
|
+
...input.runId === void 0 ? {} : { runId: exact(input.runId, "runId") }
|
|
1750
|
+
};
|
|
1751
|
+
if (operation === "fields") return base;
|
|
1752
|
+
if (input.resource === "logs" && !base.runId)
|
|
1753
|
+
fail("Run event logs require runId.");
|
|
1754
|
+
if (input.cursor !== void 0 && (typeof input.cursor !== "string" || !input.cursor.length || input.cursor.length > RUN_EVENT_QUERY_MAX_CURSOR_BYTES))
|
|
1755
|
+
fail("Run event cursor is invalid.");
|
|
1756
|
+
const rawCriteria = Object.fromEntries(
|
|
1757
|
+
Object.entries(input).filter(
|
|
1758
|
+
([key]) => CRITERIA_KEYS.includes(key) && (key !== "runId" || input.resource === "events")
|
|
1759
|
+
)
|
|
1760
|
+
);
|
|
1761
|
+
const query = normalizeRunEventQuery(
|
|
1762
|
+
input.cursor === void 0 ? rawCriteria : { ...rawCriteria, since: nowMs - 36e5, until: nowMs },
|
|
1763
|
+
nowMs
|
|
1764
|
+
);
|
|
1765
|
+
const criteria = { ...query };
|
|
1766
|
+
if (input.cursor !== void 0) {
|
|
1767
|
+
if (input.since === void 0) delete criteria.since;
|
|
1768
|
+
else criteria.since = time(input.since, nowMs, nowMs - 36e5);
|
|
1769
|
+
if (input.until === void 0) delete criteria.until;
|
|
1770
|
+
else criteria.until = time(input.until, nowMs, nowMs);
|
|
1771
|
+
}
|
|
1772
|
+
if (operation === "count")
|
|
1773
|
+
return { ...base, ...criteria };
|
|
1774
|
+
const limit = input.limit ?? (input.includePayloads === true ? 10 : 50);
|
|
1775
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > RUN_EVENT_QUERY_MAX_LIMIT)
|
|
1776
|
+
fail("Run event limit must be between 1 and 200.");
|
|
1777
|
+
const page = {
|
|
1778
|
+
limit,
|
|
1779
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
1780
|
+
};
|
|
1781
|
+
if (operation === "list") {
|
|
1782
|
+
if (input.includePayloads !== void 0 && typeof input.includePayloads !== "boolean")
|
|
1783
|
+
fail("Run event includePayloads requires a boolean.");
|
|
1784
|
+
if (input.includePayloads === true && limit > RUN_EVENT_QUERY_MAX_PAYLOAD_LIMIT)
|
|
1785
|
+
fail("Run event payload pages cannot exceed 10 rows.");
|
|
1786
|
+
return {
|
|
1787
|
+
...base,
|
|
1788
|
+
...criteria,
|
|
1789
|
+
...page,
|
|
1790
|
+
...input.includePayloads === void 0 ? {} : { includePayloads: input.includePayloads }
|
|
2024
1791
|
};
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
1792
|
+
}
|
|
1793
|
+
if (!Array.isArray(input.groupBy) || input.groupBy.length > 4)
|
|
1794
|
+
fail("Run event groupBy requires at most four fields.");
|
|
1795
|
+
const groupBy = input.groupBy.map(validateRunEventField);
|
|
1796
|
+
if (new Set(groupBy).size !== groupBy.length)
|
|
1797
|
+
fail("Run event groupBy fields must be distinct.");
|
|
1798
|
+
const rawMeasures = input.measures ?? [{ op: "count" }];
|
|
1799
|
+
if (!Array.isArray(rawMeasures) || rawMeasures.length < 1 || rawMeasures.length > 8)
|
|
1800
|
+
fail("Run event measures require between 1 and 8 entries.");
|
|
1801
|
+
const names = /* @__PURE__ */ new Set();
|
|
1802
|
+
const measures = rawMeasures.map((measure) => {
|
|
1803
|
+
if (!object(measure) || measure.op !== "count" && measure.op !== "sum")
|
|
1804
|
+
fail("Run event measures support count and sum.");
|
|
1805
|
+
for (const key of Object.keys(measure)) {
|
|
1806
|
+
if (!["op", "as", ...measure.op === "sum" ? ["field"] : []].includes(key))
|
|
1807
|
+
fail(`Unsupported run event measure option: ${key}.`);
|
|
1808
|
+
}
|
|
1809
|
+
const field = measure.op === "sum" ? validateRunEventField(measure.field) : void 0;
|
|
1810
|
+
if (field && !field.startsWith("context.") && RUN_EVENT_QUERY_FIELD_CATALOG[field].type !== "number")
|
|
1811
|
+
fail(`Run event sum requires a numeric field: ${field}.`);
|
|
1812
|
+
const name = measure.as ?? (field ? `sum_${field.replaceAll(".", "_")}` : "count");
|
|
1813
|
+
if (typeof name !== "string" || !/^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(name) || names.has(name))
|
|
1814
|
+
fail("Run event measure names must be distinct identifiers.");
|
|
1815
|
+
names.add(name);
|
|
1816
|
+
return field ? { op: "sum", field, as: name } : { op: "count", as: name };
|
|
2028
1817
|
});
|
|
1818
|
+
return {
|
|
1819
|
+
...base,
|
|
1820
|
+
...criteria,
|
|
1821
|
+
...page,
|
|
1822
|
+
groupBy,
|
|
1823
|
+
measures
|
|
1824
|
+
};
|
|
2029
1825
|
}
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
const record = response;
|
|
2035
|
-
const snapshot = record.snapshot;
|
|
2036
|
-
if (record.runId !== runId || !snapshot || typeof snapshot !== "object" || Array.isArray(snapshot) || snapshot.runId !== runId || typeof snapshot.status !== "string" || !Array.isArray(snapshot.logs) || !snapshot.logs.every(
|
|
2037
|
-
(line) => typeof line === "string"
|
|
2038
|
-
) || typeof snapshot.totalLogCount !== "number" || !Array.isArray(snapshot.nodeStates)) {
|
|
2039
|
-
throw new Error(`Run watch returned an invalid snapshot for ${runId}.`);
|
|
2040
|
-
}
|
|
2041
|
-
return { runId, snapshot };
|
|
1826
|
+
|
|
1827
|
+
// src/types.ts
|
|
1828
|
+
function isStopAllPlayRunsDryRunResult(result) {
|
|
1829
|
+
return "dryRun" in result && result.dryRun === true;
|
|
2042
1830
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
1831
|
+
|
|
1832
|
+
// ../plays/trigger-config-route.ts
|
|
1833
|
+
function playTriggerConfigPath(playName) {
|
|
1834
|
+
return `/api/v2/plays/${encodeURIComponent(playName)}/triggers`;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// src/plays/input-example.ts
|
|
1838
|
+
function playInputExampleArgs(schema) {
|
|
1839
|
+
if (!schema) return " --input '@<input.json>'";
|
|
1840
|
+
if (schema.$ref || schema.allOf) return " --input '@<input.json>'";
|
|
1841
|
+
const csv = schema.csvInput;
|
|
1842
|
+
const csvField = typeof csv?.inputField === "string" ? csv.inputField : null;
|
|
1843
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
1844
|
+
const csvOnlyRequired = required.every((key) => key === csvField);
|
|
1845
|
+
const alternatives = schema.oneOf ?? schema.anyOf;
|
|
1846
|
+
const hasCsvAlternative = Array.isArray(alternatives) && alternatives.some((branch) => {
|
|
1847
|
+
if (!branch || typeof branch !== "object") return false;
|
|
1848
|
+
const keys = branch.required;
|
|
1849
|
+
return Array.isArray(keys) && keys.includes(csvField) && keys.every((key) => key === csvField);
|
|
1850
|
+
});
|
|
1851
|
+
if (csvField && csvOnlyRequired && (!alternatives || hasCsvAlternative)) {
|
|
1852
|
+
if (csvField === "csv") return " --csv '<contacts.csv>'";
|
|
1853
|
+
const value = JSON.stringify({ [csvField]: "<contacts.csv>" });
|
|
1854
|
+
return ` --input '${value.replace(/'/g, "'\\''")}'`;
|
|
2046
1855
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
return input.snapshot;
|
|
2050
|
-
}
|
|
2051
|
-
const lines = [];
|
|
2052
|
-
let cursor = input.previous.lastLogSeq;
|
|
2053
|
-
const targetSeq = gap.tailFirstSeq - 1;
|
|
2054
|
-
while (cursor < targetSeq) {
|
|
2055
|
-
const limit = Math.min(1e3, targetSeq - cursor);
|
|
2056
|
-
const page = await input.http.get(
|
|
2057
|
-
`/api/v2/runs/${encodeURIComponent(input.runId)}/logs?afterSeq=${cursor}&limit=${limit}`,
|
|
2058
|
-
{ signal: input.signal }
|
|
2059
|
-
);
|
|
2060
|
-
if (!page || !Array.isArray(page.entries) || page.entries.length === 0) {
|
|
2061
|
-
return input.snapshot;
|
|
2062
|
-
}
|
|
2063
|
-
for (const entry of page.entries) {
|
|
2064
|
-
if (!entry || entry.seq !== cursor + 1 || typeof entry.line !== "string") {
|
|
2065
|
-
return input.snapshot;
|
|
2066
|
-
}
|
|
2067
|
-
lines.push(entry.line);
|
|
2068
|
-
cursor = entry.seq;
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
if (cursor !== targetSeq) {
|
|
2072
|
-
return input.snapshot;
|
|
2073
|
-
}
|
|
2074
|
-
return { ...input.snapshot, logs: [...lines, ...input.snapshot.logs] };
|
|
2075
|
-
}
|
|
2076
|
-
async function* watchRunEvents(options) {
|
|
2077
|
-
const { http, runId, signal } = options;
|
|
2078
|
-
const path = `/api/v2/runs/${encodeURIComponent(runId)}?watchSnapshot=true`;
|
|
2079
|
-
const streamId = `play-run-watch:${runId}`;
|
|
2080
|
-
const pollIntervalMs = options.pollIntervalMs ?? RUN_WATCH_POLL_INTERVAL_MS;
|
|
2081
|
-
let previous = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
|
|
2082
|
-
let consecutiveFailures = 0;
|
|
2083
|
-
let outageNoticeSent = false;
|
|
2084
|
-
while (!signal?.aborted) {
|
|
2085
|
-
const pollStartedAt = Date.now();
|
|
2086
|
-
let snapshot;
|
|
2087
|
-
try {
|
|
2088
|
-
const response = await http.get(path, { signal });
|
|
2089
|
-
snapshot = await backfillRotatedLogTail({
|
|
2090
|
-
http,
|
|
2091
|
-
runId,
|
|
2092
|
-
snapshot: readSnapshotResponse(response, runId).snapshot,
|
|
2093
|
-
previous,
|
|
2094
|
-
signal
|
|
2095
|
-
});
|
|
2096
|
-
} catch (error) {
|
|
2097
|
-
if (signal?.aborted) {
|
|
2098
|
-
return;
|
|
2099
|
-
}
|
|
2100
|
-
if (!isTransientPlayRunReadError(error)) {
|
|
2101
|
-
throw error;
|
|
2102
|
-
}
|
|
2103
|
-
consecutiveFailures += 1;
|
|
2104
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
2105
|
-
if (!outageNoticeSent) {
|
|
2106
|
-
options.onRetry?.({
|
|
2107
|
-
attempt: consecutiveFailures,
|
|
2108
|
-
delayMs: pollIntervalMs,
|
|
2109
|
-
reason
|
|
2110
|
-
});
|
|
2111
|
-
options.onNotice?.(
|
|
2112
|
-
`[watch] run snapshot request failed; retrying every ${pollIntervalMs / 1e3}s (${reason})`
|
|
2113
|
-
);
|
|
2114
|
-
outageNoticeSent = true;
|
|
2115
|
-
}
|
|
2116
|
-
const remaining2 = Math.max(
|
|
2117
|
-
0,
|
|
2118
|
-
pollIntervalMs - (Date.now() - pollStartedAt)
|
|
2119
|
-
);
|
|
2120
|
-
if (!await waitForNextPoll(remaining2, signal)) {
|
|
2121
|
-
return;
|
|
2122
|
-
}
|
|
2123
|
-
continue;
|
|
2124
|
-
}
|
|
2125
|
-
if (outageNoticeSent) {
|
|
2126
|
-
options.onNotice?.("[watch] run snapshot polling recovered");
|
|
2127
|
-
outageNoticeSent = false;
|
|
2128
|
-
}
|
|
2129
|
-
consecutiveFailures = 0;
|
|
2130
|
-
const diff = diffPlayRunStreamEvents({
|
|
2131
|
-
streamId,
|
|
2132
|
-
snapshot,
|
|
2133
|
-
previous
|
|
2134
|
-
});
|
|
2135
|
-
previous = diff.next;
|
|
2136
|
-
for (const event of diff.events) {
|
|
2137
|
-
yield event;
|
|
2138
|
-
}
|
|
2139
|
-
if (isTerminalPlayRunLiveStatus(snapshot.status)) {
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
const remaining = Math.max(
|
|
2143
|
-
0,
|
|
2144
|
-
pollIntervalMs - (Date.now() - pollStartedAt)
|
|
2145
|
-
);
|
|
2146
|
-
if (!await waitForNextPoll(remaining, signal)) {
|
|
2147
|
-
return;
|
|
2148
|
-
}
|
|
1856
|
+
if (Array.isArray(schema.required) && !required.length && !csv && !alternatives) {
|
|
1857
|
+
return "";
|
|
2149
1858
|
}
|
|
1859
|
+
return " --input '@<input.json>'";
|
|
2150
1860
|
}
|
|
2151
1861
|
|
|
2152
|
-
//
|
|
2153
|
-
var
|
|
2154
|
-
function
|
|
2155
|
-
|
|
1862
|
+
// ../product-notifications/configuration.ts
|
|
1863
|
+
var PRODUCT_NOTIFICATION_EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1864
|
+
function normalizeProductNotificationEmail(value) {
|
|
1865
|
+
const email = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
1866
|
+
return email && email.length <= 320 && PRODUCT_NOTIFICATION_EMAIL_PATTERN.test(email) ? email : null;
|
|
2156
1867
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
1868
|
+
var DEFAULT_PRODUCT_NOTIFICATION_DELIVERY_TIMING = {
|
|
1869
|
+
mode: "rolling_limit",
|
|
1870
|
+
windowMs: 36e5
|
|
1871
|
+
};
|
|
1872
|
+
|
|
1873
|
+
// ../play-runtime/live-events.ts
|
|
1874
|
+
function projectPlayStepProgressCounters(progress) {
|
|
1875
|
+
return {
|
|
1876
|
+
completed: progress.completed,
|
|
1877
|
+
total: progress.total,
|
|
1878
|
+
failed: progress.failed,
|
|
1879
|
+
startedRows: progress.startedRows,
|
|
1880
|
+
activeRows: progress.activeRows,
|
|
1881
|
+
waitingRows: progress.waitingRows,
|
|
1882
|
+
completedRows: progress.completedRows,
|
|
1883
|
+
supersededRows: progress.supersededRows
|
|
1884
|
+
};
|
|
2159
1885
|
}
|
|
2160
|
-
function
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
);
|
|
2170
|
-
}
|
|
2171
|
-
return parsed;
|
|
1886
|
+
function resolveTimingWindow(input) {
|
|
1887
|
+
const startedAt = typeof input.startedAt === "number" && Number.isFinite(input.startedAt) ? input.startedAt : null;
|
|
1888
|
+
const completedAt = typeof input.completedAt === "number" && Number.isFinite(input.completedAt) ? input.completedAt : null;
|
|
1889
|
+
const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : null;
|
|
1890
|
+
return {
|
|
1891
|
+
startedAt,
|
|
1892
|
+
completedAt,
|
|
1893
|
+
updatedAt
|
|
1894
|
+
};
|
|
2172
1895
|
}
|
|
2173
1896
|
|
|
2174
|
-
// ../play-runtime/run-
|
|
2175
|
-
var
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
var RUN_EVENT_QUERY_MAX_LIMIT = 200;
|
|
2198
|
-
var RUN_EVENT_QUERY_MAX_PAYLOAD_LIMIT = 10;
|
|
2199
|
-
var RUN_EVENT_QUERY_MAX_CURSOR_BYTES = 48 * 1024;
|
|
2200
|
-
var RUN_EVENT_QUERY_MAX_REQUEST_BYTES = 64 * 1024;
|
|
2201
|
-
var MAX_BYTES = 8192;
|
|
2202
|
-
var MAX_DEPTH = 8;
|
|
2203
|
-
var MAX_PREDICATES = 64;
|
|
2204
|
-
var OPERATORS = [
|
|
2205
|
-
"eq",
|
|
2206
|
-
"ne",
|
|
2207
|
-
"gt",
|
|
2208
|
-
"gte",
|
|
2209
|
-
"lt",
|
|
2210
|
-
"lte",
|
|
2211
|
-
"exists",
|
|
2212
|
-
"in"
|
|
2213
|
-
];
|
|
2214
|
-
var RunEventQueryInputError = class extends TypeError {
|
|
2215
|
-
constructor(message, code = "invalid_run_event_query") {
|
|
2216
|
-
super(message);
|
|
2217
|
-
this.code = code;
|
|
2218
|
-
this.name = "RunEventQueryInputError";
|
|
1897
|
+
// ../play-runtime/run-failure.ts
|
|
1898
|
+
var PUBLIC_FAILURE_TEXT_BYTE_LIMIT = 64 * 1024;
|
|
1899
|
+
|
|
1900
|
+
// ../play-runtime/log-context.ts
|
|
1901
|
+
var utf8 = new TextEncoder();
|
|
1902
|
+
|
|
1903
|
+
// ../play-runtime/run-log-buffer.ts
|
|
1904
|
+
var RUNNER_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
1905
|
+
var RUNNER_LOG_CONTEXT_MAX_BYTES = 128 * 1024;
|
|
1906
|
+
var TERMINAL_RESERVE_BYTES = 64 * 1024;
|
|
1907
|
+
var encoder = new TextEncoder();
|
|
1908
|
+
|
|
1909
|
+
// ../play-runtime/secret-redaction.ts
|
|
1910
|
+
var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
|
|
1911
|
+
var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
1912
|
+
var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
1913
|
+
var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
1914
|
+
var EMAIL_LOCAL_CHAR_RE = /[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]/;
|
|
1915
|
+
var EMAIL_DOMAIN_RE = /^[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?\.[A-Za-z]{2,63}\b/;
|
|
1916
|
+
function isEmailLocalPart(value, start, end) {
|
|
1917
|
+
let localStart = start;
|
|
1918
|
+
while (localStart > 0 && EMAIL_LOCAL_CHAR_RE.test(value[localStart - 1])) {
|
|
1919
|
+
localStart -= 1;
|
|
2219
1920
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
1921
|
+
let localEnd = end;
|
|
1922
|
+
while (localEnd < value.length && EMAIL_LOCAL_CHAR_RE.test(value[localEnd])) {
|
|
1923
|
+
localEnd += 1;
|
|
1924
|
+
}
|
|
1925
|
+
return localStart < localEnd && localEnd - localStart <= 64 && value[localEnd] === "@" && EMAIL_DOMAIN_RE.test(value.slice(localEnd + 1));
|
|
2223
1926
|
}
|
|
2224
|
-
function
|
|
2225
|
-
return
|
|
1927
|
+
function redactTokenUnlessEmail(match, offset, source) {
|
|
1928
|
+
return isEmailLocalPart(source, offset, offset + match.length) ? match : SECRET_REDACTION_PLACEHOLDER;
|
|
2226
1929
|
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
1930
|
+
var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
1931
|
+
var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
|
|
1932
|
+
var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
|
1933
|
+
var GENERIC_PREFIXED_SECRET_RE = /\b(?:key|token|secret|api[_-]?key)_[A-Za-z0-9_./+=:-]{12,}\b/gi;
|
|
1934
|
+
var URL_SECRET_VALUE_RE = /\b(?:[spr]k|pat|ghp|github_pat|xox)[A-Za-z0-9_./+=:-]{12,}\b/i;
|
|
1935
|
+
var GENERIC_SECRET_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*["']?[^"',\s]{12,}["']?/gi;
|
|
1936
|
+
var HIGH_ENTROPY_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password|authorization|(?:access|refresh)[_-]?token)\b\s*[:=]\s*["']?[^"',\s]{16,}["']?/gi;
|
|
1937
|
+
var SENSITIVE_URL_PARAM_RE = /[?#&](?:\w*(?:key|token)|secret|password|authorization|credential|signature|sig)(?:=|$)/i;
|
|
1938
|
+
var SENSITIVE_FIELD_NAME_RE = new RegExp(
|
|
1939
|
+
`(^|[._-])(?:${SENSITIVE_FIELD_NAME})$`,
|
|
1940
|
+
"i"
|
|
1941
|
+
);
|
|
1942
|
+
var SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES = 4096;
|
|
1943
|
+
var sensitiveFieldNameCache = /* @__PURE__ */ new Map();
|
|
1944
|
+
function isSensitiveFieldName(key) {
|
|
1945
|
+
const cached = sensitiveFieldNameCache.get(key);
|
|
1946
|
+
if (cached !== void 0) return cached;
|
|
1947
|
+
const sensitive = SENSITIVE_FIELD_NAME_RE.test(key);
|
|
1948
|
+
if (sensitiveFieldNameCache.size >= SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES) {
|
|
1949
|
+
sensitiveFieldNameCache.clear();
|
|
2233
1950
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
1951
|
+
sensitiveFieldNameCache.set(key, sensitive);
|
|
1952
|
+
return sensitive;
|
|
2236
1953
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
return field;
|
|
2244
|
-
const alternatives = {
|
|
2245
|
-
step: "stepId",
|
|
2246
|
-
severity: "level",
|
|
2247
|
-
timestamp: "occurredAt",
|
|
2248
|
-
play: "playName",
|
|
2249
|
-
run: "runId",
|
|
2250
|
-
attempt: "producerAttempt"
|
|
2251
|
-
};
|
|
2252
|
-
fail(
|
|
2253
|
-
`Unsupported run event field: ${field}. Use ${alternatives[field] ?? "a catalog field or context.<key>"}; call fields() for supported fields.`
|
|
2254
|
-
);
|
|
1954
|
+
var SERIALIZED_SECRET_FIELD_RE = new RegExp(
|
|
1955
|
+
`"(${SENSITIVE_FIELD_NAME})"\\s*:\\s*(?:"[^"\\\\]*"|\\{[^{}]*\\})`,
|
|
1956
|
+
"gi"
|
|
1957
|
+
);
|
|
1958
|
+
function escapeRegExp(value) {
|
|
1959
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2255
1960
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
1961
|
+
var REDACTION_TRIGGER_RE = new RegExp(
|
|
1962
|
+
[
|
|
1963
|
+
// Sensitive field names, in object keys and in `name=`/`"name":` text.
|
|
1964
|
+
"token",
|
|
1965
|
+
"secret",
|
|
1966
|
+
"password",
|
|
1967
|
+
"authorization",
|
|
1968
|
+
"cookie",
|
|
1969
|
+
"credential",
|
|
1970
|
+
// One `key` branch instead of `api[_-]?key`, `private[_-]?key` and `key_`.
|
|
1971
|
+
// A regex alternation costs roughly one substring scan per branch, and a
|
|
1972
|
+
// broader branch can only send more payloads down the exact path.
|
|
1973
|
+
"key",
|
|
1974
|
+
// Self-identifying credential prefixes.
|
|
1975
|
+
"bearer",
|
|
1976
|
+
"basic",
|
|
1977
|
+
"-----BEGIN",
|
|
1978
|
+
"xox[baprs]-",
|
|
1979
|
+
"[prs]k_",
|
|
1980
|
+
"pat_",
|
|
1981
|
+
"ghp_",
|
|
1982
|
+
// URL credentials: `//user:pass@host` userinfo and sensitive query params.
|
|
1983
|
+
// The userinfo class excludes `/` exactly as the rule it guards does.
|
|
1984
|
+
"//[^\\s/]{0,256}@",
|
|
1985
|
+
"[?#&]sig",
|
|
1986
|
+
// JWTs carry no keyword at all, so the shape itself is the marker. Anchor
|
|
1987
|
+
// on the separating dot and drop JWT_RE's leading `{8,}` segment: a dot is
|
|
1988
|
+
// rare enough for the engine to skip between candidates, which makes this
|
|
1989
|
+
// gate roughly ten times cheaper than re-running JWT_RE itself, and
|
|
1990
|
+
// dropping a required segment can only widen what it accepts.
|
|
1991
|
+
"\\.[A-Za-z0-9_-]{1,16384}\\.[A-Za-z0-9_-]{8}"
|
|
1992
|
+
].join("|"),
|
|
1993
|
+
"i"
|
|
1994
|
+
);
|
|
1995
|
+
function mightContainRedactableSecret(value) {
|
|
1996
|
+
return REDACTION_TRIGGER_RE.test(value);
|
|
1997
|
+
}
|
|
1998
|
+
function redactSecretLikeString(value) {
|
|
1999
|
+
if (!mightContainRedactableSecret(value)) return value;
|
|
2000
|
+
const serializedRedacted = value.replace(
|
|
2001
|
+
SERIALIZED_SECRET_FIELD_RE,
|
|
2002
|
+
`"$1":"${SECRET_REDACTION_PLACEHOLDER}"`
|
|
2003
|
+
);
|
|
2004
|
+
const trimmed = serializedRedacted.trim();
|
|
2005
|
+
if (/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
2006
|
+
if (/^https?:\/\/[^/?#@]+@/i.test(trimmed) || SENSITIVE_URL_PARAM_RE.test(trimmed)) {
|
|
2007
|
+
return SECRET_REDACTION_PLACEHOLDER;
|
|
2008
|
+
}
|
|
2009
|
+
if (!URL_SECRET_VALUE_RE.test(serializedRedacted)) {
|
|
2010
|
+
return serializedRedacted;
|
|
2276
2011
|
}
|
|
2277
2012
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2013
|
+
return serializedRedacted.replace(PRIVATE_KEY_RE, SECRET_REDACTION_PLACEHOLDER).replace(BEARER_TOKEN_RE, `Bearer ${SECRET_REDACTION_PLACEHOLDER}`).replace(BASIC_CREDENTIAL_RE, `Basic ${SECRET_REDACTION_PLACEHOLDER}`).replace(JWT_RE, redactTokenUnlessEmail).replace(COMMON_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_PREFIXED_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_SECRET_ASSIGNMENT_RE, SECRET_REDACTION_PLACEHOLDER).replace(HIGH_ENTROPY_ASSIGNMENT_RE, (match) => {
|
|
2014
|
+
const separator = match.includes("=") ? "=" : ":";
|
|
2015
|
+
const [key] = match.split(separator, 1);
|
|
2016
|
+
return `${key.trim()}${separator}${SECRET_REDACTION_PLACEHOLDER}`;
|
|
2017
|
+
});
|
|
2281
2018
|
}
|
|
2282
|
-
function
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
if (
|
|
2287
|
-
fail("Run event exists requires a boolean.");
|
|
2288
|
-
return { field, op, value };
|
|
2289
|
-
}
|
|
2290
|
-
if (op === "in") {
|
|
2291
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 32)
|
|
2292
|
-
fail("Run event in requires between 1 and 32 scalars.");
|
|
2293
|
-
return { field, op, value: value.map((item) => scalar(field, item, "eq")) };
|
|
2019
|
+
function createSecretRedactionContext(initialValues = []) {
|
|
2020
|
+
const exactSecrets = /* @__PURE__ */ new Set();
|
|
2021
|
+
const exactSecretMatchers = /* @__PURE__ */ new Map();
|
|
2022
|
+
function register(value) {
|
|
2023
|
+
if (value.length > 0) exactSecrets.add(value);
|
|
2294
2024
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
if (!object(input) || !Object.keys(input).length)
|
|
2304
|
-
fail("Run event where requires a nonempty object.");
|
|
2305
|
-
const terms = [];
|
|
2306
|
-
const fieldTerms = (raw, filter) => {
|
|
2307
|
-
const field = validateRunEventField(raw);
|
|
2308
|
-
const operators = object(filter) ? Object.entries(filter) : [["eq", filter]];
|
|
2309
|
-
if (!operators.length)
|
|
2310
|
-
fail(`Run event ${field} has an empty operator object.`);
|
|
2311
|
-
for (const [op, value] of operators) {
|
|
2312
|
-
if (++budget.count > MAX_PREDICATES)
|
|
2313
|
-
fail("Run event where exceeds 64 predicates.");
|
|
2314
|
-
terms.push(comparison(field, op, value));
|
|
2025
|
+
function matchersForSecret(secret) {
|
|
2026
|
+
const cached = exactSecretMatchers.get(secret);
|
|
2027
|
+
if (cached) return cached;
|
|
2028
|
+
const literals = [secret];
|
|
2029
|
+
try {
|
|
2030
|
+
const encoded = encodeURIComponent(secret);
|
|
2031
|
+
if (encoded !== secret) literals.push(encoded);
|
|
2032
|
+
} catch {
|
|
2315
2033
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
(item) => admitObject(item, depth + 1, budget)
|
|
2323
|
-
);
|
|
2324
|
-
terms.push(key === "and" ? { and: children } : { or: children });
|
|
2325
|
-
} else if (key === "context") {
|
|
2326
|
-
if (!object(filter) || !Object.keys(filter).length)
|
|
2327
|
-
fail("Run event context requires flat scalar fields.");
|
|
2328
|
-
for (const [contextKey, value] of Object.entries(filter))
|
|
2329
|
-
fieldTerms(`context.${contextKey}`, value);
|
|
2330
|
-
} else fieldTerms(key, filter);
|
|
2034
|
+
const built = literals.map((literal) => ({
|
|
2035
|
+
literal,
|
|
2036
|
+
matcher: new RegExp(escapeRegExp(literal), "g")
|
|
2037
|
+
}));
|
|
2038
|
+
exactSecretMatchers.set(secret, built);
|
|
2039
|
+
return built;
|
|
2331
2040
|
}
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
for (const
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
if (!combined) return { and: predicate.and.map(compact) };
|
|
2347
|
-
context[name] = combined;
|
|
2348
|
-
} else context[name] = filter2;
|
|
2349
|
-
}
|
|
2350
|
-
merged.context = context;
|
|
2351
|
-
} else if (Object.prototype.hasOwnProperty.call(merged, key)) {
|
|
2352
|
-
const combined = key === "or" ? void 0 : combineFilters(merged[key], value);
|
|
2353
|
-
if (!combined) return { and: predicate.and.map(compact) };
|
|
2354
|
-
merged[key] = combined;
|
|
2355
|
-
} else merged[key] = value;
|
|
2041
|
+
for (const value of initialValues) register(value);
|
|
2042
|
+
function containsRegisteredSecret(value, {
|
|
2043
|
+
includeEncoded = false,
|
|
2044
|
+
minimumLength = 1
|
|
2045
|
+
} = {}) {
|
|
2046
|
+
for (const secret of exactSecrets) {
|
|
2047
|
+
if (secret.length < minimumLength) continue;
|
|
2048
|
+
if (value.includes(secret)) return true;
|
|
2049
|
+
if (includeEncoded) {
|
|
2050
|
+
try {
|
|
2051
|
+
const encoded = encodeURIComponent(secret);
|
|
2052
|
+
if (encoded !== secret && value.includes(encoded)) return true;
|
|
2053
|
+
} catch {
|
|
2054
|
+
}
|
|
2356
2055
|
}
|
|
2357
2056
|
}
|
|
2358
|
-
return
|
|
2057
|
+
return false;
|
|
2359
2058
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
return void 0;
|
|
2373
|
-
return { ...first, ...second };
|
|
2374
|
-
}
|
|
2375
|
-
function canonical(predicate) {
|
|
2376
|
-
if ("field" in predicate) return predicate;
|
|
2377
|
-
const operator = "and" in predicate ? "and" : "or";
|
|
2378
|
-
const source = "and" in predicate ? predicate.and : predicate.or;
|
|
2379
|
-
const terms = source.map(canonical).flatMap((term) => {
|
|
2380
|
-
if (operator === "and" && "and" in term) return term.and;
|
|
2381
|
-
if (operator === "or" && "or" in term) return term.or;
|
|
2382
|
-
return [term];
|
|
2383
|
-
});
|
|
2384
|
-
terms.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
2385
|
-
const unique = terms.filter(
|
|
2386
|
-
(term, i) => i === 0 || JSON.stringify(term) !== JSON.stringify(terms[i - 1])
|
|
2387
|
-
);
|
|
2388
|
-
if (unique.length === 1) return unique[0];
|
|
2389
|
-
return operator === "and" ? { and: unique } : { or: unique };
|
|
2390
|
-
}
|
|
2391
|
-
function parseDsl(input) {
|
|
2392
|
-
let position = 0;
|
|
2393
|
-
let count = 0;
|
|
2394
|
-
const skip = () => {
|
|
2395
|
-
while (/\s/.test(input[position] ?? "") && position < input.length)
|
|
2396
|
-
position++;
|
|
2397
|
-
};
|
|
2398
|
-
const take = (token) => {
|
|
2399
|
-
skip();
|
|
2400
|
-
if (input.startsWith(token, position)) {
|
|
2401
|
-
position += token.length;
|
|
2402
|
-
return true;
|
|
2059
|
+
function matchesRegisteredSecret(value) {
|
|
2060
|
+
return exactSecrets.has(value);
|
|
2061
|
+
}
|
|
2062
|
+
function redactRegisteredSecrets(value) {
|
|
2063
|
+
if (exactSecrets.size === 0) return value;
|
|
2064
|
+
let output = value;
|
|
2065
|
+
for (const secret of exactSecrets) {
|
|
2066
|
+
for (const { literal, matcher } of matchersForSecret(secret)) {
|
|
2067
|
+
if (!output.includes(literal)) continue;
|
|
2068
|
+
matcher.lastIndex = 0;
|
|
2069
|
+
output = output.replace(matcher, SECRET_REDACTION_PLACEHOLDER);
|
|
2070
|
+
}
|
|
2403
2071
|
}
|
|
2404
|
-
return
|
|
2405
|
-
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2072
|
+
return output;
|
|
2073
|
+
}
|
|
2074
|
+
function redactString(value) {
|
|
2075
|
+
const output = redactRegisteredSecrets(value);
|
|
2076
|
+
return redactSecretLikeString(output);
|
|
2077
|
+
}
|
|
2078
|
+
function redact(value) {
|
|
2079
|
+
if (typeof value === "string") return redactString(value);
|
|
2080
|
+
if (Array.isArray(value)) {
|
|
2081
|
+
let changed = false;
|
|
2082
|
+
const next = new Array(value.length);
|
|
2083
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
2084
|
+
const entry = value[index];
|
|
2085
|
+
const redacted = redact(entry);
|
|
2086
|
+
next[index] = redacted;
|
|
2087
|
+
if (redacted !== entry) changed = true;
|
|
2419
2088
|
}
|
|
2420
|
-
|
|
2421
|
-
} else {
|
|
2422
|
-
const match = /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/.exec(
|
|
2423
|
-
input.slice(position)
|
|
2424
|
-
);
|
|
2425
|
-
if (!match)
|
|
2426
|
-
fail("Run event DSL requires a JSON quoted string or scalar.");
|
|
2427
|
-
position += match[0].length;
|
|
2428
|
-
}
|
|
2429
|
-
try {
|
|
2430
|
-
return JSON.parse(input.slice(start, position));
|
|
2431
|
-
} catch {
|
|
2432
|
-
fail("Run event DSL requires valid JSON scalars.");
|
|
2433
|
-
}
|
|
2434
|
-
};
|
|
2435
|
-
const term = (depth) => {
|
|
2436
|
-
if (depth > MAX_DEPTH) fail("Run event where exceeds depth 8.");
|
|
2437
|
-
if (take("(")) {
|
|
2438
|
-
const nested = or(depth + 1);
|
|
2439
|
-
if (!take(")")) fail("Run event DSL is missing a closing parenthesis.");
|
|
2440
|
-
return nested;
|
|
2089
|
+
return changed ? next : value;
|
|
2441
2090
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
);
|
|
2455
|
-
if (!operation) {
|
|
2456
|
-
const token = /^(?:[=!<>~]+|[A-Za-z_]+)/.exec(input.slice(position))?.[0] ?? input[position] ?? "<end>";
|
|
2457
|
-
if (token === "=") {
|
|
2458
|
-
const corrected = `${input.slice(0, position)}==${input.slice(position + 1)}`;
|
|
2459
|
-
fail(
|
|
2460
|
-
`Run event DSL does not support "=" at character ${position + 1}. For equality, replace this "=" with "==": ${JSON.stringify(corrected)}.`
|
|
2461
|
-
);
|
|
2091
|
+
if (value && typeof value === "object") {
|
|
2092
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2093
|
+
let changed = prototype !== Object.prototype && prototype !== null;
|
|
2094
|
+
const source = value;
|
|
2095
|
+
const keys = Object.keys(source);
|
|
2096
|
+
const redactedValues = new Array(keys.length);
|
|
2097
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
2098
|
+
const key = keys[index];
|
|
2099
|
+
const entry = source[key];
|
|
2100
|
+
const redacted = isSensitiveFieldName(key) ? SECRET_REDACTION_PLACEHOLDER : redact(entry);
|
|
2101
|
+
redactedValues[index] = redacted;
|
|
2102
|
+
if (redacted !== entry) changed = true;
|
|
2462
2103
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2104
|
+
if (!changed) return value;
|
|
2105
|
+
return Object.fromEntries(
|
|
2106
|
+
keys.map((key, index) => [key, redactedValues[index]])
|
|
2465
2107
|
);
|
|
2466
2108
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
">": "gt",
|
|
2474
|
-
"<": "lt"
|
|
2475
|
-
};
|
|
2476
|
-
const op = symbols[operation[0]] ?? operation[0];
|
|
2477
|
-
if (op === "in") {
|
|
2478
|
-
if (!take("[")) fail("Run event DSL in requires a JSON scalar array.");
|
|
2479
|
-
const values = [];
|
|
2480
|
-
if (!take("]")) {
|
|
2481
|
-
do {
|
|
2482
|
-
values.push(json());
|
|
2483
|
-
if (values.length > 32)
|
|
2484
|
-
fail("Run event in requires between 1 and 32 scalars.");
|
|
2485
|
-
} while (take(","));
|
|
2486
|
-
if (!take("]")) fail("Run event DSL in requires a closing bracket.");
|
|
2487
|
-
}
|
|
2488
|
-
return comparison(field, op, values);
|
|
2109
|
+
return value;
|
|
2110
|
+
}
|
|
2111
|
+
function redactKnownSecrets(value) {
|
|
2112
|
+
if (typeof value === "string") return redactRegisteredSecrets(value);
|
|
2113
|
+
if (Array.isArray(value)) {
|
|
2114
|
+
return value.map((entry) => redactKnownSecrets(entry));
|
|
2489
2115
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2116
|
+
if (value && typeof value === "object") {
|
|
2117
|
+
return Object.fromEntries(
|
|
2118
|
+
Object.entries(value).map(([key, entry]) => [
|
|
2119
|
+
key,
|
|
2120
|
+
redactKnownSecrets(entry)
|
|
2121
|
+
])
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
return value;
|
|
2125
|
+
}
|
|
2126
|
+
return {
|
|
2127
|
+
register,
|
|
2128
|
+
containsRegisteredSecret,
|
|
2129
|
+
matchesRegisteredSecret,
|
|
2130
|
+
redactRegisteredSecrets,
|
|
2131
|
+
redactString,
|
|
2132
|
+
redactKnownSecrets,
|
|
2133
|
+
redact
|
|
2496
2134
|
};
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// ../play-runtime/output-size-limits.ts
|
|
2138
|
+
var TERMINAL_RUN_RESULT_MAX_BYTES = 6 * 1024 * 1024;
|
|
2139
|
+
var LEDGER_TERMINAL_RESULT_MAX_BYTES = 256 * 1024;
|
|
2140
|
+
var CUSTOMER_OUTPUT_VALUE_MAX_BYTES = 5 * 1024 * 1024;
|
|
2141
|
+
var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
|
|
2142
|
+
var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
|
|
2143
|
+
var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
|
|
2144
|
+
var RUNTIME_RECEIPT_CONTROL_BUFFER_MAX_BYTES = 2 * 1024 * 1024;
|
|
2145
|
+
var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
|
2146
|
+
var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
|
|
2147
|
+
var RUNTIME_RECEIPT_WRITER_TARGET_BATCH_BYTES = 8 * 1024 * 1024;
|
|
2148
|
+
var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
|
|
2149
|
+
var JSON_SIZE_LIMIT_REACHED = Symbol("JSON_SIZE_LIMIT_REACHED");
|
|
2150
|
+
|
|
2151
|
+
// ../play-runtime/ledger-safe-payload.ts
|
|
2152
|
+
var ledgerIngressRedactor = createSecretRedactionContext();
|
|
2153
|
+
|
|
2154
|
+
// ../play-runtime/docflow-node-io.ts
|
|
2155
|
+
var utf8Encoder = new TextEncoder();
|
|
2156
|
+
var PLAY_DATASET_BRAND = Symbol.for("deepline.play.dataset");
|
|
2157
|
+
|
|
2158
|
+
// ../play-data-plane/runtime-sheet-orm.ts
|
|
2159
|
+
var RUNTIME_SHEET_CELL_META_COLUMN = {
|
|
2160
|
+
name: "_cell_meta",
|
|
2161
|
+
dataType: "jsonb",
|
|
2162
|
+
nullable: "NO",
|
|
2163
|
+
defaultSql: "'{}'::jsonb"
|
|
2164
|
+
};
|
|
2165
|
+
var RUNTIME_SHEET_COLUMN_CONTRACTS = [
|
|
2166
|
+
{
|
|
2167
|
+
name: "_key",
|
|
2168
|
+
sqlType: "text",
|
|
2169
|
+
dataType: "text",
|
|
2170
|
+
nullable: "NO",
|
|
2171
|
+
defaultSql: null,
|
|
2172
|
+
repairable: false,
|
|
2173
|
+
requiresDefault: false
|
|
2174
|
+
},
|
|
2175
|
+
{
|
|
2176
|
+
name: "_status",
|
|
2177
|
+
sqlType: "text NOT NULL",
|
|
2178
|
+
dataType: "text",
|
|
2179
|
+
nullable: "NO",
|
|
2180
|
+
defaultSql: null,
|
|
2181
|
+
repairable: false,
|
|
2182
|
+
requiresDefault: false
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
name: "_run_id",
|
|
2186
|
+
sqlType: "text",
|
|
2187
|
+
dataType: "text",
|
|
2188
|
+
nullable: "YES",
|
|
2189
|
+
defaultSql: null,
|
|
2190
|
+
repairable: true,
|
|
2191
|
+
requiresDefault: false
|
|
2192
|
+
},
|
|
2193
|
+
{
|
|
2194
|
+
name: "_error",
|
|
2195
|
+
sqlType: "text",
|
|
2196
|
+
dataType: "text",
|
|
2197
|
+
nullable: "YES",
|
|
2198
|
+
defaultSql: null,
|
|
2199
|
+
repairable: true,
|
|
2200
|
+
requiresDefault: false
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
name: "_stage",
|
|
2204
|
+
sqlType: "text",
|
|
2205
|
+
dataType: "text",
|
|
2206
|
+
nullable: "YES",
|
|
2207
|
+
defaultSql: null,
|
|
2208
|
+
repairable: true,
|
|
2209
|
+
requiresDefault: false
|
|
2210
|
+
},
|
|
2211
|
+
{
|
|
2212
|
+
name: "_provider",
|
|
2213
|
+
sqlType: "text",
|
|
2214
|
+
dataType: "text",
|
|
2215
|
+
nullable: "YES",
|
|
2216
|
+
defaultSql: null,
|
|
2217
|
+
repairable: true,
|
|
2218
|
+
requiresDefault: false
|
|
2219
|
+
},
|
|
2220
|
+
{
|
|
2221
|
+
name: "_input_index",
|
|
2222
|
+
sqlType: "bigint",
|
|
2223
|
+
dataType: "bigint",
|
|
2224
|
+
nullable: "YES",
|
|
2225
|
+
defaultSql: null,
|
|
2226
|
+
repairable: true,
|
|
2227
|
+
requiresDefault: false
|
|
2228
|
+
},
|
|
2229
|
+
{
|
|
2230
|
+
name: "_attempt_id",
|
|
2231
|
+
sqlType: "text",
|
|
2232
|
+
dataType: "text",
|
|
2233
|
+
nullable: "YES",
|
|
2234
|
+
defaultSql: null,
|
|
2235
|
+
repairable: true,
|
|
2236
|
+
requiresDefault: false
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
name: "_attempt_owner_run_id",
|
|
2240
|
+
sqlType: "text",
|
|
2241
|
+
dataType: "text",
|
|
2242
|
+
nullable: "YES",
|
|
2243
|
+
defaultSql: null,
|
|
2244
|
+
repairable: true,
|
|
2245
|
+
requiresDefault: false
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
name: "_attempt_expires_at",
|
|
2249
|
+
sqlType: "timestamptz",
|
|
2250
|
+
dataType: "timestamp with time zone",
|
|
2251
|
+
nullable: "YES",
|
|
2252
|
+
defaultSql: null,
|
|
2253
|
+
repairable: true,
|
|
2254
|
+
requiresDefault: false
|
|
2255
|
+
},
|
|
2256
|
+
{
|
|
2257
|
+
name: "_attempt_seq",
|
|
2258
|
+
sqlType: "integer",
|
|
2259
|
+
dataType: "integer",
|
|
2260
|
+
nullable: "YES",
|
|
2261
|
+
defaultSql: null,
|
|
2262
|
+
repairable: true,
|
|
2263
|
+
requiresDefault: false
|
|
2264
|
+
},
|
|
2265
|
+
{
|
|
2266
|
+
name: "_write_version",
|
|
2267
|
+
sqlType: "bigint NOT NULL DEFAULT 0",
|
|
2268
|
+
dataType: "bigint",
|
|
2269
|
+
nullable: "NO",
|
|
2270
|
+
defaultSql: "0",
|
|
2271
|
+
repairable: true,
|
|
2272
|
+
requiresDefault: true
|
|
2273
|
+
},
|
|
2274
|
+
{
|
|
2275
|
+
name: "_writer_run_id",
|
|
2276
|
+
sqlType: "text",
|
|
2277
|
+
dataType: "text",
|
|
2278
|
+
nullable: "YES",
|
|
2279
|
+
defaultSql: null,
|
|
2280
|
+
repairable: true,
|
|
2281
|
+
requiresDefault: false
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "_created_at",
|
|
2285
|
+
sqlType: "timestamptz NOT NULL DEFAULT now()",
|
|
2286
|
+
dataType: "timestamp with time zone",
|
|
2287
|
+
nullable: "NO",
|
|
2288
|
+
defaultSql: "now()",
|
|
2289
|
+
repairable: true,
|
|
2290
|
+
requiresDefault: true
|
|
2291
|
+
},
|
|
2292
|
+
{
|
|
2293
|
+
name: "_updated_at",
|
|
2294
|
+
sqlType: "timestamptz NOT NULL DEFAULT now()",
|
|
2295
|
+
dataType: "timestamp with time zone",
|
|
2296
|
+
nullable: "NO",
|
|
2297
|
+
defaultSql: "now()",
|
|
2298
|
+
repairable: true,
|
|
2299
|
+
requiresDefault: true
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: "_version",
|
|
2303
|
+
sqlType: "bigint NOT NULL",
|
|
2304
|
+
dataType: "bigint",
|
|
2305
|
+
nullable: "NO",
|
|
2306
|
+
defaultSql: null,
|
|
2307
|
+
defaultKind: "version_sequence",
|
|
2308
|
+
repairable: true,
|
|
2309
|
+
requiresDefault: true
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
...RUNTIME_SHEET_CELL_META_COLUMN,
|
|
2313
|
+
sqlType: `jsonb NOT NULL DEFAULT ${RUNTIME_SHEET_CELL_META_COLUMN.defaultSql}`,
|
|
2314
|
+
repairable: true,
|
|
2315
|
+
requiresDefault: true
|
|
2316
|
+
}
|
|
2317
|
+
];
|
|
2318
|
+
var RUNTIME_SHEET_ATTEMPT_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
2319
|
+
(column) => column.name.startsWith("_attempt_")
|
|
2320
|
+
).map((column) => column.name);
|
|
2321
|
+
var RUNTIME_SHEET_WRITE_VERSION_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
2322
|
+
(column) => column.name === "_write_version" || column.name === "_writer_run_id"
|
|
2323
|
+
).map((column) => column.name);
|
|
2324
|
+
var RUNTIME_SHEET_TIMESTAMP_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
2325
|
+
(column) => column.name === "_created_at" || column.name === "_updated_at"
|
|
2326
|
+
).map((column) => column.name);
|
|
2327
|
+
var RUNTIME_SHEET_SYSTEM_COLUMNS = new Set(
|
|
2328
|
+
RUNTIME_SHEET_COLUMN_CONTRACTS.map((column) => column.name)
|
|
2329
|
+
);
|
|
2330
|
+
var RUNTIME_SHEET_REPAIRABLE_COLUMNS = RUNTIME_SHEET_COLUMN_CONTRACTS.filter(
|
|
2331
|
+
(column) => column.repairable
|
|
2332
|
+
);
|
|
2333
|
+
|
|
2334
|
+
// ../play-runtime/run-ledger.ts
|
|
2335
|
+
var TERMINAL_STATUS_BY_EVENT_TYPE = {
|
|
2336
|
+
[RUN_EVENT_KIND.runCompleted]: "completed",
|
|
2337
|
+
[RUN_EVENT_KIND.runFailed]: "failed",
|
|
2338
|
+
[RUN_EVENT_KIND.runCancelled]: "cancelled"
|
|
2339
|
+
};
|
|
2340
|
+
|
|
2341
|
+
// ../play-runtime/run-snapshot-stream.ts
|
|
2342
|
+
function isTerminalPlayRunLiveStatus(status) {
|
|
2343
|
+
return isTerminalPlayRunLifecycleStatus(status);
|
|
2344
|
+
}
|
|
2345
|
+
function makeRunStreamEvent(input) {
|
|
2346
|
+
return {
|
|
2347
|
+
...input,
|
|
2348
|
+
scope: "play",
|
|
2349
|
+
at: input.at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
2501
2350
|
};
|
|
2502
|
-
const result = or(0);
|
|
2503
|
-
skip();
|
|
2504
|
-
if (position !== input.length)
|
|
2505
|
-
fail(`Run event DSL has unsupported input at position ${position}.`);
|
|
2506
|
-
return result;
|
|
2507
2351
|
}
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2352
|
+
var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
|
|
2353
|
+
runSignature: "",
|
|
2354
|
+
snapshotSignature: "",
|
|
2355
|
+
stepStatusSignature: "",
|
|
2356
|
+
stepProgressSignature: "",
|
|
2357
|
+
lastLogSeq: 0
|
|
2358
|
+
};
|
|
2359
|
+
function getSnapshotCursor(snapshot) {
|
|
2360
|
+
return String(snapshot.updatedAt ?? Date.now());
|
|
2361
|
+
}
|
|
2362
|
+
function getRunSignature(snapshot) {
|
|
2363
|
+
return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
|
|
2364
|
+
}
|
|
2365
|
+
function getStepStatusSignature(snapshot) {
|
|
2366
|
+
return snapshot.nodeStates.map(
|
|
2367
|
+
(state) => [
|
|
2368
|
+
state.nodeId,
|
|
2369
|
+
state.status,
|
|
2370
|
+
state.artifactTableNamespace ?? "",
|
|
2371
|
+
state.startedAt ?? "",
|
|
2372
|
+
state.completedAt ?? "",
|
|
2373
|
+
state.progress?.startedAt ?? "",
|
|
2374
|
+
state.progress?.completedAt ?? "",
|
|
2375
|
+
state.updatedAt ?? "",
|
|
2376
|
+
state.progress?.updatedAt ?? ""
|
|
2377
|
+
].join(":")
|
|
2378
|
+
).join("|");
|
|
2379
|
+
}
|
|
2380
|
+
function getStepProgressSignature(snapshot) {
|
|
2381
|
+
return JSON.stringify(
|
|
2382
|
+
snapshot.nodeStates.map((state) => [
|
|
2383
|
+
state.nodeId,
|
|
2384
|
+
projectPlayStepProgressCounters(state.progress ?? {}),
|
|
2385
|
+
state.progress?.artifactTableNamespace ?? "",
|
|
2386
|
+
state.progress?.startedAt ?? "",
|
|
2387
|
+
state.progress?.completedAt ?? "",
|
|
2388
|
+
state.progress?.updatedAt ?? "",
|
|
2389
|
+
state.progress?.message ?? ""
|
|
2390
|
+
])
|
|
2512
2391
|
);
|
|
2513
|
-
bounded(where);
|
|
2514
|
-
return where;
|
|
2515
2392
|
}
|
|
2516
|
-
function
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
if (!timestamp)
|
|
2523
|
-
fail(
|
|
2524
|
-
"Run event time bounds require an ISO timestamp with a timezone and at most six fractional digits."
|
|
2525
|
-
);
|
|
2526
|
-
extraMicros = (timestamp[1] ?? "").padEnd(6, "0").slice(3);
|
|
2393
|
+
function getSnapshotSignature(snapshot) {
|
|
2394
|
+
return JSON.stringify(snapshot);
|
|
2395
|
+
}
|
|
2396
|
+
function resolvePlayRunLogGap(snapshot, lastLogSeq) {
|
|
2397
|
+
if (snapshot.totalLogCount <= lastLogSeq) {
|
|
2398
|
+
return null;
|
|
2527
2399
|
}
|
|
2528
|
-
const
|
|
2529
|
-
if (
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
return
|
|
2400
|
+
const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
|
|
2401
|
+
if (lastLogSeq + 1 >= tailFirstSeq) {
|
|
2402
|
+
return null;
|
|
2403
|
+
}
|
|
2404
|
+
return {
|
|
2405
|
+
missingCount: tailFirstSeq - 1 - lastLogSeq,
|
|
2406
|
+
tailFirstSeq
|
|
2407
|
+
};
|
|
2533
2408
|
}
|
|
2534
|
-
function
|
|
2535
|
-
const
|
|
2536
|
-
|
|
2409
|
+
function diffLogLines(input) {
|
|
2410
|
+
const { logs, totalLogCount } = input.snapshot;
|
|
2411
|
+
if (input.snapshot.logsUnavailable) {
|
|
2412
|
+
return {
|
|
2413
|
+
lines: [],
|
|
2414
|
+
lastLogSeq: input.lastLogSeq,
|
|
2415
|
+
firstSeq: null
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
if (totalLogCount <= input.lastLogSeq) {
|
|
2419
|
+
return { lines: [], lastLogSeq: input.lastLogSeq, firstSeq: null };
|
|
2420
|
+
}
|
|
2421
|
+
const tailFirstSeq = totalLogCount - logs.length + 1;
|
|
2422
|
+
if (input.lastLogSeq + 1 >= tailFirstSeq) {
|
|
2423
|
+
return {
|
|
2424
|
+
lines: logs.slice(input.lastLogSeq + 1 - tailFirstSeq),
|
|
2425
|
+
lastLogSeq: totalLogCount,
|
|
2426
|
+
firstSeq: input.lastLogSeq + 1
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
const missingCount = tailFirstSeq - 1 - input.lastLogSeq;
|
|
2430
|
+
return {
|
|
2431
|
+
lines: [
|
|
2432
|
+
`[watch] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
|
|
2433
|
+
...logs
|
|
2434
|
+
],
|
|
2435
|
+
lastLogSeq: totalLogCount,
|
|
2436
|
+
firstSeq: null
|
|
2437
|
+
};
|
|
2537
2438
|
}
|
|
2538
|
-
function
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2439
|
+
function diffPlayRunStreamEvents(input) {
|
|
2440
|
+
const { snapshot, streamId, previous } = input;
|
|
2441
|
+
const cursor = getSnapshotCursor(snapshot);
|
|
2442
|
+
const logDiff = diffLogLines({
|
|
2443
|
+
snapshot,
|
|
2444
|
+
lastLogSeq: previous.lastLogSeq
|
|
2445
|
+
});
|
|
2446
|
+
const next = {
|
|
2447
|
+
runSignature: getRunSignature(snapshot),
|
|
2448
|
+
stepStatusSignature: getStepStatusSignature(snapshot),
|
|
2449
|
+
stepProgressSignature: getStepProgressSignature(snapshot),
|
|
2450
|
+
snapshotSignature: getSnapshotSignature(snapshot),
|
|
2451
|
+
lastLogSeq: logDiff.lastLogSeq
|
|
2452
|
+
};
|
|
2453
|
+
const events = [];
|
|
2454
|
+
if (next.stepStatusSignature !== previous.stepStatusSignature) {
|
|
2455
|
+
for (const state of snapshot.nodeStates) {
|
|
2456
|
+
if (state.status === "idle") {
|
|
2457
|
+
continue;
|
|
2458
|
+
}
|
|
2459
|
+
const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
|
|
2460
|
+
const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
|
|
2461
|
+
events.push(
|
|
2462
|
+
makeRunStreamEvent({
|
|
2463
|
+
cursor,
|
|
2464
|
+
streamId,
|
|
2465
|
+
type: "play.step.status",
|
|
2466
|
+
payload: {
|
|
2467
|
+
runId: snapshot.runId,
|
|
2468
|
+
stepId: state.nodeId,
|
|
2469
|
+
status: state.status,
|
|
2470
|
+
artifactTableNamespace: state.artifactTableNamespace ?? null,
|
|
2471
|
+
...resolveTimingWindow({
|
|
2472
|
+
startedAt: persistedStartedAt,
|
|
2473
|
+
completedAt: persistedCompletedAt,
|
|
2474
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
2475
|
+
})
|
|
2476
|
+
}
|
|
2477
|
+
})
|
|
2478
|
+
);
|
|
2555
2479
|
}
|
|
2556
|
-
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2480
|
+
}
|
|
2481
|
+
if (next.stepProgressSignature !== previous.stepProgressSignature) {
|
|
2482
|
+
for (const state of snapshot.nodeStates) {
|
|
2483
|
+
if (!state.progress) {
|
|
2484
|
+
continue;
|
|
2485
|
+
}
|
|
2486
|
+
events.push(
|
|
2487
|
+
makeRunStreamEvent({
|
|
2488
|
+
cursor: String(
|
|
2489
|
+
state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
|
|
2490
|
+
),
|
|
2491
|
+
streamId,
|
|
2492
|
+
type: "play.step.progress",
|
|
2493
|
+
payload: {
|
|
2494
|
+
runId: snapshot.runId,
|
|
2495
|
+
stepId: state.nodeId,
|
|
2496
|
+
...projectPlayStepProgressCounters(state.progress),
|
|
2497
|
+
message: state.progress.message,
|
|
2498
|
+
artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
|
|
2499
|
+
...resolveTimingWindow({
|
|
2500
|
+
startedAt: state.startedAt ?? state.progress.startedAt ?? null,
|
|
2501
|
+
completedAt: state.completedAt ?? state.progress.completedAt ?? null,
|
|
2502
|
+
updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
|
|
2503
|
+
})
|
|
2504
|
+
}
|
|
2505
|
+
})
|
|
2506
|
+
);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
if (logDiff.lines.length > 0) {
|
|
2510
|
+
events.push(
|
|
2511
|
+
makeRunStreamEvent({
|
|
2512
|
+
cursor,
|
|
2513
|
+
streamId,
|
|
2514
|
+
type: "play.run.log",
|
|
2515
|
+
payload: {
|
|
2516
|
+
runId: snapshot.runId,
|
|
2517
|
+
lines: logDiff.lines,
|
|
2518
|
+
source: "worker",
|
|
2519
|
+
...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
|
|
2520
|
+
totalLogCount: snapshot.totalLogCount
|
|
2521
|
+
}
|
|
2522
|
+
})
|
|
2563
2523
|
);
|
|
2564
|
-
return value;
|
|
2565
|
-
}
|
|
2566
|
-
var CRITERIA_KEYS = [
|
|
2567
|
-
"since",
|
|
2568
|
-
"until",
|
|
2569
|
-
"where",
|
|
2570
|
-
"runId",
|
|
2571
|
-
"stepId",
|
|
2572
|
-
"minLevel",
|
|
2573
|
-
"text",
|
|
2574
|
-
"playName",
|
|
2575
|
-
"kind"
|
|
2576
|
-
];
|
|
2577
|
-
function normalizeRunEventQuery(criteria, nowMs = Date.now()) {
|
|
2578
|
-
if (!object(criteria)) fail("Run event criteria requires an object.");
|
|
2579
|
-
bounded(criteria, RUN_EVENT_QUERY_MAX_REQUEST_BYTES);
|
|
2580
|
-
for (const key of Object.keys(criteria)) {
|
|
2581
|
-
if (!CRITERIA_KEYS.includes(key))
|
|
2582
|
-
fail(`Unsupported run event criterion: ${key}.`);
|
|
2583
2524
|
}
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2525
|
+
if (next.snapshotSignature !== previous.snapshotSignature) {
|
|
2526
|
+
const enrichedNodeStates = snapshot.nodeStates.map((state) => {
|
|
2527
|
+
const timing = resolveTimingWindow({
|
|
2528
|
+
startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
|
|
2529
|
+
completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
|
|
2530
|
+
updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
|
|
2531
|
+
});
|
|
2532
|
+
return {
|
|
2533
|
+
...state,
|
|
2534
|
+
...timing,
|
|
2535
|
+
progress: state.progress ? {
|
|
2536
|
+
...state.progress,
|
|
2537
|
+
...resolveTimingWindow({
|
|
2538
|
+
startedAt: state.progress.startedAt ?? state.startedAt ?? null,
|
|
2539
|
+
completedAt: state.progress.completedAt ?? state.completedAt ?? null,
|
|
2540
|
+
updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
|
|
2541
|
+
})
|
|
2542
|
+
} : state.progress
|
|
2543
|
+
};
|
|
2544
|
+
});
|
|
2545
|
+
events.push(
|
|
2546
|
+
makeRunStreamEvent({
|
|
2547
|
+
cursor,
|
|
2548
|
+
streamId,
|
|
2549
|
+
type: "play.run.snapshot",
|
|
2550
|
+
payload: { ...snapshot, nodeStates: enrichedNodeStates }
|
|
2551
|
+
})
|
|
2552
|
+
);
|
|
2593
2553
|
}
|
|
2594
|
-
if (
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2554
|
+
if (next.runSignature !== previous.runSignature) {
|
|
2555
|
+
events.push(
|
|
2556
|
+
makeRunStreamEvent({
|
|
2557
|
+
cursor,
|
|
2558
|
+
streamId,
|
|
2559
|
+
type: "play.run.status",
|
|
2560
|
+
payload: {
|
|
2561
|
+
runId: snapshot.runId,
|
|
2562
|
+
status: snapshot.status,
|
|
2563
|
+
updatedAt: snapshot.updatedAt,
|
|
2564
|
+
...snapshot.activity ? { activity: snapshot.activity } : {}
|
|
2565
|
+
}
|
|
2566
|
+
})
|
|
2567
|
+
);
|
|
2598
2568
|
}
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2569
|
+
return { events, next };
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
// src/runs/watch-polling.ts
|
|
2573
|
+
var RUN_WATCH_POLL_INTERVAL_MS = 2e3;
|
|
2574
|
+
function waitForNextPoll(ms, signal) {
|
|
2575
|
+
if (signal?.aborted) {
|
|
2576
|
+
return Promise.resolve(false);
|
|
2603
2577
|
}
|
|
2604
|
-
return
|
|
2578
|
+
return new Promise((resolve) => {
|
|
2579
|
+
const finish = (elapsed) => {
|
|
2580
|
+
clearTimeout(timeout);
|
|
2581
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2582
|
+
resolve(elapsed);
|
|
2583
|
+
};
|
|
2584
|
+
const onAbort = () => finish(false);
|
|
2585
|
+
const timeout = setTimeout(() => finish(true), ms);
|
|
2586
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2587
|
+
});
|
|
2605
2588
|
}
|
|
2606
|
-
function
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
const operation = input.operation;
|
|
2610
|
-
if (!["list", "count", "summarize", "fields"].includes(String(operation)))
|
|
2611
|
-
fail("Run event operation requires list, count, summarize or fields.");
|
|
2612
|
-
if (input.resource !== "logs" && input.resource !== "events")
|
|
2613
|
-
fail("Run event resource requires logs or events.");
|
|
2614
|
-
const allowed = [
|
|
2615
|
-
"operation",
|
|
2616
|
-
"resource",
|
|
2617
|
-
"runId",
|
|
2618
|
-
...operation === "fields" ? [] : CRITERIA_KEYS,
|
|
2619
|
-
...operation === "list" ? ["limit", "cursor", "includePayloads"] : operation === "summarize" ? ["groupBy", "measures", "limit", "cursor"] : []
|
|
2620
|
-
];
|
|
2621
|
-
for (const key of Object.keys(input)) {
|
|
2622
|
-
if (!allowed.includes(key))
|
|
2623
|
-
fail(`Run event ${operation} does not support ${key}.`);
|
|
2589
|
+
function readSnapshotResponse(response, runId) {
|
|
2590
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
2591
|
+
throw new Error("Run watch returned an invalid snapshot response.");
|
|
2624
2592
|
}
|
|
2625
|
-
const
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
if (input.resource === "logs" && !base.runId)
|
|
2632
|
-
fail("Run event logs require runId.");
|
|
2633
|
-
if (input.cursor !== void 0 && (typeof input.cursor !== "string" || !input.cursor.length || input.cursor.length > RUN_EVENT_QUERY_MAX_CURSOR_BYTES))
|
|
2634
|
-
fail("Run event cursor is invalid.");
|
|
2635
|
-
const rawCriteria = Object.fromEntries(
|
|
2636
|
-
Object.entries(input).filter(
|
|
2637
|
-
([key]) => CRITERIA_KEYS.includes(key) && (key !== "runId" || input.resource === "events")
|
|
2638
|
-
)
|
|
2639
|
-
);
|
|
2640
|
-
const query = normalizeRunEventQuery(
|
|
2641
|
-
input.cursor === void 0 ? rawCriteria : { ...rawCriteria, since: nowMs - 36e5, until: nowMs },
|
|
2642
|
-
nowMs
|
|
2643
|
-
);
|
|
2644
|
-
const criteria = { ...query };
|
|
2645
|
-
if (input.cursor !== void 0) {
|
|
2646
|
-
if (input.since === void 0) delete criteria.since;
|
|
2647
|
-
else criteria.since = time(input.since, nowMs, nowMs - 36e5);
|
|
2648
|
-
if (input.until === void 0) delete criteria.until;
|
|
2649
|
-
else criteria.until = time(input.until, nowMs, nowMs);
|
|
2593
|
+
const record = response;
|
|
2594
|
+
const snapshot = record.snapshot;
|
|
2595
|
+
if (record.runId !== runId || !snapshot || typeof snapshot !== "object" || Array.isArray(snapshot) || snapshot.runId !== runId || typeof snapshot.status !== "string" || !Array.isArray(snapshot.logs) || !snapshot.logs.every(
|
|
2596
|
+
(line) => typeof line === "string"
|
|
2597
|
+
) || typeof snapshot.totalLogCount !== "number" || !Array.isArray(snapshot.nodeStates)) {
|
|
2598
|
+
throw new Error(`Run watch returned an invalid snapshot for ${runId}.`);
|
|
2650
2599
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
if (!
|
|
2655
|
-
|
|
2656
|
-
const page = {
|
|
2657
|
-
limit,
|
|
2658
|
-
...input.cursor === void 0 ? {} : { cursor: input.cursor }
|
|
2659
|
-
};
|
|
2660
|
-
if (operation === "list") {
|
|
2661
|
-
if (input.includePayloads !== void 0 && typeof input.includePayloads !== "boolean")
|
|
2662
|
-
fail("Run event includePayloads requires a boolean.");
|
|
2663
|
-
if (input.includePayloads === true && limit > RUN_EVENT_QUERY_MAX_PAYLOAD_LIMIT)
|
|
2664
|
-
fail("Run event payload pages cannot exceed 10 rows.");
|
|
2665
|
-
return {
|
|
2666
|
-
...base,
|
|
2667
|
-
...criteria,
|
|
2668
|
-
...page,
|
|
2669
|
-
...input.includePayloads === void 0 ? {} : { includePayloads: input.includePayloads }
|
|
2670
|
-
};
|
|
2600
|
+
return { runId, snapshot };
|
|
2601
|
+
}
|
|
2602
|
+
async function backfillRotatedLogTail(input) {
|
|
2603
|
+
if (!input.previous.runSignature || input.snapshot.logsUnavailable) {
|
|
2604
|
+
return input.snapshot;
|
|
2671
2605
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2606
|
+
const gap = resolvePlayRunLogGap(input.snapshot, input.previous.lastLogSeq);
|
|
2607
|
+
if (!gap) {
|
|
2608
|
+
return input.snapshot;
|
|
2609
|
+
}
|
|
2610
|
+
const lines = [];
|
|
2611
|
+
let cursor = input.previous.lastLogSeq;
|
|
2612
|
+
const targetSeq = gap.tailFirstSeq - 1;
|
|
2613
|
+
while (cursor < targetSeq) {
|
|
2614
|
+
const limit = Math.min(1e3, targetSeq - cursor);
|
|
2615
|
+
const page = await input.http.get(
|
|
2616
|
+
`/api/v2/runs/${encodeURIComponent(input.runId)}/logs?afterSeq=${cursor}&limit=${limit}`,
|
|
2617
|
+
{ signal: input.signal }
|
|
2618
|
+
);
|
|
2619
|
+
if (!page || !Array.isArray(page.entries) || page.entries.length === 0) {
|
|
2620
|
+
return input.snapshot;
|
|
2687
2621
|
}
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
...page,
|
|
2701
|
-
groupBy,
|
|
2702
|
-
measures
|
|
2703
|
-
};
|
|
2622
|
+
for (const entry of page.entries) {
|
|
2623
|
+
if (!entry || entry.seq !== cursor + 1 || typeof entry.line !== "string") {
|
|
2624
|
+
return input.snapshot;
|
|
2625
|
+
}
|
|
2626
|
+
lines.push(entry.line);
|
|
2627
|
+
cursor = entry.seq;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
if (cursor !== targetSeq) {
|
|
2631
|
+
return input.snapshot;
|
|
2632
|
+
}
|
|
2633
|
+
return { ...input.snapshot, logs: [...lines, ...input.snapshot.logs] };
|
|
2704
2634
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2635
|
+
async function* watchRunEvents(options) {
|
|
2636
|
+
const { http, runId, signal } = options;
|
|
2637
|
+
const path = `/api/v2/runs/${encodeURIComponent(runId)}?watchSnapshot=true`;
|
|
2638
|
+
const streamId = `play-run-watch:${runId}`;
|
|
2639
|
+
const pollIntervalMs = options.pollIntervalMs ?? RUN_WATCH_POLL_INTERVAL_MS;
|
|
2640
|
+
let previous = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
|
|
2641
|
+
let consecutiveFailures = 0;
|
|
2642
|
+
let outageNoticeSent = false;
|
|
2643
|
+
while (!signal?.aborted) {
|
|
2644
|
+
const pollStartedAt = Date.now();
|
|
2645
|
+
let snapshot;
|
|
2646
|
+
try {
|
|
2647
|
+
const response = await http.get(path, { signal });
|
|
2648
|
+
snapshot = await backfillRotatedLogTail({
|
|
2649
|
+
http,
|
|
2650
|
+
runId,
|
|
2651
|
+
snapshot: readSnapshotResponse(response, runId).snapshot,
|
|
2652
|
+
previous,
|
|
2653
|
+
signal
|
|
2654
|
+
});
|
|
2655
|
+
} catch (error) {
|
|
2656
|
+
if (signal?.aborted) {
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
if (!isTransientPlayRunReadError(error)) {
|
|
2660
|
+
throw error;
|
|
2661
|
+
}
|
|
2662
|
+
consecutiveFailures += 1;
|
|
2663
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2664
|
+
if (!outageNoticeSent) {
|
|
2665
|
+
options.onRetry?.({
|
|
2666
|
+
attempt: consecutiveFailures,
|
|
2667
|
+
delayMs: pollIntervalMs,
|
|
2668
|
+
reason
|
|
2669
|
+
});
|
|
2670
|
+
options.onNotice?.(
|
|
2671
|
+
`[watch] run snapshot request failed; retrying every ${pollIntervalMs / 1e3}s (${reason})`
|
|
2672
|
+
);
|
|
2673
|
+
outageNoticeSent = true;
|
|
2674
|
+
}
|
|
2675
|
+
const remaining2 = Math.max(
|
|
2676
|
+
0,
|
|
2677
|
+
pollIntervalMs - (Date.now() - pollStartedAt)
|
|
2678
|
+
);
|
|
2679
|
+
if (!await waitForNextPoll(remaining2, signal)) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
if (outageNoticeSent) {
|
|
2685
|
+
options.onNotice?.("[watch] run snapshot polling recovered");
|
|
2686
|
+
outageNoticeSent = false;
|
|
2687
|
+
}
|
|
2688
|
+
consecutiveFailures = 0;
|
|
2689
|
+
const diff = diffPlayRunStreamEvents({
|
|
2690
|
+
streamId,
|
|
2691
|
+
snapshot,
|
|
2692
|
+
previous
|
|
2693
|
+
});
|
|
2694
|
+
previous = diff.next;
|
|
2695
|
+
for (const event of diff.events) {
|
|
2696
|
+
yield event;
|
|
2697
|
+
}
|
|
2698
|
+
if (isTerminalPlayRunLiveStatus(snapshot.status)) {
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
const remaining = Math.max(
|
|
2702
|
+
0,
|
|
2703
|
+
pollIntervalMs - (Date.now() - pollStartedAt)
|
|
2704
|
+
);
|
|
2705
|
+
if (!await waitForNextPoll(remaining, signal)) {
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
2709
|
}
|
|
2710
2710
|
|
|
2711
2711
|
// ../play-runtime/runtime-environment.ts
|