deepline 0.1.100 → 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 +434 -20
- package/dist/cli/index.mjs +437 -22
- package/dist/index.js +69 -3
- package/dist/index.mjs +69 -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 +7 -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
|
@@ -209,10 +209,15 @@ var SDK_RELEASE = {
|
|
|
209
209
|
// 0.1.94 is claimed by PR #1527 — this watch-render fix ships as 0.1.95.
|
|
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
|
+
// 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",
|
|
213
218
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
214
219
|
supportPolicy: {
|
|
215
|
-
latest: "0.1.
|
|
220
|
+
latest: "0.1.102",
|
|
216
221
|
minimumSupported: "0.1.53",
|
|
217
222
|
deprecatedBelow: "0.1.53"
|
|
218
223
|
}
|
|
@@ -363,6 +368,22 @@ var HttpClient = class {
|
|
|
363
368
|
parsed = body;
|
|
364
369
|
}
|
|
365
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
|
+
}
|
|
366
387
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
367
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}`;
|
|
368
389
|
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
@@ -423,6 +444,22 @@ var HttpClient = class {
|
|
|
423
444
|
}
|
|
424
445
|
if (!response.ok) {
|
|
425
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
|
+
}
|
|
426
463
|
const parsed = parseResponseBody(body);
|
|
427
464
|
throw new DeeplineError(
|
|
428
465
|
apiErrorMessage(parsed, response.status),
|
|
@@ -488,6 +525,31 @@ function parseResponseBody(body) {
|
|
|
488
525
|
return body;
|
|
489
526
|
}
|
|
490
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
|
+
}
|
|
491
553
|
function apiErrorMessage(parsed, status) {
|
|
492
554
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
493
555
|
if (typeof errorValue === "string") {
|
|
@@ -583,7 +645,7 @@ function isTransientPlayStreamError(error) {
|
|
|
583
645
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
584
646
|
}
|
|
585
647
|
const text = error instanceof Error ? error.message : String(error);
|
|
586
|
-
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(
|
|
587
649
|
text
|
|
588
650
|
);
|
|
589
651
|
}
|
|
@@ -815,6 +877,10 @@ function buildSnapshotFromLedger(snapshot) {
|
|
|
815
877
|
return {
|
|
816
878
|
runId: snapshot.runId,
|
|
817
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,
|
|
818
884
|
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
819
885
|
logs: snapshot.logTail,
|
|
820
886
|
totalLogCount: snapshot.totalLogCount,
|
|
@@ -4537,14 +4603,25 @@ import { writeFileSync as writeFileSync4 } from "fs";
|
|
|
4537
4603
|
import { resolve as resolve4 } from "path";
|
|
4538
4604
|
|
|
4539
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
|
+
}
|
|
4540
4613
|
function datasetSummaryPercentText(numerator, denominator) {
|
|
4541
4614
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
4542
4615
|
}
|
|
4543
4616
|
function readCount(value) {
|
|
4544
4617
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : 0;
|
|
4545
4618
|
}
|
|
4546
|
-
function
|
|
4547
|
-
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 = {
|
|
4548
4625
|
queued: datasetSummaryPercentText(readCount(raw.queued), denominator),
|
|
4549
4626
|
running: datasetSummaryPercentText(readCount(raw.running), denominator),
|
|
4550
4627
|
"completed:executed": datasetSummaryPercentText(
|
|
@@ -4565,6 +4642,17 @@ function formatDatasetExecutionStats(raw, denominator) {
|
|
|
4565
4642
|
),
|
|
4566
4643
|
failed: datasetSummaryPercentText(readCount(raw.failed), denominator)
|
|
4567
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;
|
|
4568
4656
|
}
|
|
4569
4657
|
|
|
4570
4658
|
// src/cli/dataset-stats.ts
|
|
@@ -4714,7 +4802,8 @@ function canonicalRowsInfoFromCandidate(input2) {
|
|
|
4714
4802
|
complete: rows2.length === totalRows2,
|
|
4715
4803
|
source: candidate.source,
|
|
4716
4804
|
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
4717
|
-
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 } : {}
|
|
4718
4807
|
};
|
|
4719
4808
|
}
|
|
4720
4809
|
if (candidate.serializedOnly) {
|
|
@@ -4864,18 +4953,46 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
4864
4953
|
}
|
|
4865
4954
|
return infos;
|
|
4866
4955
|
}
|
|
4867
|
-
function
|
|
4956
|
+
function collectPackagedStepDatasetCandidates(statusOrResult) {
|
|
4868
4957
|
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4869
|
-
|
|
4870
|
-
if (!result) {
|
|
4958
|
+
if (!root) {
|
|
4871
4959
|
return [];
|
|
4872
4960
|
}
|
|
4961
|
+
const pkg = isRecord3(root.package) ? root.package : root;
|
|
4962
|
+
const steps = Array.isArray(pkg.steps) ? pkg.steps : [];
|
|
4873
4963
|
const candidates = [];
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
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));
|
|
4879
4996
|
const seen = /* @__PURE__ */ new Set();
|
|
4880
4997
|
const infos = [];
|
|
4881
4998
|
for (const candidate of candidates) {
|
|
@@ -5574,9 +5691,11 @@ import {
|
|
|
5574
5691
|
readFileSync as readFileSync6,
|
|
5575
5692
|
readdirSync,
|
|
5576
5693
|
realpathSync,
|
|
5694
|
+
statSync as statSync2,
|
|
5577
5695
|
writeFileSync as writeFileSync7
|
|
5578
5696
|
} from "fs";
|
|
5579
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";
|
|
5580
5699
|
|
|
5581
5700
|
// src/plays/bundle-play-file.ts
|
|
5582
5701
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -9389,6 +9508,177 @@ function inputContainsLocalFilePath(value) {
|
|
|
9389
9508
|
}
|
|
9390
9509
|
return false;
|
|
9391
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
|
+
}
|
|
9392
9682
|
function namedRunNeedsPlayDefinition(input2) {
|
|
9393
9683
|
return input2.revisionSelector === "latest" || inputContainsLocalFilePath(input2.runtimeInput);
|
|
9394
9684
|
}
|
|
@@ -9623,6 +9913,21 @@ function isRetryablePendingStartFailure(status) {
|
|
|
9623
9913
|
if (status.runId && status.runId !== "pending") return false;
|
|
9624
9914
|
return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
|
|
9625
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
|
+
}
|
|
9626
9931
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
9627
9932
|
"completed",
|
|
9628
9933
|
"failed",
|
|
@@ -10402,6 +10707,23 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10402
10707
|
progress: input2.progress
|
|
10403
10708
|
});
|
|
10404
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
|
+
}
|
|
10405
10727
|
throw error;
|
|
10406
10728
|
} finally {
|
|
10407
10729
|
if (timeout) {
|
|
@@ -10453,6 +10775,28 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10453
10775
|
function formatInteger(value) {
|
|
10454
10776
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
10455
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
|
+
}
|
|
10456
10800
|
var BULKY_RETURN_KEYS = /* @__PURE__ */ new Set([
|
|
10457
10801
|
"contract",
|
|
10458
10802
|
"staticPipeline",
|
|
@@ -10537,8 +10881,7 @@ function formatJsonPreview(value) {
|
|
|
10537
10881
|
return [];
|
|
10538
10882
|
}
|
|
10539
10883
|
const MAX_CHARS = 4e3;
|
|
10540
|
-
const truncated = json
|
|
10541
|
-
... truncated; use --json for full output` : json;
|
|
10884
|
+
const truncated = clampJsonPreviewText(json, MAX_CHARS);
|
|
10542
10885
|
return truncated.split("\n").map((line) => ` ${line}`);
|
|
10543
10886
|
}
|
|
10544
10887
|
function formatReturnValue(result) {
|
|
@@ -11069,6 +11412,9 @@ function readRecordArray(value) {
|
|
|
11069
11412
|
function readRecord(value) {
|
|
11070
11413
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11071
11414
|
}
|
|
11415
|
+
function readNonNegativeInteger(value) {
|
|
11416
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
11417
|
+
}
|
|
11072
11418
|
function formatSummaryScalar(value) {
|
|
11073
11419
|
if (typeof value === "number") {
|
|
11074
11420
|
return Number.isFinite(value) ? formatInteger(Math.trunc(value)) : null;
|
|
@@ -11101,7 +11447,25 @@ function formatPackageDatasetSummaryLines(summary, indent2 = " ") {
|
|
|
11101
11447
|
return [];
|
|
11102
11448
|
}
|
|
11103
11449
|
const lines = [];
|
|
11104
|
-
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
|
+
);
|
|
11105
11469
|
if (parts.length > 0) {
|
|
11106
11470
|
lines.push(`${indent2}summary: ${parts.join(" ")}`);
|
|
11107
11471
|
}
|
|
@@ -11162,7 +11526,7 @@ function buildRunPackageTextLines(packaged) {
|
|
|
11162
11526
|
];
|
|
11163
11527
|
const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
|
|
11164
11528
|
if (runError && (status === "failed" || status === "cancelled")) {
|
|
11165
|
-
lines.push(` error: ${runError
|
|
11529
|
+
lines.push(` error: ${truncateErrorForDisplay(runError, runId)}`);
|
|
11166
11530
|
}
|
|
11167
11531
|
for (const step of readRecordArray(packaged.steps)) {
|
|
11168
11532
|
const output2 = step.output && typeof step.output === "object" && !Array.isArray(step.output) ? step.output : null;
|
|
@@ -11247,7 +11611,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
11247
11611
|
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
11248
11612
|
}
|
|
11249
11613
|
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
11250
|
-
lines.push(` error: ${displayError
|
|
11614
|
+
lines.push(` error: ${truncateErrorForDisplay(displayError, runId)}`);
|
|
11251
11615
|
}
|
|
11252
11616
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
11253
11617
|
if (result) {
|
|
@@ -11418,12 +11782,51 @@ async function fetchBackingDatasetRows(input2) {
|
|
|
11418
11782
|
source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
11419
11783
|
};
|
|
11420
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
|
+
}
|
|
11421
11824
|
async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
11422
11825
|
if (!outPath) {
|
|
11423
11826
|
return null;
|
|
11424
11827
|
}
|
|
11425
11828
|
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
11426
|
-
const rowsInfo = options.datasetPath ? availableRows
|
|
11829
|
+
const rowsInfo = options.datasetPath ? resolveDatasetByName(availableRows, options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
11427
11830
|
if (!rowsInfo && options.datasetPath) {
|
|
11428
11831
|
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
11429
11832
|
throw new DeeplineError(
|
|
@@ -12169,6 +12572,12 @@ async function handleFileBackedRun(options) {
|
|
|
12169
12572
|
() => readFileSync6(absolutePlayPath, "utf-8")
|
|
12170
12573
|
);
|
|
12171
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
|
+
}
|
|
12172
12581
|
let graph;
|
|
12173
12582
|
try {
|
|
12174
12583
|
graph = await traceCliSpan(
|
|
@@ -12326,6 +12735,12 @@ async function handleNamedRun(options) {
|
|
|
12326
12735
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
12327
12736
|
const playName = options.target.name;
|
|
12328
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
|
+
}
|
|
12329
12744
|
const needsPlayDefinition = namedRunNeedsPlayDefinition({
|
|
12330
12745
|
runtimeInput,
|
|
12331
12746
|
revisionSelector: options.revisionSelector
|
|
@@ -19505,7 +19920,7 @@ import {
|
|
|
19505
19920
|
mkdirSync as mkdirSync5,
|
|
19506
19921
|
readdirSync as readdirSync2,
|
|
19507
19922
|
readFileSync as readFileSync8,
|
|
19508
|
-
statSync as
|
|
19923
|
+
statSync as statSync3,
|
|
19509
19924
|
writeFileSync as writeFileSync10
|
|
19510
19925
|
} from "fs";
|
|
19511
19926
|
import { homedir as homedir6 } from "os";
|
|
@@ -19579,7 +19994,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
|
19579
19994
|
const scan = (dir) => {
|
|
19580
19995
|
for (const entry of readdirSync2(dir)) {
|
|
19581
19996
|
const path = join13(dir, entry);
|
|
19582
|
-
const stat4 =
|
|
19997
|
+
const stat4 = statSync3(path);
|
|
19583
19998
|
if (stat4.isDirectory()) {
|
|
19584
19999
|
if (scan(path)) return true;
|
|
19585
20000
|
continue;
|