deepline 0.1.33 → 0.1.35

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
@@ -216,8 +216,8 @@ function resolveConfig(options) {
216
216
  }
217
217
 
218
218
  // src/version.ts
219
- var SDK_VERSION = "0.1.33";
220
- var SDK_API_CONTRACT = "2026-05-host-env-generic-play-input-flags";
219
+ var SDK_VERSION = "0.1.35";
220
+ var SDK_API_CONTRACT = "2026-05-v2-tool-result-contract";
221
221
 
222
222
  // ../shared_libs/play-runtime/coordinator-headers.ts
223
223
  var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
@@ -299,7 +299,7 @@ var HttpClient = class {
299
299
  const response = await fetch(candidateUrl, {
300
300
  method,
301
301
  headers,
302
- body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
302
+ body: options?.formData !== void 0 ? typeof options.formData === "function" ? options.formData() : options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
303
303
  signal: controller.signal
304
304
  });
305
305
  clearTimeout(timeoutId);
@@ -382,10 +382,13 @@ var HttpClient = class {
382
382
  throw new AuthError();
383
383
  }
384
384
  if (!response.ok) {
385
+ const body = await response.text();
386
+ const parsed = parseResponseBody(body);
385
387
  throw new DeeplineError(
386
- `HTTP ${response.status}`,
388
+ apiErrorMessage(parsed, response.status),
387
389
  response.status,
388
- "API_ERROR"
390
+ "API_ERROR",
391
+ { response: parsed }
389
392
  );
390
393
  }
391
394
  if (!response.body) {
@@ -435,6 +438,26 @@ var HttpClient = class {
435
438
  return this.request(path, { method: "DELETE" });
436
439
  }
437
440
  };
441
+ function parseResponseBody(body) {
442
+ try {
443
+ return JSON.parse(body);
444
+ } catch {
445
+ return body;
446
+ }
447
+ }
448
+ function apiErrorMessage(parsed, status) {
449
+ const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
450
+ if (typeof errorValue === "string") {
451
+ return errorValue;
452
+ }
453
+ if (errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string") {
454
+ return errorValue.message;
455
+ }
456
+ if (typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string") {
457
+ return parsed.message;
458
+ }
459
+ return `HTTP ${status}`;
460
+ }
438
461
  function parseRetryAfter(response) {
439
462
  const header = response.headers.get("retry-after");
440
463
  if (header) {
@@ -498,15 +521,17 @@ function decodeSseFrame(frame) {
498
521
  return parsed;
499
522
  }
500
523
  function sleep(ms) {
501
- return new Promise((resolve10) => setTimeout(resolve10, ms));
524
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
502
525
  }
503
526
 
504
527
  // src/client.ts
505
528
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
506
529
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
530
+ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
531
+ var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-execution-result";
507
532
  var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
508
533
  function sleep2(ms) {
509
- return new Promise((resolve10) => setTimeout(resolve10, ms));
534
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
510
535
  }
511
536
  function isTransientCompileManifestError(error) {
512
537
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
@@ -772,13 +797,16 @@ var DeeplineClient = class {
772
797
  /**
773
798
  * Execute a tool and return the standard execution envelope.
774
799
  *
775
- * The `result.data` field contains the provider payload. `result.meta`
776
- * contains provider/upstream metadata such as HTTP status or paging details.
800
+ * The `toolExecutionResult.toolOutput.raw` field contains the raw tool output.
801
+ * `toolExecutionResult.toolOutput.meta` contains tool/provider metadata.
777
802
  * Top-level fields such as `status`, `job_id`, and `billing` describe the
778
- * Deepline execution.
803
+ * Deepline execution envelope.
779
804
  */
780
805
  async executeTool(toolId, input, options) {
781
- const headers = options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : void 0;
806
+ const headers = {
807
+ [EXECUTE_RESPONSE_CONTRACT_HEADER]: V2_EXECUTE_RESPONSE_CONTRACT,
808
+ ...options?.includeToolMetadata ? { [INCLUDE_TOOL_METADATA_HEADER]: "true" } : {}
809
+ };
782
810
  return this.http.post(
783
811
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
784
812
  { payload: input },
@@ -1076,34 +1104,37 @@ var DeeplineClient = class {
1076
1104
  * ```
1077
1105
  */
1078
1106
  async stagePlayFiles(files) {
1079
- const formData = new FormData();
1080
- formData.set(
1081
- "metadata",
1082
- JSON.stringify({
1083
- files: files.map((file, index) => ({
1084
- index,
1085
- logicalPath: file.logicalPath,
1086
- contentHash: file.contentHash,
1087
- contentType: file.contentType,
1088
- bytes: file.bytes
1089
- }))
1090
- })
1091
- );
1092
- for (const [index, file] of files.entries()) {
1093
- const bytes = decodeBase64Bytes(file.contentBase64);
1094
- const body = bytes.buffer.slice(
1095
- bytes.byteOffset,
1096
- bytes.byteOffset + bytes.byteLength
1097
- );
1107
+ const buildFormData = () => {
1108
+ const formData = new FormData();
1098
1109
  formData.set(
1099
- `file:${index}`,
1100
- new Blob([body], { type: file.contentType }),
1101
- file.logicalPath
1110
+ "metadata",
1111
+ JSON.stringify({
1112
+ files: files.map((file, index) => ({
1113
+ index,
1114
+ logicalPath: file.logicalPath,
1115
+ contentHash: file.contentHash,
1116
+ contentType: file.contentType,
1117
+ bytes: file.bytes
1118
+ }))
1119
+ })
1102
1120
  );
1103
- }
1121
+ for (const [index, file] of files.entries()) {
1122
+ const bytes = decodeBase64Bytes(file.contentBase64);
1123
+ const body = bytes.buffer.slice(
1124
+ bytes.byteOffset,
1125
+ bytes.byteOffset + bytes.byteLength
1126
+ );
1127
+ formData.set(
1128
+ `file:${index}`,
1129
+ new Blob([body], { type: file.contentType }),
1130
+ file.logicalPath
1131
+ );
1132
+ }
1133
+ return formData;
1134
+ };
1104
1135
  const response = await this.http.postFormData(
1105
1136
  "/api/v2/plays/files/stage",
1106
- formData
1137
+ buildFormData
1107
1138
  );
1108
1139
  return response.files;
1109
1140
  }
@@ -1892,6 +1923,103 @@ function csvStringFromRows(rows, columns) {
1892
1923
  ...columns?.length ? { columns } : {}
1893
1924
  });
1894
1925
  }
1926
+ function parseMaybeJsonObject(value) {
1927
+ if (typeof value !== "string") {
1928
+ return value;
1929
+ }
1930
+ const trimmed = value.trim();
1931
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
1932
+ return value;
1933
+ }
1934
+ try {
1935
+ return JSON.parse(trimmed);
1936
+ } catch {
1937
+ return value;
1938
+ }
1939
+ }
1940
+ function flattenObjectColumns(row) {
1941
+ const flattened = {};
1942
+ for (const [key, rawValue] of Object.entries(row)) {
1943
+ const value = parseMaybeJsonObject(rawValue);
1944
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1945
+ for (const [nestedKey, nestedValue] of Object.entries(
1946
+ value
1947
+ )) {
1948
+ flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
1949
+ }
1950
+ continue;
1951
+ }
1952
+ flattened[key] = Array.isArray(value) ? JSON.stringify(value) : value;
1953
+ }
1954
+ return flattened;
1955
+ }
1956
+ function recordRows(value) {
1957
+ return value.filter(
1958
+ (row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
1959
+ );
1960
+ }
1961
+ function dataExportRows(rows) {
1962
+ return rows.map((row) => flattenObjectColumns(row));
1963
+ }
1964
+ function dataExportColumns(rows, preferredColumns = []) {
1965
+ const discoveredColumns = [
1966
+ ...new Set(rows.flatMap((row) => Object.keys(row)))
1967
+ ];
1968
+ if (rows.length === 0) {
1969
+ return [...new Set(preferredColumns.filter(Boolean))];
1970
+ }
1971
+ const discovered = new Set(discoveredColumns);
1972
+ const columns = [];
1973
+ const seen = /* @__PURE__ */ new Set();
1974
+ for (const column of preferredColumns) {
1975
+ if (!column) {
1976
+ continue;
1977
+ }
1978
+ const expandedColumns = discovered.has(column) ? [column] : discoveredColumns.filter(
1979
+ (discoveredColumn) => discoveredColumn.startsWith(`${column}.`)
1980
+ );
1981
+ for (const expandedColumn of expandedColumns) {
1982
+ if (seen.has(expandedColumn)) {
1983
+ continue;
1984
+ }
1985
+ seen.add(expandedColumn);
1986
+ columns.push(expandedColumn);
1987
+ }
1988
+ }
1989
+ for (const column of discoveredColumns) {
1990
+ if (seen.has(column)) {
1991
+ continue;
1992
+ }
1993
+ seen.add(column);
1994
+ columns.push(column);
1995
+ }
1996
+ return columns;
1997
+ }
1998
+ function dataExportCsvString(rows, preferredColumns = []) {
1999
+ const flattenedRows = dataExportRows(rows);
2000
+ return csvStringFromRows(
2001
+ flattenedRows,
2002
+ dataExportColumns(flattenedRows, preferredColumns)
2003
+ );
2004
+ }
2005
+ function markdownCell(value) {
2006
+ if (value === null || value === void 0) {
2007
+ return "";
2008
+ }
2009
+ const text = typeof value === "object" ? JSON.stringify(value) : String(value);
2010
+ return text.replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
2011
+ }
2012
+ function markdownTableFromRows(rows, preferredColumns = []) {
2013
+ const flattenedRows = dataExportRows(rows);
2014
+ const columns = dataExportColumns(flattenedRows, preferredColumns);
2015
+ const header = `| ${columns.map(markdownCell).join(" | ")} |`;
2016
+ const separator = `| ${columns.map(() => "---").join(" | ")} |`;
2017
+ const body = flattenedRows.map(
2018
+ (row) => `| ${columns.map((column) => markdownCell(row[column])).join(" | ")} |`
2019
+ );
2020
+ return `${[header, separator, ...body].join("\n")}
2021
+ `;
2022
+ }
1895
2023
  function printJson(value) {
1896
2024
  process.stdout.write(`${JSON.stringify(value, null, 2)}
1897
2025
  `);
@@ -2068,7 +2196,7 @@ function buildCandidateUrls2(url) {
2068
2196
  }
2069
2197
  }
2070
2198
  function sleep3(ms) {
2071
- return new Promise((resolve10) => setTimeout(resolve10, ms));
2199
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
2072
2200
  }
2073
2201
  function printDeeplineLogo() {
2074
2202
  if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
@@ -3268,10 +3396,12 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
3268
3396
  rows: rowsInfo.rows,
3269
3397
  columns: rowsInfo.columns
3270
3398
  });
3399
+ const rows = dataExportRows(sanitized.rows);
3400
+ const columns = dataExportColumns(rows, sanitized.columns);
3271
3401
  const resolved = (0, import_node_path5.resolve)(outPath);
3272
3402
  (0, import_node_fs4.writeFileSync)(
3273
3403
  resolved,
3274
- csvStringFromRows(sanitized.rows, sanitized.columns),
3404
+ csvStringFromRows(rows, columns),
3275
3405
  "utf-8"
3276
3406
  );
3277
3407
  return resolved;
@@ -3392,6 +3522,14 @@ Examples:
3392
3522
  }
3393
3523
 
3394
3524
  // src/cli/commands/db.ts
3525
+ var import_node_fs5 = require("fs");
3526
+ var import_node_path6 = require("path");
3527
+ var CUSTOMER_DB_QUERY_FORMATS = /* @__PURE__ */ new Set([
3528
+ "table",
3529
+ "json",
3530
+ "csv",
3531
+ "markdown"
3532
+ ]);
3395
3533
  function parsePositiveInteger(value, flagName) {
3396
3534
  const parsed = Number.parseInt(value, 10);
3397
3535
  if (!Number.isFinite(parsed) || parsed <= 0) {
@@ -3405,10 +3543,8 @@ function formatCell(value) {
3405
3543
  return text.length > 80 ? `${text.slice(0, 77)}...` : text;
3406
3544
  }
3407
3545
  function tableLines(result) {
3408
- const rows = result.rows.filter(
3409
- (row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
3410
- );
3411
- const responseColumns = result.columns.length > 0 ? result.columns.map((column) => column.name) : [...new Set(rows.flatMap((row) => Object.keys(row)))];
3546
+ const rows = dataExportRows(customerDbRows(result));
3547
+ const responseColumns = dataExportColumns(rows, customerDbColumnNames(result));
3412
3548
  const businessColumns = responseColumns.filter((column) => !column.startsWith("_"));
3413
3549
  const columns = businessColumns.length > 0 ? businessColumns : responseColumns;
3414
3550
  const hiddenColumns = responseColumns.filter((column) => !columns.includes(column));
@@ -3442,22 +3578,146 @@ function tableLines(result) {
3442
3578
  }
3443
3579
  return lines;
3444
3580
  }
3581
+ function customerDbRows(result) {
3582
+ return recordRows(result.rows);
3583
+ }
3584
+ function customerDbColumnNames(result) {
3585
+ return result.columns.map((column) => column.name).filter(Boolean);
3586
+ }
3587
+ function writeCustomerDbCsv(result, outPath) {
3588
+ const resolved = (0, import_node_path6.resolve)(outPath);
3589
+ (0, import_node_fs5.writeFileSync)(
3590
+ resolved,
3591
+ dataExportCsvString(customerDbRows(result), customerDbColumnNames(result)),
3592
+ "utf-8"
3593
+ );
3594
+ return resolved;
3595
+ }
3596
+ function dbQueryExportEnvelope(input) {
3597
+ const destination = input.outPath ?? "stdout";
3598
+ return {
3599
+ command: input.result.command,
3600
+ format: input.format,
3601
+ row_count: input.result.row_count,
3602
+ row_count_returned: input.result.row_count_returned,
3603
+ truncated: input.result.truncated,
3604
+ ...input.outPath ? { file: input.outPath, local: { file: input.outPath } } : {},
3605
+ next: { toolEquivalent: input.toolCommand },
3606
+ render: {
3607
+ sections: [
3608
+ {
3609
+ title: "customer db export",
3610
+ lines: [
3611
+ `Rendered ${input.result.row_count_returned} row(s) as ${input.format} to ${destination}`
3612
+ ]
3613
+ }
3614
+ ],
3615
+ actions: [{ label: "Tool equivalent", command: input.toolCommand }]
3616
+ }
3617
+ };
3618
+ }
3445
3619
  async function handleDbQuery(args) {
3446
3620
  const sqlIndex = args.indexOf("--sql");
3447
3621
  const sql = sqlIndex >= 0 ? args[sqlIndex + 1]?.trim() : "";
3448
3622
  if (!sql) {
3449
- console.error('Usage: deepline db query --sql "select * from table limit 20" [--max-rows N] [--json]');
3623
+ console.error(
3624
+ 'Usage: deepline db query --sql "select * from table limit 20" [--max-rows N] [--json]'
3625
+ );
3450
3626
  return 1;
3451
3627
  }
3452
3628
  const maxRowsIndex = args.indexOf("--max-rows");
3453
3629
  const maxRows = maxRowsIndex >= 0 && args[maxRowsIndex + 1] ? parsePositiveInteger(args[maxRowsIndex + 1], "--max-rows") : void 0;
3630
+ const formatIndex = args.indexOf("--format");
3631
+ const format = formatIndex >= 0 ? args[formatIndex + 1]?.trim().toLowerCase() : "";
3632
+ if (format && !CUSTOMER_DB_QUERY_FORMATS.has(format)) {
3633
+ console.error(
3634
+ 'Usage: deepline db query --sql "select * from table limit 20" [--format table|json|csv|markdown] [--out path]'
3635
+ );
3636
+ return 1;
3637
+ }
3638
+ const outIndex = args.indexOf("--out");
3639
+ const outPath = outIndex >= 0 ? args[outIndex + 1]?.trim() : "";
3640
+ if (outIndex >= 0 && !outPath) {
3641
+ console.error("--out requires a path.");
3642
+ return 1;
3643
+ }
3454
3644
  const jsonOutput = argsWantJson(args);
3645
+ const explicitJsonOutput = args.includes("--json");
3455
3646
  const client = new DeeplineClient();
3456
3647
  const result = await client.queryCustomerDb({ sql, maxRows });
3457
3648
  const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify({
3458
3649
  sql,
3459
3650
  ...maxRows ? { max_rows: maxRows } : {}
3460
3651
  })} --json`;
3652
+ if (format === "csv") {
3653
+ if (outPath) {
3654
+ const exportedPath = writeCustomerDbCsv(result, outPath);
3655
+ printCommandEnvelope(
3656
+ dbQueryExportEnvelope({
3657
+ result,
3658
+ format,
3659
+ outPath: exportedPath,
3660
+ toolCommand
3661
+ }),
3662
+ {
3663
+ json: jsonOutput,
3664
+ text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
3665
+ `
3666
+ }
3667
+ );
3668
+ return 0;
3669
+ }
3670
+ printCommandEnvelope(
3671
+ dbQueryExportEnvelope({
3672
+ result,
3673
+ format,
3674
+ outPath: null,
3675
+ toolCommand
3676
+ }),
3677
+ {
3678
+ json: explicitJsonOutput,
3679
+ text: dataExportCsvString(customerDbRows(result), customerDbColumnNames(result))
3680
+ }
3681
+ );
3682
+ return 0;
3683
+ }
3684
+ if (format === "markdown") {
3685
+ const content = markdownTableFromRows(
3686
+ customerDbRows(result),
3687
+ customerDbColumnNames(result)
3688
+ );
3689
+ if (outPath) {
3690
+ const exportedPath = (0, import_node_path6.resolve)(outPath);
3691
+ (0, import_node_fs5.writeFileSync)(exportedPath, content, "utf-8");
3692
+ printCommandEnvelope(
3693
+ dbQueryExportEnvelope({
3694
+ result,
3695
+ format,
3696
+ outPath: exportedPath,
3697
+ toolCommand
3698
+ }),
3699
+ {
3700
+ json: jsonOutput,
3701
+ text: `Exported ${result.row_count_returned} row(s) to ${exportedPath}
3702
+ `
3703
+ }
3704
+ );
3705
+ return 0;
3706
+ }
3707
+ printCommandEnvelope(
3708
+ dbQueryExportEnvelope({
3709
+ result,
3710
+ format,
3711
+ outPath: null,
3712
+ toolCommand
3713
+ }),
3714
+ {
3715
+ json: explicitJsonOutput,
3716
+ text: content
3717
+ }
3718
+ );
3719
+ return 0;
3720
+ }
3461
3721
  printCommandEnvelope({
3462
3722
  ...result,
3463
3723
  next: { toolEquivalent: toolCommand },
@@ -3465,7 +3725,7 @@ async function handleDbQuery(args) {
3465
3725
  sections: [{ title: "customer db query", lines: tableLines(result) }],
3466
3726
  actions: [{ label: "Tool equivalent", command: toolCommand }]
3467
3727
  }
3468
- }, { json: jsonOutput });
3728
+ }, { json: jsonOutput || format === "json" });
3469
3729
  return 0;
3470
3730
  }
3471
3731
  function registerDbCommands(program) {
@@ -3475,11 +3735,14 @@ function registerDbCommands(program) {
3475
3735
  Notes:
3476
3736
  Runs SQL against the active workspace customer database through Deepline APIs.
3477
3737
  Results are bounded by the server and --max-rows. Use --json for stable output.
3738
+ Use --format csv or --format markdown for agent-readable exports and display tables.
3478
3739
 
3479
3740
  Examples:
3480
3741
  deepline db query --sql "select * from companies limit 20"
3481
3742
  deepline db query --sql "select domain, name from companies limit 20" --json
3482
3743
  deepline db query --sql "select * from contacts" --max-rows 100 --json
3744
+ deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
3745
+ deepline db query --sql "select domain, name from companies limit 20" --format markdown
3483
3746
  `
3484
3747
  );
3485
3748
  db.command("query").alias("psql").description("Run SQL against the tenant customer database.").addHelpText(
@@ -3488,17 +3751,23 @@ Examples:
3488
3751
  Notes:
3489
3752
  Requires --sql. Output is a compact table in a terminal and raw JSON with
3490
3753
  --json or when stdout is piped. The active auth workspace determines scope.
3754
+ --format csv and --format markdown are explicit data/display formats and can
3755
+ be written directly with --out.
3491
3756
 
3492
3757
  Examples:
3493
3758
  deepline db query --sql "select * from companies limit 20"
3494
3759
  deepline db query --sql "select domain, name from companies limit 20" --json
3495
3760
  deepline db psql --sql "select count(*) from contacts" --json
3761
+ deepline db query --sql "select * from contacts limit 20" --format csv --out contacts.csv
3762
+ deepline db query --sql "select domain, name from companies limit 20" --format markdown
3496
3763
  `
3497
- ).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
3764
+ ).requiredOption("--sql <sql>", "SQL statement").option("--max-rows <n>", "Maximum returned rows").option("--format <format>", "Output format: table, json, csv, or markdown").option("--out <path>", "Write csv or markdown output to a file").option("--json", "Emit raw JSON response. Also automatic when stdout is piped").action(async (options) => {
3498
3765
  process.exitCode = await handleDbQuery([
3499
3766
  "--sql",
3500
3767
  options.sql,
3501
3768
  ...options.maxRows ? ["--max-rows", options.maxRows] : [],
3769
+ ...options.format ? ["--format", options.format] : [],
3770
+ ...options.out ? ["--out", options.out] : [],
3502
3771
  ...options.json ? ["--json"] : []
3503
3772
  ]);
3504
3773
  });
@@ -3674,21 +3943,21 @@ Examples:
3674
3943
 
3675
3944
  // src/cli/commands/play.ts
3676
3945
  var import_node_crypto3 = require("crypto");
3677
- var import_node_fs7 = require("fs");
3678
- var import_node_path9 = require("path");
3946
+ var import_node_fs8 = require("fs");
3947
+ var import_node_path10 = require("path");
3679
3948
 
3680
3949
  // src/plays/bundle-play-file.ts
3681
3950
  var import_node_os5 = require("os");
3682
- var import_node_path8 = require("path");
3951
+ var import_node_path9 = require("path");
3683
3952
  var import_node_url = require("url");
3684
- var import_node_fs6 = require("fs");
3953
+ var import_node_fs7 = require("fs");
3685
3954
 
3686
3955
  // ../shared_libs/plays/bundling/index.ts
3687
3956
  var import_node_crypto = require("crypto");
3688
- var import_node_fs5 = require("fs");
3957
+ var import_node_fs6 = require("fs");
3689
3958
  var import_promises3 = require("fs/promises");
3690
3959
  var import_node_os4 = require("os");
3691
- var import_node_path6 = require("path");
3960
+ var import_node_path7 = require("path");
3692
3961
  var import_node_module = require("module");
3693
3962
  var import_esbuild = require("esbuild");
3694
3963
 
@@ -3747,7 +4016,7 @@ function buildPlayContractCompatibility(input) {
3747
4016
  var PLAY_BUNDLE_CACHE_VERSION = 24;
3748
4017
  var MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
3749
4018
  var MAX_ESM_WORKERS_BUNDLE_BYTES = 115e4;
3750
- var PLAY_ARTIFACT_CACHE_DIR = (0, import_node_path6.join)(
4019
+ var PLAY_ARTIFACT_CACHE_DIR = (0, import_node_path7.join)(
3751
4020
  (0, import_node_os4.tmpdir)(),
3752
4021
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`
3753
4022
  );
@@ -3780,13 +4049,13 @@ async function normalizeLocalPath(filePath) {
3780
4049
  try {
3781
4050
  return await (0, import_promises3.realpath)(filePath);
3782
4051
  } catch {
3783
- return (0, import_node_path6.resolve)(filePath);
4052
+ return (0, import_node_path7.resolve)(filePath);
3784
4053
  }
3785
4054
  }
3786
4055
  function createPlayWorkspace(entryFile) {
3787
4056
  return {
3788
4057
  entryFile,
3789
- rootDir: (0, import_node_path6.dirname)(entryFile)
4058
+ rootDir: (0, import_node_path7.dirname)(entryFile)
3790
4059
  };
3791
4060
  }
3792
4061
  function isPathInsideDirectory(filePath, directory) {
@@ -3939,7 +4208,7 @@ function extractDefinedPlayName(sourceCode) {
3939
4208
  }
3940
4209
  function readPackageVersionFromPackageJson(packageJsonPath, packageName) {
3941
4210
  try {
3942
- const packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8"));
4211
+ const packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf-8"));
3943
4212
  if (packageJson.name === packageName && typeof packageJson.version === "string") {
3944
4213
  return packageJson.version;
3945
4214
  }
@@ -3949,18 +4218,18 @@ function readPackageVersionFromPackageJson(packageJsonPath, packageName) {
3949
4218
  return null;
3950
4219
  }
3951
4220
  function findPackageJsonPathFrom(startDir, packageName) {
3952
- let current = (0, import_node_path6.resolve)(startDir);
4221
+ let current = (0, import_node_path7.resolve)(startDir);
3953
4222
  while (true) {
3954
- const packageJsonPath = (0, import_node_path6.join)(
4223
+ const packageJsonPath = (0, import_node_path7.join)(
3955
4224
  current,
3956
4225
  "node_modules",
3957
4226
  packageName,
3958
4227
  "package.json"
3959
4228
  );
3960
- if ((0, import_node_fs5.existsSync)(packageJsonPath)) {
4229
+ if ((0, import_node_fs6.existsSync)(packageJsonPath)) {
3961
4230
  return packageJsonPath;
3962
4231
  }
3963
- const parent = (0, import_node_path6.dirname)(current);
4232
+ const parent = (0, import_node_path7.dirname)(current);
3964
4233
  if (parent === current) {
3965
4234
  return null;
3966
4235
  }
@@ -3969,29 +4238,29 @@ function findPackageJsonPathFrom(startDir, packageName) {
3969
4238
  }
3970
4239
  function findPackageJsonPath(packageName, fromFile, adapter) {
3971
4240
  const startDirs = [
3972
- (0, import_node_path6.dirname)(fromFile),
4241
+ (0, import_node_path7.dirname)(fromFile),
3973
4242
  adapter.projectRoot,
3974
- (0, import_node_path6.dirname)(adapter.sdkPackageJson),
4243
+ (0, import_node_path7.dirname)(adapter.sdkPackageJson),
3975
4244
  process.cwd()
3976
4245
  ];
3977
4246
  const seen = /* @__PURE__ */ new Set();
3978
4247
  for (const startDir of startDirs) {
3979
- const normalized = (0, import_node_path6.resolve)(startDir);
4248
+ const normalized = (0, import_node_path7.resolve)(startDir);
3980
4249
  if (seen.has(normalized)) continue;
3981
4250
  seen.add(normalized);
3982
4251
  const packageJsonPath = findPackageJsonPathFrom(normalized, packageName);
3983
4252
  if (packageJsonPath) return packageJsonPath;
3984
4253
  }
3985
- const adapterNodeModulesPackageJson = (0, import_node_path6.join)(
4254
+ const adapterNodeModulesPackageJson = (0, import_node_path7.join)(
3986
4255
  adapter.nodeModulesDir,
3987
4256
  packageName,
3988
4257
  "package.json"
3989
4258
  );
3990
- return (0, import_node_fs5.existsSync)(adapterNodeModulesPackageJson) ? adapterNodeModulesPackageJson : null;
4259
+ return (0, import_node_fs6.existsSync)(adapterNodeModulesPackageJson) ? adapterNodeModulesPackageJson : null;
3991
4260
  }
3992
4261
  function localSdkAliasPlugin(adapter, options) {
3993
4262
  const entryFile = options?.workersRuntime ? adapter.sdkWorkersEntryFile : adapter.sdkEntryFile;
3994
- if (!(0, import_node_fs5.existsSync)(entryFile)) {
4263
+ if (!(0, import_node_fs6.existsSync)(entryFile)) {
3995
4264
  return null;
3996
4265
  }
3997
4266
  return {
@@ -4031,7 +4300,7 @@ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
4031
4300
  contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
4032
4301
  `,
4033
4302
  loader: "ts",
4034
- resolveDir: (0, import_node_path6.dirname)(playFilePath)
4303
+ resolveDir: (0, import_node_path7.dirname)(playFilePath)
4035
4304
  })
4036
4305
  );
4037
4306
  }
@@ -4195,7 +4464,7 @@ function importedPlayProxyPlugin(importedPlayDependencies) {
4195
4464
  return {
4196
4465
  contents: buildImportedPlayProxyModule(dependency.playName),
4197
4466
  loader: "ts",
4198
- resolveDir: (0, import_node_path6.dirname)(args.path)
4467
+ resolveDir: (0, import_node_path7.dirname)(args.path)
4199
4468
  };
4200
4469
  });
4201
4470
  }
@@ -4213,12 +4482,12 @@ async function resolveLocalImport(fromFile, specifier) {
4213
4482
  if (specifier.startsWith("file:")) {
4214
4483
  return normalizeLocalPath(new URL(specifier).pathname);
4215
4484
  }
4216
- const base = (0, import_node_path6.isAbsolute)(specifier) ? (0, import_node_path6.resolve)(specifier) : (0, import_node_path6.resolve)((0, import_node_path6.dirname)(fromFile), specifier);
4485
+ const base = (0, import_node_path7.isAbsolute)(specifier) ? (0, import_node_path7.resolve)(specifier) : (0, import_node_path7.resolve)((0, import_node_path7.dirname)(fromFile), specifier);
4217
4486
  const candidates = [base];
4218
- const explicitExtension = (0, import_node_path6.extname)(base).toLowerCase();
4487
+ const explicitExtension = (0, import_node_path7.extname)(base).toLowerCase();
4219
4488
  if (!explicitExtension) {
4220
4489
  candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`));
4221
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => (0, import_node_path6.join)(base, `index${extension}`)));
4490
+ candidates.push(...SOURCE_EXTENSIONS.map((extension) => (0, import_node_path7.join)(base, `index${extension}`)));
4222
4491
  } else if ([".js", ".jsx", ".mjs", ".cjs"].includes(explicitExtension)) {
4223
4492
  const stem = base.slice(0, -explicitExtension.length);
4224
4493
  candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${stem}${extension}`));
@@ -4232,9 +4501,9 @@ async function resolveLocalImport(fromFile, specifier) {
4232
4501
  }
4233
4502
  function resolvePackageImport(specifier, fromFile, adapter) {
4234
4503
  const packageName = getPackageName(specifier);
4235
- if (packageName === "deepline" && (0, import_node_fs5.existsSync)(adapter.sdkPackageJson)) {
4504
+ if (packageName === "deepline" && (0, import_node_fs6.existsSync)(adapter.sdkPackageJson)) {
4236
4505
  const packageJson = JSON.parse(
4237
- (0, import_node_fs5.readFileSync)(adapter.sdkPackageJson, "utf-8")
4506
+ (0, import_node_fs6.readFileSync)(adapter.sdkPackageJson, "utf-8")
4238
4507
  );
4239
4508
  return {
4240
4509
  name: "deepline",
@@ -4266,7 +4535,7 @@ async function analyzeSourceGraph(entryFile, adapter) {
4266
4535
  visited.add(absolutePath);
4267
4536
  const sourceCode2 = await (0, import_promises3.readFile)(absolutePath, "utf-8");
4268
4537
  localFiles.set(absolutePath, sourceCode2);
4269
- if ((0, import_node_path6.extname)(absolutePath).toLowerCase() === ".json") {
4538
+ if ((0, import_node_path7.extname)(absolutePath).toLowerCase() === ".json") {
4270
4539
  return;
4271
4540
  }
4272
4541
  const handleSpecifier = async (specifier, line, column, kind) => {
@@ -4370,13 +4639,13 @@ async function computeWorkersHarnessFingerprintWithAdapter(adapter) {
4370
4639
  const tsFiles = entries.filter((e) => e.isFile() && /\.[cm]?ts$/.test(e.name)).map((e) => e.name).sort();
4371
4640
  const parts = [];
4372
4641
  for (const name of tsFiles) {
4373
- const contents = await (0, import_promises3.readFile)((0, import_node_path6.join)(adapter.workersHarnessFilesDir, name), "utf-8");
4642
+ const contents = await (0, import_promises3.readFile)((0, import_node_path7.join)(adapter.workersHarnessFilesDir, name), "utf-8");
4374
4643
  parts.push({ name, hash: sha256(contents) });
4375
4644
  }
4376
4645
  return sha256(JSON.stringify(parts));
4377
4646
  }
4378
4647
  function artifactCachePath(graphHash, artifactKind, adapter) {
4379
- return (0, import_node_path6.join)(
4648
+ return (0, import_node_path7.join)(
4380
4649
  adapter.cacheDir ?? PLAY_ARTIFACT_CACHE_DIR,
4381
4650
  `${graphHash}.${artifactKind}.json`
4382
4651
  );
@@ -4411,7 +4680,7 @@ function normalizeSourceMapForRuntime(sourceMapText) {
4411
4680
  if (sourcePath.startsWith("data:") || sourcePath.startsWith("node:") || sourcePath.startsWith("/") || /^[a-zA-Z]+:\/\//.test(sourcePath)) {
4412
4681
  return sourcePath;
4413
4682
  }
4414
- return (0, import_node_path6.resolve)(process.cwd(), sourcePath);
4683
+ return (0, import_node_path7.resolve)(process.cwd(), sourcePath);
4415
4684
  });
4416
4685
  parsed.sourceRoot = void 0;
4417
4686
  return JSON.stringify(parsed);
@@ -4443,8 +4712,8 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
4443
4712
  ...namedExportShim ? {
4444
4713
  stdin: {
4445
4714
  contents: namedExportShim,
4446
- resolveDir: (0, import_node_path6.dirname)(entryFile),
4447
- sourcefile: `${(0, import_node_path6.basename)(entryFile)}.${exportName}.entry.ts`,
4715
+ resolveDir: (0, import_node_path7.dirname)(entryFile),
4716
+ sourcefile: `${(0, import_node_path7.basename)(entryFile)}.${exportName}.entry.ts`,
4448
4717
  loader: "ts"
4449
4718
  }
4450
4719
  } : { entryPoints: [entryFile] },
@@ -4620,10 +4889,10 @@ workers-harness:${harnessFingerprint}`
4620
4889
  }
4621
4890
  const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
4622
4891
  const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
4623
- const virtualBaseName = exportName === "default" ? (0, import_node_path6.basename)(absolutePath).replace(/\.[^.]+$/, "") : `${(0, import_node_path6.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
4892
+ const virtualBaseName = exportName === "default" ? (0, import_node_path7.basename)(absolutePath).replace(/\.[^.]+$/, "") : `${(0, import_node_path7.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
4624
4893
  const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
4625
4894
  const executableCode = `${bundledCode}
4626
- //# sourceMappingURL=${(0, import_node_path6.basename)(virtualFilename)}.map
4895
+ //# sourceMappingURL=${(0, import_node_path7.basename)(virtualFilename)}.map
4627
4896
  `;
4628
4897
  const bundleSizeError = getBundleSizeError(
4629
4898
  absolutePath,
@@ -4733,13 +5002,13 @@ function resolveExecutionProfile(override) {
4733
5002
  // src/plays/local-file-discovery.ts
4734
5003
  var import_node_crypto2 = require("crypto");
4735
5004
  var import_promises4 = require("fs/promises");
4736
- var import_node_path7 = require("path");
5005
+ var import_node_path8 = require("path");
4737
5006
  var SOURCE_EXTENSIONS2 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
4738
5007
  function sha2562(buffer) {
4739
5008
  return (0, import_node_crypto2.createHash)("sha256").update(buffer).digest("hex");
4740
5009
  }
4741
5010
  function contentTypeForFile(filePath) {
4742
- const extension = (0, import_node_path7.extname)(filePath).toLowerCase();
5011
+ const extension = (0, import_node_path8.extname)(filePath).toLowerCase();
4743
5012
  if (extension === ".csv") return "text/csv";
4744
5013
  if (extension === ".json") return "application/json";
4745
5014
  if (extension === ".txt") return "text/plain";
@@ -4930,16 +5199,16 @@ async function fileExists2(filePath) {
4930
5199
  }
4931
5200
  }
4932
5201
  function isPathInsideDirectory2(filePath, directory) {
4933
- const relativePath = (0, import_node_path7.relative)(directory, filePath);
4934
- return relativePath === "" || !relativePath.startsWith("..") && !(0, import_node_path7.isAbsolute)(relativePath);
5202
+ const relativePath = (0, import_node_path8.relative)(directory, filePath);
5203
+ return relativePath === "" || !relativePath.startsWith("..") && !(0, import_node_path8.isAbsolute)(relativePath);
4935
5204
  }
4936
5205
  async function resolveLocalImport2(fromFile, specifier) {
4937
- const base = (0, import_node_path7.isAbsolute)(specifier) ? (0, import_node_path7.resolve)(specifier) : (0, import_node_path7.resolve)((0, import_node_path7.dirname)(fromFile), specifier);
5206
+ const base = (0, import_node_path8.isAbsolute)(specifier) ? (0, import_node_path8.resolve)(specifier) : (0, import_node_path8.resolve)((0, import_node_path8.dirname)(fromFile), specifier);
4938
5207
  const candidates = [base];
4939
- const explicitExtension = (0, import_node_path7.extname)(base).toLowerCase();
5208
+ const explicitExtension = (0, import_node_path8.extname)(base).toLowerCase();
4940
5209
  if (!explicitExtension) {
4941
5210
  candidates.push(...SOURCE_EXTENSIONS2.map((extension) => `${base}${extension}`));
4942
- candidates.push(...SOURCE_EXTENSIONS2.map((extension) => (0, import_node_path7.join)(base, `index${extension}`)));
5211
+ candidates.push(...SOURCE_EXTENSIONS2.map((extension) => (0, import_node_path8.join)(base, `index${extension}`)));
4943
5212
  } else if ([".js", ".jsx", ".mjs", ".cjs"].includes(explicitExtension)) {
4944
5213
  const stem = base.slice(0, -explicitExtension.length);
4945
5214
  candidates.push(...SOURCE_EXTENSIONS2.map((extension) => `${stem}${extension}`));
@@ -4952,13 +5221,13 @@ async function resolveLocalImport2(fromFile, specifier) {
4952
5221
  throw new Error(`Could not resolve local import "${specifier}" from ${fromFile}`);
4953
5222
  }
4954
5223
  async function discoverPackagedLocalFiles(entryFile) {
4955
- const absoluteEntryFile = (0, import_node_path7.resolve)(entryFile);
4956
- const packagingRoot = (0, import_node_path7.dirname)(absoluteEntryFile);
5224
+ const absoluteEntryFile = (0, import_node_path8.resolve)(entryFile);
5225
+ const packagingRoot = (0, import_node_path8.dirname)(absoluteEntryFile);
4957
5226
  const files = /* @__PURE__ */ new Map();
4958
5227
  const unresolved = [];
4959
5228
  const visitedFiles = /* @__PURE__ */ new Set();
4960
5229
  const visitSourceFile = async (filePath) => {
4961
- const absolutePath = (0, import_node_path7.resolve)(filePath);
5230
+ const absolutePath = (0, import_node_path8.resolve)(filePath);
4962
5231
  if (visitedFiles.has(absolutePath)) {
4963
5232
  return;
4964
5233
  }
@@ -4990,8 +5259,8 @@ async function discoverPackagedLocalFiles(entryFile) {
4990
5259
  message: "Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file."
4991
5260
  });
4992
5261
  } else {
4993
- const absoluteCsvPath = (0, import_node_path7.resolve)((0, import_node_path7.dirname)(absolutePath), resolvedPath);
4994
- if ((0, import_node_path7.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
5262
+ const absoluteCsvPath = (0, import_node_path8.resolve)((0, import_node_path8.dirname)(absolutePath), resolvedPath);
5263
+ if ((0, import_node_path8.isAbsolute)(resolvedPath) || !isPathInsideDirectory2(absoluteCsvPath, packagingRoot)) {
4995
5264
  unresolved.push({
4996
5265
  sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
4997
5266
  message: "ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead."
@@ -5030,24 +5299,24 @@ async function discoverPackagedLocalFiles(entryFile) {
5030
5299
  // src/plays/bundle-play-file.ts
5031
5300
  var import_meta = {};
5032
5301
  var PLAY_BUNDLE_CACHE_VERSION2 = 30;
5033
- var MODULE_DIR = (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
5034
- var SDK_PACKAGE_ROOT = (0, import_node_path8.resolve)(MODULE_DIR, "..", "..");
5035
- var SOURCE_REPO_ROOT = (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "..");
5036
- var HAS_SOURCE_BUNDLING_SOURCES = (0, import_node_fs6.existsSync)(
5037
- (0, import_node_path8.resolve)(SOURCE_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
5302
+ var MODULE_DIR = (0, import_node_path9.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
5303
+ var SDK_PACKAGE_ROOT = (0, import_node_path9.resolve)(MODULE_DIR, "..", "..");
5304
+ var SOURCE_REPO_ROOT = (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "..");
5305
+ var HAS_SOURCE_BUNDLING_SOURCES = (0, import_node_fs7.existsSync)(
5306
+ (0, import_node_path9.resolve)(SOURCE_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
5038
5307
  );
5039
- var PACKAGED_REPO_ROOT = (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "dist", "repo");
5040
- var HAS_PACKAGED_BUNDLING_SOURCES = (0, import_node_fs6.existsSync)(
5041
- (0, import_node_path8.resolve)(PACKAGED_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
5308
+ var PACKAGED_REPO_ROOT = (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "dist", "repo");
5309
+ var HAS_PACKAGED_BUNDLING_SOURCES = (0, import_node_fs7.existsSync)(
5310
+ (0, import_node_path9.resolve)(PACKAGED_REPO_ROOT, "apps", "play-runner-workers", "src", "entry.ts")
5042
5311
  );
5043
- var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT : (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "..");
5044
- var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? (0, import_node_path8.resolve)(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? (0, import_node_path8.resolve)(PACKAGED_REPO_ROOT, "sdk", "src") : (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "src");
5045
- var SDK_PACKAGE_JSON = (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "package.json");
5046
- var SDK_ENTRY_FILE = (0, import_node_path8.resolve)(SDK_SOURCE_ROOT, "index.ts");
5047
- var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_path8.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
5048
- var SDK_WORKERS_ENTRY_FILE = (0, import_node_path8.resolve)(SDK_SOURCE_ROOT, "worker-play-entry.ts");
5049
- var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path8.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
5050
- var WORKERS_HARNESS_FILES_DIR = (0, import_node_path8.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src");
5312
+ var PROJECT_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? SOURCE_REPO_ROOT : HAS_PACKAGED_BUNDLING_SOURCES ? PACKAGED_REPO_ROOT : (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "..");
5313
+ var SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES ? (0, import_node_path9.resolve)(SOURCE_REPO_ROOT, "sdk", "src") : HAS_PACKAGED_BUNDLING_SOURCES ? (0, import_node_path9.resolve)(PACKAGED_REPO_ROOT, "sdk", "src") : (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "src");
5314
+ var SDK_PACKAGE_JSON = (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "package.json");
5315
+ var SDK_ENTRY_FILE = (0, import_node_path9.resolve)(SDK_SOURCE_ROOT, "index.ts");
5316
+ var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
5317
+ var SDK_WORKERS_ENTRY_FILE = (0, import_node_path9.resolve)(SDK_SOURCE_ROOT, "worker-play-entry.ts");
5318
+ var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path9.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
5319
+ var WORKERS_HARNESS_FILES_DIR = (0, import_node_path9.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src");
5051
5320
  var hasWarnedAboutNonDevelopmentBundling = false;
5052
5321
  function warnAboutNonDevelopmentBundling(filePath) {
5053
5322
  if (hasWarnedAboutNonDevelopmentBundling) {
@@ -5071,12 +5340,12 @@ function defaultPlayBundleTarget() {
5071
5340
  function createSdkPlayBundlingAdapter() {
5072
5341
  return {
5073
5342
  projectRoot: PROJECT_ROOT,
5074
- nodeModulesDir: (0, import_node_path8.resolve)(PROJECT_ROOT, "node_modules"),
5075
- cacheDir: (0, import_node_path8.join)((0, import_node_os5.tmpdir)(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
5343
+ nodeModulesDir: (0, import_node_path9.resolve)(PROJECT_ROOT, "node_modules"),
5344
+ cacheDir: (0, import_node_path9.join)((0, import_node_os5.tmpdir)(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
5076
5345
  sdkSourceRoot: SDK_SOURCE_ROOT,
5077
5346
  sdkPackageJson: SDK_PACKAGE_JSON,
5078
5347
  sdkEntryFile: SDK_ENTRY_FILE,
5079
- sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !(0, import_node_fs6.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
5348
+ sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !(0, import_node_fs7.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
5080
5349
  sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
5081
5350
  workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
5082
5351
  workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
@@ -5309,7 +5578,7 @@ function traceCliSync(phase, fields, run) {
5309
5578
  }
5310
5579
  }
5311
5580
  function sleep4(ms) {
5312
- return new Promise((resolve10) => setTimeout(resolve10, ms));
5581
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
5313
5582
  }
5314
5583
  function parseReferencedPlayTarget(target) {
5315
5584
  const trimmed = target.trim();
@@ -5355,7 +5624,7 @@ function formatPlayListReference(play) {
5355
5624
  function defaultMaterializedPlayPath(reference) {
5356
5625
  const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
5357
5626
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5358
- return (0, import_node_path9.resolve)(`${safeName || "play"}.play.ts`);
5627
+ return (0, import_node_path10.resolve)(`${safeName || "play"}.play.ts`);
5359
5628
  }
5360
5629
  function materializeRemotePlaySource(input) {
5361
5630
  if (isFileTarget(input.target)) {
@@ -5365,15 +5634,15 @@ function materializeRemotePlaySource(input) {
5365
5634
  return null;
5366
5635
  }
5367
5636
  const outputPath = input.outPath ?? defaultMaterializedPlayPath(input.playName);
5368
- if ((0, import_node_fs7.existsSync)(outputPath)) {
5369
- const existingSource = (0, import_node_fs7.readFileSync)(outputPath, "utf-8");
5637
+ if ((0, import_node_fs8.existsSync)(outputPath)) {
5638
+ const existingSource = (0, import_node_fs8.readFileSync)(outputPath, "utf-8");
5370
5639
  if (existingSource === input.sourceCode) {
5371
5640
  return { path: outputPath, status: "unchanged", created: false };
5372
5641
  }
5373
- (0, import_node_fs7.writeFileSync)(outputPath, input.sourceCode, "utf-8");
5642
+ (0, import_node_fs8.writeFileSync)(outputPath, input.sourceCode, "utf-8");
5374
5643
  return { path: outputPath, status: "updated", created: false };
5375
5644
  }
5376
- (0, import_node_fs7.writeFileSync)(outputPath, input.sourceCode, "utf-8");
5645
+ (0, import_node_fs8.writeFileSync)(outputPath, input.sourceCode, "utf-8");
5377
5646
  return { path: outputPath, status: "created", created: true };
5378
5647
  }
5379
5648
  function formatLoadedPlayMessage(materializedFile) {
@@ -5418,7 +5687,7 @@ function extractPlayName(code, filePath) {
5418
5687
  throw buildMissingDefinePlayError(filePath);
5419
5688
  }
5420
5689
  function isFileTarget(target) {
5421
- return (0, import_node_fs7.existsSync)((0, import_node_path9.resolve)(target));
5690
+ return (0, import_node_fs8.existsSync)((0, import_node_path10.resolve)(target));
5422
5691
  }
5423
5692
  function looksLikeFilePath(target) {
5424
5693
  if (target.trim().toLowerCase().startsWith("prebuilt/")) {
@@ -5437,7 +5706,7 @@ function parsePositiveInteger2(value, flagName) {
5437
5706
  return parsed;
5438
5707
  }
5439
5708
  function parseJsonInput(raw) {
5440
- const source = raw.startsWith("@") ? (0, import_node_fs7.readFileSync)((0, import_node_path9.resolve)(raw.slice(1)), "utf-8") : raw;
5709
+ const source = raw.startsWith("@") ? (0, import_node_fs8.readFileSync)((0, import_node_path10.resolve)(raw.slice(1)), "utf-8") : raw;
5441
5710
  const parsed = JSON.parse(source);
5442
5711
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
5443
5712
  throw new Error("--input must be a JSON object.");
@@ -5538,7 +5807,7 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
5538
5807
  function isLocalFilePathValue(value) {
5539
5808
  if (typeof value !== "string" || !value.trim()) return false;
5540
5809
  if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
5541
- return (0, import_node_fs7.existsSync)((0, import_node_path9.resolve)(value));
5810
+ return (0, import_node_fs8.existsSync)((0, import_node_path10.resolve)(value));
5542
5811
  }
5543
5812
  function inputContainsLocalFilePath(value) {
5544
5813
  if (isLocalFilePathValue(value)) {
@@ -5564,8 +5833,8 @@ async function stageFileInputArgs(input) {
5564
5833
  const localFiles = uniqueBindings.flatMap((binding) => {
5565
5834
  const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
5566
5835
  if (!isLocalFilePathValue(value)) return [];
5567
- const absolutePath = (0, import_node_path9.resolve)(value);
5568
- return [{ binding, absolutePath, logicalPath: (0, import_node_path9.basename)(absolutePath) }];
5836
+ const absolutePath = (0, import_node_path10.resolve)(value);
5837
+ return [{ binding, absolutePath, logicalPath: (0, import_node_path10.basename)(absolutePath) }];
5569
5838
  });
5570
5839
  if (localFiles.length === 0) {
5571
5840
  return { inputFile: null, packagedFiles: [] };
@@ -5589,7 +5858,7 @@ async function stageFileInputArgs(input) {
5589
5858
  };
5590
5859
  }
5591
5860
  function stageFile(logicalPath, absolutePath) {
5592
- const buffer = (0, import_node_fs7.readFileSync)(absolutePath);
5861
+ const buffer = (0, import_node_fs8.readFileSync)(absolutePath);
5593
5862
  return {
5594
5863
  logicalPath,
5595
5864
  contentBase64: buffer.toString("base64"),
@@ -5600,9 +5869,9 @@ function stageFile(logicalPath, absolutePath) {
5600
5869
  }
5601
5870
  function normalizePlayPath(filePath) {
5602
5871
  try {
5603
- return import_node_fs7.realpathSync.native((0, import_node_path9.resolve)(filePath));
5872
+ return import_node_fs8.realpathSync.native((0, import_node_path10.resolve)(filePath));
5604
5873
  } catch {
5605
- return (0, import_node_path9.resolve)(filePath);
5874
+ return (0, import_node_path10.resolve)(filePath);
5606
5875
  }
5607
5876
  }
5608
5877
  function formatBundlingErrors(filePath, errors) {
@@ -5755,11 +6024,42 @@ function isTransientPlayStreamError(error) {
5755
6024
  text
5756
6025
  );
5757
6026
  }
6027
+ function playStatusErrorText(status) {
6028
+ const chunks = [];
6029
+ const progressError = status.progress?.error;
6030
+ if (typeof progressError === "string" && progressError.trim()) {
6031
+ chunks.push(progressError.trim());
6032
+ }
6033
+ const errorValue = status.error;
6034
+ if (typeof errorValue === "string" && errorValue.trim()) {
6035
+ chunks.push(errorValue.trim());
6036
+ }
6037
+ const errors = status.errors;
6038
+ if (Array.isArray(errors)) {
6039
+ for (const error of errors) {
6040
+ if (typeof error === "string" && error.trim()) {
6041
+ chunks.push(error.trim());
6042
+ } else if (error && typeof error === "object") {
6043
+ const message = error.message;
6044
+ if (typeof message === "string" && message.trim()) {
6045
+ chunks.push(message.trim());
6046
+ }
6047
+ }
6048
+ }
6049
+ }
6050
+ return chunks.join("; ") || status.status;
6051
+ }
6052
+ function isRetryablePendingStartFailure(status) {
6053
+ if (status.status !== "failed") return false;
6054
+ if (status.runId && status.runId !== "pending") return false;
6055
+ return isTransientPlayStreamError(new Error(playStatusErrorText(status)));
6056
+ }
5758
6057
  var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
5759
6058
  "completed",
5760
6059
  "failed",
5761
6060
  "cancelled"
5762
6061
  ]);
6062
+ var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
5763
6063
  function getEventPayload(event) {
5764
6064
  return event.payload && typeof event.payload === "object" ? event.payload : {};
5765
6065
  }
@@ -5921,6 +6221,34 @@ async function waitForPlayCompletionByStream(input) {
5921
6221
  );
5922
6222
  }
5923
6223
  async function startAndWaitForPlayCompletionByStream(input) {
6224
+ for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
6225
+ const status = await startAndWaitForPlayCompletionByStreamOnce(input);
6226
+ const retryDelayMs = PLAY_START_TRANSIENT_RETRY_DELAYS_MS[attempt];
6227
+ if (retryDelayMs === void 0 || !isRetryablePendingStartFailure(status)) {
6228
+ return status;
6229
+ }
6230
+ if (!input.jsonOutput) {
6231
+ input.progress.writeLine(
6232
+ `[play watch] start failed before run id with a transient error; retrying (${playStatusErrorText(status)})`
6233
+ );
6234
+ }
6235
+ recordCliTrace({
6236
+ phase: "cli.play_start_stream_retry",
6237
+ ms: 0,
6238
+ ok: true,
6239
+ playName: input.playName,
6240
+ attempt: attempt + 1,
6241
+ reason: playStatusErrorText(status)
6242
+ });
6243
+ await sleep4(retryDelayMs);
6244
+ }
6245
+ throw new DeeplineError(
6246
+ `Play ${input.playName} did not start after retrying transient start failures.`,
6247
+ void 0,
6248
+ "PLAY_START_RETRY_EXHAUSTED"
6249
+ );
6250
+ }
6251
+ async function startAndWaitForPlayCompletionByStreamOnce(input) {
5924
6252
  const startedAt = Date.now();
5925
6253
  const dashboardUrl = buildPlayDashboardUrl(
5926
6254
  input.client.baseUrl,
@@ -6247,14 +6575,23 @@ function buildRunWarnings(status, rowsInfo) {
6247
6575
  }
6248
6576
  return [];
6249
6577
  }
6250
- function buildRunNextCommands(runId, dashboardUrl) {
6578
+ function buildRunNextCommands(status) {
6579
+ const runId = status.runId?.trim();
6580
+ if (!runId) {
6581
+ const playName = extractRunPlayName(status);
6582
+ return playName ? {
6583
+ list: `deepline runs list --play ${playName} --json`
6584
+ } : {
6585
+ list: "deepline runs list --json"
6586
+ };
6587
+ }
6251
6588
  const commands = {
6252
6589
  get: `deepline runs get ${runId} --json`,
6253
6590
  stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
6254
6591
  logs: `deepline runs logs ${runId} --out run.log --json`
6255
6592
  };
6256
- if (dashboardUrl) {
6257
- commands.open = `Open ${dashboardUrl} to see results.`;
6593
+ if (status.dashboardUrl) {
6594
+ commands.open = `Open ${status.dashboardUrl} to see results.`;
6258
6595
  }
6259
6596
  return commands;
6260
6597
  }
@@ -6277,6 +6614,121 @@ function getTimestampField(value, key) {
6277
6614
  }
6278
6615
  return typeof field === "string" && field.trim() ? field : null;
6279
6616
  }
6617
+ function getObjectField(value, key) {
6618
+ const field = getRecordField(value, key);
6619
+ return field && typeof field === "object" && !Array.isArray(field) ? field : null;
6620
+ }
6621
+ function formatCreditAmount(value) {
6622
+ if (typeof value !== "number" || !Number.isFinite(value)) {
6623
+ return String(value ?? "-");
6624
+ }
6625
+ return Number(value.toFixed(8)).toString();
6626
+ }
6627
+ function extractJsonObjectFromText(text) {
6628
+ const start = text.indexOf("{");
6629
+ if (start < 0) {
6630
+ return null;
6631
+ }
6632
+ const suffix = text.slice(start);
6633
+ try {
6634
+ const parsed = JSON.parse(suffix);
6635
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
6636
+ } catch {
6637
+ let depth = 0;
6638
+ let inString = false;
6639
+ let escaped = false;
6640
+ for (let index = start; index < text.length; index += 1) {
6641
+ const char = text[index];
6642
+ if (inString) {
6643
+ if (escaped) {
6644
+ escaped = false;
6645
+ } else if (char === "\\") {
6646
+ escaped = true;
6647
+ } else if (char === '"') {
6648
+ inString = false;
6649
+ }
6650
+ continue;
6651
+ }
6652
+ if (char === '"') {
6653
+ inString = true;
6654
+ } else if (char === "{") {
6655
+ depth += 1;
6656
+ } else if (char === "}") {
6657
+ depth -= 1;
6658
+ if (depth === 0) {
6659
+ try {
6660
+ const parsed = JSON.parse(text.slice(start, index + 1));
6661
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
6662
+ } catch {
6663
+ return null;
6664
+ }
6665
+ }
6666
+ }
6667
+ }
6668
+ }
6669
+ return null;
6670
+ }
6671
+ function extractBillingFromText(text) {
6672
+ if (!text) {
6673
+ return null;
6674
+ }
6675
+ const parsed = extractJsonObjectFromText(text);
6676
+ return getObjectField(parsed, "billing");
6677
+ }
6678
+ function extractToolIdFromErrorText(text) {
6679
+ if (!text) {
6680
+ return null;
6681
+ }
6682
+ const lowerToolMatch = /\btool\s+([A-Za-z0-9_.:-]+)\s+\d{3}\b/.exec(text);
6683
+ if (lowerToolMatch?.[1]) {
6684
+ return lowerToolMatch[1];
6685
+ }
6686
+ const upperToolMatch = /\bTool\s+([A-Za-z0-9_.:-]+)\s+failed\b/.exec(text);
6687
+ return upperToolMatch?.[1] ?? null;
6688
+ }
6689
+ function isInsufficientCreditsBilling(billing) {
6690
+ return billing?.kind === "insufficient_credits";
6691
+ }
6692
+ function extractBillingForStatus(status, error) {
6693
+ const errorBilling = getObjectField(status, "errorBilling");
6694
+ if (errorBilling) {
6695
+ return errorBilling;
6696
+ }
6697
+ const directErrors = getRecordField(status, "errors");
6698
+ if (Array.isArray(directErrors)) {
6699
+ for (const entry of directErrors) {
6700
+ const billing = getObjectField(entry, "billing");
6701
+ if (billing) {
6702
+ return billing;
6703
+ }
6704
+ }
6705
+ }
6706
+ const progressError = getStringField(status.progress, "error");
6707
+ return extractBillingFromText(error) ?? extractBillingFromText(progressError) ?? getObjectField(status, "billing");
6708
+ }
6709
+ function formatInsufficientCreditsMessage(input) {
6710
+ const operation = getStringField(input.billing, "operation_id") ?? getStringField(input.billing, "operation") ?? extractToolIdFromErrorText(input.error) ?? getStringField(input.billing, "provider") ?? "tool call";
6711
+ const balance = formatCreditAmount(input.billing.balance_credits);
6712
+ const required = formatCreditAmount(input.billing.required_credits);
6713
+ const recommended = formatCreditAmount(
6714
+ input.billing.recommended_add_credits ?? input.billing.needed_credits
6715
+ );
6716
+ const billingUrl = getStringField(input.billing, "billing_url");
6717
+ const workspace = getStringField(input.billing, "workspace_id") ?? getStringField(input.billing, "workspaceId");
6718
+ const workspaceSuffix = workspace ? ` (workspace=${workspace})` : "";
6719
+ const addSuffix = billingUrl && recommended !== "-" ? ` Add >=${recommended} at ${billingUrl}.` : billingUrl ? ` Add credits at ${billingUrl}.` : "";
6720
+ return `Workspace balance ${balance} < required ${required} for ${operation}${workspaceSuffix}.${addSuffix}`;
6721
+ }
6722
+ function formatPlayErrorForDisplay(status, error) {
6723
+ if (!error) {
6724
+ return null;
6725
+ }
6726
+ const billing = extractBillingForStatus(status, error);
6727
+ if (isInsufficientCreditsBilling(billing)) {
6728
+ return formatInsufficientCreditsMessage({ billing, error });
6729
+ }
6730
+ return error;
6731
+ }
6280
6732
  function normalizeRunStatusForEnvelope(status) {
6281
6733
  const run = status.run ?? null;
6282
6734
  return {
@@ -6327,18 +6779,33 @@ function normalizeErrorsForEnvelope(status, error) {
6327
6779
  if (Array.isArray(directErrors)) {
6328
6780
  return directErrors.filter(
6329
6781
  (entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)
6330
- );
6782
+ ).map((entry) => {
6783
+ const message2 = typeof entry.message === "string" && entry.message.trim() ? entry.message : error;
6784
+ const billing2 = getObjectField(entry, "billing");
6785
+ if (!isInsufficientCreditsBilling(billing2) || !message2) {
6786
+ return entry;
6787
+ }
6788
+ return {
6789
+ ...entry,
6790
+ message: formatInsufficientCreditsMessage({ billing: billing2, error: message2 }),
6791
+ billing: stripProviderSpendFromBilling(billing2)
6792
+ };
6793
+ });
6331
6794
  }
6332
6795
  if (!error) {
6333
6796
  return [];
6334
6797
  }
6798
+ const nextCommands = buildRunNextCommands(status);
6799
+ const billing = extractBillingForStatus(status, error);
6800
+ const message = formatPlayErrorForDisplay(status, error) ?? error;
6335
6801
  return [
6336
6802
  {
6337
6803
  code: getStringField(status, "errorCode") ?? "RUN_FAILED",
6338
6804
  phase: getStringField(status, "errorPhase") ?? "runtime",
6339
- message: error,
6805
+ message,
6340
6806
  retryable: typeof getRecordField(status, "retryable") === "boolean" ? getRecordField(status, "retryable") : null,
6341
- nextAction: `deepline runs get ${status.runId} --json`
6807
+ ...billing ? { billing: stripProviderSpendFromBilling(billing) } : {},
6808
+ nextAction: nextCommands.get ?? nextCommands.list
6342
6809
  }
6343
6810
  ];
6344
6811
  }
@@ -6387,6 +6854,7 @@ function compactPlayStatus(status) {
6387
6854
  ) : null;
6388
6855
  const progressError = status.progress?.error;
6389
6856
  const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
6857
+ const displayError = formatPlayErrorForDisplay(status, error);
6390
6858
  return {
6391
6859
  runId: status.runId,
6392
6860
  apiVersion: status.apiVersion ?? 1,
@@ -6399,13 +6867,13 @@ function compactPlayStatus(status) {
6399
6867
  steps: normalizeStepsForEnvelope(status),
6400
6868
  errors: normalizeErrorsForEnvelope(status, error),
6401
6869
  logs: normalizeLogsForEnvelope(status),
6402
- ...error ? { error } : {},
6870
+ ...displayError ? { error: displayError } : {},
6403
6871
  ...warnings.length > 0 ? { warnings } : {},
6404
6872
  ...result !== void 0 ? { result } : {},
6405
6873
  ...status.resultView ? { resultView: status.resultView } : {},
6406
6874
  ...datasetStats ? { dataset_stats: datasetStats } : {},
6407
6875
  ...billing ? { billing } : {},
6408
- next: buildRunNextCommands(status.runId, status.dashboardUrl)
6876
+ next: buildRunNextCommands(status)
6409
6877
  };
6410
6878
  }
6411
6879
  function enrichPlayStatusWithDatasetStats(status) {
@@ -6479,7 +6947,8 @@ function writePlayResult(status, jsonOutput, options) {
6479
6947
  lines.push(...formatDatasetStatsLines(datasetStats));
6480
6948
  const progressError = status.progress?.error;
6481
6949
  if (progressError && typeof progressError === "string") {
6482
- lines.push(` error: ${progressError.slice(0, 200)}`);
6950
+ const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
6951
+ lines.push(` error: ${displayError.slice(0, 200)}`);
6483
6952
  }
6484
6953
  const renderedServerView = renderServerResultView(status.resultView);
6485
6954
  if (result) {
@@ -6503,8 +6972,11 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
6503
6972
  function shellSingleQuote(value) {
6504
6973
  return `'${value.replace(/'/g, `'\\''`)}'`;
6505
6974
  }
6975
+ function sqlStringLiteral(value) {
6976
+ return `'${value.replace(/'/g, "''")}'`;
6977
+ }
6506
6978
  function runExportRetryCommand(runId, outPath, datasetPath) {
6507
- return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path9.resolve)(outPath))}`;
6979
+ return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path10.resolve)(outPath))}`;
6508
6980
  }
6509
6981
  function extractRunPlayName(status) {
6510
6982
  const run = status.run;
@@ -6521,6 +6993,26 @@ function extractRunPlayName(status) {
6521
6993
  }
6522
6994
  return null;
6523
6995
  }
6996
+ function normalizeCustomerDbIdentifier(value) {
6997
+ return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
6998
+ }
6999
+ function buildCustomerDbQueryPlan(input) {
7000
+ const playName = extractRunPlayName(input.status);
7001
+ const tableNamespace = input.rowsInfo.tableNamespace?.trim();
7002
+ if (!playName || !tableNamespace || input.rowsInfo.totalRows <= 0) {
7003
+ return null;
7004
+ }
7005
+ const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
7006
+ tableNamespace
7007
+ )}`;
7008
+ const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input.status.runId)} limit ${input.rowsInfo.totalRows}`;
7009
+ const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input.rowsInfo.totalRows}`;
7010
+ return {
7011
+ sql,
7012
+ json: `${base} --json`,
7013
+ csv: `${base} --format csv --out ${shellSingleQuote((0, import_node_path10.resolve)(input.outPath))}`
7014
+ };
7015
+ }
6524
7016
  function exportableSheetRow(row) {
6525
7017
  if (!row || typeof row !== "object" || Array.isArray(row)) {
6526
7018
  return null;
@@ -6692,6 +7184,60 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
6692
7184
  }
6693
7185
  return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
6694
7186
  }
7187
+ function extractActiveRunsFromError(error) {
7188
+ if (!(error instanceof DeeplineError)) {
7189
+ return [];
7190
+ }
7191
+ const response = error.details?.response;
7192
+ if (!response || typeof response !== "object" || Array.isArray(response)) {
7193
+ return [];
7194
+ }
7195
+ const details = response.details;
7196
+ if (!details || typeof details !== "object" || Array.isArray(details)) {
7197
+ return [];
7198
+ }
7199
+ const activeRuns = details.activeRuns;
7200
+ return Array.isArray(activeRuns) ? activeRuns.filter(
7201
+ (run) => Boolean(run) && typeof run === "object" && !Array.isArray(run)
7202
+ ) : [];
7203
+ }
7204
+ function activeRunId(run) {
7205
+ return getStringField(run, "workflowId") ?? getStringField(run, "runId");
7206
+ }
7207
+ function formatActiveRunConflictError(input) {
7208
+ const lines = [
7209
+ `Active run exists for ${input.playName}. Use --force to supersede, or inspect/stop the active run first.`
7210
+ ];
7211
+ for (const run of input.activeRuns.slice(0, 3)) {
7212
+ const runId = activeRunId(run);
7213
+ if (!runId) {
7214
+ continue;
7215
+ }
7216
+ const status = getStringField(run, "status");
7217
+ const startedAt = getStringField(run, "startedAt") ?? getStringField(run, "startTime");
7218
+ lines.push(
7219
+ ` active: ${runId}${status ? ` status=${status}` : ""}${startedAt ? ` startedAt=${startedAt}` : ""}`
7220
+ );
7221
+ lines.push(` get: deepline runs get ${runId} --json`);
7222
+ lines.push(
7223
+ ` stop: deepline runs stop ${runId} --reason "stale lock" --json`
7224
+ );
7225
+ }
7226
+ lines.push(` rerun: add --force to the same deepline plays run command`);
7227
+ return lines.join("\n");
7228
+ }
7229
+ function normalizePlayStartError(error, playName) {
7230
+ const activeRuns = extractActiveRunsFromError(error);
7231
+ if (activeRuns.length === 0) {
7232
+ return error;
7233
+ }
7234
+ return new DeeplineError(
7235
+ formatActiveRunConflictError({ playName, activeRuns }),
7236
+ error instanceof DeeplineError ? error.statusCode : 409,
7237
+ "ACTIVE_RUN_EXISTS",
7238
+ error instanceof DeeplineError ? error.details : void 0
7239
+ );
7240
+ }
6695
7241
  function renderServerResultView(value) {
6696
7242
  if (!value || typeof value !== "object" || Array.isArray(value)) {
6697
7243
  return { lines: [], actions: [] };
@@ -6927,12 +7473,12 @@ function shouldUseLocalOnlyPlayCheck() {
6927
7473
  async function handlePlayCheck(args) {
6928
7474
  const options = parsePlayCheckOptions(args);
6929
7475
  if (!isFileTarget(options.target)) {
6930
- const resolved = (0, import_node_path9.resolve)(options.target);
7476
+ const resolved = (0, import_node_path10.resolve)(options.target);
6931
7477
  console.error(`File not found: ${resolved}`);
6932
7478
  return 1;
6933
7479
  }
6934
- const absolutePlayPath = (0, import_node_path9.resolve)(options.target);
6935
- const sourceCode = (0, import_node_fs7.readFileSync)(absolutePlayPath, "utf-8");
7480
+ const absolutePlayPath = (0, import_node_path10.resolve)(options.target);
7481
+ const sourceCode = (0, import_node_fs8.readFileSync)(absolutePlayPath, "utf-8");
6936
7482
  let graph;
6937
7483
  try {
6938
7484
  graph = await collectBundledPlayGraph(absolutePlayPath);
@@ -6995,12 +7541,12 @@ async function handleFileBackedRun(options) {
6995
7541
  }
6996
7542
  const client = new DeeplineClient();
6997
7543
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
6998
- const absolutePlayPath = (0, import_node_path9.resolve)(options.target.path);
7544
+ const absolutePlayPath = (0, import_node_path10.resolve)(options.target.path);
6999
7545
  progress.phase("compiling play");
7000
7546
  const sourceCode = traceCliSync(
7001
7547
  "cli.play_file_read_source",
7002
7548
  { targetKind: "file" },
7003
- () => (0, import_node_fs7.readFileSync)(absolutePlayPath, "utf-8")
7549
+ () => (0, import_node_fs8.readFileSync)(absolutePlayPath, "utf-8")
7004
7550
  );
7005
7551
  const runtimeInput = options.input ? { ...options.input } : {};
7006
7552
  let graph;
@@ -7081,6 +7627,8 @@ async function handleFileBackedRun(options) {
7081
7627
  waitTimeoutMs: options.waitTimeoutMs,
7082
7628
  noOpen: options.noOpen,
7083
7629
  progress
7630
+ }).catch((error) => {
7631
+ throw normalizePlayStartError(error, playName);
7084
7632
  })
7085
7633
  );
7086
7634
  if (finalStatus.status === "completed") {
@@ -7099,7 +7647,9 @@ async function handleFileBackedRun(options) {
7099
7647
  const started = await traceCliSpan(
7100
7648
  "cli.play_start_unwatched",
7101
7649
  { targetKind: "file", playName },
7102
- () => client.startPlayRun(startRequest)
7650
+ () => client.startPlayRun(startRequest).catch((error) => {
7651
+ throw normalizePlayStartError(error, playName);
7652
+ })
7103
7653
  );
7104
7654
  const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
7105
7655
  openPlayDashboard({
@@ -7216,6 +7766,8 @@ async function handleNamedRun(options) {
7216
7766
  waitTimeoutMs: options.waitTimeoutMs,
7217
7767
  noOpen: options.noOpen,
7218
7768
  progress
7769
+ }).catch((error) => {
7770
+ throw normalizePlayStartError(error, playName);
7219
7771
  })
7220
7772
  );
7221
7773
  if (finalStatus.status === "completed") {
@@ -7234,7 +7786,9 @@ async function handleNamedRun(options) {
7234
7786
  const started = await traceCliSpan(
7235
7787
  "cli.play_start_unwatched",
7236
7788
  { targetKind: "name", playName },
7237
- () => client.startPlayRun(startRequest)
7789
+ () => client.startPlayRun(startRequest).catch((error) => {
7790
+ throw normalizePlayStartError(error, playName);
7791
+ })
7238
7792
  );
7239
7793
  const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
7240
7794
  openPlayDashboard({
@@ -7260,19 +7814,19 @@ async function handlePlayRun(args) {
7260
7814
  if (isFileTarget(options.target.path)) {
7261
7815
  return handleFileBackedRun(options);
7262
7816
  }
7263
- const resolved = (0, import_node_path9.resolve)(options.target.path);
7817
+ const resolved = (0, import_node_path10.resolve)(options.target.path);
7264
7818
  console.error(`File not found: ${resolved}`);
7265
- const dir = (0, import_node_path9.dirname)(resolved);
7266
- if ((0, import_node_fs7.existsSync)(dir)) {
7267
- const base = (0, import_node_path9.basename)(resolved);
7819
+ const dir = (0, import_node_path10.dirname)(resolved);
7820
+ if ((0, import_node_fs8.existsSync)(dir)) {
7821
+ const base = (0, import_node_path10.basename)(resolved);
7268
7822
  try {
7269
- const siblings = (0, import_node_fs7.readdirSync)(dir).filter(
7823
+ const siblings = (0, import_node_fs8.readdirSync)(dir).filter(
7270
7824
  (f) => f.includes(base.replace(/\.(play\.)?ts$/, "")) || f.endsWith(".play.ts")
7271
7825
  );
7272
7826
  if (siblings.length > 0) {
7273
7827
  console.error(`Did you mean one of these?`);
7274
7828
  for (const s of siblings.slice(0, 5)) {
7275
- console.error(` ${(0, import_node_path9.join)(dir, s)}`);
7829
+ console.error(` ${(0, import_node_path10.join)(dir, s)}`);
7276
7830
  }
7277
7831
  }
7278
7832
  } catch {
@@ -7407,14 +7961,14 @@ async function handleRunLogs(args) {
7407
7961
  continue;
7408
7962
  }
7409
7963
  if (arg === "--out" && args[index + 1]) {
7410
- outPath = (0, import_node_path9.resolve)(args[++index]);
7964
+ outPath = (0, import_node_path10.resolve)(args[++index]);
7411
7965
  }
7412
7966
  }
7413
7967
  const client = new DeeplineClient();
7414
7968
  const status = await client.runs.get(runId);
7415
7969
  const logs = status.progress?.logs ?? [];
7416
7970
  if (outPath) {
7417
- (0, import_node_fs7.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
7971
+ (0, import_node_fs8.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
7418
7972
  printCommandEnvelope({
7419
7973
  runId: status.runId,
7420
7974
  log_path: outPath,
@@ -7470,7 +8024,7 @@ async function handleRunStop(args) {
7470
8024
  return 0;
7471
8025
  }
7472
8026
  async function handleRunExport(args) {
7473
- const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--json]";
8027
+ const usage = "Usage: deepline runs export <run-id> [--dataset result.rows] --out output.csv [--metadata-out export.json] [--json]";
7474
8028
  let runId;
7475
8029
  try {
7476
8030
  runId = parseRunIdPositional(args, usage);
@@ -7480,14 +8034,19 @@ async function handleRunExport(args) {
7480
8034
  }
7481
8035
  let outPath = null;
7482
8036
  let datasetPath = null;
8037
+ let metadataOutPath = null;
7483
8038
  for (let index = 0; index < args.length; index += 1) {
7484
8039
  const arg = args[index];
7485
8040
  if (arg === "--out" && args[index + 1]) {
7486
- outPath = (0, import_node_path9.resolve)(args[++index]);
8041
+ outPath = (0, import_node_path10.resolve)(args[++index]);
7487
8042
  continue;
7488
8043
  }
7489
8044
  if (arg === "--dataset" && args[index + 1]) {
7490
8045
  datasetPath = args[++index];
8046
+ continue;
8047
+ }
8048
+ if (arg === "--metadata-out" && args[index + 1]) {
8049
+ metadataOutPath = (0, import_node_path10.resolve)(args[++index]);
7491
8050
  }
7492
8051
  }
7493
8052
  if (!outPath) {
@@ -7499,15 +8058,59 @@ async function handleRunExport(args) {
7499
8058
  const exportResult = await exportPlayStatusRows(client, status, outPath, {
7500
8059
  datasetPath
7501
8060
  });
7502
- printCommandEnvelope({
8061
+ const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
8062
+ const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
8063
+ status,
8064
+ rowsInfo: exportResult.rowsInfo,
8065
+ outPath
8066
+ }) : null;
8067
+ const next = {
8068
+ ...buildRunNextCommands(status),
8069
+ export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
8070
+ ...queryPlan ? {
8071
+ queryJson: queryPlan.json,
8072
+ queryCsv: queryPlan.csv
8073
+ } : {}
8074
+ };
8075
+ const payload = {
7503
8076
  runId: status.runId,
7504
8077
  ...datasetPath ? { dataset: datasetPath } : {},
7505
8078
  csv_path: exportResult?.path ?? null,
8079
+ source,
7506
8080
  rowCount: exportResult?.rowsInfo.totalRows ?? null,
7507
8081
  columns: exportResult?.rowsInfo.columns ?? [],
8082
+ ...queryPlan ? {
8083
+ query: {
8084
+ sql: queryPlan.sql,
8085
+ json: queryPlan.json,
8086
+ csv: queryPlan.csv
8087
+ }
8088
+ } : {},
8089
+ ...metadataOutPath ? { metadata_path: metadataOutPath } : {},
7508
8090
  local: { csv_path: exportResult?.path ?? null },
7509
- render: { sections: [{ title: "run export", lines: [`Exported ${status.runId} to ${exportResult?.path ?? outPath}`] }] }
7510
- }, { json: argsWantJson(args) });
8091
+ next,
8092
+ render: {
8093
+ sections: [
8094
+ {
8095
+ title: "run export",
8096
+ lines: [
8097
+ `Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
8098
+ ...source ? [`source=${source}`] : [],
8099
+ ...queryPlan ? [`query=${queryPlan.json}`] : []
8100
+ ]
8101
+ }
8102
+ ]
8103
+ }
8104
+ };
8105
+ if (metadataOutPath) {
8106
+ (0, import_node_fs8.writeFileSync)(
8107
+ metadataOutPath,
8108
+ `${JSON.stringify(payload, null, 2)}
8109
+ `,
8110
+ "utf-8"
8111
+ );
8112
+ }
8113
+ printCommandEnvelope(payload, { json: argsWantJson(args) });
7511
8114
  return 0;
7512
8115
  }
7513
8116
  async function handlePlayGet(args) {
@@ -7524,10 +8127,10 @@ async function handlePlayGet(args) {
7524
8127
  for (let index = 1; index < args.length; index += 1) {
7525
8128
  const arg = args[index];
7526
8129
  if (arg === "--out" && args[index + 1]) {
7527
- outPath = (0, import_node_path9.resolve)(args[++index]);
8130
+ outPath = (0, import_node_path10.resolve)(args[++index]);
7528
8131
  }
7529
8132
  }
7530
- const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs7.readFileSync)((0, import_node_path9.resolve)(target), "utf-8"), (0, import_node_path9.resolve)(target)) : parseReferencedPlayTarget(target).playName;
8133
+ const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs8.readFileSync)((0, import_node_path10.resolve)(target), "utf-8"), (0, import_node_path10.resolve)(target)) : parseReferencedPlayTarget(target).playName;
7531
8134
  const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
7532
8135
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
7533
8136
  const materializedFile = outPath ? materializeRemotePlaySource({
@@ -7819,7 +8422,7 @@ async function handlePlayPublish(args) {
7819
8422
  }
7820
8423
  let graph;
7821
8424
  try {
7822
- graph = await collectBundledPlayGraph((0, import_node_path9.resolve)(playName));
8425
+ graph = await collectBundledPlayGraph((0, import_node_path10.resolve)(playName));
7823
8426
  await compileBundledPlayGraphManifests(client, graph);
7824
8427
  await publishImportedPlayDependencies(client, graph);
7825
8428
  } catch (error) {
@@ -8305,32 +8908,36 @@ Examples:
8305
8908
  Notes:
8306
8909
  Writes a returned dataset handle to the requested local CSV path. Use runs get
8307
8910
  first to inspect dataset paths like result.rows or result.nested.contacts.
8911
+ --metadata-out writes the same export metadata object returned by --json,
8912
+ including source and follow-on customer-db query commands when available.
8308
8913
 
8309
8914
  Examples:
8310
8915
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
8311
8916
  deepline runs export play/my-play/run/20260501t000000-000 --dataset result.rows --out output.csv
8917
+ deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --metadata-out output.meta.json
8312
8918
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
8313
8919
  `
8314
- ).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
8920
+ ).requiredOption("--out <path>", "Output CSV path").option("--dataset <path>", "Returned dataset handle path, such as result.rows").option("--metadata-out <path>", "Write export metadata JSON to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
8315
8921
  process.exitCode = await handleRunExport([
8316
8922
  runId,
8317
8923
  ...options.dataset ? ["--dataset", options.dataset] : [],
8318
8924
  "--out",
8319
8925
  options.out,
8926
+ ...options.metadataOut ? ["--metadata-out", options.metadataOut] : [],
8320
8927
  ...options.json ? ["--json"] : []
8321
8928
  ]);
8322
8929
  });
8323
8930
  }
8324
8931
 
8325
8932
  // src/cli/commands/tools.ts
8326
- var import_node_fs9 = require("fs");
8933
+ var import_node_fs10 = require("fs");
8327
8934
  var import_node_os7 = require("os");
8328
- var import_node_path11 = require("path");
8935
+ var import_node_path12 = require("path");
8329
8936
 
8330
8937
  // src/tool-output.ts
8331
- var import_node_fs8 = require("fs");
8938
+ var import_node_fs9 = require("fs");
8332
8939
  var import_node_os6 = require("os");
8333
- var import_node_path10 = require("path");
8940
+ var import_node_path11 = require("path");
8334
8941
  function isPlainObject(value) {
8335
8942
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8336
8943
  }
@@ -8353,6 +8960,25 @@ function normalizeRows(value) {
8353
8960
  }
8354
8961
  function candidateRoots(payload) {
8355
8962
  const roots = [{ path: null, value: payload }];
8963
+ if (isPlainObject(payload) && isPlainObject(payload.toolExecutionResult)) {
8964
+ roots.push({ path: "toolExecutionResult", value: payload.toolExecutionResult });
8965
+ const toolOutput = payload.toolExecutionResult.toolOutput;
8966
+ if (isPlainObject(toolOutput)) {
8967
+ roots.push({ path: "toolExecutionResult.toolOutput", value: toolOutput });
8968
+ if (Object.prototype.hasOwnProperty.call(toolOutput, "raw")) {
8969
+ roots.push({
8970
+ path: "toolExecutionResult.toolOutput.raw",
8971
+ value: toolOutput.raw
8972
+ });
8973
+ }
8974
+ }
8975
+ }
8976
+ if (isPlainObject(payload) && isPlainObject(payload.output)) {
8977
+ roots.push({ path: "output", value: payload.output });
8978
+ if (Object.prototype.hasOwnProperty.call(payload.output, "body")) {
8979
+ roots.push({ path: "output.body", value: payload.output.body });
8980
+ }
8981
+ }
8356
8982
  if (isPlainObject(payload) && isPlainObject(payload.result)) {
8357
8983
  roots.push({ path: "result", value: payload.result });
8358
8984
  if (isPlainObject(payload.result.data)) {
@@ -8405,19 +9031,19 @@ function tryConvertToList(payload, options) {
8405
9031
  return null;
8406
9032
  }
8407
9033
  function ensureOutputDir() {
8408
- const outputDir = (0, import_node_path10.join)((0, import_node_os6.homedir)(), ".local", "share", "deepline", "data");
8409
- (0, import_node_fs8.mkdirSync)(outputDir, { recursive: true });
9034
+ const outputDir = (0, import_node_path11.join)((0, import_node_os6.homedir)(), ".local", "share", "deepline", "data");
9035
+ (0, import_node_fs9.mkdirSync)(outputDir, { recursive: true });
8410
9036
  return outputDir;
8411
9037
  }
8412
9038
  function writeJsonOutputFile(payload, stem) {
8413
9039
  const outputDir = ensureOutputDir();
8414
- const outputPath = (0, import_node_path10.join)(outputDir, `${stem}_${Date.now()}.json`);
8415
- (0, import_node_fs8.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf-8");
9040
+ const outputPath = (0, import_node_path11.join)(outputDir, `${stem}_${Date.now()}.json`);
9041
+ (0, import_node_fs9.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf-8");
8416
9042
  return outputPath;
8417
9043
  }
8418
9044
  function writeCsvOutputFile(rows, stem) {
8419
9045
  const outputDir = ensureOutputDir();
8420
- const outputPath = (0, import_node_path10.join)(outputDir, `${stem}_${Date.now()}.csv`);
9046
+ const outputPath = (0, import_node_path11.join)(outputDir, `${stem}_${Date.now()}.csv`);
8421
9047
  const seen = /* @__PURE__ */ new Set();
8422
9048
  const columns = [];
8423
9049
  for (const row of rows) {
@@ -8440,7 +9066,7 @@ function writeCsvOutputFile(rows, stem) {
8440
9066
  for (const row of rows) {
8441
9067
  lines.push(columns.map((column) => escapeCell(row[column])).join(","));
8442
9068
  }
8443
- (0, import_node_fs8.writeFileSync)(outputPath, `${lines.join("\n")}
9069
+ (0, import_node_fs9.writeFileSync)(outputPath, `${lines.join("\n")}
8444
9070
  `, "utf-8");
8445
9071
  const previewRows = rows.slice(0, 5);
8446
9072
  const previewColumns = columns.slice(0, 5);
@@ -8486,8 +9112,7 @@ async function listTools(args) {
8486
9112
  title: `${items.length} tools available:`,
8487
9113
  lines: items.flatMap((item) => {
8488
9114
  const cats = item.categories.length ? ` [${item.categories.join(", ")}]` : "";
8489
- const listHint = item.listExtractorPaths?.length ? ` listExtractorPaths=${item.listExtractorPaths.join(",")}` : "";
8490
- return [`${item.toolId}${cats}`, ` ${item.description}${listHint}`];
9115
+ return [`${item.toolId}${cats}`, ` ${item.description}`];
8491
9116
  })
8492
9117
  }
8493
9118
  ]
@@ -8566,7 +9191,7 @@ Common commands:
8566
9191
  deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
8567
9192
 
8568
9193
  Output:
8569
- Use describe for tool contracts. get is accepted as a compatibility alias.
9194
+ Use describe for tool contracts.
8570
9195
  Use execute to run a tool. run is accepted as a compatibility alias.
8571
9196
  `
8572
9197
  );
@@ -8613,8 +9238,8 @@ Examples:
8613
9238
  `
8614
9239
  Notes:
8615
9240
  Shows the tool contract, input schema, output schema, Deepline cost, aliases,
8616
- and metadata. describe is the preferred discovery verb. get is kept as a
8617
- compatibility alias for the same metadata surface.
9241
+ and metadata. describe is the supported discovery verb. get is removed in
9242
+ the V2 SDK CLI; use describe for the same metadata surface.
8618
9243
 
8619
9244
  Examples:
8620
9245
  deepline tools describe hunter_email_verifier
@@ -8627,7 +9252,22 @@ Examples:
8627
9252
  ...options.json ? ["--json"] : []
8628
9253
  ]);
8629
9254
  });
8630
- addToolMetadataCommand(tools.command("describe <toolId>").alias("get"));
9255
+ addToolMetadataCommand(tools.command("describe <toolId>"));
9256
+ tools.command("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
9257
+ "after",
9258
+ `
9259
+ Examples:
9260
+ deepline tools describe hunter_email_verifier --json
9261
+ `
9262
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
9263
+ const message = `tools get has been removed from the V2 SDK CLI. Use: deepline tools describe ${toolId} --json`;
9264
+ if (options.json || shouldEmitJson()) {
9265
+ printJsonError({ message, code: "TOOLS_GET_REMOVED" });
9266
+ } else {
9267
+ console.error(message);
9268
+ }
9269
+ process.exitCode = 2;
9270
+ });
8631
9271
  tools.command("execute <toolId>").alias("run").description("Execute a tool by id.").addHelpText(
8632
9272
  "after",
8633
9273
  `
@@ -8708,6 +9348,7 @@ function printToolDetails(tool, requestedToolId) {
8708
9348
  const stepContributions = arrayField(tool, "stepContributions", "step_contributions");
8709
9349
  const playExpansion = recordField(tool, "playExpansion", "play_expansion");
8710
9350
  const samples = recordField(tool, "samples");
9351
+ const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
8711
9352
  console.log(`Tool: ${toolId}`);
8712
9353
  if (displayName) {
8713
9354
  console.log(" Display name:");
@@ -8771,14 +9412,16 @@ function printToolDetails(tool, requestedToolId) {
8771
9412
  console.log(" Tip: pass --payload with a JSON object.");
8772
9413
  }
8773
9414
  printSamples(samples);
9415
+ printUsageGuidance(usageGuidance);
8774
9416
  if (isPlayTool(tool)) {
8775
9417
  console.log(" Play contract:");
8776
9418
  console.log(" - This is a deepline-native waterfall; the returned rows are extracted by target getters, not by hand-authored payload shape.");
8777
9419
  if (playExpansion && typeof playExpansion.group === "string" && playExpansion.group.trim()) {
8778
9420
  console.log(` - Output alias/runtime group is: ${playExpansion.group.trim()}`);
8779
9421
  }
8780
- const getters = recordField(tool, "resultIdentityGetters", "result_identity_getters");
8781
- const targets = Object.keys(getters).filter(Boolean).sort();
9422
+ const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult");
9423
+ const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
9424
+ const targets = extractedValues.map((entry) => isRecord3(entry) && typeof entry.name === "string" ? entry.name : "").filter(Boolean).sort();
8782
9425
  if (targets.length) {
8783
9426
  console.log(` - Built-in extract targets: ${targets.join(", ")}`);
8784
9427
  }
@@ -8797,7 +9440,53 @@ function printToolDetails(tool, requestedToolId) {
8797
9440
  } else {
8798
9441
  console.log(` deepline tools execute ${toolId} --payload '{...}'`);
8799
9442
  }
8800
- console.log(" deepline tools get <tool_id> --json # full machine-readable output");
9443
+ console.log(" deepline tools describe <tool_id> --json");
9444
+ }
9445
+ function printUsageGuidance(usageGuidance) {
9446
+ if (Object.keys(usageGuidance).length === 0) return;
9447
+ const execute = stringField(usageGuidance, "execute");
9448
+ const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
9449
+ const toolOutput = recordField(toolExecutionResult, "toolOutput", "tool_output");
9450
+ const extractedLists = extractionEntries(toolExecutionResult, "extractedLists", "extracted_lists");
9451
+ const extractedValues = extractionEntries(toolExecutionResult, "extractedValues", "extracted_values");
9452
+ console.log(" Usage guidance:");
9453
+ if (execute) console.log(` ${execute}`);
9454
+ const raw = pathField(toolOutput, "raw");
9455
+ const meta = pathField(toolOutput, "meta");
9456
+ if (raw) console.log(` Raw tool output: ${raw}`);
9457
+ if (meta) console.log(` Tool output metadata: ${meta}`);
9458
+ printExtractions("Extracted lists", extractedLists);
9459
+ printExtractions("Extracted values", extractedValues);
9460
+ }
9461
+ function pathField(record, key) {
9462
+ const value = record[key];
9463
+ if (typeof value === "string") return value.trim();
9464
+ if (isRecord3(value)) return stringField(value, "path");
9465
+ return "";
9466
+ }
9467
+ function extractionEntries(record, camelKey, snakeKey) {
9468
+ const value = record[camelKey] ?? record[snakeKey];
9469
+ if (Array.isArray(value)) return value;
9470
+ if (!isRecord3(value)) return [];
9471
+ return Object.entries(value).map(
9472
+ ([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
9473
+ );
9474
+ }
9475
+ function printExtractions(label, entries) {
9476
+ if (!entries.length) return;
9477
+ console.log(` ${label}:`);
9478
+ for (const entry of entries) {
9479
+ if (!isRecord3(entry)) continue;
9480
+ const name = stringField(entry, "name");
9481
+ const expression = stringField(entry, "expression");
9482
+ const details = recordField(entry, "details");
9483
+ const rawToolOutputPaths = arrayField(details, "rawToolOutputPaths", "raw_tool_output_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
9484
+ const candidatePaths = arrayField(details, "candidatePaths", "candidate_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
9485
+ if (!name || !expression) continue;
9486
+ const paths = candidatePaths.length ? candidatePaths : rawToolOutputPaths;
9487
+ const pathSuffix = paths.length ? ` from ${paths.join(", ")}` : "";
9488
+ console.log(` - ${name}: ${expression}${pathSuffix}`);
9489
+ }
8801
9490
  }
8802
9491
  function printToolCost(input) {
8803
9492
  const { cost, billingSource, deeplineCredits, deeplineUsdPerPricingUnit } = input;
@@ -8865,6 +9554,17 @@ function samplePayload(samples, key) {
8865
9554
  function commandEnvelopeFromRawResponse(rawResponse) {
8866
9555
  return isRecord3(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
8867
9556
  }
9557
+ function listExtractorPathsFromUsageGuidance(tool) {
9558
+ const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
9559
+ const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord3(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
9560
+ return extractedLists.flatMap((entry) => {
9561
+ const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
9562
+ if (!Array.isArray(paths)) return [];
9563
+ return paths.map(
9564
+ (path) => path.trim().replace(/^toolExecutionResult\.toolOutput\.raw\.?/, "").replace(/^\./, "")
9565
+ ).filter(Boolean);
9566
+ });
9567
+ }
8868
9568
  function isPlayTool(tool) {
8869
9569
  const provider = typeof tool.provider === "string" ? tool.provider : "";
8870
9570
  return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
@@ -8938,6 +9638,7 @@ function parseExecuteOptions(args) {
8938
9638
  const params = {};
8939
9639
  let outputFormat = "auto";
8940
9640
  let noPreview = false;
9641
+ let fullOutput = false;
8941
9642
  for (let index = 1; index < args.length; index += 1) {
8942
9643
  const arg = args[index];
8943
9644
  if ((arg === "--param" || arg === "-p") && args[index + 1]) {
@@ -8964,6 +9665,7 @@ function parseExecuteOptions(args) {
8964
9665
  }
8965
9666
  if (arg === "--full-output") {
8966
9667
  outputFormat = "json";
9668
+ fullOutput = true;
8967
9669
  continue;
8968
9670
  }
8969
9671
  if (arg === "--no-preview") {
@@ -8972,7 +9674,7 @@ function parseExecuteOptions(args) {
8972
9674
  }
8973
9675
  throw new Error(`Unknown option: ${arg}`);
8974
9676
  }
8975
- return { toolId, params, outputFormat, noPreview };
9677
+ return { toolId, params, outputFormat, noPreview, fullOutput };
8976
9678
  }
8977
9679
  function safeFileStem(value) {
8978
9680
  return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
@@ -8986,9 +9688,9 @@ function powerShellQuote(value) {
8986
9688
  function seedToolListScript(input) {
8987
9689
  const stem = safeFileStem(input.toolId);
8988
9690
  const fileName = `${stem}-workflow-seed-${Date.now()}.play.ts`;
8989
- const scriptDir = (0, import_node_fs9.mkdtempSync)((0, import_node_path11.join)((0, import_node_os7.tmpdir)(), "deepline-workflow-seed-"));
8990
- (0, import_node_fs9.chmodSync)(scriptDir, 448);
8991
- const scriptPath = (0, import_node_path11.join)(scriptDir, fileName);
9691
+ const scriptDir = (0, import_node_fs10.mkdtempSync)((0, import_node_path12.join)((0, import_node_os7.tmpdir)(), "deepline-workflow-seed-"));
9692
+ (0, import_node_fs10.chmodSync)(scriptDir, 448);
9693
+ const scriptPath = (0, import_node_path12.join)(scriptDir, fileName);
8992
9694
  const projectDir = `deepline/projects/${stem}-workflow`;
8993
9695
  const playName = `${stem}-workflow`;
8994
9696
  const sampleRows = input.rows.length > 0 ? `${JSON.stringify(input.rows.slice(0, 2)).replace(/\]$/, "")}, ...]` : "[]";
@@ -9004,7 +9706,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
9004
9706
  description: ${JSON.stringify(`Seed ${input.toolId} rows for workflow expansion.`)},
9005
9707
  });
9006
9708
 
9007
- const list = Object.values(result.lists)[0];
9709
+ const list = Object.values(result.extractedLists)[0];
9008
9710
  const rows = (list?.get() ?? []).slice(0, 100);
9009
9711
  // ${sampleRows}
9010
9712
  // columns: ${columns}
@@ -9021,7 +9723,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
9021
9723
  };
9022
9724
  });
9023
9725
  `;
9024
- (0, import_node_fs9.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
9726
+ (0, import_node_fs10.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
9025
9727
  return {
9026
9728
  path: scriptPath,
9027
9729
  projectDir,
@@ -9032,7 +9734,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
9032
9734
  function buildToolExecuteBaseEnvelope(input) {
9033
9735
  const envelope = commandEnvelopeFromRawResponse(input.rawResponse);
9034
9736
  const summaryEntries = Object.entries(input.summary);
9035
- const output = input.listConversion ? {
9737
+ const outputPreview = input.listConversion ? {
9036
9738
  kind: "list",
9037
9739
  rowCount: input.listConversion.rows.length,
9038
9740
  columns: Object.keys(input.listConversion.rows[0] ?? {}),
@@ -9043,6 +9745,7 @@ function buildToolExecuteBaseEnvelope(input) {
9043
9745
  kind: summaryEntries.length > 0 ? "object" : "raw",
9044
9746
  summary: input.summary
9045
9747
  };
9748
+ const envelopeHasCanonicalOutput = isRecord3(envelope.toolExecutionResult) && isRecord3(envelope.toolExecutionResult.toolOutput) && Object.prototype.hasOwnProperty.call(envelope.toolExecutionResult.toolOutput, "raw");
9046
9749
  const actions = input.listConversion ? [
9047
9750
  {
9048
9751
  label: "next",
@@ -9051,7 +9754,7 @@ function buildToolExecuteBaseEnvelope(input) {
9051
9754
  ] : [];
9052
9755
  return {
9053
9756
  ...envelope,
9054
- output,
9757
+ ...envelopeHasCanonicalOutput ? { output_preview: outputPreview } : { output: outputPreview },
9055
9758
  ...summaryEntries.length > 0 ? { summary: input.summary } : {},
9056
9759
  next: input.listConversion ? {
9057
9760
  expandToPlay: "Use stable map and step keys so reruns are idempotent: completed rows are reused, and only missing or stale work runs again."
@@ -9104,8 +9807,12 @@ async function executeTool(args) {
9104
9807
  throw error;
9105
9808
  }
9106
9809
  const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
9810
+ if (parsed.fullOutput) {
9811
+ printJson(rawResponse);
9812
+ return 0;
9813
+ }
9107
9814
  const listConversion = tryConvertToList(rawResponse, {
9108
- listExtractorPaths: metadata.listExtractorPaths ?? []
9815
+ listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
9109
9816
  });
9110
9817
  const summary = extractSummaryFields(rawResponse);
9111
9818
  const baseEnvelope = buildToolExecuteBaseEnvelope({
@@ -9236,8 +9943,8 @@ async function executeTool(args) {
9236
9943
 
9237
9944
  // src/cli/commands/update.ts
9238
9945
  var import_node_child_process = require("child_process");
9239
- var import_node_fs10 = require("fs");
9240
- var import_node_path12 = require("path");
9946
+ var import_node_fs11 = require("fs");
9947
+ var import_node_path13 = require("path");
9241
9948
  function posixShellQuote(value) {
9242
9949
  return `'${value.replace(/'/g, `'\\''`)}'`;
9243
9950
  }
@@ -9256,19 +9963,19 @@ function buildSourceUpdateCommand(sourceRoot) {
9256
9963
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
9257
9964
  }
9258
9965
  function findRepoBackedSdkRoot(startPath) {
9259
- let current = (0, import_node_path12.resolve)(startPath);
9966
+ let current = (0, import_node_path13.resolve)(startPath);
9260
9967
  while (true) {
9261
- if ((0, import_node_fs10.existsSync)((0, import_node_path12.join)(current, "sdk", "package.json")) && (0, import_node_fs10.existsSync)((0, import_node_path12.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
9968
+ if ((0, import_node_fs11.existsSync)((0, import_node_path13.join)(current, "sdk", "package.json")) && (0, import_node_fs11.existsSync)((0, import_node_path13.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
9262
9969
  return current;
9263
9970
  }
9264
- const parent = (0, import_node_path12.dirname)(current);
9971
+ const parent = (0, import_node_path13.dirname)(current);
9265
9972
  if (parent === current) return null;
9266
9973
  current = parent;
9267
9974
  }
9268
9975
  }
9269
9976
  function resolveUpdatePlan() {
9270
- const entrypoint = process.argv[1] ? (0, import_node_path12.resolve)(process.argv[1]) : "";
9271
- const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path12.dirname)(entrypoint)) : null;
9977
+ const entrypoint = process.argv[1] ? (0, import_node_path13.resolve)(process.argv[1]) : "";
9978
+ const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path13.dirname)(entrypoint)) : null;
9272
9979
  if (sourceRoot) {
9273
9980
  return {
9274
9981
  kind: "source",
@@ -9361,9 +10068,9 @@ Examples:
9361
10068
 
9362
10069
  // src/cli/skills-sync.ts
9363
10070
  var import_node_child_process2 = require("child_process");
9364
- var import_node_fs11 = require("fs");
10071
+ var import_node_fs12 = require("fs");
9365
10072
  var import_node_os8 = require("os");
9366
- var import_node_path13 = require("path");
10073
+ var import_node_path14 = require("path");
9367
10074
  var CHECK_TIMEOUT_MS2 = 3e3;
9368
10075
  var SDK_SKILL_NAME = "deepline-sdk";
9369
10076
  var SKILL_AGENTS = ["codex", "claude-code", "cursor"];
@@ -9374,23 +10081,59 @@ function shouldSkipSkillsSync() {
9374
10081
  }
9375
10082
  function sdkSkillsVersionPath(baseUrl) {
9376
10083
  const home = process.env.HOME?.trim() || (0, import_node_os8.homedir)();
9377
- return (0, import_node_path13.join)(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
10084
+ return (0, import_node_path14.join)(home, ".local", "deepline", baseUrlSlug(baseUrl), "sdk-skills", ".version");
9378
10085
  }
9379
10086
  function readLocalSkillsVersion(baseUrl) {
9380
10087
  const path = sdkSkillsVersionPath(baseUrl);
9381
- if (!(0, import_node_fs11.existsSync)(path)) return "";
10088
+ if (!(0, import_node_fs12.existsSync)(path)) return "";
9382
10089
  try {
9383
- return (0, import_node_fs11.readFileSync)(path, "utf-8").trim();
10090
+ return (0, import_node_fs12.readFileSync)(path, "utf-8").trim();
9384
10091
  } catch {
9385
10092
  return "";
9386
10093
  }
9387
10094
  }
9388
10095
  function writeLocalSkillsVersion(baseUrl, version) {
9389
10096
  const path = sdkSkillsVersionPath(baseUrl);
9390
- (0, import_node_fs11.mkdirSync)((0, import_node_path13.dirname)(path), { recursive: true });
9391
- (0, import_node_fs11.writeFileSync)(path, `${version}
10097
+ (0, import_node_fs12.mkdirSync)((0, import_node_path14.dirname)(path), { recursive: true });
10098
+ (0, import_node_fs12.writeFileSync)(path, `${version}
9392
10099
  `, "utf-8");
9393
10100
  }
10101
+ function installedSdkSkillHasStalePositionalExecuteExamples() {
10102
+ const home = process.env.HOME?.trim() || (0, import_node_os8.homedir)();
10103
+ const roots = [
10104
+ (0, import_node_path14.join)(home, ".claude", "skills", SDK_SKILL_NAME),
10105
+ (0, import_node_path14.join)(home, ".agents", "skills", SDK_SKILL_NAME)
10106
+ ];
10107
+ const staleMarkers = [
10108
+ "ctx.tools.execute(key",
10109
+ "ctx.tools.execute('",
10110
+ 'ctx.tools.execute("',
10111
+ "rowCtx.tools.execute('",
10112
+ 'rowCtx.tools.execute("'
10113
+ ];
10114
+ const scan = (dir) => {
10115
+ for (const entry of (0, import_node_fs12.readdirSync)(dir)) {
10116
+ const path = (0, import_node_path14.join)(dir, entry);
10117
+ const stat3 = (0, import_node_fs12.statSync)(path);
10118
+ if (stat3.isDirectory()) {
10119
+ if (scan(path)) return true;
10120
+ continue;
10121
+ }
10122
+ if (!entry.endsWith(".md")) continue;
10123
+ const text = (0, import_node_fs12.readFileSync)(path, "utf-8");
10124
+ if (staleMarkers.some((marker) => text.includes(marker))) return true;
10125
+ }
10126
+ return false;
10127
+ };
10128
+ for (const root of roots) {
10129
+ try {
10130
+ if ((0, import_node_fs12.existsSync)(root) && scan(root)) return true;
10131
+ } catch {
10132
+ continue;
10133
+ }
10134
+ }
10135
+ return false;
10136
+ }
9394
10137
  async function fetchSkillsUpdate(baseUrl, localVersion) {
9395
10138
  const controller = new AbortController();
9396
10139
  const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS2);
@@ -9426,7 +10169,7 @@ function buildSkillsInstallArgs(baseUrl) {
9426
10169
  "skills",
9427
10170
  "add",
9428
10171
  packageUrl,
9429
- "--agents",
10172
+ "--agent",
9430
10173
  ...SKILL_AGENTS,
9431
10174
  "--global",
9432
10175
  "--yes",
@@ -9442,7 +10185,7 @@ function buildBunxSkillsInstallArgs(baseUrl) {
9442
10185
  "skills",
9443
10186
  "add",
9444
10187
  packageUrl,
9445
- "--agents",
10188
+ "--agent",
9446
10189
  ...SKILL_AGENTS,
9447
10190
  "--global",
9448
10191
  "--yes",
@@ -9482,7 +10225,7 @@ function resolveSkillsInstallCommands(baseUrl) {
9482
10225
  return [npxInstall];
9483
10226
  }
9484
10227
  function runOneSkillsInstall(install) {
9485
- return new Promise((resolve10) => {
10228
+ return new Promise((resolve11) => {
9486
10229
  const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
9487
10230
  stdio: ["ignore", "ignore", "pipe"],
9488
10231
  env: process.env
@@ -9492,7 +10235,7 @@ function runOneSkillsInstall(install) {
9492
10235
  stderr += chunk.toString("utf-8");
9493
10236
  });
9494
10237
  child.on("error", (error) => {
9495
- resolve10({
10238
+ resolve11({
9496
10239
  ok: false,
9497
10240
  detail: `failed to start ${install.command}: ${error.message}`,
9498
10241
  manualCommand: install.manualCommand
@@ -9500,11 +10243,11 @@ function runOneSkillsInstall(install) {
9500
10243
  });
9501
10244
  child.on("close", (code) => {
9502
10245
  if (code === 0) {
9503
- resolve10({ ok: true, detail: "", manualCommand: install.manualCommand });
10246
+ resolve11({ ok: true, detail: "", manualCommand: install.manualCommand });
9504
10247
  return;
9505
10248
  }
9506
10249
  const detail = stderr.trim();
9507
- resolve10({
10250
+ resolve11({
9508
10251
  ok: false,
9509
10252
  detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
9510
10253
  manualCommand: install.manualCommand
@@ -9543,10 +10286,19 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
9543
10286
  attemptedSync = true;
9544
10287
  const localVersion = readLocalSkillsVersion(baseUrl);
9545
10288
  const update = await fetchSkillsUpdate(baseUrl, localVersion);
9546
- if (!update?.needsUpdate || !update.remoteVersion) return;
10289
+ const hasStaleInstalledSkill = installedSdkSkillHasStalePositionalExecuteExamples();
10290
+ if (!update?.needsUpdate && !hasStaleInstalledSkill || !update?.remoteVersion) {
10291
+ return;
10292
+ }
9547
10293
  writeSdkSkillsStatusLine("SDK skills changed; syncing deepline-sdk skill...");
9548
10294
  const installed = await runSkillsInstall(baseUrl);
9549
10295
  if (!installed) return;
10296
+ if (installedSdkSkillHasStalePositionalExecuteExamples()) {
10297
+ process.stderr.write(
10298
+ "SDK skills sync completed, but installed deepline-sdk docs still contain stale positional ctx.tools.execute examples.\n"
10299
+ );
10300
+ return;
10301
+ }
9550
10302
  writeLocalSkillsVersion(baseUrl, update.remoteVersion);
9551
10303
  writeSdkSkillsStatusLine("SDK skills are up to date.");
9552
10304
  }