deepline 0.1.101 → 0.1.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +433 -20
- package/dist/cli/index.mjs +436 -22
- package/dist/index.js +68 -3
- package/dist/index.mjs +68 -3
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +209 -68
- package/dist/repo/apps/play-runner-workers/src/entry.ts +141 -33
- package/dist/repo/sdk/src/http.ts +89 -0
- package/dist/repo/sdk/src/release.ts +6 -2
- package/dist/repo/sdk/src/stream-reconnect.ts +1 -1
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +204 -0
- package/dist/repo/shared_libs/play-runtime/run-ledger.ts +7 -2
- package/dist/repo/shared_libs/play-runtime/run-snapshot-stream.ts +8 -0
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -210,10 +210,14 @@ var SDK_RELEASE = {
|
|
|
210
210
|
// 0.1.98 ships the duplicate-browser-tab fix (default-browser detection).
|
|
211
211
|
// 0.1.99 ships prebuilt job-change source-column preservation and validation fixes.
|
|
212
212
|
// 0.1.101 ships retryable play artifact publish failures and CI retry hardening.
|
|
213
|
-
|
|
213
|
+
// 0.1.102 ships the job-change ledger fixes: recovered-dataset export on
|
|
214
|
+
// failed runs, persisted/succeeded/failed row counts, strict local CSV
|
|
215
|
+
// preflight (existence, data rows, quotes, duplicate headers), HTML error
|
|
216
|
+
// scrubbing, and word-boundary watch truncation.
|
|
217
|
+
version: "0.1.102",
|
|
214
218
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
215
219
|
supportPolicy: {
|
|
216
|
-
latest: "0.1.
|
|
220
|
+
latest: "0.1.102",
|
|
217
221
|
minimumSupported: "0.1.53",
|
|
218
222
|
deprecatedBelow: "0.1.53"
|
|
219
223
|
}
|
|
@@ -364,6 +368,22 @@ var HttpClient = class {
|
|
|
364
368
|
parsed = body;
|
|
365
369
|
}
|
|
366
370
|
if (!response.ok) {
|
|
371
|
+
const htmlError = detectHtmlErrorBody(
|
|
372
|
+
body,
|
|
373
|
+
response.headers.get("content-type")
|
|
374
|
+
);
|
|
375
|
+
if (htmlError) {
|
|
376
|
+
throw new DeeplineError(
|
|
377
|
+
htmlError.message(response.status),
|
|
378
|
+
response.status,
|
|
379
|
+
"API_ERROR",
|
|
380
|
+
{
|
|
381
|
+
htmlErrorPage: true,
|
|
382
|
+
...htmlError.title ? { title: htmlError.title } : {},
|
|
383
|
+
...htmlError.workerThrewException ? { workerThrewException: true } : {}
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
}
|
|
367
387
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
368
388
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
369
389
|
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
@@ -424,6 +444,22 @@ var HttpClient = class {
|
|
|
424
444
|
}
|
|
425
445
|
if (!response.ok) {
|
|
426
446
|
const body = await response.text();
|
|
447
|
+
const htmlError = detectHtmlErrorBody(
|
|
448
|
+
body,
|
|
449
|
+
response.headers.get("content-type")
|
|
450
|
+
);
|
|
451
|
+
if (htmlError) {
|
|
452
|
+
throw new DeeplineError(
|
|
453
|
+
htmlError.message(response.status),
|
|
454
|
+
response.status,
|
|
455
|
+
"API_ERROR",
|
|
456
|
+
{
|
|
457
|
+
htmlErrorPage: true,
|
|
458
|
+
...htmlError.title ? { title: htmlError.title } : {},
|
|
459
|
+
...htmlError.workerThrewException ? { workerThrewException: true } : {}
|
|
460
|
+
}
|
|
461
|
+
);
|
|
462
|
+
}
|
|
427
463
|
const parsed = parseResponseBody(body);
|
|
428
464
|
throw new DeeplineError(
|
|
429
465
|
apiErrorMessage(parsed, response.status),
|
|
@@ -489,6 +525,31 @@ function parseResponseBody(body) {
|
|
|
489
525
|
return body;
|
|
490
526
|
}
|
|
491
527
|
}
|
|
528
|
+
function detectHtmlErrorBody(body, contentType) {
|
|
529
|
+
const trimmed = body.trim();
|
|
530
|
+
const lower = trimmed.toLowerCase();
|
|
531
|
+
const isHtml = (contentType ?? "").toLowerCase().includes("text/html") || lower.startsWith("<!doctype") || lower.startsWith("<html");
|
|
532
|
+
if (!isHtml) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
const titleMatch = trimmed.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
536
|
+
const title = titleMatch?.[1]?.replace(/\s+/g, " ").trim() || void 0;
|
|
537
|
+
const workerThrewException = /worker threw exception/i.test(trimmed);
|
|
538
|
+
return {
|
|
539
|
+
title,
|
|
540
|
+
workerThrewException,
|
|
541
|
+
message: (status) => {
|
|
542
|
+
const segments = [`HTTP ${status}`];
|
|
543
|
+
if (workerThrewException) {
|
|
544
|
+
segments.push("Worker threw exception");
|
|
545
|
+
}
|
|
546
|
+
if (title) {
|
|
547
|
+
segments.push(title);
|
|
548
|
+
}
|
|
549
|
+
return `${segments.join(": ")} (Cloudflare HTML error page suppressed)`;
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
492
553
|
function apiErrorMessage(parsed, status) {
|
|
493
554
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
494
555
|
if (typeof errorValue === "string") {
|
|
@@ -584,7 +645,7 @@ function isTransientPlayStreamError(error) {
|
|
|
584
645
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
585
646
|
}
|
|
586
647
|
const text = error instanceof Error ? error.message : String(error);
|
|
587
|
-
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
648
|
+
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(
|
|
588
649
|
text
|
|
589
650
|
);
|
|
590
651
|
}
|
|
@@ -816,6 +877,10 @@ function buildSnapshotFromLedger(snapshot) {
|
|
|
816
877
|
return {
|
|
817
878
|
runId: snapshot.runId,
|
|
818
879
|
status: normalizePlayRunLiveStatus(snapshot.status),
|
|
880
|
+
createdAt: snapshot.createdAt ?? null,
|
|
881
|
+
startedAt: snapshot.startedAt ?? null,
|
|
882
|
+
finishedAt: snapshot.finishedAt ?? null,
|
|
883
|
+
durationMs: snapshot.durationMs ?? null,
|
|
819
884
|
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
820
885
|
logs: snapshot.logTail,
|
|
821
886
|
totalLogCount: snapshot.totalLogCount,
|
|
@@ -4538,14 +4603,25 @@ import { writeFileSync as writeFileSync4 } from "fs";
|
|
|
4538
4603
|
import { resolve as resolve4 } from "path";
|
|
4539
4604
|
|
|
4540
4605
|
// ../shared_libs/plays/dataset-summary.ts
|
|
4606
|
+
function formatDatasetRowCountsLine(counts) {
|
|
4607
|
+
const { persisted, succeeded, failed } = counts;
|
|
4608
|
+
if (succeeded === persisted && failed === 0) {
|
|
4609
|
+
return `${persisted} persisted`;
|
|
4610
|
+
}
|
|
4611
|
+
return `${persisted} persisted (${succeeded} succeeded, ${failed} failed)`;
|
|
4612
|
+
}
|
|
4541
4613
|
function datasetSummaryPercentText(numerator, denominator) {
|
|
4542
4614
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
4543
4615
|
}
|
|
4544
4616
|
function readCount(value) {
|
|
4545
4617
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : 0;
|
|
4546
4618
|
}
|
|
4547
|
-
function
|
|
4548
|
-
return
|
|
4619
|
+
function executionAttemptTotal(raw) {
|
|
4620
|
+
return readCount(raw.queued) + readCount(raw.running) + readCount(raw.completed) + readCount(raw.cached) + readCount(raw.skipped) + readCount(raw.missed) + readCount(raw.failed);
|
|
4621
|
+
}
|
|
4622
|
+
function formatDatasetExecutionStats(raw, _persistedRowTotal) {
|
|
4623
|
+
const denominator = executionAttemptTotal(raw);
|
|
4624
|
+
const stats = {
|
|
4549
4625
|
queued: datasetSummaryPercentText(readCount(raw.queued), denominator),
|
|
4550
4626
|
running: datasetSummaryPercentText(readCount(raw.running), denominator),
|
|
4551
4627
|
"completed:executed": datasetSummaryPercentText(
|
|
@@ -4566,6 +4642,17 @@ function formatDatasetExecutionStats(raw, denominator) {
|
|
|
4566
4642
|
),
|
|
4567
4643
|
failed: datasetSummaryPercentText(readCount(raw.failed), denominator)
|
|
4568
4644
|
};
|
|
4645
|
+
if (Object.values(stats).some((text) => {
|
|
4646
|
+
const match = /\((\d+)%\)/.exec(text);
|
|
4647
|
+
return match ? Number(match[1]) > 100 : false;
|
|
4648
|
+
})) {
|
|
4649
|
+
throw new Error(
|
|
4650
|
+
`formatDatasetExecutionStats produced a >100% execution stat; column counts are corrupt: ${JSON.stringify(
|
|
4651
|
+
raw
|
|
4652
|
+
)}`
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
return stats;
|
|
4569
4656
|
}
|
|
4570
4657
|
|
|
4571
4658
|
// src/cli/dataset-stats.ts
|
|
@@ -4715,7 +4802,8 @@ function canonicalRowsInfoFromCandidate(input2) {
|
|
|
4715
4802
|
complete: rows2.length === totalRows2,
|
|
4716
4803
|
source: candidate.source,
|
|
4717
4804
|
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
4718
|
-
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
4805
|
+
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null,
|
|
4806
|
+
...candidate.value.recovered === true ? { recovered: true } : {}
|
|
4719
4807
|
};
|
|
4720
4808
|
}
|
|
4721
4809
|
if (candidate.serializedOnly) {
|
|
@@ -4865,18 +4953,46 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
4865
4953
|
}
|
|
4866
4954
|
return infos;
|
|
4867
4955
|
}
|
|
4868
|
-
function
|
|
4956
|
+
function collectPackagedStepDatasetCandidates(statusOrResult) {
|
|
4869
4957
|
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4870
|
-
|
|
4871
|
-
if (!result) {
|
|
4958
|
+
if (!root) {
|
|
4872
4959
|
return [];
|
|
4873
4960
|
}
|
|
4961
|
+
const pkg = isRecord3(root.package) ? root.package : root;
|
|
4962
|
+
const steps = Array.isArray(pkg.steps) ? pkg.steps : [];
|
|
4874
4963
|
const candidates = [];
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4964
|
+
for (const step of steps) {
|
|
4965
|
+
if (!isRecord3(step) || !isRecord3(step.output)) {
|
|
4966
|
+
continue;
|
|
4967
|
+
}
|
|
4968
|
+
const output2 = step.output;
|
|
4969
|
+
if (!isPackagedDatasetOutput(output2)) {
|
|
4970
|
+
continue;
|
|
4971
|
+
}
|
|
4972
|
+
const source = typeof output2.path === "string" && output2.path.trim() ? output2.path.trim() : typeof step.id === "string" ? step.id : null;
|
|
4973
|
+
if (!source) {
|
|
4974
|
+
continue;
|
|
4975
|
+
}
|
|
4976
|
+
candidates.push({
|
|
4977
|
+
source,
|
|
4978
|
+
value: output2,
|
|
4979
|
+
total: output2.rowCount ?? (isRecord3(output2.preview) ? output2.preview.totalRows : void 0) ?? (isRecord3(step.progress) ? step.progress.total : void 0)
|
|
4980
|
+
});
|
|
4981
|
+
}
|
|
4982
|
+
return candidates;
|
|
4983
|
+
}
|
|
4984
|
+
function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
4985
|
+
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4986
|
+
const result = isRecord3(root?.result) ? root.result : root;
|
|
4987
|
+
const candidates = [];
|
|
4988
|
+
if (result) {
|
|
4989
|
+
collectDatasetCandidates({
|
|
4990
|
+
value: result,
|
|
4991
|
+
path: "result",
|
|
4992
|
+
output: candidates
|
|
4993
|
+
});
|
|
4994
|
+
}
|
|
4995
|
+
candidates.push(...collectPackagedStepDatasetCandidates(statusOrResult));
|
|
4880
4996
|
const seen = /* @__PURE__ */ new Set();
|
|
4881
4997
|
const infos = [];
|
|
4882
4998
|
for (const candidate of candidates) {
|
|
@@ -5575,9 +5691,11 @@ import {
|
|
|
5575
5691
|
readFileSync as readFileSync6,
|
|
5576
5692
|
readdirSync,
|
|
5577
5693
|
realpathSync,
|
|
5694
|
+
statSync as statSync2,
|
|
5578
5695
|
writeFileSync as writeFileSync7
|
|
5579
5696
|
} from "fs";
|
|
5580
5697
|
import { basename as basename3, dirname as dirname8, join as join7, resolve as resolve10 } from "path";
|
|
5698
|
+
import { parse as parseCsvSync2 } from "csv-parse/sync";
|
|
5581
5699
|
|
|
5582
5700
|
// src/plays/bundle-play-file.ts
|
|
5583
5701
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -9390,6 +9508,177 @@ function inputContainsLocalFilePath(value) {
|
|
|
9390
9508
|
}
|
|
9391
9509
|
return false;
|
|
9392
9510
|
}
|
|
9511
|
+
function isUrlValue(value) {
|
|
9512
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim());
|
|
9513
|
+
}
|
|
9514
|
+
function looksLikeStagedFileRef(value) {
|
|
9515
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9516
|
+
return false;
|
|
9517
|
+
}
|
|
9518
|
+
const record = value;
|
|
9519
|
+
return typeof record.contentHash === "string" || typeof record.contentBase64 === "string" || typeof record.logicalPath === "string" && typeof record.bytes === "number";
|
|
9520
|
+
}
|
|
9521
|
+
var CSV_DATA_INPUT_KEY = "csv";
|
|
9522
|
+
function collectLocalFileInputRefs(value, inputPath, key, out) {
|
|
9523
|
+
if (typeof value === "string") {
|
|
9524
|
+
const trimmed = value.trim();
|
|
9525
|
+
if (!trimmed || isUrlValue(trimmed)) {
|
|
9526
|
+
return;
|
|
9527
|
+
}
|
|
9528
|
+
const keyIsCsvData = key === CSV_DATA_INPUT_KEY;
|
|
9529
|
+
const endsWithCsv = /\.csv$/i.test(trimmed);
|
|
9530
|
+
const looksLikeFile = /\.[a-z0-9]{1,8}$/i.test(trimmed);
|
|
9531
|
+
if (keyIsCsvData) {
|
|
9532
|
+
out.push({ inputPath, value: trimmed, isCsvData: true });
|
|
9533
|
+
} else if (endsWithCsv || looksLikeFile && existsSync7(resolve10(trimmed))) {
|
|
9534
|
+
out.push({ inputPath, value: trimmed, isCsvData: false });
|
|
9535
|
+
}
|
|
9536
|
+
return;
|
|
9537
|
+
}
|
|
9538
|
+
if (Array.isArray(value)) {
|
|
9539
|
+
value.forEach(
|
|
9540
|
+
(entry, index) => collectLocalFileInputRefs(entry, `${inputPath}[${index}]`, key, out)
|
|
9541
|
+
);
|
|
9542
|
+
return;
|
|
9543
|
+
}
|
|
9544
|
+
if (looksLikeStagedFileRef(value)) {
|
|
9545
|
+
return;
|
|
9546
|
+
}
|
|
9547
|
+
if (value && typeof value === "object") {
|
|
9548
|
+
for (const [childKey, child] of Object.entries(value)) {
|
|
9549
|
+
collectLocalFileInputRefs(
|
|
9550
|
+
child,
|
|
9551
|
+
inputPath ? `${inputPath}.${childKey}` : childKey,
|
|
9552
|
+
childKey,
|
|
9553
|
+
out
|
|
9554
|
+
);
|
|
9555
|
+
}
|
|
9556
|
+
}
|
|
9557
|
+
}
|
|
9558
|
+
function preflightLocalFileInputs(runtimeInput) {
|
|
9559
|
+
if (CSV_DATA_INPUT_KEY in runtimeInput) {
|
|
9560
|
+
const csvValue = runtimeInput[CSV_DATA_INPUT_KEY];
|
|
9561
|
+
if (typeof csvValue === "string" && csvValue.trim().length === 0) {
|
|
9562
|
+
throw new DeeplineError(
|
|
9563
|
+
`Input ${CSV_DATA_INPUT_KEY} is an empty string. Provide a path to a CSV file (or a CSV URL). No run was created.`,
|
|
9564
|
+
void 0,
|
|
9565
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9566
|
+
{ inputPath: CSV_DATA_INPUT_KEY, reason: "csv_empty_string" }
|
|
9567
|
+
);
|
|
9568
|
+
}
|
|
9569
|
+
}
|
|
9570
|
+
const refs = [];
|
|
9571
|
+
for (const [key, value] of Object.entries(runtimeInput)) {
|
|
9572
|
+
collectLocalFileInputRefs(value, key, key, refs);
|
|
9573
|
+
}
|
|
9574
|
+
for (const ref of refs) {
|
|
9575
|
+
const absolutePath = resolve10(ref.value);
|
|
9576
|
+
if (!existsSync7(absolutePath)) {
|
|
9577
|
+
throw new DeeplineError(
|
|
9578
|
+
`Input ${ref.inputPath} references a local file that does not exist: ${ref.value} (resolved to ${absolutePath}). No run was created.`,
|
|
9579
|
+
void 0,
|
|
9580
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9581
|
+
{ inputPath: ref.inputPath, path: ref.value, resolved: absolutePath }
|
|
9582
|
+
);
|
|
9583
|
+
}
|
|
9584
|
+
let stat4;
|
|
9585
|
+
try {
|
|
9586
|
+
stat4 = statSync2(absolutePath);
|
|
9587
|
+
} catch (error) {
|
|
9588
|
+
throw new DeeplineError(
|
|
9589
|
+
`Input ${ref.inputPath} references a local file that is not readable: ${ref.value} (${error instanceof Error ? error.message : String(error)}). No run was created.`,
|
|
9590
|
+
void 0,
|
|
9591
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9592
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9593
|
+
);
|
|
9594
|
+
}
|
|
9595
|
+
if (!stat4.isFile()) {
|
|
9596
|
+
throw new DeeplineError(
|
|
9597
|
+
`Input ${ref.inputPath} references ${ref.value}, which is not a file. No run was created.`,
|
|
9598
|
+
void 0,
|
|
9599
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9600
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9601
|
+
);
|
|
9602
|
+
}
|
|
9603
|
+
if (!ref.isCsvData) {
|
|
9604
|
+
continue;
|
|
9605
|
+
}
|
|
9606
|
+
preflightCsvDataInput(ref, absolutePath);
|
|
9607
|
+
}
|
|
9608
|
+
}
|
|
9609
|
+
function preflightCsvDataInput(ref, absolutePath) {
|
|
9610
|
+
let content;
|
|
9611
|
+
try {
|
|
9612
|
+
content = readFileSync6(absolutePath, "utf-8");
|
|
9613
|
+
} catch (error) {
|
|
9614
|
+
throw new DeeplineError(
|
|
9615
|
+
`Input ${ref.inputPath} CSV ${ref.value} is not readable: ${error instanceof Error ? error.message : String(error)}. No run was created.`,
|
|
9616
|
+
void 0,
|
|
9617
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9618
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9619
|
+
);
|
|
9620
|
+
}
|
|
9621
|
+
let records;
|
|
9622
|
+
try {
|
|
9623
|
+
records = parseCsvSync2(content, {
|
|
9624
|
+
bom: true,
|
|
9625
|
+
columns: false,
|
|
9626
|
+
skip_empty_lines: true,
|
|
9627
|
+
relax_column_count: true,
|
|
9628
|
+
// STRICT RFC quoting: an unclosed quote or ragged quoting is a hard
|
|
9629
|
+
// error rather than being swallowed into one giant field.
|
|
9630
|
+
relax_quotes: false,
|
|
9631
|
+
trim: true
|
|
9632
|
+
});
|
|
9633
|
+
} catch (error) {
|
|
9634
|
+
throw new DeeplineError(
|
|
9635
|
+
`Input ${ref.inputPath} could not be parsed as CSV (${ref.value}): ${error instanceof Error ? error.message : String(error)}. This usually means an unclosed quote or ragged quoting. No run was created.`,
|
|
9636
|
+
void 0,
|
|
9637
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9638
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9639
|
+
);
|
|
9640
|
+
}
|
|
9641
|
+
if (records.length === 0) {
|
|
9642
|
+
throw new DeeplineError(
|
|
9643
|
+
`${ref.value} has a header row but no data rows. No run was created.`,
|
|
9644
|
+
void 0,
|
|
9645
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9646
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9647
|
+
);
|
|
9648
|
+
}
|
|
9649
|
+
const header = records[0];
|
|
9650
|
+
const seen = /* @__PURE__ */ new Map();
|
|
9651
|
+
for (let i = 0; i < header.length; i++) {
|
|
9652
|
+
const raw = header[i] ?? "";
|
|
9653
|
+
const name = raw.trim();
|
|
9654
|
+
if (name.length === 0) continue;
|
|
9655
|
+
const prior = seen.get(name);
|
|
9656
|
+
if (prior) {
|
|
9657
|
+
throw new DeeplineError(
|
|
9658
|
+
`Input ${ref.inputPath} (${ref.value}) has a duplicate CSV header "${name}" (columns ${prior.index + 1} and ${i + 1}). The second column would silently overwrite the first. Rename one of them. No run was created.`,
|
|
9659
|
+
void 0,
|
|
9660
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9661
|
+
{
|
|
9662
|
+
inputPath: ref.inputPath,
|
|
9663
|
+
path: ref.value,
|
|
9664
|
+
duplicateHeader: name,
|
|
9665
|
+
columns: [prior.index + 1, i + 1],
|
|
9666
|
+
rawSpellings: [prior.raw, raw]
|
|
9667
|
+
}
|
|
9668
|
+
);
|
|
9669
|
+
}
|
|
9670
|
+
seen.set(name, { index: i, raw });
|
|
9671
|
+
}
|
|
9672
|
+
const dataRowCount = records.length - 1;
|
|
9673
|
+
if (dataRowCount < 1) {
|
|
9674
|
+
throw new DeeplineError(
|
|
9675
|
+
`${ref.value} has a header row but no data rows. No run was created.`,
|
|
9676
|
+
void 0,
|
|
9677
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9678
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9679
|
+
);
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9393
9682
|
function namedRunNeedsPlayDefinition(input2) {
|
|
9394
9683
|
return input2.revisionSelector === "latest" || inputContainsLocalFilePath(input2.runtimeInput);
|
|
9395
9684
|
}
|
|
@@ -9624,6 +9913,21 @@ function isRetryablePendingStartFailure(status) {
|
|
|
9624
9913
|
if (status.runId && status.runId !== "pending") return false;
|
|
9625
9914
|
return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
|
|
9626
9915
|
}
|
|
9916
|
+
function pendingStartFailureStatus(input2) {
|
|
9917
|
+
const message = input2.error instanceof Error ? input2.error.message : String(input2.error);
|
|
9918
|
+
return {
|
|
9919
|
+
runId: "pending",
|
|
9920
|
+
name: input2.playName,
|
|
9921
|
+
playName: input2.playName,
|
|
9922
|
+
dashboardUrl: input2.dashboardUrl,
|
|
9923
|
+
status: "failed",
|
|
9924
|
+
progress: {
|
|
9925
|
+
status: "failed",
|
|
9926
|
+
logs: [],
|
|
9927
|
+
error: message
|
|
9928
|
+
}
|
|
9929
|
+
};
|
|
9930
|
+
}
|
|
9627
9931
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
9628
9932
|
"completed",
|
|
9629
9933
|
"failed",
|
|
@@ -10403,6 +10707,23 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10403
10707
|
progress: input2.progress
|
|
10404
10708
|
});
|
|
10405
10709
|
}
|
|
10710
|
+
if (!lastKnownWorkflowId && isTransientPlayStreamError(error)) {
|
|
10711
|
+
recordCliTrace({
|
|
10712
|
+
phase: "cli.play_start_stream_transient_failure",
|
|
10713
|
+
ms: Date.now() - startedAt,
|
|
10714
|
+
ok: false,
|
|
10715
|
+
playName: input2.playName,
|
|
10716
|
+
eventCount,
|
|
10717
|
+
firstRunIdMs,
|
|
10718
|
+
lastPhase,
|
|
10719
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
10720
|
+
});
|
|
10721
|
+
return pendingStartFailureStatus({
|
|
10722
|
+
playName: input2.playName,
|
|
10723
|
+
dashboardUrl,
|
|
10724
|
+
error
|
|
10725
|
+
});
|
|
10726
|
+
}
|
|
10406
10727
|
throw error;
|
|
10407
10728
|
} finally {
|
|
10408
10729
|
if (timeout) {
|
|
@@ -10454,6 +10775,28 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10454
10775
|
function formatInteger(value) {
|
|
10455
10776
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
10456
10777
|
}
|
|
10778
|
+
var RUN_ERROR_DISPLAY_MAX_CHARS = 2e3;
|
|
10779
|
+
function truncateErrorForDisplay(message, runId, maxChars = RUN_ERROR_DISPLAY_MAX_CHARS) {
|
|
10780
|
+
const compact = message.replace(/\s+/g, " ").trim();
|
|
10781
|
+
if (compact.length <= maxChars) {
|
|
10782
|
+
return compact;
|
|
10783
|
+
}
|
|
10784
|
+
const slice = compact.slice(0, maxChars);
|
|
10785
|
+
const lastSpace = slice.lastIndexOf(" ");
|
|
10786
|
+
const wordBoundary = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;
|
|
10787
|
+
const fullErrorHint = runId ? ` (full error: deepline runs get ${runId} --full)` : " (full error: deepline runs get <runId> --full)";
|
|
10788
|
+
return `${wordBoundary}\u2026${fullErrorHint}`;
|
|
10789
|
+
}
|
|
10790
|
+
function clampJsonPreviewText(json, maxChars) {
|
|
10791
|
+
if (json.length <= maxChars) {
|
|
10792
|
+
return json;
|
|
10793
|
+
}
|
|
10794
|
+
const slice = json.slice(0, maxChars);
|
|
10795
|
+
const lastNewline = slice.lastIndexOf("\n");
|
|
10796
|
+
const head = lastNewline > 0 ? slice.slice(0, lastNewline) : slice;
|
|
10797
|
+
return `${head}
|
|
10798
|
+
... truncated; use --json for full output`;
|
|
10799
|
+
}
|
|
10457
10800
|
var BULKY_RETURN_KEYS = /* @__PURE__ */ new Set([
|
|
10458
10801
|
"contract",
|
|
10459
10802
|
"staticPipeline",
|
|
@@ -10538,8 +10881,7 @@ function formatJsonPreview(value) {
|
|
|
10538
10881
|
return [];
|
|
10539
10882
|
}
|
|
10540
10883
|
const MAX_CHARS = 4e3;
|
|
10541
|
-
const truncated = json
|
|
10542
|
-
... truncated; use --json for full output` : json;
|
|
10884
|
+
const truncated = clampJsonPreviewText(json, MAX_CHARS);
|
|
10543
10885
|
return truncated.split("\n").map((line) => ` ${line}`);
|
|
10544
10886
|
}
|
|
10545
10887
|
function formatReturnValue(result) {
|
|
@@ -11070,6 +11412,9 @@ function readRecordArray(value) {
|
|
|
11070
11412
|
function readRecord(value) {
|
|
11071
11413
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11072
11414
|
}
|
|
11415
|
+
function readNonNegativeInteger(value) {
|
|
11416
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
11417
|
+
}
|
|
11073
11418
|
function formatSummaryScalar(value) {
|
|
11074
11419
|
if (typeof value === "number") {
|
|
11075
11420
|
return Number.isFinite(value) ? formatInteger(Math.trunc(value)) : null;
|
|
@@ -11102,7 +11447,25 @@ function formatPackageDatasetSummaryLines(summary, indent2 = " ") {
|
|
|
11102
11447
|
return [];
|
|
11103
11448
|
}
|
|
11104
11449
|
const lines = [];
|
|
11105
|
-
const
|
|
11450
|
+
const rowCounts = readRecord(record.rowCounts);
|
|
11451
|
+
if (rowCounts) {
|
|
11452
|
+
const persisted = readNonNegativeInteger(rowCounts.persisted);
|
|
11453
|
+
const succeeded = readNonNegativeInteger(rowCounts.succeeded);
|
|
11454
|
+
const failed = readNonNegativeInteger(rowCounts.failed);
|
|
11455
|
+
if (persisted !== null) {
|
|
11456
|
+
lines.push(
|
|
11457
|
+
`${indent2}rows: ${formatDatasetRowCountsLine({
|
|
11458
|
+
persisted,
|
|
11459
|
+
succeeded: succeeded ?? persisted,
|
|
11460
|
+
failed: failed ?? 0
|
|
11461
|
+
})}`
|
|
11462
|
+
);
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
const parts = formatSummaryScalarParts(
|
|
11466
|
+
record,
|
|
11467
|
+
/* @__PURE__ */ new Set(["columnStats", "rowCounts"])
|
|
11468
|
+
);
|
|
11106
11469
|
if (parts.length > 0) {
|
|
11107
11470
|
lines.push(`${indent2}summary: ${parts.join(" ")}`);
|
|
11108
11471
|
}
|
|
@@ -11163,7 +11526,7 @@ function buildRunPackageTextLines(packaged) {
|
|
|
11163
11526
|
];
|
|
11164
11527
|
const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
|
|
11165
11528
|
if (runError && (status === "failed" || status === "cancelled")) {
|
|
11166
|
-
lines.push(` error: ${runError
|
|
11529
|
+
lines.push(` error: ${truncateErrorForDisplay(runError, runId)}`);
|
|
11167
11530
|
}
|
|
11168
11531
|
for (const step of readRecordArray(packaged.steps)) {
|
|
11169
11532
|
const output2 = step.output && typeof step.output === "object" && !Array.isArray(step.output) ? step.output : null;
|
|
@@ -11248,7 +11611,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
11248
11611
|
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
11249
11612
|
}
|
|
11250
11613
|
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
11251
|
-
lines.push(` error: ${displayError
|
|
11614
|
+
lines.push(` error: ${truncateErrorForDisplay(displayError, runId)}`);
|
|
11252
11615
|
}
|
|
11253
11616
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
11254
11617
|
if (result) {
|
|
@@ -11419,12 +11782,51 @@ async function fetchBackingDatasetRows(input2) {
|
|
|
11419
11782
|
source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
11420
11783
|
};
|
|
11421
11784
|
}
|
|
11785
|
+
function resolveDatasetByName(available, datasetPath) {
|
|
11786
|
+
const target = datasetPath.trim();
|
|
11787
|
+
const exact = available.find((info) => info.source === target);
|
|
11788
|
+
if (exact) {
|
|
11789
|
+
return exact;
|
|
11790
|
+
}
|
|
11791
|
+
const byNamespace = available.find(
|
|
11792
|
+
(info) => info.tableNamespace && info.tableNamespace === target
|
|
11793
|
+
);
|
|
11794
|
+
if (byNamespace) {
|
|
11795
|
+
return byNamespace;
|
|
11796
|
+
}
|
|
11797
|
+
const trailing = target.split(".").filter(Boolean).at(-1);
|
|
11798
|
+
if (!trailing) {
|
|
11799
|
+
return null;
|
|
11800
|
+
}
|
|
11801
|
+
const byTrailing = available.find(
|
|
11802
|
+
(info) => info.tableNamespace === trailing || typeof info.source === "string" && info.source.split(".").filter(Boolean).at(-1) === trailing
|
|
11803
|
+
);
|
|
11804
|
+
if (byTrailing) {
|
|
11805
|
+
return byTrailing;
|
|
11806
|
+
}
|
|
11807
|
+
if (target.split(".").filter(Boolean)[0] === "result") {
|
|
11808
|
+
const recovered = available.filter((info) => info.recovered);
|
|
11809
|
+
if (recovered.length === 1) {
|
|
11810
|
+
return recovered[0];
|
|
11811
|
+
}
|
|
11812
|
+
if (recovered.length > 1) {
|
|
11813
|
+
const names = recovered.map((info) => info.tableNamespace ?? info.source).filter((name) => typeof name === "string");
|
|
11814
|
+
throw new DeeplineError(
|
|
11815
|
+
`Run returned multiple recovered datasets; '${target}' is ambiguous. Choose one with --dataset <path>: ${names.join(", ")}.`,
|
|
11816
|
+
void 0,
|
|
11817
|
+
"RUN_EXPORT_DATASET_AMBIGUOUS",
|
|
11818
|
+
{ dataset: target, available: names }
|
|
11819
|
+
);
|
|
11820
|
+
}
|
|
11821
|
+
}
|
|
11822
|
+
return null;
|
|
11823
|
+
}
|
|
11422
11824
|
async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
11423
11825
|
if (!outPath) {
|
|
11424
11826
|
return null;
|
|
11425
11827
|
}
|
|
11426
11828
|
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
11427
|
-
const rowsInfo = options.datasetPath ? availableRows
|
|
11829
|
+
const rowsInfo = options.datasetPath ? resolveDatasetByName(availableRows, options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
11428
11830
|
if (!rowsInfo && options.datasetPath) {
|
|
11429
11831
|
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
11430
11832
|
throw new DeeplineError(
|
|
@@ -12170,6 +12572,12 @@ async function handleFileBackedRun(options) {
|
|
|
12170
12572
|
() => readFileSync6(absolutePlayPath, "utf-8")
|
|
12171
12573
|
);
|
|
12172
12574
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
12575
|
+
try {
|
|
12576
|
+
preflightLocalFileInputs(runtimeInput);
|
|
12577
|
+
} catch (error) {
|
|
12578
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
12579
|
+
return 1;
|
|
12580
|
+
}
|
|
12173
12581
|
let graph;
|
|
12174
12582
|
try {
|
|
12175
12583
|
graph = await traceCliSpan(
|
|
@@ -12327,6 +12735,12 @@ async function handleNamedRun(options) {
|
|
|
12327
12735
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
12328
12736
|
const playName = options.target.name;
|
|
12329
12737
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
12738
|
+
try {
|
|
12739
|
+
preflightLocalFileInputs(runtimeInput);
|
|
12740
|
+
} catch (error) {
|
|
12741
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
12742
|
+
return 1;
|
|
12743
|
+
}
|
|
12330
12744
|
const needsPlayDefinition = namedRunNeedsPlayDefinition({
|
|
12331
12745
|
runtimeInput,
|
|
12332
12746
|
revisionSelector: options.revisionSelector
|
|
@@ -19506,7 +19920,7 @@ import {
|
|
|
19506
19920
|
mkdirSync as mkdirSync5,
|
|
19507
19921
|
readdirSync as readdirSync2,
|
|
19508
19922
|
readFileSync as readFileSync8,
|
|
19509
|
-
statSync as
|
|
19923
|
+
statSync as statSync3,
|
|
19510
19924
|
writeFileSync as writeFileSync10
|
|
19511
19925
|
} from "fs";
|
|
19512
19926
|
import { homedir as homedir6 } from "os";
|
|
@@ -19580,7 +19994,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
|
19580
19994
|
const scan = (dir) => {
|
|
19581
19995
|
for (const entry of readdirSync2(dir)) {
|
|
19582
19996
|
const path = join13(dir, entry);
|
|
19583
|
-
const stat4 =
|
|
19997
|
+
const stat4 = statSync3(path);
|
|
19584
19998
|
if (stat4.isDirectory()) {
|
|
19585
19999
|
if (scan(path)) return true;
|
|
19586
20000
|
continue;
|