deepline 0.1.101 → 0.1.103
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/README.md +14 -12
- package/dist/cli/index.js +1138 -147
- package/dist/cli/index.mjs +1149 -150
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +72 -6
- package/dist/index.mjs +72 -6
- 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/client.ts +3 -3
- package/dist/repo/sdk/src/http.ts +89 -0
- package/dist/repo/sdk/src/index.ts +1 -1
- package/dist/repo/sdk/src/play.ts +2 -2
- package/dist/repo/sdk/src/release.ts +7 -2
- package/dist/repo/sdk/src/stream-reconnect.ts +1 -1
- package/dist/repo/sdk/src/types.ts +2 -2
- 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.js
CHANGED
|
@@ -25,8 +25,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/cli/index.ts
|
|
27
27
|
var import_promises7 = require("fs/promises");
|
|
28
|
-
var
|
|
29
|
-
var
|
|
28
|
+
var import_node_path20 = require("path");
|
|
29
|
+
var import_node_os12 = require("os");
|
|
30
30
|
var import_commander3 = require("commander");
|
|
31
31
|
|
|
32
32
|
// src/config.ts
|
|
@@ -233,10 +233,15 @@ var SDK_RELEASE = {
|
|
|
233
233
|
// 0.1.98 ships the duplicate-browser-tab fix (default-browser detection).
|
|
234
234
|
// 0.1.99 ships prebuilt job-change source-column preservation and validation fixes.
|
|
235
235
|
// 0.1.101 ships retryable play artifact publish failures and CI retry hardening.
|
|
236
|
-
|
|
236
|
+
// 0.1.102 ships the job-change ledger fixes: recovered-dataset export on
|
|
237
|
+
// failed runs, persisted/succeeded/failed row counts, strict local CSV
|
|
238
|
+
// preflight (existence, data rows, quotes, duplicate headers), HTML error
|
|
239
|
+
// scrubbing, and word-boundary watch truncation.
|
|
240
|
+
// 0.1.103 ships the refined SDK CLI command surface.
|
|
241
|
+
version: "0.1.103",
|
|
237
242
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
238
243
|
supportPolicy: {
|
|
239
|
-
latest: "0.1.
|
|
244
|
+
latest: "0.1.103",
|
|
240
245
|
minimumSupported: "0.1.53",
|
|
241
246
|
deprecatedBelow: "0.1.53"
|
|
242
247
|
}
|
|
@@ -387,6 +392,22 @@ var HttpClient = class {
|
|
|
387
392
|
parsed = body;
|
|
388
393
|
}
|
|
389
394
|
if (!response.ok) {
|
|
395
|
+
const htmlError = detectHtmlErrorBody(
|
|
396
|
+
body,
|
|
397
|
+
response.headers.get("content-type")
|
|
398
|
+
);
|
|
399
|
+
if (htmlError) {
|
|
400
|
+
throw new DeeplineError(
|
|
401
|
+
htmlError.message(response.status),
|
|
402
|
+
response.status,
|
|
403
|
+
"API_ERROR",
|
|
404
|
+
{
|
|
405
|
+
htmlErrorPage: true,
|
|
406
|
+
...htmlError.title ? { title: htmlError.title } : {},
|
|
407
|
+
...htmlError.workerThrewException ? { workerThrewException: true } : {}
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
}
|
|
390
411
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
391
412
|
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}`;
|
|
392
413
|
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
@@ -447,6 +468,22 @@ var HttpClient = class {
|
|
|
447
468
|
}
|
|
448
469
|
if (!response.ok) {
|
|
449
470
|
const body = await response.text();
|
|
471
|
+
const htmlError = detectHtmlErrorBody(
|
|
472
|
+
body,
|
|
473
|
+
response.headers.get("content-type")
|
|
474
|
+
);
|
|
475
|
+
if (htmlError) {
|
|
476
|
+
throw new DeeplineError(
|
|
477
|
+
htmlError.message(response.status),
|
|
478
|
+
response.status,
|
|
479
|
+
"API_ERROR",
|
|
480
|
+
{
|
|
481
|
+
htmlErrorPage: true,
|
|
482
|
+
...htmlError.title ? { title: htmlError.title } : {},
|
|
483
|
+
...htmlError.workerThrewException ? { workerThrewException: true } : {}
|
|
484
|
+
}
|
|
485
|
+
);
|
|
486
|
+
}
|
|
450
487
|
const parsed = parseResponseBody(body);
|
|
451
488
|
throw new DeeplineError(
|
|
452
489
|
apiErrorMessage(parsed, response.status),
|
|
@@ -512,6 +549,31 @@ function parseResponseBody(body) {
|
|
|
512
549
|
return body;
|
|
513
550
|
}
|
|
514
551
|
}
|
|
552
|
+
function detectHtmlErrorBody(body, contentType) {
|
|
553
|
+
const trimmed = body.trim();
|
|
554
|
+
const lower = trimmed.toLowerCase();
|
|
555
|
+
const isHtml = (contentType ?? "").toLowerCase().includes("text/html") || lower.startsWith("<!doctype") || lower.startsWith("<html");
|
|
556
|
+
if (!isHtml) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
const titleMatch = trimmed.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
560
|
+
const title = titleMatch?.[1]?.replace(/\s+/g, " ").trim() || void 0;
|
|
561
|
+
const workerThrewException = /worker threw exception/i.test(trimmed);
|
|
562
|
+
return {
|
|
563
|
+
title,
|
|
564
|
+
workerThrewException,
|
|
565
|
+
message: (status) => {
|
|
566
|
+
const segments = [`HTTP ${status}`];
|
|
567
|
+
if (workerThrewException) {
|
|
568
|
+
segments.push("Worker threw exception");
|
|
569
|
+
}
|
|
570
|
+
if (title) {
|
|
571
|
+
segments.push(title);
|
|
572
|
+
}
|
|
573
|
+
return `${segments.join(": ")} (Cloudflare HTML error page suppressed)`;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
515
577
|
function apiErrorMessage(parsed, status) {
|
|
516
578
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
517
579
|
if (typeof errorValue === "string") {
|
|
@@ -588,7 +650,7 @@ function decodeSseFrame(frame) {
|
|
|
588
650
|
return parsed;
|
|
589
651
|
}
|
|
590
652
|
function sleep(ms) {
|
|
591
|
-
return new Promise((
|
|
653
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
592
654
|
}
|
|
593
655
|
|
|
594
656
|
// src/stream-reconnect.ts
|
|
@@ -607,7 +669,7 @@ function isTransientPlayStreamError(error) {
|
|
|
607
669
|
return error.statusCode >= 500 && error.statusCode < 600;
|
|
608
670
|
}
|
|
609
671
|
const text = error instanceof Error ? error.message : String(error);
|
|
610
|
-
return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
|
|
672
|
+
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(
|
|
611
673
|
text
|
|
612
674
|
);
|
|
613
675
|
}
|
|
@@ -839,6 +901,10 @@ function buildSnapshotFromLedger(snapshot) {
|
|
|
839
901
|
return {
|
|
840
902
|
runId: snapshot.runId,
|
|
841
903
|
status: normalizePlayRunLiveStatus(snapshot.status),
|
|
904
|
+
createdAt: snapshot.createdAt ?? null,
|
|
905
|
+
startedAt: snapshot.startedAt ?? null,
|
|
906
|
+
finishedAt: snapshot.finishedAt ?? null,
|
|
907
|
+
durationMs: snapshot.durationMs ?? null,
|
|
842
908
|
updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
|
|
843
909
|
logs: snapshot.logTail,
|
|
844
910
|
totalLogCount: snapshot.totalLogCount,
|
|
@@ -1295,14 +1361,14 @@ async function* observeRunEvents(options) {
|
|
|
1295
1361
|
try {
|
|
1296
1362
|
for (; ; ) {
|
|
1297
1363
|
if (queue.length === 0) {
|
|
1298
|
-
const waitForItem = new Promise((
|
|
1299
|
-
wake =
|
|
1364
|
+
const waitForItem = new Promise((resolve16) => {
|
|
1365
|
+
wake = resolve16;
|
|
1300
1366
|
});
|
|
1301
1367
|
if (!sawFirstSnapshot) {
|
|
1302
1368
|
const timedOut = await Promise.race([
|
|
1303
1369
|
waitForItem.then(() => false),
|
|
1304
1370
|
new Promise(
|
|
1305
|
-
(
|
|
1371
|
+
(resolve16) => setTimeout(() => resolve16(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
|
|
1306
1372
|
)
|
|
1307
1373
|
]);
|
|
1308
1374
|
if (timedOut && queue.length === 0) {
|
|
@@ -1399,7 +1465,7 @@ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
|
1399
1465
|
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
|
|
1400
1466
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
1401
1467
|
function sleep2(ms) {
|
|
1402
|
-
return new Promise((
|
|
1468
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
1403
1469
|
}
|
|
1404
1470
|
function isTransientCompileManifestError(error) {
|
|
1405
1471
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
@@ -1841,7 +1907,7 @@ var DeeplineClient = class {
|
|
|
1841
1907
|
* or {@link runPlay}.
|
|
1842
1908
|
*
|
|
1843
1909
|
* Supported invocation surfaces intentionally share this same run contract:
|
|
1844
|
-
* `deepline
|
|
1910
|
+
* `deepline plays run`, repo scripts such as `bun run deepline -- plays run`,
|
|
1845
1911
|
* SDK context calls like `Deepline.connect().play(name).run()`, and direct
|
|
1846
1912
|
* `POST /api/v2/plays/run` calls all return a workflow/run id. The completed
|
|
1847
1913
|
* output is always retrievable from `getPlayStatus(runId).result` (or from
|
|
@@ -2010,7 +2076,7 @@ var DeeplineClient = class {
|
|
|
2010
2076
|
*
|
|
2011
2077
|
* Unlike {@link registerPlayArtifact}, this does not store the artifact,
|
|
2012
2078
|
* publish a revision, or start a run. It is the authoritative cloud validation
|
|
2013
|
-
* path used by `deepline
|
|
2079
|
+
* path used by `deepline plays check`.
|
|
2014
2080
|
*/
|
|
2015
2081
|
async checkPlayArtifact(input2) {
|
|
2016
2082
|
return this.http.post("/api/v2/plays/check", input2);
|
|
@@ -2203,7 +2269,7 @@ var DeeplineClient = class {
|
|
|
2203
2269
|
* Get the current status of a play execution.
|
|
2204
2270
|
*
|
|
2205
2271
|
* Internal/advanced primitive. Public callers should usually prefer
|
|
2206
|
-
* {@link runPlay}, {@link PlayJob.get}, or `deepline
|
|
2272
|
+
* {@link runPlay}, {@link PlayJob.get}, or `deepline plays run --watch`.
|
|
2207
2273
|
*
|
|
2208
2274
|
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
2209
2275
|
* @returns Current status with progress logs and partial results
|
|
@@ -3046,9 +3112,9 @@ async function writeOutputFile(filename, content) {
|
|
|
3046
3112
|
return fullPath;
|
|
3047
3113
|
}
|
|
3048
3114
|
function browserOpenStateFile() {
|
|
3049
|
-
const
|
|
3115
|
+
const homeDir2 = process.env.HOME || (0, import_node_os3.homedir)();
|
|
3050
3116
|
return (0, import_node_path3.join)(
|
|
3051
|
-
|
|
3117
|
+
homeDir2,
|
|
3052
3118
|
".local",
|
|
3053
3119
|
"deepline",
|
|
3054
3120
|
"runtime",
|
|
@@ -3522,8 +3588,8 @@ function printCommandEnvelope(envelope, options = {}) {
|
|
|
3522
3588
|
|
|
3523
3589
|
// src/cli/commands/auth.ts
|
|
3524
3590
|
var EXIT_OK = 0;
|
|
3525
|
-
var EXIT_AUTH =
|
|
3526
|
-
var EXIT_SERVER =
|
|
3591
|
+
var EXIT_AUTH = 3;
|
|
3592
|
+
var EXIT_SERVER = 5;
|
|
3527
3593
|
function envFilePath(baseUrl) {
|
|
3528
3594
|
return hostEnvFilePath(baseUrl);
|
|
3529
3595
|
}
|
|
@@ -3615,7 +3681,7 @@ function buildCandidateUrls2(url) {
|
|
|
3615
3681
|
}
|
|
3616
3682
|
}
|
|
3617
3683
|
function sleep4(ms) {
|
|
3618
|
-
return new Promise((
|
|
3684
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
3619
3685
|
}
|
|
3620
3686
|
function printDeeplineLogo() {
|
|
3621
3687
|
if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
|
|
@@ -3878,7 +3944,7 @@ async function handleStatus(args) {
|
|
|
3878
3944
|
...hostStatusPayload ?? { host: baseUrl },
|
|
3879
3945
|
status: "not connected",
|
|
3880
3946
|
connected: false,
|
|
3881
|
-
next: "deepline auth register",
|
|
3947
|
+
next: "deepline auth register --no-wait && deepline auth wait",
|
|
3882
3948
|
render: {
|
|
3883
3949
|
sections: [
|
|
3884
3950
|
{
|
|
@@ -3886,7 +3952,10 @@ async function handleStatus(args) {
|
|
|
3886
3952
|
lines: [...hostLines, "Status: not connected"]
|
|
3887
3953
|
}
|
|
3888
3954
|
],
|
|
3889
|
-
actions: [
|
|
3955
|
+
actions: [
|
|
3956
|
+
{ label: "Register", command: "deepline auth register --no-wait" },
|
|
3957
|
+
{ label: "Wait", command: "deepline auth wait" }
|
|
3958
|
+
]
|
|
3890
3959
|
}
|
|
3891
3960
|
},
|
|
3892
3961
|
{ json: jsonOutput }
|
|
@@ -3908,7 +3977,7 @@ async function handleStatus(args) {
|
|
|
3908
3977
|
...hostStatusPayload ?? { host: baseUrl },
|
|
3909
3978
|
status: "unauthorized",
|
|
3910
3979
|
connected: false,
|
|
3911
|
-
next: "deepline auth register",
|
|
3980
|
+
next: "deepline auth register --no-wait && deepline auth wait",
|
|
3912
3981
|
render: {
|
|
3913
3982
|
sections: [
|
|
3914
3983
|
{
|
|
@@ -3916,7 +3985,10 @@ async function handleStatus(args) {
|
|
|
3916
3985
|
lines: [...hostLines, "Status: unauthorized"]
|
|
3917
3986
|
}
|
|
3918
3987
|
],
|
|
3919
|
-
actions: [
|
|
3988
|
+
actions: [
|
|
3989
|
+
{ label: "Register", command: "deepline auth register --no-wait" },
|
|
3990
|
+
{ label: "Wait", command: "deepline auth wait" }
|
|
3991
|
+
]
|
|
3920
3992
|
}
|
|
3921
3993
|
},
|
|
3922
3994
|
{ json: jsonOutput }
|
|
@@ -4549,14 +4621,25 @@ var import_node_fs5 = require("fs");
|
|
|
4549
4621
|
var import_node_path6 = require("path");
|
|
4550
4622
|
|
|
4551
4623
|
// ../shared_libs/plays/dataset-summary.ts
|
|
4624
|
+
function formatDatasetRowCountsLine(counts) {
|
|
4625
|
+
const { persisted, succeeded, failed } = counts;
|
|
4626
|
+
if (succeeded === persisted && failed === 0) {
|
|
4627
|
+
return `${persisted} persisted`;
|
|
4628
|
+
}
|
|
4629
|
+
return `${persisted} persisted (${succeeded} succeeded, ${failed} failed)`;
|
|
4630
|
+
}
|
|
4552
4631
|
function datasetSummaryPercentText(numerator, denominator) {
|
|
4553
4632
|
return denominator > 0 ? `${numerator}/${denominator} (${Math.round(100 * numerator / denominator)}%)` : "0/0 (0%)";
|
|
4554
4633
|
}
|
|
4555
4634
|
function readCount(value) {
|
|
4556
4635
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : 0;
|
|
4557
4636
|
}
|
|
4558
|
-
function
|
|
4559
|
-
return
|
|
4637
|
+
function executionAttemptTotal(raw) {
|
|
4638
|
+
return readCount(raw.queued) + readCount(raw.running) + readCount(raw.completed) + readCount(raw.cached) + readCount(raw.skipped) + readCount(raw.missed) + readCount(raw.failed);
|
|
4639
|
+
}
|
|
4640
|
+
function formatDatasetExecutionStats(raw, _persistedRowTotal) {
|
|
4641
|
+
const denominator = executionAttemptTotal(raw);
|
|
4642
|
+
const stats = {
|
|
4560
4643
|
queued: datasetSummaryPercentText(readCount(raw.queued), denominator),
|
|
4561
4644
|
running: datasetSummaryPercentText(readCount(raw.running), denominator),
|
|
4562
4645
|
"completed:executed": datasetSummaryPercentText(
|
|
@@ -4577,6 +4660,17 @@ function formatDatasetExecutionStats(raw, denominator) {
|
|
|
4577
4660
|
),
|
|
4578
4661
|
failed: datasetSummaryPercentText(readCount(raw.failed), denominator)
|
|
4579
4662
|
};
|
|
4663
|
+
if (Object.values(stats).some((text) => {
|
|
4664
|
+
const match = /\((\d+)%\)/.exec(text);
|
|
4665
|
+
return match ? Number(match[1]) > 100 : false;
|
|
4666
|
+
})) {
|
|
4667
|
+
throw new Error(
|
|
4668
|
+
`formatDatasetExecutionStats produced a >100% execution stat; column counts are corrupt: ${JSON.stringify(
|
|
4669
|
+
raw
|
|
4670
|
+
)}`
|
|
4671
|
+
);
|
|
4672
|
+
}
|
|
4673
|
+
return stats;
|
|
4580
4674
|
}
|
|
4581
4675
|
|
|
4582
4676
|
// src/cli/dataset-stats.ts
|
|
@@ -4726,7 +4820,8 @@ function canonicalRowsInfoFromCandidate(input2) {
|
|
|
4726
4820
|
complete: rows2.length === totalRows2,
|
|
4727
4821
|
source: candidate.source,
|
|
4728
4822
|
datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
|
|
4729
|
-
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
|
|
4823
|
+
tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null,
|
|
4824
|
+
...candidate.value.recovered === true ? { recovered: true } : {}
|
|
4730
4825
|
};
|
|
4731
4826
|
}
|
|
4732
4827
|
if (candidate.serializedOnly) {
|
|
@@ -4876,18 +4971,46 @@ function collectCanonicalRowsInfos(statusOrResult) {
|
|
|
4876
4971
|
}
|
|
4877
4972
|
return infos;
|
|
4878
4973
|
}
|
|
4879
|
-
function
|
|
4974
|
+
function collectPackagedStepDatasetCandidates(statusOrResult) {
|
|
4880
4975
|
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
4881
|
-
|
|
4882
|
-
if (!result) {
|
|
4976
|
+
if (!root) {
|
|
4883
4977
|
return [];
|
|
4884
4978
|
}
|
|
4979
|
+
const pkg = isRecord3(root.package) ? root.package : root;
|
|
4980
|
+
const steps = Array.isArray(pkg.steps) ? pkg.steps : [];
|
|
4885
4981
|
const candidates = [];
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4982
|
+
for (const step of steps) {
|
|
4983
|
+
if (!isRecord3(step) || !isRecord3(step.output)) {
|
|
4984
|
+
continue;
|
|
4985
|
+
}
|
|
4986
|
+
const output2 = step.output;
|
|
4987
|
+
if (!isPackagedDatasetOutput(output2)) {
|
|
4988
|
+
continue;
|
|
4989
|
+
}
|
|
4990
|
+
const source = typeof output2.path === "string" && output2.path.trim() ? output2.path.trim() : typeof step.id === "string" ? step.id : null;
|
|
4991
|
+
if (!source) {
|
|
4992
|
+
continue;
|
|
4993
|
+
}
|
|
4994
|
+
candidates.push({
|
|
4995
|
+
source,
|
|
4996
|
+
value: output2,
|
|
4997
|
+
total: output2.rowCount ?? (isRecord3(output2.preview) ? output2.preview.totalRows : void 0) ?? (isRecord3(step.progress) ? step.progress.total : void 0)
|
|
4998
|
+
});
|
|
4999
|
+
}
|
|
5000
|
+
return candidates;
|
|
5001
|
+
}
|
|
5002
|
+
function collectSerializedDatasetRowsInfos(statusOrResult) {
|
|
5003
|
+
const root = isRecord3(statusOrResult) ? statusOrResult : null;
|
|
5004
|
+
const result = isRecord3(root?.result) ? root.result : root;
|
|
5005
|
+
const candidates = [];
|
|
5006
|
+
if (result) {
|
|
5007
|
+
collectDatasetCandidates({
|
|
5008
|
+
value: result,
|
|
5009
|
+
path: "result",
|
|
5010
|
+
output: candidates
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
5013
|
+
candidates.push(...collectPackagedStepDatasetCandidates(statusOrResult));
|
|
4891
5014
|
const seen = /* @__PURE__ */ new Set();
|
|
4892
5015
|
const infos = [];
|
|
4893
5016
|
for (const candidate of candidates) {
|
|
@@ -5511,7 +5634,7 @@ async function handleDbQuery(args) {
|
|
|
5511
5634
|
return 0;
|
|
5512
5635
|
}
|
|
5513
5636
|
function registerDbCommands(program) {
|
|
5514
|
-
const db = program.command("db").
|
|
5637
|
+
const db = program.command("db").description("Query the tenant customer database.").addHelpText(
|
|
5515
5638
|
"after",
|
|
5516
5639
|
`
|
|
5517
5640
|
Notes:
|
|
@@ -5532,7 +5655,7 @@ Examples:
|
|
|
5532
5655
|
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
5533
5656
|
`
|
|
5534
5657
|
);
|
|
5535
|
-
db.command("query").
|
|
5658
|
+
db.command("query").description("Run SQL against the tenant customer database.").addHelpText(
|
|
5536
5659
|
"after",
|
|
5537
5660
|
`
|
|
5538
5661
|
Notes:
|
|
@@ -5548,7 +5671,7 @@ Examples:
|
|
|
5548
5671
|
deepline db query --sql "select * from companies limit 20"
|
|
5549
5672
|
deepline db query --sql "select domain, name from companies limit 20" --json
|
|
5550
5673
|
deepline db query --sql "create table if not exists storage.agent_notes (id text primary key, note text not null)"
|
|
5551
|
-
deepline db
|
|
5674
|
+
deepline db query --sql "select count(*) from contacts" --json
|
|
5552
5675
|
deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
|
|
5553
5676
|
deepline db query --sql "select domain, name from companies limit 20" --format markdown
|
|
5554
5677
|
`
|
|
@@ -5576,6 +5699,7 @@ var import_node_path13 = require("path");
|
|
|
5576
5699
|
var import_node_crypto3 = require("crypto");
|
|
5577
5700
|
var import_node_fs10 = require("fs");
|
|
5578
5701
|
var import_node_path12 = require("path");
|
|
5702
|
+
var import_sync5 = require("csv-parse/sync");
|
|
5579
5703
|
|
|
5580
5704
|
// src/plays/bundle-play-file.ts
|
|
5581
5705
|
var import_node_os6 = require("os");
|
|
@@ -9104,7 +9228,7 @@ function traceCliSync(phase, fields, run) {
|
|
|
9104
9228
|
}
|
|
9105
9229
|
}
|
|
9106
9230
|
function sleep5(ms) {
|
|
9107
|
-
return new Promise((
|
|
9231
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
9108
9232
|
}
|
|
9109
9233
|
function parseReferencedPlayTarget2(target) {
|
|
9110
9234
|
const trimmed = target.trim();
|
|
@@ -9374,6 +9498,177 @@ function inputContainsLocalFilePath(value) {
|
|
|
9374
9498
|
}
|
|
9375
9499
|
return false;
|
|
9376
9500
|
}
|
|
9501
|
+
function isUrlValue(value) {
|
|
9502
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim());
|
|
9503
|
+
}
|
|
9504
|
+
function looksLikeStagedFileRef(value) {
|
|
9505
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9506
|
+
return false;
|
|
9507
|
+
}
|
|
9508
|
+
const record = value;
|
|
9509
|
+
return typeof record.contentHash === "string" || typeof record.contentBase64 === "string" || typeof record.logicalPath === "string" && typeof record.bytes === "number";
|
|
9510
|
+
}
|
|
9511
|
+
var CSV_DATA_INPUT_KEY = "csv";
|
|
9512
|
+
function collectLocalFileInputRefs(value, inputPath, key, out) {
|
|
9513
|
+
if (typeof value === "string") {
|
|
9514
|
+
const trimmed = value.trim();
|
|
9515
|
+
if (!trimmed || isUrlValue(trimmed)) {
|
|
9516
|
+
return;
|
|
9517
|
+
}
|
|
9518
|
+
const keyIsCsvData = key === CSV_DATA_INPUT_KEY;
|
|
9519
|
+
const endsWithCsv = /\.csv$/i.test(trimmed);
|
|
9520
|
+
const looksLikeFile = /\.[a-z0-9]{1,8}$/i.test(trimmed);
|
|
9521
|
+
if (keyIsCsvData) {
|
|
9522
|
+
out.push({ inputPath, value: trimmed, isCsvData: true });
|
|
9523
|
+
} else if (endsWithCsv || looksLikeFile && (0, import_node_fs10.existsSync)((0, import_node_path12.resolve)(trimmed))) {
|
|
9524
|
+
out.push({ inputPath, value: trimmed, isCsvData: false });
|
|
9525
|
+
}
|
|
9526
|
+
return;
|
|
9527
|
+
}
|
|
9528
|
+
if (Array.isArray(value)) {
|
|
9529
|
+
value.forEach(
|
|
9530
|
+
(entry, index) => collectLocalFileInputRefs(entry, `${inputPath}[${index}]`, key, out)
|
|
9531
|
+
);
|
|
9532
|
+
return;
|
|
9533
|
+
}
|
|
9534
|
+
if (looksLikeStagedFileRef(value)) {
|
|
9535
|
+
return;
|
|
9536
|
+
}
|
|
9537
|
+
if (value && typeof value === "object") {
|
|
9538
|
+
for (const [childKey, child] of Object.entries(value)) {
|
|
9539
|
+
collectLocalFileInputRefs(
|
|
9540
|
+
child,
|
|
9541
|
+
inputPath ? `${inputPath}.${childKey}` : childKey,
|
|
9542
|
+
childKey,
|
|
9543
|
+
out
|
|
9544
|
+
);
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
}
|
|
9548
|
+
function preflightLocalFileInputs(runtimeInput) {
|
|
9549
|
+
if (CSV_DATA_INPUT_KEY in runtimeInput) {
|
|
9550
|
+
const csvValue = runtimeInput[CSV_DATA_INPUT_KEY];
|
|
9551
|
+
if (typeof csvValue === "string" && csvValue.trim().length === 0) {
|
|
9552
|
+
throw new DeeplineError(
|
|
9553
|
+
`Input ${CSV_DATA_INPUT_KEY} is an empty string. Provide a path to a CSV file (or a CSV URL). No run was created.`,
|
|
9554
|
+
void 0,
|
|
9555
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9556
|
+
{ inputPath: CSV_DATA_INPUT_KEY, reason: "csv_empty_string" }
|
|
9557
|
+
);
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9560
|
+
const refs = [];
|
|
9561
|
+
for (const [key, value] of Object.entries(runtimeInput)) {
|
|
9562
|
+
collectLocalFileInputRefs(value, key, key, refs);
|
|
9563
|
+
}
|
|
9564
|
+
for (const ref of refs) {
|
|
9565
|
+
const absolutePath = (0, import_node_path12.resolve)(ref.value);
|
|
9566
|
+
if (!(0, import_node_fs10.existsSync)(absolutePath)) {
|
|
9567
|
+
throw new DeeplineError(
|
|
9568
|
+
`Input ${ref.inputPath} references a local file that does not exist: ${ref.value} (resolved to ${absolutePath}). No run was created.`,
|
|
9569
|
+
void 0,
|
|
9570
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9571
|
+
{ inputPath: ref.inputPath, path: ref.value, resolved: absolutePath }
|
|
9572
|
+
);
|
|
9573
|
+
}
|
|
9574
|
+
let stat4;
|
|
9575
|
+
try {
|
|
9576
|
+
stat4 = (0, import_node_fs10.statSync)(absolutePath);
|
|
9577
|
+
} catch (error) {
|
|
9578
|
+
throw new DeeplineError(
|
|
9579
|
+
`Input ${ref.inputPath} references a local file that is not readable: ${ref.value} (${error instanceof Error ? error.message : String(error)}). No run was created.`,
|
|
9580
|
+
void 0,
|
|
9581
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9582
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9583
|
+
);
|
|
9584
|
+
}
|
|
9585
|
+
if (!stat4.isFile()) {
|
|
9586
|
+
throw new DeeplineError(
|
|
9587
|
+
`Input ${ref.inputPath} references ${ref.value}, which is not a file. No run was created.`,
|
|
9588
|
+
void 0,
|
|
9589
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9590
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9591
|
+
);
|
|
9592
|
+
}
|
|
9593
|
+
if (!ref.isCsvData) {
|
|
9594
|
+
continue;
|
|
9595
|
+
}
|
|
9596
|
+
preflightCsvDataInput(ref, absolutePath);
|
|
9597
|
+
}
|
|
9598
|
+
}
|
|
9599
|
+
function preflightCsvDataInput(ref, absolutePath) {
|
|
9600
|
+
let content;
|
|
9601
|
+
try {
|
|
9602
|
+
content = (0, import_node_fs10.readFileSync)(absolutePath, "utf-8");
|
|
9603
|
+
} catch (error) {
|
|
9604
|
+
throw new DeeplineError(
|
|
9605
|
+
`Input ${ref.inputPath} CSV ${ref.value} is not readable: ${error instanceof Error ? error.message : String(error)}. No run was created.`,
|
|
9606
|
+
void 0,
|
|
9607
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9608
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9609
|
+
);
|
|
9610
|
+
}
|
|
9611
|
+
let records;
|
|
9612
|
+
try {
|
|
9613
|
+
records = (0, import_sync5.parse)(content, {
|
|
9614
|
+
bom: true,
|
|
9615
|
+
columns: false,
|
|
9616
|
+
skip_empty_lines: true,
|
|
9617
|
+
relax_column_count: true,
|
|
9618
|
+
// STRICT RFC quoting: an unclosed quote or ragged quoting is a hard
|
|
9619
|
+
// error rather than being swallowed into one giant field.
|
|
9620
|
+
relax_quotes: false,
|
|
9621
|
+
trim: true
|
|
9622
|
+
});
|
|
9623
|
+
} catch (error) {
|
|
9624
|
+
throw new DeeplineError(
|
|
9625
|
+
`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.`,
|
|
9626
|
+
void 0,
|
|
9627
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9628
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9629
|
+
);
|
|
9630
|
+
}
|
|
9631
|
+
if (records.length === 0) {
|
|
9632
|
+
throw new DeeplineError(
|
|
9633
|
+
`${ref.value} has a header row but no data rows. No run was created.`,
|
|
9634
|
+
void 0,
|
|
9635
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9636
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9637
|
+
);
|
|
9638
|
+
}
|
|
9639
|
+
const header = records[0];
|
|
9640
|
+
const seen = /* @__PURE__ */ new Map();
|
|
9641
|
+
for (let i = 0; i < header.length; i++) {
|
|
9642
|
+
const raw = header[i] ?? "";
|
|
9643
|
+
const name = raw.trim();
|
|
9644
|
+
if (name.length === 0) continue;
|
|
9645
|
+
const prior = seen.get(name);
|
|
9646
|
+
if (prior) {
|
|
9647
|
+
throw new DeeplineError(
|
|
9648
|
+
`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.`,
|
|
9649
|
+
void 0,
|
|
9650
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9651
|
+
{
|
|
9652
|
+
inputPath: ref.inputPath,
|
|
9653
|
+
path: ref.value,
|
|
9654
|
+
duplicateHeader: name,
|
|
9655
|
+
columns: [prior.index + 1, i + 1],
|
|
9656
|
+
rawSpellings: [prior.raw, raw]
|
|
9657
|
+
}
|
|
9658
|
+
);
|
|
9659
|
+
}
|
|
9660
|
+
seen.set(name, { index: i, raw });
|
|
9661
|
+
}
|
|
9662
|
+
const dataRowCount = records.length - 1;
|
|
9663
|
+
if (dataRowCount < 1) {
|
|
9664
|
+
throw new DeeplineError(
|
|
9665
|
+
`${ref.value} has a header row but no data rows. No run was created.`,
|
|
9666
|
+
void 0,
|
|
9667
|
+
"PLAY_INPUT_FILE_PREFLIGHT",
|
|
9668
|
+
{ inputPath: ref.inputPath, path: ref.value }
|
|
9669
|
+
);
|
|
9670
|
+
}
|
|
9671
|
+
}
|
|
9377
9672
|
function namedRunNeedsPlayDefinition(input2) {
|
|
9378
9673
|
return input2.revisionSelector === "latest" || inputContainsLocalFilePath(input2.runtimeInput);
|
|
9379
9674
|
}
|
|
@@ -9608,6 +9903,21 @@ function isRetryablePendingStartFailure(status) {
|
|
|
9608
9903
|
if (status.runId && status.runId !== "pending") return false;
|
|
9609
9904
|
return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
|
|
9610
9905
|
}
|
|
9906
|
+
function pendingStartFailureStatus(input2) {
|
|
9907
|
+
const message = input2.error instanceof Error ? input2.error.message : String(input2.error);
|
|
9908
|
+
return {
|
|
9909
|
+
runId: "pending",
|
|
9910
|
+
name: input2.playName,
|
|
9911
|
+
playName: input2.playName,
|
|
9912
|
+
dashboardUrl: input2.dashboardUrl,
|
|
9913
|
+
status: "failed",
|
|
9914
|
+
progress: {
|
|
9915
|
+
status: "failed",
|
|
9916
|
+
logs: [],
|
|
9917
|
+
error: message
|
|
9918
|
+
}
|
|
9919
|
+
};
|
|
9920
|
+
}
|
|
9611
9921
|
var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
|
|
9612
9922
|
"completed",
|
|
9613
9923
|
"failed",
|
|
@@ -10387,6 +10697,23 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10387
10697
|
progress: input2.progress
|
|
10388
10698
|
});
|
|
10389
10699
|
}
|
|
10700
|
+
if (!lastKnownWorkflowId && isTransientPlayStreamError(error)) {
|
|
10701
|
+
recordCliTrace({
|
|
10702
|
+
phase: "cli.play_start_stream_transient_failure",
|
|
10703
|
+
ms: Date.now() - startedAt,
|
|
10704
|
+
ok: false,
|
|
10705
|
+
playName: input2.playName,
|
|
10706
|
+
eventCount,
|
|
10707
|
+
firstRunIdMs,
|
|
10708
|
+
lastPhase,
|
|
10709
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
10710
|
+
});
|
|
10711
|
+
return pendingStartFailureStatus({
|
|
10712
|
+
playName: input2.playName,
|
|
10713
|
+
dashboardUrl,
|
|
10714
|
+
error
|
|
10715
|
+
});
|
|
10716
|
+
}
|
|
10390
10717
|
throw error;
|
|
10391
10718
|
} finally {
|
|
10392
10719
|
if (timeout) {
|
|
@@ -10438,6 +10765,28 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
|
|
|
10438
10765
|
function formatInteger(value) {
|
|
10439
10766
|
return typeof value === "number" && Number.isFinite(value) ? value.toLocaleString("en-US") : String(value ?? "-");
|
|
10440
10767
|
}
|
|
10768
|
+
var RUN_ERROR_DISPLAY_MAX_CHARS = 2e3;
|
|
10769
|
+
function truncateErrorForDisplay(message, runId, maxChars = RUN_ERROR_DISPLAY_MAX_CHARS) {
|
|
10770
|
+
const compact = message.replace(/\s+/g, " ").trim();
|
|
10771
|
+
if (compact.length <= maxChars) {
|
|
10772
|
+
return compact;
|
|
10773
|
+
}
|
|
10774
|
+
const slice = compact.slice(0, maxChars);
|
|
10775
|
+
const lastSpace = slice.lastIndexOf(" ");
|
|
10776
|
+
const wordBoundary = lastSpace > 0 ? slice.slice(0, lastSpace) : slice;
|
|
10777
|
+
const fullErrorHint = runId ? ` (full error: deepline runs get ${runId} --full)` : " (full error: deepline runs get <runId> --full)";
|
|
10778
|
+
return `${wordBoundary}\u2026${fullErrorHint}`;
|
|
10779
|
+
}
|
|
10780
|
+
function clampJsonPreviewText(json, maxChars) {
|
|
10781
|
+
if (json.length <= maxChars) {
|
|
10782
|
+
return json;
|
|
10783
|
+
}
|
|
10784
|
+
const slice = json.slice(0, maxChars);
|
|
10785
|
+
const lastNewline = slice.lastIndexOf("\n");
|
|
10786
|
+
const head = lastNewline > 0 ? slice.slice(0, lastNewline) : slice;
|
|
10787
|
+
return `${head}
|
|
10788
|
+
... truncated; use --json for full output`;
|
|
10789
|
+
}
|
|
10441
10790
|
var BULKY_RETURN_KEYS = /* @__PURE__ */ new Set([
|
|
10442
10791
|
"contract",
|
|
10443
10792
|
"staticPipeline",
|
|
@@ -10522,8 +10871,7 @@ function formatJsonPreview(value) {
|
|
|
10522
10871
|
return [];
|
|
10523
10872
|
}
|
|
10524
10873
|
const MAX_CHARS = 4e3;
|
|
10525
|
-
const truncated = json
|
|
10526
|
-
... truncated; use --json for full output` : json;
|
|
10874
|
+
const truncated = clampJsonPreviewText(json, MAX_CHARS);
|
|
10527
10875
|
return truncated.split("\n").map((line) => ` ${line}`);
|
|
10528
10876
|
}
|
|
10529
10877
|
function formatReturnValue(result) {
|
|
@@ -11054,6 +11402,9 @@ function readRecordArray(value) {
|
|
|
11054
11402
|
function readRecord(value) {
|
|
11055
11403
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11056
11404
|
}
|
|
11405
|
+
function readNonNegativeInteger(value) {
|
|
11406
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.trunc(value) : null;
|
|
11407
|
+
}
|
|
11057
11408
|
function formatSummaryScalar(value) {
|
|
11058
11409
|
if (typeof value === "number") {
|
|
11059
11410
|
return Number.isFinite(value) ? formatInteger(Math.trunc(value)) : null;
|
|
@@ -11086,7 +11437,25 @@ function formatPackageDatasetSummaryLines(summary, indent2 = " ") {
|
|
|
11086
11437
|
return [];
|
|
11087
11438
|
}
|
|
11088
11439
|
const lines = [];
|
|
11089
|
-
const
|
|
11440
|
+
const rowCounts = readRecord(record.rowCounts);
|
|
11441
|
+
if (rowCounts) {
|
|
11442
|
+
const persisted = readNonNegativeInteger(rowCounts.persisted);
|
|
11443
|
+
const succeeded = readNonNegativeInteger(rowCounts.succeeded);
|
|
11444
|
+
const failed = readNonNegativeInteger(rowCounts.failed);
|
|
11445
|
+
if (persisted !== null) {
|
|
11446
|
+
lines.push(
|
|
11447
|
+
`${indent2}rows: ${formatDatasetRowCountsLine({
|
|
11448
|
+
persisted,
|
|
11449
|
+
succeeded: succeeded ?? persisted,
|
|
11450
|
+
failed: failed ?? 0
|
|
11451
|
+
})}`
|
|
11452
|
+
);
|
|
11453
|
+
}
|
|
11454
|
+
}
|
|
11455
|
+
const parts = formatSummaryScalarParts(
|
|
11456
|
+
record,
|
|
11457
|
+
/* @__PURE__ */ new Set(["columnStats", "rowCounts"])
|
|
11458
|
+
);
|
|
11090
11459
|
if (parts.length > 0) {
|
|
11091
11460
|
lines.push(`${indent2}summary: ${parts.join(" ")}`);
|
|
11092
11461
|
}
|
|
@@ -11147,7 +11516,7 @@ function buildRunPackageTextLines(packaged) {
|
|
|
11147
11516
|
];
|
|
11148
11517
|
const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
|
|
11149
11518
|
if (runError && (status === "failed" || status === "cancelled")) {
|
|
11150
|
-
lines.push(` error: ${runError
|
|
11519
|
+
lines.push(` error: ${truncateErrorForDisplay(runError, runId)}`);
|
|
11151
11520
|
}
|
|
11152
11521
|
for (const step of readRecordArray(packaged.steps)) {
|
|
11153
11522
|
const output2 = step.output && typeof step.output === "object" && !Array.isArray(step.output) ? step.output : null;
|
|
@@ -11232,7 +11601,7 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
11232
11601
|
lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
|
|
11233
11602
|
}
|
|
11234
11603
|
const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
|
|
11235
|
-
lines.push(` error: ${displayError
|
|
11604
|
+
lines.push(` error: ${truncateErrorForDisplay(displayError, runId)}`);
|
|
11236
11605
|
}
|
|
11237
11606
|
const renderedServerView = renderServerResultView(status.resultView);
|
|
11238
11607
|
if (result) {
|
|
@@ -11403,12 +11772,51 @@ async function fetchBackingDatasetRows(input2) {
|
|
|
11403
11772
|
source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
|
|
11404
11773
|
};
|
|
11405
11774
|
}
|
|
11775
|
+
function resolveDatasetByName(available, datasetPath) {
|
|
11776
|
+
const target = datasetPath.trim();
|
|
11777
|
+
const exact = available.find((info) => info.source === target);
|
|
11778
|
+
if (exact) {
|
|
11779
|
+
return exact;
|
|
11780
|
+
}
|
|
11781
|
+
const byNamespace = available.find(
|
|
11782
|
+
(info) => info.tableNamespace && info.tableNamespace === target
|
|
11783
|
+
);
|
|
11784
|
+
if (byNamespace) {
|
|
11785
|
+
return byNamespace;
|
|
11786
|
+
}
|
|
11787
|
+
const trailing = target.split(".").filter(Boolean).at(-1);
|
|
11788
|
+
if (!trailing) {
|
|
11789
|
+
return null;
|
|
11790
|
+
}
|
|
11791
|
+
const byTrailing = available.find(
|
|
11792
|
+
(info) => info.tableNamespace === trailing || typeof info.source === "string" && info.source.split(".").filter(Boolean).at(-1) === trailing
|
|
11793
|
+
);
|
|
11794
|
+
if (byTrailing) {
|
|
11795
|
+
return byTrailing;
|
|
11796
|
+
}
|
|
11797
|
+
if (target.split(".").filter(Boolean)[0] === "result") {
|
|
11798
|
+
const recovered = available.filter((info) => info.recovered);
|
|
11799
|
+
if (recovered.length === 1) {
|
|
11800
|
+
return recovered[0];
|
|
11801
|
+
}
|
|
11802
|
+
if (recovered.length > 1) {
|
|
11803
|
+
const names = recovered.map((info) => info.tableNamespace ?? info.source).filter((name) => typeof name === "string");
|
|
11804
|
+
throw new DeeplineError(
|
|
11805
|
+
`Run returned multiple recovered datasets; '${target}' is ambiguous. Choose one with --dataset <path>: ${names.join(", ")}.`,
|
|
11806
|
+
void 0,
|
|
11807
|
+
"RUN_EXPORT_DATASET_AMBIGUOUS",
|
|
11808
|
+
{ dataset: target, available: names }
|
|
11809
|
+
);
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
return null;
|
|
11813
|
+
}
|
|
11406
11814
|
async function exportPlayStatusRows(client2, status, outPath, options = {}) {
|
|
11407
11815
|
if (!outPath) {
|
|
11408
11816
|
return null;
|
|
11409
11817
|
}
|
|
11410
11818
|
const availableRows = collectSerializedDatasetRowsInfos(status);
|
|
11411
|
-
const rowsInfo = options.datasetPath ? availableRows
|
|
11819
|
+
const rowsInfo = options.datasetPath ? resolveDatasetByName(availableRows, options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
|
|
11412
11820
|
if (!rowsInfo && options.datasetPath) {
|
|
11413
11821
|
const available = availableRows.map((info) => info.source).filter((source) => typeof source === "string");
|
|
11414
11822
|
throw new DeeplineError(
|
|
@@ -11888,7 +12296,7 @@ function parsePlayRunOptions(args) {
|
|
|
11888
12296
|
function parsePlayCheckOptions(args) {
|
|
11889
12297
|
const target = args[0];
|
|
11890
12298
|
if (!target) {
|
|
11891
|
-
throw new Error("Usage: deepline
|
|
12299
|
+
throw new Error("Usage: deepline plays check <play-file.ts> [--json]");
|
|
11892
12300
|
}
|
|
11893
12301
|
const jsonOutput = argsWantJson(args);
|
|
11894
12302
|
return { target, jsonOutput };
|
|
@@ -12154,6 +12562,12 @@ async function handleFileBackedRun(options) {
|
|
|
12154
12562
|
() => (0, import_node_fs10.readFileSync)(absolutePlayPath, "utf-8")
|
|
12155
12563
|
);
|
|
12156
12564
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
12565
|
+
try {
|
|
12566
|
+
preflightLocalFileInputs(runtimeInput);
|
|
12567
|
+
} catch (error) {
|
|
12568
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
12569
|
+
return 1;
|
|
12570
|
+
}
|
|
12157
12571
|
let graph;
|
|
12158
12572
|
try {
|
|
12159
12573
|
graph = await traceCliSpan(
|
|
@@ -12311,6 +12725,12 @@ async function handleNamedRun(options) {
|
|
|
12311
12725
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
12312
12726
|
const playName = options.target.name;
|
|
12313
12727
|
const runtimeInput = options.input ? { ...options.input } : {};
|
|
12728
|
+
try {
|
|
12729
|
+
preflightLocalFileInputs(runtimeInput);
|
|
12730
|
+
} catch (error) {
|
|
12731
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
12732
|
+
return 1;
|
|
12733
|
+
}
|
|
12314
12734
|
const needsPlayDefinition = namedRunNeedsPlayDefinition({
|
|
12315
12735
|
runtimeInput,
|
|
12316
12736
|
revisionSelector: options.revisionSelector
|
|
@@ -12806,7 +13226,9 @@ async function handleRunExport(args) {
|
|
|
12806
13226
|
async function handlePlayGet(args) {
|
|
12807
13227
|
const target = args[0];
|
|
12808
13228
|
if (!target) {
|
|
12809
|
-
console.error(
|
|
13229
|
+
console.error(
|
|
13230
|
+
"Usage: deepline plays get <play-file.ts|play-name> [--json]"
|
|
13231
|
+
);
|
|
12810
13232
|
return 1;
|
|
12811
13233
|
}
|
|
12812
13234
|
if (looksLikeRunId(target)) {
|
|
@@ -12904,7 +13326,7 @@ async function handlePlayVersions(args) {
|
|
|
12904
13326
|
const nameIndex = args.indexOf("--name");
|
|
12905
13327
|
const playName = nameIndex >= 0 ? args[nameIndex + 1] : void 0;
|
|
12906
13328
|
if (!playName) {
|
|
12907
|
-
console.error("Usage: deepline
|
|
13329
|
+
console.error("Usage: deepline plays versions --name <name> [--json]");
|
|
12908
13330
|
return 1;
|
|
12909
13331
|
}
|
|
12910
13332
|
const client2 = new DeeplineClient();
|
|
@@ -13284,7 +13706,7 @@ async function handlePlayPublish(args) {
|
|
|
13284
13706
|
const playName = args[0];
|
|
13285
13707
|
if (!playName) {
|
|
13286
13708
|
console.error(
|
|
13287
|
-
"Usage: deepline
|
|
13709
|
+
"Usage: deepline plays publish <play-file.ts|play-name> [--latest|--revision-id <id>] [--json]"
|
|
13288
13710
|
);
|
|
13289
13711
|
return 1;
|
|
13290
13712
|
}
|
|
@@ -13428,7 +13850,7 @@ async function handlePlayDelete(args) {
|
|
|
13428
13850
|
return result.deleted ? 0 : 1;
|
|
13429
13851
|
}
|
|
13430
13852
|
function registerPlayCommands(program) {
|
|
13431
|
-
const play = program.command("plays").
|
|
13853
|
+
const play = program.command("plays").description("Search, validate, run, and manage cloud plays.").addHelpText(
|
|
13432
13854
|
"after",
|
|
13433
13855
|
`
|
|
13434
13856
|
Concepts:
|
|
@@ -15935,7 +16357,7 @@ async function runGeneratedEnrichPlay(runArgs, options = {}) {
|
|
|
15935
16357
|
});
|
|
15936
16358
|
} catch (error) {
|
|
15937
16359
|
if (attempt === 0 && isPlayStartStreamEndedError(error)) {
|
|
15938
|
-
await new Promise((
|
|
16360
|
+
await new Promise((resolve16) => setTimeout(resolve16, 250));
|
|
15939
16361
|
continue;
|
|
15940
16362
|
}
|
|
15941
16363
|
throw error;
|
|
@@ -16735,19 +17157,16 @@ Notes:
|
|
|
16735
17157
|
Use --command and --payload to attach a reproducible command shape.
|
|
16736
17158
|
|
|
16737
17159
|
Examples:
|
|
16738
|
-
deepline feedback "plays run failed after upload" --command "deepline plays run my.play.ts --watch"
|
|
16739
|
-
deepline feedback "unexpected billing output" --payload '{"command":"billing usage"}' --json
|
|
17160
|
+
deepline feedback send "plays run failed after upload" --command "deepline plays run my.play.ts --watch"
|
|
17161
|
+
deepline feedback send "unexpected billing output" --payload '{"command":"billing usage"}' --json
|
|
16740
17162
|
`
|
|
16741
17163
|
);
|
|
16742
|
-
feedback.
|
|
16743
|
-
program.command("provide-feedback").description("Legacy alias for `deepline feedback`.").addHelpText(
|
|
17164
|
+
feedback.command("send").description("Send CLI feedback to Deepline.").addHelpText(
|
|
16744
17165
|
"after",
|
|
16745
17166
|
`
|
|
16746
|
-
Notes:
|
|
16747
|
-
Compatibility alias. Prefer deepline feedback in new scripts and docs.
|
|
16748
|
-
|
|
16749
17167
|
Examples:
|
|
16750
|
-
deepline feedback "tools search returned stale results" --json
|
|
17168
|
+
deepline feedback send "tools search returned stale results" --json
|
|
17169
|
+
deepline feedback send "plays run failed after upload" --command "deepline plays run my.play.ts --watch"
|
|
16751
17170
|
`
|
|
16752
17171
|
).argument("<text>", "Feedback text").option("--command <command>", "Command that reproduced the issue").option("--payload <payload>", "JSON or plain-text payload for the repro").option("--json", "Emit JSON output").action(handleFeedback);
|
|
16753
17172
|
}
|
|
@@ -16868,8 +17287,44 @@ async function handleOrgSwitch(selection, options) {
|
|
|
16868
17287
|
{ json: options.json }
|
|
16869
17288
|
);
|
|
16870
17289
|
}
|
|
17290
|
+
async function handleOrgCreate(name, options) {
|
|
17291
|
+
const config = resolveConfig();
|
|
17292
|
+
const http = new HttpClient(config);
|
|
17293
|
+
const created = await http.post("/api/v2/auth/cli/org-create", {
|
|
17294
|
+
api_key: config.apiKey,
|
|
17295
|
+
name
|
|
17296
|
+
});
|
|
17297
|
+
saveHostEnvValues(config.baseUrl, {
|
|
17298
|
+
DEEPLINE_API_KEY: created.api_key,
|
|
17299
|
+
DEEPLINE_ACTIVE_ORG_ID: created.org_id,
|
|
17300
|
+
DEEPLINE_ACTIVE_ORG_NAME: created.org_name
|
|
17301
|
+
});
|
|
17302
|
+
const { api_key: _apiKey, ...publicCreated } = created;
|
|
17303
|
+
printCommandEnvelope(
|
|
17304
|
+
{
|
|
17305
|
+
ok: true,
|
|
17306
|
+
...publicCreated,
|
|
17307
|
+
api_key_saved: true,
|
|
17308
|
+
switched: true,
|
|
17309
|
+
host_env_path: hostEnvFilePath(config.baseUrl),
|
|
17310
|
+
render: {
|
|
17311
|
+
sections: [
|
|
17312
|
+
{
|
|
17313
|
+
title: "org create",
|
|
17314
|
+
lines: [
|
|
17315
|
+
`Created organization: ${created.org_name}.`,
|
|
17316
|
+
`Switched to ${created.org_name}.`,
|
|
17317
|
+
`Saved host auth in ${hostEnvFilePath(config.baseUrl)}`
|
|
17318
|
+
]
|
|
17319
|
+
}
|
|
17320
|
+
]
|
|
17321
|
+
}
|
|
17322
|
+
},
|
|
17323
|
+
{ json: options.json }
|
|
17324
|
+
);
|
|
17325
|
+
}
|
|
16871
17326
|
function registerOrgCommands(program) {
|
|
16872
|
-
const org = program.command("org").description("List and switch organizations.").addHelpText(
|
|
17327
|
+
const org = program.command("org").description("List, create, and switch organizations.").addHelpText(
|
|
16873
17328
|
"after",
|
|
16874
17329
|
`
|
|
16875
17330
|
Notes:
|
|
@@ -16878,6 +17333,7 @@ Notes:
|
|
|
16878
17333
|
|
|
16879
17334
|
Examples:
|
|
16880
17335
|
deepline org list --json
|
|
17336
|
+
deepline org create Acme --json
|
|
16881
17337
|
deepline org switch 2
|
|
16882
17338
|
deepline org switch --org-id org_123 --json
|
|
16883
17339
|
`
|
|
@@ -16893,6 +17349,19 @@ Examples:
|
|
|
16893
17349
|
deepline org list --json
|
|
16894
17350
|
`
|
|
16895
17351
|
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgList);
|
|
17352
|
+
org.command("create <name>").description("Create a new organization and switch this CLI to it.").addHelpText(
|
|
17353
|
+
"after",
|
|
17354
|
+
`
|
|
17355
|
+
Notes:
|
|
17356
|
+
Mutates workspace state. The new organization is created for the current
|
|
17357
|
+
authenticated user, then the returned API key is saved for this host so later
|
|
17358
|
+
CLI commands target the new organization.
|
|
17359
|
+
|
|
17360
|
+
Examples:
|
|
17361
|
+
deepline org create Acme
|
|
17362
|
+
deepline org create "Acme Sales" --json
|
|
17363
|
+
`
|
|
17364
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleOrgCreate);
|
|
16896
17365
|
org.command("switch [selection]").description(
|
|
16897
17366
|
"Switch to another organization and save the new API key in the host auth file."
|
|
16898
17367
|
).addHelpText(
|
|
@@ -16937,7 +17406,7 @@ async function readHiddenLine(prompt) {
|
|
|
16937
17406
|
if (typeof import_node_process.stdin.setRawMode === "function") import_node_process.stdin.setRawMode(true);
|
|
16938
17407
|
let value = "";
|
|
16939
17408
|
import_node_process.stdin.resume();
|
|
16940
|
-
return await new Promise((
|
|
17409
|
+
return await new Promise((resolve16, reject) => {
|
|
16941
17410
|
let settled = false;
|
|
16942
17411
|
const cleanup = () => {
|
|
16943
17412
|
import_node_process.stdin.off("data", onData);
|
|
@@ -16952,7 +17421,7 @@ async function readHiddenLine(prompt) {
|
|
|
16952
17421
|
settled = true;
|
|
16953
17422
|
import_node_process.stdout.write("\n");
|
|
16954
17423
|
cleanup();
|
|
16955
|
-
|
|
17424
|
+
resolve16(line);
|
|
16956
17425
|
};
|
|
16957
17426
|
const fail = (error) => {
|
|
16958
17427
|
if (settled) return;
|
|
@@ -17122,16 +17591,553 @@ Examples:
|
|
|
17122
17591
|
);
|
|
17123
17592
|
}
|
|
17124
17593
|
|
|
17594
|
+
// src/cli/commands/sessions.ts
|
|
17595
|
+
var import_node_fs11 = require("fs");
|
|
17596
|
+
var import_node_os8 = require("os");
|
|
17597
|
+
var import_node_path14 = require("path");
|
|
17598
|
+
var import_node_zlib = require("zlib");
|
|
17599
|
+
var import_node_crypto4 = require("crypto");
|
|
17600
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17601
|
+
var MAX_SESSION_UPLOAD_BYTES = 35e5;
|
|
17602
|
+
var MAX_DIRECT_SESSION_DECODED_BYTES = 50 * 1024 * 1024;
|
|
17603
|
+
var CHUNK_SIZE_BYTES = 25e5;
|
|
17604
|
+
var MAX_EVENT_STRING_CHARS = 8e3;
|
|
17605
|
+
var MAX_EVENT_LIST_ITEMS = 40;
|
|
17606
|
+
var MAX_EVENT_OBJECT_KEYS = 80;
|
|
17607
|
+
var TRUNCATION_MARKER = "...[truncated]";
|
|
17608
|
+
var NOISE_EVENT_TYPES = /* @__PURE__ */ new Set(["progress", "file-history-snapshot"]);
|
|
17609
|
+
function homeDir() {
|
|
17610
|
+
return process.env.HOME?.trim() || (0, import_node_os8.homedir)();
|
|
17611
|
+
}
|
|
17612
|
+
function detectShellContext() {
|
|
17613
|
+
const shellPath = process.env.SHELL?.trim() || process.env.ComSpec?.trim() || process.env.COMSPEC?.trim() || "";
|
|
17614
|
+
return {
|
|
17615
|
+
shell: shellPath ? (0, import_node_path14.basename)(shellPath).replace(/\.exe$/i, "") : "unknown",
|
|
17616
|
+
shell_path: shellPath || null,
|
|
17617
|
+
os: (0, import_node_os8.platform)(),
|
|
17618
|
+
cwd: process.cwd()
|
|
17619
|
+
};
|
|
17620
|
+
}
|
|
17621
|
+
function claudeProjectsRoot() {
|
|
17622
|
+
return (0, import_node_path14.join)(homeDir(), ".claude", "projects");
|
|
17623
|
+
}
|
|
17624
|
+
function listClaudeSessionFiles() {
|
|
17625
|
+
const root = claudeProjectsRoot();
|
|
17626
|
+
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
17627
|
+
const projectDirs = readDirectoryNames(root);
|
|
17628
|
+
const files = [];
|
|
17629
|
+
for (const projectDir of projectDirs) {
|
|
17630
|
+
const fullProjectDir = (0, import_node_path14.join)(root, projectDir);
|
|
17631
|
+
for (const fileName of readDirectoryNames(fullProjectDir)) {
|
|
17632
|
+
if (fileName.endsWith(".jsonl")) {
|
|
17633
|
+
files.push((0, import_node_path14.join)(fullProjectDir, fileName));
|
|
17634
|
+
}
|
|
17635
|
+
}
|
|
17636
|
+
}
|
|
17637
|
+
return files;
|
|
17638
|
+
}
|
|
17639
|
+
function readDirectoryNames(dir) {
|
|
17640
|
+
try {
|
|
17641
|
+
return (0, import_node_fs11.readdirSync)(dir);
|
|
17642
|
+
} catch {
|
|
17643
|
+
return [];
|
|
17644
|
+
}
|
|
17645
|
+
}
|
|
17646
|
+
function newestClaudeSessionFile() {
|
|
17647
|
+
let newest = null;
|
|
17648
|
+
for (const filePath of listClaudeSessionFiles()) {
|
|
17649
|
+
try {
|
|
17650
|
+
const stat4 = (0, import_node_fs11.statSync)(filePath);
|
|
17651
|
+
if (!newest || stat4.mtimeMs > newest.mtimeMs) {
|
|
17652
|
+
newest = { filePath, mtimeMs: stat4.mtimeMs };
|
|
17653
|
+
}
|
|
17654
|
+
} catch {
|
|
17655
|
+
continue;
|
|
17656
|
+
}
|
|
17657
|
+
}
|
|
17658
|
+
return newest?.filePath ?? null;
|
|
17659
|
+
}
|
|
17660
|
+
function sessionIdFromFilePath(filePath) {
|
|
17661
|
+
return (0, import_node_path14.basename)(filePath, ".jsonl");
|
|
17662
|
+
}
|
|
17663
|
+
function findSessionFile(sessionId) {
|
|
17664
|
+
if (!UUID_RE.test(sessionId)) {
|
|
17665
|
+
throw new Error(
|
|
17666
|
+
"Invalid session ID format. Expected a UUID such as 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca."
|
|
17667
|
+
);
|
|
17668
|
+
}
|
|
17669
|
+
for (const filePath of listClaudeSessionFiles()) {
|
|
17670
|
+
if (sessionIdFromFilePath(filePath) === sessionId) {
|
|
17671
|
+
return filePath;
|
|
17672
|
+
}
|
|
17673
|
+
}
|
|
17674
|
+
return null;
|
|
17675
|
+
}
|
|
17676
|
+
function resolveSessionTargets(input2) {
|
|
17677
|
+
const targets = [];
|
|
17678
|
+
if (input2.currentSession) {
|
|
17679
|
+
const currentFile = newestClaudeSessionFile();
|
|
17680
|
+
if (!currentFile) {
|
|
17681
|
+
throw new Error("No session files found in ~/.claude/projects/*/.");
|
|
17682
|
+
}
|
|
17683
|
+
const sessionId = sessionIdFromFilePath(currentFile);
|
|
17684
|
+
targets.push({
|
|
17685
|
+
sessionId,
|
|
17686
|
+
label: `session-${sessionId}`,
|
|
17687
|
+
filePath: currentFile
|
|
17688
|
+
});
|
|
17689
|
+
}
|
|
17690
|
+
for (const [index, sessionId] of (input2.sessionIds ?? []).entries()) {
|
|
17691
|
+
const filePath = findSessionFile(sessionId);
|
|
17692
|
+
if (!filePath) {
|
|
17693
|
+
throw new Error(
|
|
17694
|
+
`Session file not found: ~/.claude/projects/*/${sessionId}.jsonl`
|
|
17695
|
+
);
|
|
17696
|
+
}
|
|
17697
|
+
targets.push({
|
|
17698
|
+
sessionId,
|
|
17699
|
+
label: input2.labels?.[index] ?? `session-${sessionId}`,
|
|
17700
|
+
filePath
|
|
17701
|
+
});
|
|
17702
|
+
}
|
|
17703
|
+
if (targets.length === 0) {
|
|
17704
|
+
throw new Error("One of --session-id or --current-session is required.");
|
|
17705
|
+
}
|
|
17706
|
+
return targets;
|
|
17707
|
+
}
|
|
17708
|
+
function parseJsonLine(line) {
|
|
17709
|
+
try {
|
|
17710
|
+
return JSON.parse(line);
|
|
17711
|
+
} catch {
|
|
17712
|
+
return null;
|
|
17713
|
+
}
|
|
17714
|
+
}
|
|
17715
|
+
function normalizedJsonLines(raw) {
|
|
17716
|
+
return raw.toString("utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
17717
|
+
}
|
|
17718
|
+
function stripNoiseEvents(raw) {
|
|
17719
|
+
const lines = [];
|
|
17720
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
17721
|
+
const parsed = parseJsonLine(line);
|
|
17722
|
+
if (parsed && typeof parsed === "object" && NOISE_EVENT_TYPES.has(String(parsed.type ?? ""))) {
|
|
17723
|
+
continue;
|
|
17724
|
+
}
|
|
17725
|
+
lines.push(line);
|
|
17726
|
+
}
|
|
17727
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
17728
|
+
` : "", "utf8");
|
|
17729
|
+
}
|
|
17730
|
+
function messageContentKey(value) {
|
|
17731
|
+
const message = value.message;
|
|
17732
|
+
if (!message || typeof message !== "object") return null;
|
|
17733
|
+
const content = message.content;
|
|
17734
|
+
if (typeof content === "string") return content;
|
|
17735
|
+
if (!Array.isArray(content)) return null;
|
|
17736
|
+
return content.map((block) => {
|
|
17737
|
+
if (!block || typeof block !== "object") return String(block);
|
|
17738
|
+
const record = block;
|
|
17739
|
+
const type = String(record.type ?? "");
|
|
17740
|
+
if (type === "tool_use") {
|
|
17741
|
+
return `tool_use:${String(record.name ?? "")}:${String(record.id ?? "")}`;
|
|
17742
|
+
}
|
|
17743
|
+
if (type === "tool_result") {
|
|
17744
|
+
return `tool_result:${String(record.tool_use_id ?? "")}`;
|
|
17745
|
+
}
|
|
17746
|
+
return String(record.text ?? type);
|
|
17747
|
+
}).join("\n");
|
|
17748
|
+
}
|
|
17749
|
+
function dedupConsecutiveEvents(raw) {
|
|
17750
|
+
const rawLines = normalizedJsonLines(raw);
|
|
17751
|
+
const parsedEvents = rawLines.map(parseJsonLine);
|
|
17752
|
+
const output2 = [];
|
|
17753
|
+
let index = 0;
|
|
17754
|
+
while (index < parsedEvents.length) {
|
|
17755
|
+
const event = parsedEvents[index];
|
|
17756
|
+
if (!event || typeof event !== "object") {
|
|
17757
|
+
output2.push(rawLines[index] ?? "");
|
|
17758
|
+
index += 1;
|
|
17759
|
+
continue;
|
|
17760
|
+
}
|
|
17761
|
+
const record = event;
|
|
17762
|
+
const eventType = String(record.type ?? "");
|
|
17763
|
+
const eventKey = messageContentKey(record);
|
|
17764
|
+
if (!["user", "assistant"].includes(eventType) || !eventKey) {
|
|
17765
|
+
output2.push(rawLines[index] ?? "");
|
|
17766
|
+
index += 1;
|
|
17767
|
+
continue;
|
|
17768
|
+
}
|
|
17769
|
+
let runCount = 1;
|
|
17770
|
+
let cursor = index + 1;
|
|
17771
|
+
while (cursor < parsedEvents.length) {
|
|
17772
|
+
const next = parsedEvents[cursor];
|
|
17773
|
+
if (!next || typeof next !== "object") break;
|
|
17774
|
+
const nextRecord = next;
|
|
17775
|
+
if (String(nextRecord.type ?? "") !== eventType || messageContentKey(nextRecord) !== eventKey) {
|
|
17776
|
+
break;
|
|
17777
|
+
}
|
|
17778
|
+
runCount += 1;
|
|
17779
|
+
cursor += 1;
|
|
17780
|
+
}
|
|
17781
|
+
if (runCount > 1) {
|
|
17782
|
+
record._repeat_count = runCount;
|
|
17783
|
+
record._repeat_summary = `${runCount} consecutive identical ${eventType} messages collapsed`;
|
|
17784
|
+
output2.push(JSON.stringify(record));
|
|
17785
|
+
index = cursor;
|
|
17786
|
+
continue;
|
|
17787
|
+
}
|
|
17788
|
+
output2.push(rawLines[index] ?? "");
|
|
17789
|
+
index += 1;
|
|
17790
|
+
}
|
|
17791
|
+
return Buffer.from(output2.length > 0 ? `${output2.join("\n")}
|
|
17792
|
+
` : "", "utf8");
|
|
17793
|
+
}
|
|
17794
|
+
function compactEventValue(value) {
|
|
17795
|
+
if (typeof value === "string") {
|
|
17796
|
+
if (value.length <= MAX_EVENT_STRING_CHARS) return value;
|
|
17797
|
+
return `${value.slice(0, MAX_EVENT_STRING_CHARS - TRUNCATION_MARKER.length)}${TRUNCATION_MARKER}`;
|
|
17798
|
+
}
|
|
17799
|
+
if (Array.isArray(value)) {
|
|
17800
|
+
const compacted = value.slice(0, MAX_EVENT_LIST_ITEMS).map(compactEventValue);
|
|
17801
|
+
if (value.length > MAX_EVENT_LIST_ITEMS) {
|
|
17802
|
+
compacted.push(
|
|
17803
|
+
`${TRUNCATION_MARKER} ${value.length - MAX_EVENT_LIST_ITEMS} more item(s)`
|
|
17804
|
+
);
|
|
17805
|
+
}
|
|
17806
|
+
return compacted;
|
|
17807
|
+
}
|
|
17808
|
+
if (value && typeof value === "object") {
|
|
17809
|
+
const entries = Object.entries(value);
|
|
17810
|
+
const compacted = {};
|
|
17811
|
+
for (const [key, item] of entries.slice(0, MAX_EVENT_OBJECT_KEYS)) {
|
|
17812
|
+
compacted[key] = compactEventValue(item);
|
|
17813
|
+
}
|
|
17814
|
+
if (entries.length > MAX_EVENT_OBJECT_KEYS) {
|
|
17815
|
+
compacted._truncated_keys = entries.length - MAX_EVENT_OBJECT_KEYS;
|
|
17816
|
+
}
|
|
17817
|
+
return compacted;
|
|
17818
|
+
}
|
|
17819
|
+
return value;
|
|
17820
|
+
}
|
|
17821
|
+
function selectiveCompactToolResults(raw) {
|
|
17822
|
+
const lines = [];
|
|
17823
|
+
for (const line of normalizedJsonLines(raw)) {
|
|
17824
|
+
const parsed = parseJsonLine(line);
|
|
17825
|
+
if (!parsed || typeof parsed !== "object") {
|
|
17826
|
+
lines.push(line);
|
|
17827
|
+
continue;
|
|
17828
|
+
}
|
|
17829
|
+
const record = parsed;
|
|
17830
|
+
if (record.type === "user") {
|
|
17831
|
+
const message = record.message;
|
|
17832
|
+
const content = message && typeof message === "object" ? message.content : null;
|
|
17833
|
+
if (Array.isArray(content)) {
|
|
17834
|
+
message.content = content.map(
|
|
17835
|
+
(block) => block && typeof block === "object" && block.type === "tool_result" ? compactEventValue(block) : block
|
|
17836
|
+
);
|
|
17837
|
+
}
|
|
17838
|
+
}
|
|
17839
|
+
lines.push(JSON.stringify(record));
|
|
17840
|
+
}
|
|
17841
|
+
return Buffer.from(lines.length > 0 ? `${lines.join("\n")}
|
|
17842
|
+
` : "", "utf8");
|
|
17843
|
+
}
|
|
17844
|
+
function prepareSessionBuffer(raw) {
|
|
17845
|
+
return selectiveCompactToolResults(
|
|
17846
|
+
dedupConsecutiveEvents(stripNoiseEvents(raw))
|
|
17847
|
+
);
|
|
17848
|
+
}
|
|
17849
|
+
function buildSessionUploadContent(raw) {
|
|
17850
|
+
const prepared = prepareSessionBuffer(raw);
|
|
17851
|
+
const encoded = (0, import_node_zlib.gzipSync)(prepared).toString("base64");
|
|
17852
|
+
if (encoded.length <= MAX_SESSION_UPLOAD_BYTES && prepared.length <= MAX_DIRECT_SESSION_DECODED_BYTES) {
|
|
17853
|
+
return { encodedContent: encoded, needsChunking: false };
|
|
17854
|
+
}
|
|
17855
|
+
return { encodedContent: encoded, needsChunking: true };
|
|
17856
|
+
}
|
|
17857
|
+
async function uploadPayload(path, payload) {
|
|
17858
|
+
const { http } = getAuthedHttpClient();
|
|
17859
|
+
return await http.post(path, payload);
|
|
17860
|
+
}
|
|
17861
|
+
async function uploadChunkedSessions(sessions, options) {
|
|
17862
|
+
const uploadId = (0, import_node_crypto4.randomUUID)();
|
|
17863
|
+
for (const session of sessions) {
|
|
17864
|
+
const bytes = Buffer.from(session.encodedContent, "base64");
|
|
17865
|
+
const chunks = [];
|
|
17866
|
+
for (let offset = 0; offset < bytes.length; offset += CHUNK_SIZE_BYTES) {
|
|
17867
|
+
chunks.push(bytes.subarray(offset, offset + CHUNK_SIZE_BYTES));
|
|
17868
|
+
}
|
|
17869
|
+
process.stderr.write(
|
|
17870
|
+
`Uploading ${session.label} in ${chunks.length} chunk(s)...
|
|
17871
|
+
`
|
|
17872
|
+
);
|
|
17873
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
17874
|
+
await uploadPayload("/api/v2/cli/send-session/chunk", {
|
|
17875
|
+
upload_id: uploadId,
|
|
17876
|
+
session_id: session.sessionId,
|
|
17877
|
+
index,
|
|
17878
|
+
total_chunks: chunks.length,
|
|
17879
|
+
data: chunk.toString("base64")
|
|
17880
|
+
});
|
|
17881
|
+
}
|
|
17882
|
+
}
|
|
17883
|
+
const response = await uploadPayload("/api/v2/cli/send-session/finalize", {
|
|
17884
|
+
upload_id: uploadId,
|
|
17885
|
+
session_ids: sessions.map((session) => session.sessionId),
|
|
17886
|
+
labels: sessions.map((session) => session.label),
|
|
17887
|
+
environments: sessions.map(() => detectShellContext())
|
|
17888
|
+
});
|
|
17889
|
+
printCommandEnvelope(
|
|
17890
|
+
{
|
|
17891
|
+
...response,
|
|
17892
|
+
ok: true,
|
|
17893
|
+
uploaded: sessions.length,
|
|
17894
|
+
render: {
|
|
17895
|
+
sections: [
|
|
17896
|
+
{
|
|
17897
|
+
title: "sessions send",
|
|
17898
|
+
lines: ["Session uploaded to #internal-reports (chunked)."]
|
|
17899
|
+
}
|
|
17900
|
+
]
|
|
17901
|
+
}
|
|
17902
|
+
},
|
|
17903
|
+
{ json: options.json }
|
|
17904
|
+
);
|
|
17905
|
+
}
|
|
17906
|
+
async function handleSessionsSend(options) {
|
|
17907
|
+
if (options.file) {
|
|
17908
|
+
const filePath = (0, import_node_path14.resolve)(options.file);
|
|
17909
|
+
if (!(0, import_node_fs11.existsSync)(filePath)) {
|
|
17910
|
+
throw new Error(`File not found: ${options.file}`);
|
|
17911
|
+
}
|
|
17912
|
+
const response2 = await uploadPayload("/api/v2/cli/send-session", {
|
|
17913
|
+
file: (0, import_node_fs11.readFileSync)(filePath).toString("base64"),
|
|
17914
|
+
filename: (0, import_node_path14.basename)(filePath)
|
|
17915
|
+
});
|
|
17916
|
+
printCommandEnvelope(
|
|
17917
|
+
{
|
|
17918
|
+
...response2,
|
|
17919
|
+
ok: true,
|
|
17920
|
+
filename: (0, import_node_path14.basename)(filePath),
|
|
17921
|
+
render: {
|
|
17922
|
+
sections: [
|
|
17923
|
+
{
|
|
17924
|
+
title: "sessions send",
|
|
17925
|
+
lines: [
|
|
17926
|
+
`File '${(0, import_node_path14.basename)(filePath)}' uploaded to #internal-reports.`
|
|
17927
|
+
]
|
|
17928
|
+
}
|
|
17929
|
+
]
|
|
17930
|
+
}
|
|
17931
|
+
},
|
|
17932
|
+
{ json: options.json }
|
|
17933
|
+
);
|
|
17934
|
+
return;
|
|
17935
|
+
}
|
|
17936
|
+
const targets = resolveSessionTargets({
|
|
17937
|
+
sessionIds: options.sessionId,
|
|
17938
|
+
labels: options.label,
|
|
17939
|
+
currentSession: options.currentSession
|
|
17940
|
+
});
|
|
17941
|
+
const built = targets.map((target) => {
|
|
17942
|
+
const upload = buildSessionUploadContent((0, import_node_fs11.readFileSync)(target.filePath));
|
|
17943
|
+
return { ...target, ...upload };
|
|
17944
|
+
});
|
|
17945
|
+
if (built.some((session) => session.needsChunking)) {
|
|
17946
|
+
await uploadChunkedSessions(built, options);
|
|
17947
|
+
return;
|
|
17948
|
+
}
|
|
17949
|
+
const response = built.length === 1 && !options.label?.length ? await uploadPayload("/api/v2/cli/send-session", {
|
|
17950
|
+
session_id: built[0]?.sessionId,
|
|
17951
|
+
content: built[0]?.encodedContent,
|
|
17952
|
+
environment: detectShellContext()
|
|
17953
|
+
}) : await uploadPayload("/api/v2/cli/send-session", {
|
|
17954
|
+
sessions: built.map((session) => ({
|
|
17955
|
+
session_id: session.sessionId,
|
|
17956
|
+
content: session.encodedContent,
|
|
17957
|
+
label: session.label,
|
|
17958
|
+
environment: detectShellContext()
|
|
17959
|
+
})),
|
|
17960
|
+
environment: detectShellContext()
|
|
17961
|
+
});
|
|
17962
|
+
printCommandEnvelope(
|
|
17963
|
+
{
|
|
17964
|
+
...response,
|
|
17965
|
+
ok: true,
|
|
17966
|
+
uploaded: built.length,
|
|
17967
|
+
render: {
|
|
17968
|
+
sections: [
|
|
17969
|
+
{
|
|
17970
|
+
title: "sessions send",
|
|
17971
|
+
lines: ["Session uploaded to #internal-reports."]
|
|
17972
|
+
}
|
|
17973
|
+
]
|
|
17974
|
+
}
|
|
17975
|
+
},
|
|
17976
|
+
{ json: options.json }
|
|
17977
|
+
);
|
|
17978
|
+
}
|
|
17979
|
+
function fallbackViewerAssets() {
|
|
17980
|
+
return {
|
|
17981
|
+
css: [
|
|
17982
|
+
"body{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;margin:0;padding:16px;background:#fafafa;color:#111}",
|
|
17983
|
+
".section{background:#fff;border:1px solid #ddd;border-radius:8px;padding:12px;margin-bottom:12px}",
|
|
17984
|
+
".section h2{margin:0 0 8px 0;font-size:14px}",
|
|
17985
|
+
"pre{margin:0;white-space:pre-wrap;word-break:break-word;background:#f6f8fa;border:1px solid #e3e5e8;border-radius:6px;padding:10px}"
|
|
17986
|
+
].join(""),
|
|
17987
|
+
js: [
|
|
17988
|
+
"(() => {",
|
|
17989
|
+
'const root=document.getElementById("main-content");',
|
|
17990
|
+
'const raw=document.getElementById("raw-sessions");',
|
|
17991
|
+
"if(!root||!raw)return;",
|
|
17992
|
+
'let sessions=[];try{sessions=JSON.parse(raw.textContent||"[]")}catch{}',
|
|
17993
|
+
'root.innerHTML="";',
|
|
17994
|
+
"for(const session of sessions){",
|
|
17995
|
+
'const section=document.createElement("section");section.className="section";',
|
|
17996
|
+
'const title=document.createElement("h2");title.textContent=String(session.label||"session");',
|
|
17997
|
+
'const pre=document.createElement("pre");',
|
|
17998
|
+
'pre.textContent=(Array.isArray(session.events)?session.events:[]).map((event)=>JSON.stringify(event)).join("\\n");',
|
|
17999
|
+
"section.append(title,pre);root.appendChild(section);",
|
|
18000
|
+
"}",
|
|
18001
|
+
"})();"
|
|
18002
|
+
].join("")
|
|
18003
|
+
};
|
|
18004
|
+
}
|
|
18005
|
+
function parsePreparedEvents(buffer) {
|
|
18006
|
+
return normalizedJsonLines(buffer).map((line) => {
|
|
18007
|
+
const parsed = parseJsonLine(line);
|
|
18008
|
+
return parsed ?? line;
|
|
18009
|
+
});
|
|
18010
|
+
}
|
|
18011
|
+
async function handleSessionsRender(options) {
|
|
18012
|
+
const targets = resolveSessionTargets({
|
|
18013
|
+
sessionIds: options.sessionId,
|
|
18014
|
+
labels: options.label,
|
|
18015
|
+
currentSession: options.currentSession
|
|
18016
|
+
});
|
|
18017
|
+
let outputPath = options.output ? (0, import_node_path14.resolve)(options.output) : "";
|
|
18018
|
+
if (!outputPath) {
|
|
18019
|
+
const outputDir = (0, import_node_path14.join)(process.cwd(), "deepline", "data");
|
|
18020
|
+
(0, import_node_fs11.mkdirSync)(outputDir, { recursive: true });
|
|
18021
|
+
outputPath = (0, import_node_path14.join)(
|
|
18022
|
+
outputDir,
|
|
18023
|
+
targets.length > 1 ? "session-viewer.html" : `session-${targets[0]?.sessionId}.html`
|
|
18024
|
+
);
|
|
18025
|
+
} else {
|
|
18026
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path14.dirname)(outputPath), { recursive: true });
|
|
18027
|
+
}
|
|
18028
|
+
const sessions = targets.map((target) => ({
|
|
18029
|
+
label: target.label,
|
|
18030
|
+
events: parsePreparedEvents(
|
|
18031
|
+
prepareSessionBuffer((0, import_node_fs11.readFileSync)(target.filePath))
|
|
18032
|
+
)
|
|
18033
|
+
}));
|
|
18034
|
+
const { css, js } = fallbackViewerAssets();
|
|
18035
|
+
const refreshMeta = options.autoRefresh ? `<meta http-equiv="refresh" content="${Number.parseInt(options.autoRefresh, 10)}">` : "";
|
|
18036
|
+
const rawJson = JSON.stringify(sessions).replace(/<\//g, "<\\/");
|
|
18037
|
+
const html = `<!DOCTYPE html>
|
|
18038
|
+
<html lang="en">
|
|
18039
|
+
<head>
|
|
18040
|
+
<meta charset="UTF-8">
|
|
18041
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
18042
|
+
${refreshMeta}
|
|
18043
|
+
<title>Session Viewer</title>
|
|
18044
|
+
<style>${css}</style>
|
|
18045
|
+
</head>
|
|
18046
|
+
<body>
|
|
18047
|
+
<div class="layout">
|
|
18048
|
+
<div class="main" id="main-content"></div>
|
|
18049
|
+
</div>
|
|
18050
|
+
<script type="application/json" id="raw-sessions">${rawJson}</script>
|
|
18051
|
+
<script>${js}</script>
|
|
18052
|
+
</body>
|
|
18053
|
+
</html>`;
|
|
18054
|
+
(0, import_node_fs11.writeFileSync)(outputPath, html, "utf8");
|
|
18055
|
+
printCommandEnvelope(
|
|
18056
|
+
{
|
|
18057
|
+
ok: true,
|
|
18058
|
+
file: outputPath,
|
|
18059
|
+
session_count: targets.length,
|
|
18060
|
+
render: {
|
|
18061
|
+
sections: [
|
|
18062
|
+
{
|
|
18063
|
+
title: "sessions render",
|
|
18064
|
+
lines: [`Rendered session viewer: ${outputPath}`]
|
|
18065
|
+
}
|
|
18066
|
+
]
|
|
18067
|
+
}
|
|
18068
|
+
},
|
|
18069
|
+
{ json: options.json }
|
|
18070
|
+
);
|
|
18071
|
+
}
|
|
18072
|
+
function collectOption(value, previous) {
|
|
18073
|
+
previous.push(value);
|
|
18074
|
+
return previous;
|
|
18075
|
+
}
|
|
18076
|
+
function registerSessionsCommands(program) {
|
|
18077
|
+
const sessions = program.command("sessions").description("Upload and render local agent session transcripts.").addHelpText(
|
|
18078
|
+
"after",
|
|
18079
|
+
`
|
|
18080
|
+
Notes:
|
|
18081
|
+
Session commands operate on local Claude session JSONL files under
|
|
18082
|
+
~/.claude/projects. send uploads a compacted transcript or file to Deepline.
|
|
18083
|
+
render writes a local HTML viewer.
|
|
18084
|
+
|
|
18085
|
+
Examples:
|
|
18086
|
+
deepline sessions send --current-session --json
|
|
18087
|
+
deepline sessions send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca
|
|
18088
|
+
deepline sessions render --current-session --output session.html
|
|
18089
|
+
`
|
|
18090
|
+
);
|
|
18091
|
+
sessions.command("send").description("Upload session transcript(s) or a local file to Deepline.").addHelpText(
|
|
18092
|
+
"after",
|
|
18093
|
+
`
|
|
18094
|
+
Examples:
|
|
18095
|
+
deepline sessions send --current-session --json
|
|
18096
|
+
deepline sessions send --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --label "pilot run"
|
|
18097
|
+
deepline sessions send --file ./debug.log --json
|
|
18098
|
+
`
|
|
18099
|
+
).option(
|
|
18100
|
+
"--session-id <uuid>",
|
|
18101
|
+
"Claude session UUID. Repeat for multiple sessions.",
|
|
18102
|
+
collectOption,
|
|
18103
|
+
[]
|
|
18104
|
+
).option(
|
|
18105
|
+
"--label <label>",
|
|
18106
|
+
"Label for the preceding session id",
|
|
18107
|
+
collectOption,
|
|
18108
|
+
[]
|
|
18109
|
+
).option("--current-session", "Use the newest local Claude session JSONL").option("--file <path>", "Upload a raw local file instead of a session").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsSend);
|
|
18110
|
+
sessions.command("render").description("Render local session transcript(s) to an HTML viewer.").addHelpText(
|
|
18111
|
+
"after",
|
|
18112
|
+
`
|
|
18113
|
+
Examples:
|
|
18114
|
+
deepline sessions render --current-session
|
|
18115
|
+
deepline sessions render --session-id 5a3bfb97-a2d9-49d9-82c6-52ccc03dadca --output session.html
|
|
18116
|
+
deepline sessions render --current-session --auto-refresh 5 --json
|
|
18117
|
+
`
|
|
18118
|
+
).option(
|
|
18119
|
+
"--session-id <uuid>",
|
|
18120
|
+
"Claude session UUID. Repeat for multiple sessions.",
|
|
18121
|
+
collectOption,
|
|
18122
|
+
[]
|
|
18123
|
+
).option(
|
|
18124
|
+
"--label <label>",
|
|
18125
|
+
"Label for the preceding session id",
|
|
18126
|
+
collectOption,
|
|
18127
|
+
[]
|
|
18128
|
+
).option("--current-session", "Use the newest local Claude session JSONL").option("--auto-refresh <seconds>", "Add a browser auto-refresh interval").option("-o, --output <path>", "Output HTML path").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSessionsRender);
|
|
18129
|
+
}
|
|
18130
|
+
|
|
17125
18131
|
// src/cli/commands/tools.ts
|
|
17126
18132
|
var import_commander2 = require("commander");
|
|
18133
|
+
var import_node_fs13 = require("fs");
|
|
18134
|
+
var import_node_os10 = require("os");
|
|
18135
|
+
var import_node_path16 = require("path");
|
|
18136
|
+
|
|
18137
|
+
// src/tool-output.ts
|
|
17127
18138
|
var import_node_fs12 = require("fs");
|
|
17128
18139
|
var import_node_os9 = require("os");
|
|
17129
18140
|
var import_node_path15 = require("path");
|
|
17130
|
-
|
|
17131
|
-
// src/tool-output.ts
|
|
17132
|
-
var import_node_fs11 = require("fs");
|
|
17133
|
-
var import_node_os8 = require("os");
|
|
17134
|
-
var import_node_path14 = require("path");
|
|
17135
18141
|
function isPlainObject(value) {
|
|
17136
18142
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17137
18143
|
}
|
|
@@ -17227,19 +18233,19 @@ function tryConvertToList(payload, options) {
|
|
|
17227
18233
|
return null;
|
|
17228
18234
|
}
|
|
17229
18235
|
function ensureOutputDir() {
|
|
17230
|
-
const outputDir = (0,
|
|
17231
|
-
(0,
|
|
18236
|
+
const outputDir = (0, import_node_path15.join)((0, import_node_os9.homedir)(), ".local", "share", "deepline", "data");
|
|
18237
|
+
(0, import_node_fs12.mkdirSync)(outputDir, { recursive: true });
|
|
17232
18238
|
return outputDir;
|
|
17233
18239
|
}
|
|
17234
18240
|
function writeJsonOutputFile(payload, stem) {
|
|
17235
18241
|
const outputDir = ensureOutputDir();
|
|
17236
|
-
const outputPath = (0,
|
|
17237
|
-
(0,
|
|
18242
|
+
const outputPath = (0, import_node_path15.join)(outputDir, `${stem}_${Date.now()}.json`);
|
|
18243
|
+
(0, import_node_fs12.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
17238
18244
|
return outputPath;
|
|
17239
18245
|
}
|
|
17240
18246
|
function writeCsvOutputFile(rows, stem) {
|
|
17241
18247
|
const outputDir = ensureOutputDir();
|
|
17242
|
-
const outputPath = (0,
|
|
18248
|
+
const outputPath = (0, import_node_path15.join)(outputDir, `${stem}_${Date.now()}.csv`);
|
|
17243
18249
|
const seen = /* @__PURE__ */ new Set();
|
|
17244
18250
|
const columns = [];
|
|
17245
18251
|
for (const row of rows) {
|
|
@@ -17262,7 +18268,7 @@ function writeCsvOutputFile(rows, stem) {
|
|
|
17262
18268
|
for (const row of rows) {
|
|
17263
18269
|
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
17264
18270
|
}
|
|
17265
|
-
(0,
|
|
18271
|
+
(0, import_node_fs12.writeFileSync)(outputPath, `${lines.join("\n")}
|
|
17266
18272
|
`, "utf-8");
|
|
17267
18273
|
const previewRows = rows.slice(0, 5);
|
|
17268
18274
|
const previewColumns = columns.slice(0, 5);
|
|
@@ -17548,7 +18554,7 @@ Common commands:
|
|
|
17548
18554
|
|
|
17549
18555
|
Output:
|
|
17550
18556
|
Use describe for tool contracts.
|
|
17551
|
-
Use execute to run a tool.
|
|
18557
|
+
Use execute to run a tool.
|
|
17552
18558
|
`
|
|
17553
18559
|
);
|
|
17554
18560
|
tools.command("list").description("List available tools.").addHelpText(
|
|
@@ -17641,7 +18647,7 @@ Examples:
|
|
|
17641
18647
|
Notes:
|
|
17642
18648
|
Shows the compact agent contract by default: what the tool does, cost,
|
|
17643
18649
|
required inputs, play getters, and a runnable ctx.tools.execute snippet.
|
|
17644
|
-
|
|
18650
|
+
get is accepted as a compatibility alias for describe.
|
|
17645
18651
|
|
|
17646
18652
|
Examples:
|
|
17647
18653
|
deepline tools describe hunter_email_verifier
|
|
@@ -17675,29 +18681,13 @@ Examples:
|
|
|
17675
18681
|
gettersOnly: Boolean(options.gettersOnly)
|
|
17676
18682
|
});
|
|
17677
18683
|
});
|
|
17678
|
-
addToolMetadataCommand(tools.command("describe <toolId>"));
|
|
17679
|
-
tools.command("
|
|
17680
|
-
"after",
|
|
17681
|
-
`
|
|
17682
|
-
Examples:
|
|
17683
|
-
deepline tools describe hunter_email_verifier --json
|
|
17684
|
-
`
|
|
17685
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
|
|
17686
|
-
const message = `tools get has been removed from the V2 SDK CLI. Use: deepline tools describe ${toolId} --json`;
|
|
17687
|
-
if (options.json || shouldEmitJson()) {
|
|
17688
|
-
printJsonError({ message, code: "TOOLS_GET_REMOVED" });
|
|
17689
|
-
} else {
|
|
17690
|
-
console.error(message);
|
|
17691
|
-
}
|
|
17692
|
-
process.exitCode = 2;
|
|
17693
|
-
});
|
|
17694
|
-
tools.command("execute <toolId>").alias("run").description("Execute a tool by id.").addHelpText(
|
|
18684
|
+
addToolMetadataCommand(tools.command("describe <toolId>").alias("get"));
|
|
18685
|
+
tools.command("execute <toolId>").description("Execute a tool by id.").addHelpText(
|
|
17695
18686
|
"after",
|
|
17696
18687
|
`
|
|
17697
18688
|
Notes:
|
|
17698
18689
|
Use tools for one atomic provider/API operation. Use plays for composed workflows,
|
|
17699
18690
|
waterfalls, row maps, checkpoints, and retries.
|
|
17700
|
-
execute is the canonical execution verb. run is a compatibility alias.
|
|
17701
18691
|
Calling a provider-backed tool can spend Deepline credits. Use --json for the
|
|
17702
18692
|
stable result payload plus output preview and debugging helpers.
|
|
17703
18693
|
|
|
@@ -17745,7 +18735,7 @@ Examples:
|
|
|
17745
18735
|
}
|
|
17746
18736
|
async function getTool(toolId, options = {}) {
|
|
17747
18737
|
if (!toolId) {
|
|
17748
|
-
console.error("Usage: deepline tools
|
|
18738
|
+
console.error("Usage: deepline tools describe <toolId> [--json]");
|
|
17749
18739
|
return 1;
|
|
17750
18740
|
}
|
|
17751
18741
|
const client2 = new DeeplineClient();
|
|
@@ -18296,11 +19286,11 @@ function normalizeOutputFormat(raw) {
|
|
|
18296
19286
|
}
|
|
18297
19287
|
function resolveAtFilePath(rawPath) {
|
|
18298
19288
|
const trimmed = rawPath.trim();
|
|
18299
|
-
const resolved = (0,
|
|
18300
|
-
if ((0,
|
|
19289
|
+
const resolved = (0, import_node_path16.resolve)(trimmed);
|
|
19290
|
+
if ((0, import_node_fs13.existsSync)(resolved)) return resolved;
|
|
18301
19291
|
if (process.platform !== "win32" && trimmed.includes("\\")) {
|
|
18302
|
-
const normalized = (0,
|
|
18303
|
-
if ((0,
|
|
19292
|
+
const normalized = (0, import_node_path16.resolve)(trimmed.replace(/\\/g, "/"));
|
|
19293
|
+
if ((0, import_node_fs13.existsSync)(normalized)) return normalized;
|
|
18304
19294
|
}
|
|
18305
19295
|
return resolved;
|
|
18306
19296
|
}
|
|
@@ -18311,7 +19301,7 @@ function readJsonArgument(raw, flagName) {
|
|
|
18311
19301
|
throw new Error(`Invalid ${flagName} value: empty @file path.`);
|
|
18312
19302
|
}
|
|
18313
19303
|
try {
|
|
18314
|
-
return (0,
|
|
19304
|
+
return (0, import_node_fs13.readFileSync)(resolveAtFilePath(filePath), "utf8").replace(
|
|
18315
19305
|
/^\uFEFF/,
|
|
18316
19306
|
""
|
|
18317
19307
|
);
|
|
@@ -18402,9 +19392,9 @@ function powerShellQuote(value) {
|
|
|
18402
19392
|
function seedToolListScript(input2) {
|
|
18403
19393
|
const stem = safeFileStem(input2.toolId);
|
|
18404
19394
|
const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
|
|
18405
|
-
const scriptDir = (0,
|
|
18406
|
-
(0,
|
|
18407
|
-
const scriptPath = (0,
|
|
19395
|
+
const scriptDir = (0, import_node_fs13.mkdtempSync)((0, import_node_path16.join)((0, import_node_os10.tmpdir)(), "deepline-workflow-seed-"));
|
|
19396
|
+
(0, import_node_fs13.chmodSync)(scriptDir, 448);
|
|
19397
|
+
const scriptPath = (0, import_node_path16.join)(scriptDir, fileName);
|
|
18408
19398
|
const projectDir = `deepline/projects/${stem}-workflow`;
|
|
18409
19399
|
const playName = `${stem}-workflow`;
|
|
18410
19400
|
const sampleRows = input2.rows.length > 0 ? `${JSON.stringify(input2.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
|
|
@@ -18440,7 +19430,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
|
|
|
18440
19430
|
};
|
|
18441
19431
|
});
|
|
18442
19432
|
`;
|
|
18443
|
-
(0,
|
|
19433
|
+
(0, import_node_fs13.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
|
|
18444
19434
|
return {
|
|
18445
19435
|
path: scriptPath,
|
|
18446
19436
|
sourceCode: script,
|
|
@@ -18695,10 +19685,10 @@ async function executeTool(args) {
|
|
|
18695
19685
|
|
|
18696
19686
|
// src/cli/commands/workflow.ts
|
|
18697
19687
|
var import_promises6 = require("fs/promises");
|
|
18698
|
-
var
|
|
19688
|
+
var import_node_path17 = require("path");
|
|
18699
19689
|
|
|
18700
19690
|
// src/cli/workflow-to-play.ts
|
|
18701
|
-
var
|
|
19691
|
+
var import_node_crypto5 = require("crypto");
|
|
18702
19692
|
var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
|
|
18703
19693
|
var HITL_SLACK_TOOL = "slack_message_with_hitl";
|
|
18704
19694
|
var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
|
|
@@ -18804,7 +19794,7 @@ function sanitizePlayNameSegment(value) {
|
|
|
18804
19794
|
}
|
|
18805
19795
|
function deriveWorkflowPlayName(workflowName) {
|
|
18806
19796
|
const base = sanitizePlayNameSegment(workflowName) || "workflow";
|
|
18807
|
-
const suffix = (0,
|
|
19797
|
+
const suffix = (0, import_node_crypto5.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
|
|
18808
19798
|
const reserved = suffix.length + 1;
|
|
18809
19799
|
const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
|
|
18810
19800
|
let name = `${base.slice(0, allowedBase)}_${suffix}`;
|
|
@@ -18902,7 +19892,7 @@ function readStatus(payload) {
|
|
|
18902
19892
|
}
|
|
18903
19893
|
async function readJsonOption(payload, file) {
|
|
18904
19894
|
if (file) {
|
|
18905
|
-
const raw = await (0, import_promises6.readFile)((0,
|
|
19895
|
+
const raw = await (0, import_promises6.readFile)((0, import_node_path17.resolve)(file), "utf8");
|
|
18906
19896
|
return JSON.parse(raw);
|
|
18907
19897
|
}
|
|
18908
19898
|
if (payload) {
|
|
@@ -18936,8 +19926,8 @@ async function transformOne(api, workflowId, outDir, publish) {
|
|
|
18936
19926
|
revision.config,
|
|
18937
19927
|
{ workflowName: workflow.name, version: revision.version }
|
|
18938
19928
|
);
|
|
18939
|
-
const file = (0,
|
|
18940
|
-
await (0, import_promises6.mkdir)((0,
|
|
19929
|
+
const file = (0, import_node_path17.join)((0, import_node_path17.resolve)(outDir), `${compiled.playName}.play.ts`);
|
|
19930
|
+
await (0, import_promises6.mkdir)((0, import_node_path17.dirname)(file), { recursive: true });
|
|
18941
19931
|
await (0, import_promises6.writeFile)(file, compiled.sourceCode, "utf8");
|
|
18942
19932
|
let published = false;
|
|
18943
19933
|
if (publish) {
|
|
@@ -19184,8 +20174,8 @@ Notes:
|
|
|
19184
20174
|
|
|
19185
20175
|
// src/cli/commands/update.ts
|
|
19186
20176
|
var import_node_child_process = require("child_process");
|
|
19187
|
-
var
|
|
19188
|
-
var
|
|
20177
|
+
var import_node_fs14 = require("fs");
|
|
20178
|
+
var import_node_path18 = require("path");
|
|
19189
20179
|
function posixShellQuote(value) {
|
|
19190
20180
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
19191
20181
|
}
|
|
@@ -19204,19 +20194,19 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
19204
20194
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
19205
20195
|
}
|
|
19206
20196
|
function findRepoBackedSdkRoot(startPath) {
|
|
19207
|
-
let current = (0,
|
|
20197
|
+
let current = (0, import_node_path18.resolve)(startPath);
|
|
19208
20198
|
while (true) {
|
|
19209
|
-
if ((0,
|
|
20199
|
+
if ((0, import_node_fs14.existsSync)((0, import_node_path18.join)(current, "sdk", "package.json")) && (0, import_node_fs14.existsSync)((0, import_node_path18.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
19210
20200
|
return current;
|
|
19211
20201
|
}
|
|
19212
|
-
const parent = (0,
|
|
20202
|
+
const parent = (0, import_node_path18.dirname)(current);
|
|
19213
20203
|
if (parent === current) return null;
|
|
19214
20204
|
current = parent;
|
|
19215
20205
|
}
|
|
19216
20206
|
}
|
|
19217
20207
|
function resolveUpdatePlan() {
|
|
19218
|
-
const entrypoint = process.argv[1] ? (0,
|
|
19219
|
-
const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0,
|
|
20208
|
+
const entrypoint = process.argv[1] ? (0, import_node_path18.resolve)(process.argv[1]) : "";
|
|
20209
|
+
const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path18.dirname)(entrypoint)) : null;
|
|
19220
20210
|
if (sourceRoot) {
|
|
19221
20211
|
return {
|
|
19222
20212
|
kind: "source",
|
|
@@ -19291,7 +20281,7 @@ async function handleUpdate(options) {
|
|
|
19291
20281
|
return runCommand(plan.command, plan.args);
|
|
19292
20282
|
}
|
|
19293
20283
|
function registerUpdateCommand(program) {
|
|
19294
|
-
program.command("update").
|
|
20284
|
+
program.command("update").description("Update the Deepline SDK/CLI.").addHelpText(
|
|
19295
20285
|
"after",
|
|
19296
20286
|
`
|
|
19297
20287
|
Notes:
|
|
@@ -19319,7 +20309,7 @@ var command_compatibility_default = {
|
|
|
19319
20309
|
session: {
|
|
19320
20310
|
family: "python",
|
|
19321
20311
|
label: "a legacy Python CLI session/playground command",
|
|
19322
|
-
sdk_alternative: "Use
|
|
20312
|
+
sdk_alternative: "Use `deepline sessions send ...` or `deepline sessions render ...` for transcript workflows."
|
|
19323
20313
|
},
|
|
19324
20314
|
workflows: {
|
|
19325
20315
|
family: "python",
|
|
@@ -19339,15 +20329,14 @@ var command_compatibility_default = {
|
|
|
19339
20329
|
label: "an SDK CLI play command",
|
|
19340
20330
|
python_alternative: "Use `deepline workflows ...` only for legacy workflows."
|
|
19341
20331
|
},
|
|
19342
|
-
play: {
|
|
19343
|
-
family: "sdk",
|
|
19344
|
-
label: "an SDK CLI play command",
|
|
19345
|
-
python_alternative: "Use `deepline workflows ...` only for legacy workflows."
|
|
19346
|
-
},
|
|
19347
20332
|
runs: {
|
|
19348
20333
|
family: "sdk",
|
|
19349
20334
|
label: "an SDK CLI run inspection command"
|
|
19350
20335
|
},
|
|
20336
|
+
sessions: {
|
|
20337
|
+
family: "sdk",
|
|
20338
|
+
label: "an SDK CLI session transcript command"
|
|
20339
|
+
},
|
|
19351
20340
|
health: {
|
|
19352
20341
|
family: "sdk",
|
|
19353
20342
|
label: "an SDK CLI health command"
|
|
@@ -19479,9 +20468,9 @@ function unknownCommandNameFromMessage(message) {
|
|
|
19479
20468
|
|
|
19480
20469
|
// src/cli/skills-sync.ts
|
|
19481
20470
|
var import_node_child_process2 = require("child_process");
|
|
19482
|
-
var
|
|
19483
|
-
var
|
|
19484
|
-
var
|
|
20471
|
+
var import_node_fs15 = require("fs");
|
|
20472
|
+
var import_node_os11 = require("os");
|
|
20473
|
+
var import_node_path19 = require("path");
|
|
19485
20474
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
19486
20475
|
var SDK_SKILL_NAME = "deepline-plays";
|
|
19487
20476
|
var attemptedSync = false;
|
|
@@ -19495,20 +20484,20 @@ function activePluginSkillsDir() {
|
|
|
19495
20484
|
return "";
|
|
19496
20485
|
}
|
|
19497
20486
|
const dir = process.env.DEEPLINE_PLUGIN_SKILLS_DIR?.trim() ?? "";
|
|
19498
|
-
return dir && (0,
|
|
20487
|
+
return dir && (0, import_node_fs15.existsSync)(dir) ? dir : "";
|
|
19499
20488
|
}
|
|
19500
20489
|
function readPluginSkillsVersion() {
|
|
19501
20490
|
const dir = activePluginSkillsDir();
|
|
19502
20491
|
if (!dir) return "";
|
|
19503
20492
|
try {
|
|
19504
|
-
return (0,
|
|
20493
|
+
return (0, import_node_fs15.readFileSync)((0, import_node_path19.join)(dir, ".version"), "utf-8").trim();
|
|
19505
20494
|
} catch {
|
|
19506
20495
|
return "";
|
|
19507
20496
|
}
|
|
19508
20497
|
}
|
|
19509
20498
|
function sdkSkillsVersionPath(baseUrl) {
|
|
19510
|
-
const home = process.env.HOME?.trim() || (0,
|
|
19511
|
-
return (0,
|
|
20499
|
+
const home = process.env.HOME?.trim() || (0, import_node_os11.homedir)();
|
|
20500
|
+
return (0, import_node_path19.join)(
|
|
19512
20501
|
home,
|
|
19513
20502
|
".local",
|
|
19514
20503
|
"deepline",
|
|
@@ -19521,25 +20510,25 @@ function readLocalSkillsVersion(baseUrl) {
|
|
|
19521
20510
|
const pluginVersion = readPluginSkillsVersion();
|
|
19522
20511
|
if (pluginVersion) return pluginVersion;
|
|
19523
20512
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
19524
|
-
if (!(0,
|
|
20513
|
+
if (!(0, import_node_fs15.existsSync)(path)) return "";
|
|
19525
20514
|
try {
|
|
19526
|
-
return (0,
|
|
20515
|
+
return (0, import_node_fs15.readFileSync)(path, "utf-8").trim();
|
|
19527
20516
|
} catch {
|
|
19528
20517
|
return "";
|
|
19529
20518
|
}
|
|
19530
20519
|
}
|
|
19531
20520
|
function writeLocalSkillsVersion(baseUrl, version) {
|
|
19532
20521
|
const path = sdkSkillsVersionPath(baseUrl);
|
|
19533
|
-
(0,
|
|
19534
|
-
(0,
|
|
20522
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path19.dirname)(path), { recursive: true });
|
|
20523
|
+
(0, import_node_fs15.writeFileSync)(path, `${version}
|
|
19535
20524
|
`, "utf-8");
|
|
19536
20525
|
}
|
|
19537
20526
|
function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
19538
|
-
const home = process.env.HOME?.trim() || (0,
|
|
20527
|
+
const home = process.env.HOME?.trim() || (0, import_node_os11.homedir)();
|
|
19539
20528
|
const pluginSkillsDir = activePluginSkillsDir();
|
|
19540
|
-
const roots = pluginSkillsDir ? [(0,
|
|
19541
|
-
(0,
|
|
19542
|
-
(0,
|
|
20529
|
+
const roots = pluginSkillsDir ? [(0, import_node_path19.join)(pluginSkillsDir, SDK_SKILL_NAME)] : [
|
|
20530
|
+
(0, import_node_path19.join)(home, ".claude", "skills", SDK_SKILL_NAME),
|
|
20531
|
+
(0, import_node_path19.join)(home, ".agents", "skills", SDK_SKILL_NAME)
|
|
19543
20532
|
];
|
|
19544
20533
|
const staleMarkers = [
|
|
19545
20534
|
"ctx.tools.execute(key",
|
|
@@ -19549,22 +20538,22 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
|
19549
20538
|
'rowCtx.tools.execute("'
|
|
19550
20539
|
];
|
|
19551
20540
|
const scan = (dir) => {
|
|
19552
|
-
for (const entry of (0,
|
|
19553
|
-
const path = (0,
|
|
19554
|
-
const stat4 = (0,
|
|
20541
|
+
for (const entry of (0, import_node_fs15.readdirSync)(dir)) {
|
|
20542
|
+
const path = (0, import_node_path19.join)(dir, entry);
|
|
20543
|
+
const stat4 = (0, import_node_fs15.statSync)(path);
|
|
19555
20544
|
if (stat4.isDirectory()) {
|
|
19556
20545
|
if (scan(path)) return true;
|
|
19557
20546
|
continue;
|
|
19558
20547
|
}
|
|
19559
20548
|
if (!entry.endsWith(".md")) continue;
|
|
19560
|
-
const text = (0,
|
|
20549
|
+
const text = (0, import_node_fs15.readFileSync)(path, "utf-8");
|
|
19561
20550
|
if (staleMarkers.some((marker) => text.includes(marker))) return true;
|
|
19562
20551
|
}
|
|
19563
20552
|
return false;
|
|
19564
20553
|
};
|
|
19565
20554
|
for (const root of roots) {
|
|
19566
20555
|
try {
|
|
19567
|
-
if ((0,
|
|
20556
|
+
if ((0, import_node_fs15.existsSync)(root) && scan(root)) return true;
|
|
19568
20557
|
} catch {
|
|
19569
20558
|
continue;
|
|
19570
20559
|
}
|
|
@@ -19636,7 +20625,7 @@ function resolveSkillsInstallCommands(baseUrl) {
|
|
|
19636
20625
|
return [npxInstall];
|
|
19637
20626
|
}
|
|
19638
20627
|
function runOneSkillsInstall(install) {
|
|
19639
|
-
return new Promise((
|
|
20628
|
+
return new Promise((resolve16) => {
|
|
19640
20629
|
const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
|
|
19641
20630
|
stdio: ["ignore", "ignore", "pipe"],
|
|
19642
20631
|
env: process.env
|
|
@@ -19646,7 +20635,7 @@ function runOneSkillsInstall(install) {
|
|
|
19646
20635
|
stderr += chunk.toString("utf-8");
|
|
19647
20636
|
});
|
|
19648
20637
|
child.on("error", (error) => {
|
|
19649
|
-
|
|
20638
|
+
resolve16({
|
|
19650
20639
|
ok: false,
|
|
19651
20640
|
detail: `failed to start ${install.command}: ${error.message}`,
|
|
19652
20641
|
manualCommand: install.manualCommand
|
|
@@ -19654,11 +20643,11 @@ function runOneSkillsInstall(install) {
|
|
|
19654
20643
|
});
|
|
19655
20644
|
child.on("close", (code) => {
|
|
19656
20645
|
if (code === 0) {
|
|
19657
|
-
|
|
20646
|
+
resolve16({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
19658
20647
|
return;
|
|
19659
20648
|
}
|
|
19660
20649
|
const detail = stderr.trim();
|
|
19661
|
-
|
|
20650
|
+
resolve16({
|
|
19662
20651
|
ok: false,
|
|
19663
20652
|
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
19664
20653
|
manualCommand: install.manualCommand
|
|
@@ -19746,8 +20735,8 @@ function shouldDeferSkillsSyncForCommand() {
|
|
|
19746
20735
|
return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
|
|
19747
20736
|
}
|
|
19748
20737
|
async function runPlayRunnerHealthCheck() {
|
|
19749
|
-
const dir = await (0, import_promises7.mkdtemp)((0,
|
|
19750
|
-
const file = (0,
|
|
20738
|
+
const dir = await (0, import_promises7.mkdtemp)((0, import_node_path20.join)((0, import_node_os12.tmpdir)(), "deepline-health-play-"));
|
|
20739
|
+
const file = (0, import_node_path20.join)(dir, "health-check.play.ts");
|
|
19751
20740
|
try {
|
|
19752
20741
|
await (0, import_promises7.writeFile)(
|
|
19753
20742
|
file,
|
|
@@ -19935,6 +20924,7 @@ Common commands:
|
|
|
19935
20924
|
deepline preflight
|
|
19936
20925
|
deepline health
|
|
19937
20926
|
deepline auth status --json
|
|
20927
|
+
deepline sessions send --current-session --json
|
|
19938
20928
|
deepline plays search email --json
|
|
19939
20929
|
deepline plays describe person-linkedin-to-email --json
|
|
19940
20930
|
deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
|
|
@@ -19987,6 +20977,7 @@ Exit codes:
|
|
|
19987
20977
|
registerAuthCommands(program);
|
|
19988
20978
|
registerToolsCommands(program);
|
|
19989
20979
|
registerPlayCommands(program);
|
|
20980
|
+
registerSessionsCommands(program);
|
|
19990
20981
|
registerWorkflowCommands(program);
|
|
19991
20982
|
registerSecretsCommands(program);
|
|
19992
20983
|
registerBillingCommands(program);
|