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/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 import_node_path19 = require("path");
29
- var import_node_os11 = require("os");
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
- version: "0.1.101",
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.101",
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((resolve15) => setTimeout(resolve15, ms));
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((resolve15) => {
1299
- wake = resolve15;
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
- (resolve15) => setTimeout(() => resolve15(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
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((resolve15) => setTimeout(resolve15, ms));
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 play run`, repo scripts such as `bun run deepline -- play run`,
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 play check`.
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 play run --watch`.
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 homeDir = process.env.HOME || (0, import_node_os3.homedir)();
3115
+ const homeDir2 = process.env.HOME || (0, import_node_os3.homedir)();
3050
3116
  return (0, import_node_path3.join)(
3051
- homeDir,
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 = 1;
3526
- var EXIT_SERVER = 2;
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((resolve15) => setTimeout(resolve15, ms));
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: [{ label: "Run", command: "deepline auth register" }]
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: [{ label: "Run", command: "deepline auth register" }]
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 formatDatasetExecutionStats(raw, denominator) {
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 collectSerializedDatasetRowsInfos(statusOrResult) {
4974
+ function collectPackagedStepDatasetCandidates(statusOrResult) {
4880
4975
  const root = isRecord3(statusOrResult) ? statusOrResult : null;
4881
- const result = isRecord3(root?.result) ? root.result : root;
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
- collectDatasetCandidates({
4887
- value: result,
4888
- path: "result",
4889
- output: candidates
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").alias("customer-db").description("Query the tenant customer database.").addHelpText(
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").alias("psql").description("Run SQL against the tenant customer database.").addHelpText(
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 psql --sql "select count(*) from contacts" --json
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((resolve15) => setTimeout(resolve15, ms));
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.length > MAX_CHARS ? `${json.slice(0, MAX_CHARS)}
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 parts = formatSummaryScalarParts(record, /* @__PURE__ */ new Set(["columnStats"]));
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.slice(0, 200)}`);
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.slice(0, 200)}`);
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.find((info) => info.source === options.datasetPath) ?? null : availableRows.length === 1 ? availableRows[0] : null;
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 play check <play-file.ts> [--json]");
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("Usage: deepline play get <play-file.ts|play-name> [--json]");
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 play versions --name <name> [--json]");
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 play publish <play-file.ts|play-name> [--latest|--revision-id <id>] [--json]"
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").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
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((resolve15) => setTimeout(resolve15, 250));
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.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);
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((resolve15, reject) => {
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
- resolve15(line);
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, import_node_path14.join)((0, import_node_os8.homedir)(), ".local", "share", "deepline", "data");
17231
- (0, import_node_fs11.mkdirSync)(outputDir, { recursive: true });
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, import_node_path14.join)(outputDir, `${stem}_${Date.now()}.json`);
17237
- (0, import_node_fs11.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf-8");
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, import_node_path14.join)(outputDir, `${stem}_${Date.now()}.csv`);
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, import_node_fs11.writeFileSync)(outputPath, `${lines.join("\n")}
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. run is accepted as a compatibility alias.
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
- Use --json for the full metadata/debug payload.
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("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
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 get <toolId> [--json]");
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, import_node_path15.resolve)(trimmed);
18300
- if ((0, import_node_fs12.existsSync)(resolved)) return resolved;
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, import_node_path15.resolve)(trimmed.replace(/\\/g, "/"));
18303
- if ((0, import_node_fs12.existsSync)(normalized)) return normalized;
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, import_node_fs12.readFileSync)(resolveAtFilePath(filePath), "utf8").replace(
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, import_node_fs12.mkdtempSync)((0, import_node_path15.join)((0, import_node_os9.tmpdir)(), "deepline-workflow-seed-"));
18406
- (0, import_node_fs12.chmodSync)(scriptDir, 448);
18407
- const scriptPath = (0, import_node_path15.join)(scriptDir, fileName);
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, import_node_fs12.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
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 import_node_path16 = require("path");
19688
+ var import_node_path17 = require("path");
18699
19689
 
18700
19690
  // src/cli/workflow-to-play.ts
18701
- var import_node_crypto4 = require("crypto");
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, import_node_crypto4.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
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, import_node_path16.resolve)(file), "utf8");
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, import_node_path16.join)((0, import_node_path16.resolve)(outDir), `${compiled.playName}.play.ts`);
18940
- await (0, import_promises6.mkdir)((0, import_node_path16.dirname)(file), { recursive: true });
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 import_node_fs13 = require("fs");
19188
- var import_node_path17 = require("path");
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, import_node_path17.resolve)(startPath);
20197
+ let current = (0, import_node_path18.resolve)(startPath);
19208
20198
  while (true) {
19209
- if ((0, import_node_fs13.existsSync)((0, import_node_path17.join)(current, "sdk", "package.json")) && (0, import_node_fs13.existsSync)((0, import_node_path17.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
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, import_node_path17.dirname)(current);
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, import_node_path17.resolve)(process.argv[1]) : "";
19219
- const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path17.dirname)(entrypoint)) : null;
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").alias("upgrade").description("Update the Deepline SDK/CLI.").addHelpText(
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 SDK play run output and run commands such as `deepline plays run ...`."
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 import_node_fs14 = require("fs");
19483
- var import_node_os10 = require("os");
19484
- var import_node_path18 = require("path");
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, import_node_fs14.existsSync)(dir) ? dir : "";
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, import_node_fs14.readFileSync)((0, import_node_path18.join)(dir, ".version"), "utf-8").trim();
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, import_node_os10.homedir)();
19511
- return (0, import_node_path18.join)(
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, import_node_fs14.existsSync)(path)) return "";
20513
+ if (!(0, import_node_fs15.existsSync)(path)) return "";
19525
20514
  try {
19526
- return (0, import_node_fs14.readFileSync)(path, "utf-8").trim();
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, import_node_fs14.mkdirSync)((0, import_node_path18.dirname)(path), { recursive: true });
19534
- (0, import_node_fs14.writeFileSync)(path, `${version}
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, import_node_os10.homedir)();
20527
+ const home = process.env.HOME?.trim() || (0, import_node_os11.homedir)();
19539
20528
  const pluginSkillsDir = activePluginSkillsDir();
19540
- const roots = pluginSkillsDir ? [(0, import_node_path18.join)(pluginSkillsDir, SDK_SKILL_NAME)] : [
19541
- (0, import_node_path18.join)(home, ".claude", "skills", SDK_SKILL_NAME),
19542
- (0, import_node_path18.join)(home, ".agents", "skills", SDK_SKILL_NAME)
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, import_node_fs14.readdirSync)(dir)) {
19553
- const path = (0, import_node_path18.join)(dir, entry);
19554
- const stat4 = (0, import_node_fs14.statSync)(path);
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, import_node_fs14.readFileSync)(path, "utf-8");
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, import_node_fs14.existsSync)(root) && scan(root)) return true;
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((resolve15) => {
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
- resolve15({
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
- resolve15({ ok: true, detail: "", manualCommand: install.manualCommand });
20646
+ resolve16({ ok: true, detail: "", manualCommand: install.manualCommand });
19658
20647
  return;
19659
20648
  }
19660
20649
  const detail = stderr.trim();
19661
- resolve15({
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, import_node_path19.join)((0, import_node_os11.tmpdir)(), "deepline-health-play-"));
19750
- const file = (0, import_node_path19.join)(dir, "health-check.play.ts");
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);