deepline 0.1.10 → 0.1.12

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.
Files changed (34) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/index.js +509 -353
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/index.mjs +513 -358
  5. package/dist/cli/index.mjs.map +1 -1
  6. package/dist/index.d.mts +250 -305
  7. package/dist/index.d.ts +250 -305
  8. package/dist/index.js +174 -286
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +174 -285
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +23 -13
  13. package/dist/repo/apps/play-runner-workers/src/entry.ts +581 -1220
  14. package/dist/repo/sdk/src/cli/commands/play.ts +381 -247
  15. package/dist/repo/sdk/src/cli/commands/tools.ts +1 -1
  16. package/dist/repo/sdk/src/cli/dataset-stats.ts +86 -12
  17. package/dist/repo/sdk/src/client.ts +54 -51
  18. package/dist/repo/sdk/src/index.ts +7 -16
  19. package/dist/repo/sdk/src/play.ts +122 -135
  20. package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
  21. package/dist/repo/sdk/src/tool-output.ts +0 -111
  22. package/dist/repo/sdk/src/types.ts +2 -0
  23. package/dist/repo/sdk/src/version.ts +1 -1
  24. package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
  25. package/dist/repo/shared_libs/play-runtime/context.ts +510 -267
  26. package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
  27. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +13 -1
  28. package/dist/repo/shared_libs/play-runtime/tool-result.ts +139 -114
  29. package/dist/repo/shared_libs/plays/bundling/index.ts +68 -5
  30. package/dist/repo/shared_libs/plays/compiler-manifest.ts +1 -1
  31. package/dist/repo/shared_libs/plays/dataset.ts +1 -1
  32. package/dist/repo/shared_libs/plays/runtime-validation.ts +8 -28
  33. package/package.json +1 -1
  34. package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
package/dist/cli/index.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/index.ts
27
- var import_commander2 = require("commander");
27
+ var import_commander = require("commander");
28
28
 
29
29
  // src/config.ts
30
30
  var import_node_fs = require("fs");
@@ -192,7 +192,7 @@ function resolveConfig(options) {
192
192
  }
193
193
 
194
194
  // src/version.ts
195
- var SDK_VERSION = "0.1.10";
195
+ var SDK_VERSION = "0.1.12";
196
196
  var SDK_API_CONTRACT = "2026-04-plays-v1";
197
197
 
198
198
  // ../shared_libs/play-runtime/coordinator-headers.ts
@@ -467,6 +467,9 @@ function sleep(ms) {
467
467
 
468
468
  // src/client.ts
469
469
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
470
+ function isRecord(value) {
471
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
472
+ }
470
473
  function normalizePlayStatus(raw) {
471
474
  const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
472
475
  const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
@@ -519,12 +522,27 @@ var DeeplineClient = class {
519
522
  ).filter((field) => Boolean(field?.name)) : [];
520
523
  return fields.length > 0 ? { fields } : schema;
521
524
  }
522
- playRunCommand(name) {
523
- return `deepline plays run ${name} --input '{...}' --watch`;
525
+ schemaMetadata(schema, key) {
526
+ if (!isRecord(schema)) return null;
527
+ const value = schema[key];
528
+ return isRecord(value) ? value : null;
529
+ }
530
+ playRunCommand(play, options) {
531
+ const target = play.reference || play.name;
532
+ if (options?.csvInput) {
533
+ const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
534
+ return `deepline plays run ${target} --${inputField} leads.csv --watch`;
535
+ }
536
+ return `deepline plays run ${target} --input '{...}' --watch`;
524
537
  }
525
538
  summarizePlayListItem(play, options) {
526
539
  const aliases = play.aliases?.length ? play.aliases : [play.name];
527
- const runCommand = this.playRunCommand(play.name);
540
+ const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
541
+ const rowOutputSchema = this.schemaMetadata(
542
+ play.outputSchema,
543
+ "rowOutputSchema"
544
+ );
545
+ const runCommand = this.playRunCommand(play, { csvInput });
528
546
  return {
529
547
  name: play.name,
530
548
  ...play.reference ? { reference: play.reference } : {},
@@ -536,6 +554,8 @@ var DeeplineClient = class {
536
554
  aliases,
537
555
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
538
556
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
557
+ ...csvInput ? { csvInput } : {},
558
+ ...rowOutputSchema ? { rowOutputSchema } : {},
539
559
  runCommand,
540
560
  examples: [runCommand],
541
561
  currentPublishedVersion: play.currentPublishedVersion ?? null,
@@ -602,50 +622,14 @@ var DeeplineClient = class {
602
622
  );
603
623
  }
604
624
  /**
605
- * Execute a tool and return the extracted result.
606
- *
607
- * Sends the input payload to the tool and returns the `.result` field from the
608
- * response. For the full response envelope (including job_id, credits, etc.),
609
- * use {@link executeToolRaw}.
610
- *
611
- * @param toolId - Tool identifier (e.g. `"test_company_search"`)
612
- * @param input - Tool-specific input parameters
613
- * @returns The tool's output (shape varies by tool)
614
- * @throws {@link DeeplineError} if the tool execution fails
625
+ * Execute a tool and return the standard execution envelope.
615
626
  *
616
- * @example
617
- * ```typescript
618
- * const company = await client.executeTool('test_company_search', {
619
- * domain: 'stripe.com',
620
- * });
621
- * console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
622
- * ```
627
+ * The `result.data` field contains the provider payload. `result.meta`
628
+ * contains provider/upstream metadata such as HTTP status or paging details.
629
+ * Top-level fields such as `status`, `job_id`, and `billing` describe the
630
+ * Deepline execution.
623
631
  */
624
632
  async executeTool(toolId, input) {
625
- const res = await this.http.post(
626
- `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
627
- { payload: input }
628
- );
629
- return res.result ?? res;
630
- }
631
- /**
632
- * Execute a tool and return the full response envelope.
633
- *
634
- * Unlike {@link executeTool}, this returns the complete API response including
635
- * `job_id`, `status`, `credits`, and the raw `result` object.
636
- *
637
- * @param toolId - Tool identifier
638
- * @param input - Tool-specific input parameters
639
- * @returns Full response with job metadata and result
640
- *
641
- * @example
642
- * ```typescript
643
- * const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
644
- * console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
645
- * console.log(`Result:`, raw.result);
646
- * ```
647
- */
648
- async executeToolRaw(toolId, input) {
649
633
  return this.http.post(
650
634
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
651
635
  { payload: input }
@@ -1976,18 +1960,61 @@ function registerBillingCommands(program) {
1976
1960
  // src/cli/dataset-stats.ts
1977
1961
  var import_node_fs4 = require("fs");
1978
1962
  var import_node_path4 = require("path");
1979
- function isRecord(value) {
1963
+ var CSV_PROJECTED_FIELDS_KEY = "__deeplineCsvProjectedFields";
1964
+ function csvProjectedFields(row) {
1965
+ const serialized = row[CSV_PROJECTED_FIELDS_KEY];
1966
+ if (!Array.isArray(serialized)) {
1967
+ return /* @__PURE__ */ new Set();
1968
+ }
1969
+ return new Set(
1970
+ serialized.filter((field) => typeof field === "string")
1971
+ );
1972
+ }
1973
+ function stripCsvProjectionFields(row) {
1974
+ const projectedFields = csvProjectedFields(row);
1975
+ if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
1976
+ return row;
1977
+ }
1978
+ const stripped = { ...row };
1979
+ for (const field of projectedFields) {
1980
+ delete stripped[field];
1981
+ }
1982
+ delete stripped[CSV_PROJECTED_FIELDS_KEY];
1983
+ return stripped;
1984
+ }
1985
+ function stripCsvProjectionColumns(columns, rows) {
1986
+ const projectedFields = /* @__PURE__ */ new Set();
1987
+ let hasProjectionMetadata = false;
1988
+ for (const row of rows) {
1989
+ for (const field of csvProjectedFields(row)) {
1990
+ projectedFields.add(field);
1991
+ }
1992
+ hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
1993
+ }
1994
+ if (!hasProjectionMetadata && projectedFields.size === 0) {
1995
+ return columns;
1996
+ }
1997
+ return columns.filter(
1998
+ (column) => column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column)
1999
+ );
2000
+ }
2001
+ function sanitizeCsvProjectionInfo(input) {
2002
+ const columns = stripCsvProjectionColumns(input.columns, input.rows);
2003
+ const rows = input.rows.map(stripCsvProjectionFields);
2004
+ return { rows, columns };
2005
+ }
2006
+ function isRecord2(value) {
1980
2007
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1981
2008
  }
1982
2009
  function isSerializedDataset(value) {
1983
- return isRecord(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
2010
+ return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
1984
2011
  }
1985
2012
  function rowArray(value) {
1986
2013
  if (!Array.isArray(value)) {
1987
2014
  return null;
1988
2015
  }
1989
2016
  const rows = value.filter(
1990
- (row) => isRecord(row)
2017
+ (row) => isRecord2(row)
1991
2018
  );
1992
2019
  return rows.length === value.length ? rows : null;
1993
2020
  }
@@ -2011,12 +2038,12 @@ function inferColumns(rows) {
2011
2038
  return columns;
2012
2039
  }
2013
2040
  function extractCanonicalRowsInfo(statusOrResult) {
2014
- const root = isRecord(statusOrResult) ? statusOrResult : null;
2015
- const result = isRecord(root?.result) ? root.result : root;
2041
+ const root = isRecord2(statusOrResult) ? statusOrResult : null;
2042
+ const result = isRecord2(root?.result) ? root.result : root;
2016
2043
  if (!result) {
2017
2044
  return null;
2018
2045
  }
2019
- const metadata = isRecord(result._metadata) ? result._metadata : null;
2046
+ const metadata = isRecord2(result._metadata) ? result._metadata : null;
2020
2047
  const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
2021
2048
  const candidates = [
2022
2049
  { source: "result.contacts", value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
@@ -2024,8 +2051,8 @@ function extractCanonicalRowsInfo(statusOrResult) {
2024
2051
  { source: "result.rows", value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
2025
2052
  { source: "result.results", value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count }
2026
2053
  ];
2027
- if (isRecord(result.output)) {
2028
- const outputMetadata = isRecord(result.output._metadata) ? result.output._metadata : null;
2054
+ if (isRecord2(result.output)) {
2055
+ const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
2029
2056
  const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
2030
2057
  candidates.push(
2031
2058
  { source: "result.output.contacts", value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
@@ -2036,12 +2063,17 @@ function extractCanonicalRowsInfo(statusOrResult) {
2036
2063
  }
2037
2064
  for (const candidate of candidates) {
2038
2065
  if (isSerializedDataset(candidate.value)) {
2039
- const rows2 = rowArray(candidate.value.preview) ?? [];
2040
- const totalRows2 = readNumber(candidate.value.count) ?? rows2.length;
2066
+ const rawRows = rowArray(candidate.value.preview) ?? [];
2067
+ const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
2068
+ const rawColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rawRows);
2069
+ const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
2070
+ rows: rawRows,
2071
+ columns: rawColumns
2072
+ });
2041
2073
  return {
2042
2074
  rows: rows2,
2043
2075
  totalRows: totalRows2,
2044
- columns: Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rows2),
2076
+ columns,
2045
2077
  complete: rows2.length === totalRows2,
2046
2078
  source: candidate.source
2047
2079
  };
@@ -2051,10 +2083,14 @@ function extractCanonicalRowsInfo(statusOrResult) {
2051
2083
  continue;
2052
2084
  }
2053
2085
  const totalRows = readNumber(candidate.total) ?? rows.length;
2054
- return {
2086
+ const sanitized = sanitizeCsvProjectionInfo({
2055
2087
  rows,
2088
+ columns: inferColumns(rows)
2089
+ });
2090
+ return {
2091
+ rows: sanitized.rows,
2056
2092
  totalRows,
2057
- columns: inferColumns(rows),
2093
+ columns: sanitized.columns,
2058
2094
  complete: rows.length === totalRows,
2059
2095
  source: candidate.source
2060
2096
  };
@@ -2102,13 +2138,13 @@ function summarizeSampleValue(value, depth = 0) {
2102
2138
  if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
2103
2139
  if (depth >= 3) {
2104
2140
  if (Array.isArray(parsed)) return [];
2105
- if (isRecord(parsed)) return {};
2141
+ if (isRecord2(parsed)) return {};
2106
2142
  return compactScalar(parsed);
2107
2143
  }
2108
2144
  if (Array.isArray(parsed)) {
2109
2145
  return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
2110
2146
  }
2111
- if (isRecord(parsed)) {
2147
+ if (isRecord2(parsed)) {
2112
2148
  const out = {};
2113
2149
  for (const [key, nested] of Object.entries(parsed)) {
2114
2150
  if (["__dl", "meta", "metadata"].includes(key)) {
@@ -2138,7 +2174,7 @@ function compactCell(value) {
2138
2174
  }
2139
2175
  return `[${parsed.length} items]`;
2140
2176
  }
2141
- if (isRecord(parsed)) {
2177
+ if (isRecord2(parsed)) {
2142
2178
  for (const key of ["matched_result", "output"]) {
2143
2179
  if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
2144
2180
  return compactCell(parsed[key]);
@@ -2157,14 +2193,15 @@ function compactCell(value) {
2157
2193
  return compactScalar(parsed, 120);
2158
2194
  }
2159
2195
  function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
2196
+ const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
2160
2197
  const columnStats = {};
2161
- for (const column of columns) {
2198
+ for (const column of sanitized.columns) {
2162
2199
  let nonEmpty = 0;
2163
2200
  let empty = 0;
2164
2201
  let sampleValue;
2165
2202
  let sampleValueType = null;
2166
2203
  const valueCounts = /* @__PURE__ */ new Map();
2167
- for (const row of rows) {
2204
+ for (const row of sanitized.rows) {
2168
2205
  const raw = row[column];
2169
2206
  const value = compactCell(raw);
2170
2207
  if (value) {
@@ -2216,10 +2253,14 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
2216
2253
  `Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`
2217
2254
  );
2218
2255
  }
2256
+ const sanitized = sanitizeCsvProjectionInfo({
2257
+ rows: rowsInfo.rows,
2258
+ columns: rowsInfo.columns
2259
+ });
2219
2260
  const resolved = (0, import_node_path4.resolve)(outPath);
2220
2261
  (0, import_node_fs4.writeFileSync)(
2221
2262
  resolved,
2222
- csvStringFromRows(rowsInfo.rows, rowsInfo.columns),
2263
+ csvStringFromRows(sanitized.rows, sanitized.columns),
2223
2264
  "utf-8"
2224
2265
  );
2225
2266
  return resolved;
@@ -2511,7 +2552,6 @@ function registerOrgCommands(program) {
2511
2552
  var import_node_crypto3 = require("crypto");
2512
2553
  var import_node_fs6 = require("fs");
2513
2554
  var import_node_path8 = require("path");
2514
- var import_commander = require("commander");
2515
2555
 
2516
2556
  // src/plays/bundle-play-file.ts
2517
2557
  var import_node_os5 = require("os");
@@ -2596,6 +2636,14 @@ var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
2596
2636
  var NODE_BUILTIN_SET = new Set(
2597
2637
  import_node_module.builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
2598
2638
  );
2639
+ function assertValidExportName(exportName) {
2640
+ if (exportName === "default") return;
2641
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
2642
+ throw new Error(
2643
+ `Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`
2644
+ );
2645
+ }
2646
+ }
2599
2647
  function sha256(value) {
2600
2648
  return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
2601
2649
  }
@@ -2790,6 +2838,29 @@ function workersPlayEntryAliasPlugin(playFilePath) {
2790
2838
  }
2791
2839
  };
2792
2840
  }
2841
+ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
2842
+ return {
2843
+ name: "deepline-workers-named-play-entry-alias",
2844
+ setup(buildContext) {
2845
+ buildContext.onResolve(
2846
+ { filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
2847
+ () => ({
2848
+ path: `${playFilePath}.${exportName}.entry.ts`,
2849
+ namespace: "deepline-named-play-entry"
2850
+ })
2851
+ );
2852
+ buildContext.onLoad(
2853
+ { filter: /.*/, namespace: "deepline-named-play-entry" },
2854
+ () => ({
2855
+ contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
2856
+ `,
2857
+ loader: "ts",
2858
+ resolveDir: (0, import_node_path5.dirname)(playFilePath)
2859
+ })
2860
+ );
2861
+ }
2862
+ };
2863
+ }
2793
2864
  function workersNodeBuiltinStubPlugin() {
2794
2865
  const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
2795
2866
  return {
@@ -3221,11 +3292,20 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
3221
3292
  }
3222
3293
  return null;
3223
3294
  }
3224
- async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter) {
3295
+ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
3225
3296
  const sdkAliasPlugin = localSdkAliasPlugin(adapter);
3226
3297
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3298
+ const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
3299
+ `;
3227
3300
  const result = await (0, import_esbuild.build)({
3228
- entryPoints: [entryFile],
3301
+ ...namedExportShim ? {
3302
+ stdin: {
3303
+ contents: namedExportShim,
3304
+ resolveDir: (0, import_node_path5.dirname)(entryFile),
3305
+ sourcefile: `${(0, import_node_path5.basename)(entryFile)}.${exportName}.entry.ts`,
3306
+ loader: "ts"
3307
+ }
3308
+ } : { entryPoints: [entryFile] },
3229
3309
  absWorkingDir: adapter.projectRoot,
3230
3310
  bundle: true,
3231
3311
  format: "cjs",
@@ -3253,10 +3333,10 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
3253
3333
  outputExtension: "cjs"
3254
3334
  };
3255
3335
  }
3256
- async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter) {
3336
+ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter, exportName) {
3257
3337
  const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
3258
3338
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3259
- const playEntryAlias = workersPlayEntryAliasPlugin(playEntryFile);
3339
+ const playEntryAlias = exportName === "default" ? workersPlayEntryAliasPlugin(playEntryFile) : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
3260
3340
  const result = await (0, import_esbuild.build)({
3261
3341
  // Entry is the Workers harness; it imports the play via the virtual
3262
3342
  // `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
@@ -3327,10 +3407,16 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
3327
3407
  async function bundlePlayFile(filePath, options) {
3328
3408
  const adapter = options.adapter;
3329
3409
  const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
3410
+ const exportName = options.exportName?.trim() || "default";
3411
+ assertValidExportName(exportName);
3330
3412
  const absolutePath = await normalizeLocalPath(filePath);
3331
3413
  adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
3332
3414
  try {
3333
3415
  const analysis = await analyzeSourceGraph(absolutePath, adapter);
3416
+ analysis.graphHash = sha256(
3417
+ `${analysis.graphHash}
3418
+ entry-export:${exportName}`
3419
+ );
3334
3420
  if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
3335
3421
  const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
3336
3422
  analysis.graphHash = sha256(
@@ -3382,7 +3468,7 @@ workers-harness:${harnessFingerprint}`
3382
3468
  errors: typecheckErrors
3383
3469
  };
3384
3470
  }
3385
- const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
3471
+ const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
3386
3472
  if (Array.isArray(buildOutcome)) {
3387
3473
  return {
3388
3474
  success: false,
@@ -3392,7 +3478,8 @@ workers-harness:${harnessFingerprint}`
3392
3478
  }
3393
3479
  const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
3394
3480
  const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
3395
- const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${(0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${outputExtension}`;
3481
+ const virtualBaseName = exportName === "default" ? (0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "") : `${(0, import_node_path5.basename)(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
3482
+ const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
3396
3483
  const executableCode = `${bundledCode}
3397
3484
  //# sourceMappingURL=${(0, import_node_path5.basename)(virtualFilename)}.map
3398
3485
  `;
@@ -3776,7 +3863,7 @@ function createSdkPlayBundlingAdapter() {
3776
3863
  sdkSourceRoot: SDK_SOURCE_ROOT,
3777
3864
  sdkPackageJson: SDK_PACKAGE_JSON,
3778
3865
  sdkEntryFile: SDK_ENTRY_FILE,
3779
- sdkTypesEntryFile: (0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_TYPES_ENTRY_FILE : SDK_ENTRY_FILE,
3866
+ sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_fs5.existsSync)(SDK_TYPES_ENTRY_FILE) ? SDK_TYPES_ENTRY_FILE : SDK_ENTRY_FILE,
3780
3867
  sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
3781
3868
  workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
3782
3869
  workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
@@ -3787,6 +3874,7 @@ function createSdkPlayBundlingAdapter() {
3787
3874
  async function bundlePlayFile2(filePath, options = {}) {
3788
3875
  return bundlePlayFile(filePath, {
3789
3876
  target: options.target ?? defaultPlayBundleTarget(),
3877
+ exportName: options.exportName,
3790
3878
  adapter: createSdkPlayBundlingAdapter()
3791
3879
  });
3792
3880
  }
@@ -3936,54 +4024,6 @@ function createCliProgress(enabled) {
3936
4024
  return progress;
3937
4025
  }
3938
4026
 
3939
- // src/cli/trace.ts
3940
- var cliTraceStartedAt = Date.now();
3941
- function isTruthyEnv(value) {
3942
- return value === "1" || value === "true" || value === "yes";
3943
- }
3944
- function isCliTraceEnabled() {
3945
- return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
3946
- }
3947
- function recordCliTrace(event) {
3948
- if (!isCliTraceEnabled()) {
3949
- return;
3950
- }
3951
- const now = Date.now();
3952
- const payload = {
3953
- ts: now,
3954
- source: "cli",
3955
- sinceStartMs: now - cliTraceStartedAt,
3956
- ...event
3957
- };
3958
- process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
3959
- `);
3960
- }
3961
- async function traceCliSpan(phase, fields, run) {
3962
- if (!isCliTraceEnabled()) {
3963
- return run();
3964
- }
3965
- const startedAt = Date.now();
3966
- try {
3967
- const result = await run();
3968
- recordCliTrace({
3969
- phase,
3970
- ms: Date.now() - startedAt,
3971
- ok: true,
3972
- ...fields
3973
- });
3974
- return result;
3975
- } catch (error) {
3976
- recordCliTrace({
3977
- phase,
3978
- ms: Date.now() - startedAt,
3979
- ok: false,
3980
- error: error instanceof Error ? error.message : String(error),
3981
- ...fields
3982
- });
3983
- throw error;
3984
- }
3985
- }
3986
-
3987
4027
  // src/cli/commands/play.ts
3988
4028
  function parseReferencedPlayTarget(target) {
3989
4029
  const trimmed = target.trim();
@@ -4031,67 +4071,6 @@ function defaultMaterializedPlayPath(reference) {
4031
4071
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
4032
4072
  return (0, import_node_path8.resolve)(`${safeName || "play"}.play.ts`);
4033
4073
  }
4034
- function sanitizeGeneratedPlayName(value) {
4035
- return value.trim().toLowerCase().replace(/^prebuilt\//, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "play";
4036
- }
4037
- function buildGeneratedCsvWrapperSource(input) {
4038
- return `import { definePlay } from 'deepline';
4039
-
4040
- export default definePlay(
4041
- ${JSON.stringify(input.wrapperName)},
4042
- async (ctx, input: Record<string, unknown> & { file: string }) => {
4043
- const rows = await ctx.csv<Record<string, unknown>>(input.file);
4044
- const constants = Object.fromEntries(
4045
- Object.entries(input).filter(([key]) => key !== 'file'),
4046
- );
4047
-
4048
- const mappedRows = await ctx
4049
- .map('csv_rows', rows, {
4050
- key: (row, index) =>
4051
- String(
4052
- row.id ??
4053
- row.lead_id ??
4054
- row.email ??
4055
- row.linkedin_url ??
4056
- row.domain ??
4057
- index,
4058
- ),
4059
- })
4060
- .step('result', (row, rowCtx) =>
4061
- rowCtx.runPlay(
4062
- 'row_play',
4063
- ${JSON.stringify(input.playRef)},
4064
- {
4065
- ...constants,
4066
- ...row,
4067
- },
4068
- {
4069
- description: 'Run the source play for this CSV row.',
4070
- },
4071
- ),
4072
- )
4073
- .run({ description: 'Run the source play once per CSV row.' });
4074
-
4075
- return { rows: mappedRows };
4076
- },
4077
- );
4078
- `;
4079
- }
4080
- function writeGeneratedCsvWrapperPlay(playRef) {
4081
- const baseName = sanitizeGeneratedPlayName(
4082
- parseReferencedPlayTarget(playRef).unqualifiedPlayName
4083
- );
4084
- const wrapperName = `${baseName}-csv`;
4085
- const outputDir = (0, import_node_path8.resolve)(".deepline", "generated");
4086
- const outputPath = (0, import_node_path8.join)(outputDir, `${wrapperName}.play.ts`);
4087
- (0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
4088
- (0, import_node_fs6.writeFileSync)(
4089
- outputPath,
4090
- buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
4091
- "utf-8"
4092
- );
4093
- return outputPath;
4094
- }
4095
4074
  function materializeRemotePlaySource(input) {
4096
4075
  if (isFileTarget(input.target)) {
4097
4076
  return null;
@@ -4157,7 +4136,10 @@ function looksLikeFilePath(target) {
4157
4136
  if (target.trim().toLowerCase().startsWith("prebuilt/")) {
4158
4137
  return false;
4159
4138
  }
4160
- return target.includes("/") || target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4139
+ if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/") || target.startsWith("~/")) {
4140
+ return true;
4141
+ }
4142
+ return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4161
4143
  }
4162
4144
  function parsePositiveInteger2(value, flagName) {
4163
4145
  const parsed = Number.parseInt(value, 10);
@@ -4174,6 +4156,133 @@ function parseJsonInput(raw) {
4174
4156
  }
4175
4157
  return parsed;
4176
4158
  }
4159
+ function parseInputFieldFlag(rawFlag, nextArg) {
4160
+ const flag = rawFlag.slice(2);
4161
+ const equalsIndex = flag.indexOf("=");
4162
+ if (equalsIndex > 0) {
4163
+ const path = flag.slice(0, equalsIndex).trim();
4164
+ const value = flag.slice(equalsIndex + 1);
4165
+ if (!path) {
4166
+ throw new Error(`Invalid play input flag: ${rawFlag}`);
4167
+ }
4168
+ return { path, value };
4169
+ }
4170
+ if (!nextArg || nextArg.startsWith("--")) {
4171
+ throw new Error(`Play input flag ${rawFlag} requires a value.`);
4172
+ }
4173
+ return { path: flag, value: nextArg };
4174
+ }
4175
+ function parseInputFlagValue(raw) {
4176
+ const trimmed = raw.trim();
4177
+ if (!trimmed) return "";
4178
+ if (trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed.startsWith("{") || trimmed.startsWith("[") || /^-?\d+(\.\d+)?$/.test(trimmed)) {
4179
+ try {
4180
+ return JSON.parse(trimmed);
4181
+ } catch {
4182
+ return raw;
4183
+ }
4184
+ }
4185
+ return raw;
4186
+ }
4187
+ function getDottedInputValue(input, path) {
4188
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4189
+ let cursor = input;
4190
+ for (const part of parts) {
4191
+ if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
4192
+ return void 0;
4193
+ }
4194
+ cursor = cursor[part];
4195
+ }
4196
+ return cursor;
4197
+ }
4198
+ function setDottedInputValue(input, path, value) {
4199
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4200
+ if (parts.length === 0) {
4201
+ throw new Error(`Invalid play input flag path: ${path}`);
4202
+ }
4203
+ let cursor = input;
4204
+ for (const part of parts.slice(0, -1)) {
4205
+ const existing = cursor[part];
4206
+ if (existing !== void 0 && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
4207
+ throw new Error(
4208
+ `Cannot set --${path}; input.${part} is already a non-object value.`
4209
+ );
4210
+ }
4211
+ if (!existing) {
4212
+ cursor[part] = {};
4213
+ }
4214
+ cursor = cursor[part];
4215
+ }
4216
+ cursor[parts[parts.length - 1]] = value;
4217
+ }
4218
+ function schemaMetadata(schema, key) {
4219
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
4220
+ const value = schema[key];
4221
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
4222
+ }
4223
+ function stringMetadata(metadata, key) {
4224
+ const value = metadata?.[key];
4225
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4226
+ }
4227
+ function inputFieldFromCsvArg(csvArg) {
4228
+ if (typeof csvArg !== "string") return null;
4229
+ const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
4230
+ return match?.[1] ?? null;
4231
+ }
4232
+ function fileInputBindingsFromPlaySchema(inputSchema) {
4233
+ const csvInput = schemaMetadata(inputSchema, "csvInput");
4234
+ if (!csvInput) return [];
4235
+ return [
4236
+ {
4237
+ inputPath: stringMetadata(csvInput, "inputField") ?? "csv"
4238
+ }
4239
+ ];
4240
+ }
4241
+ function fileInputBindingsFromStaticPipeline(staticPipeline) {
4242
+ if (!staticPipeline || typeof staticPipeline !== "object" || Array.isArray(staticPipeline)) {
4243
+ return [];
4244
+ }
4245
+ const inputField = inputFieldFromCsvArg(
4246
+ staticPipeline.csvArg
4247
+ );
4248
+ return inputField ? [{ inputPath: inputField }] : [];
4249
+ }
4250
+ function isLocalFilePathValue(value) {
4251
+ if (typeof value !== "string" || !value.trim()) return false;
4252
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
4253
+ return (0, import_node_fs6.existsSync)((0, import_node_path8.resolve)(value));
4254
+ }
4255
+ async function stageFileInputArgs(input) {
4256
+ const uniqueBindings = [
4257
+ ...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
4258
+ ];
4259
+ const localFiles = uniqueBindings.flatMap((binding) => {
4260
+ const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
4261
+ if (!isLocalFilePathValue(value)) return [];
4262
+ const absolutePath = (0, import_node_path8.resolve)(value);
4263
+ return [{ binding, absolutePath, logicalPath: (0, import_node_path8.basename)(absolutePath) }];
4264
+ });
4265
+ if (localFiles.length === 0) {
4266
+ return { inputFile: null, packagedFiles: [] };
4267
+ }
4268
+ input.progress.phase(
4269
+ localFiles.length === 1 ? "staging input file" : "staging input files"
4270
+ );
4271
+ const staged = await input.client.stagePlayFiles(
4272
+ localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
4273
+ );
4274
+ for (const [index, file] of localFiles.entries()) {
4275
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
4276
+ const stagedFile = staged[index];
4277
+ if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
4278
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
4279
+ }
4280
+ }
4281
+ return {
4282
+ inputFile: staged[0] ?? null,
4283
+ packagedFiles: staged.slice(1)
4284
+ };
4285
+ }
4177
4286
  function stageFile(logicalPath, absolutePath) {
4178
4287
  const buffer = (0, import_node_fs6.readFileSync)(absolutePath);
4179
4288
  return {
@@ -4582,24 +4691,10 @@ async function startAndWaitForPlayCompletionByStream(input) {
4582
4691
  },
4583
4692
  Math.max(1, input.waitTimeoutMs)
4584
4693
  );
4585
- recordCliTrace({
4586
- phase: "cli.start_stream_request",
4587
- playName: input.playName
4588
- });
4589
4694
  try {
4590
- let eventCount = 0;
4591
4695
  for await (const event of input.client.startPlayRunStream(input.request, {
4592
4696
  signal: controller.signal
4593
4697
  })) {
4594
- eventCount += 1;
4595
- if (eventCount === 1) {
4596
- recordCliTrace({
4597
- phase: "cli.start_stream_first_event",
4598
- ms: Date.now() - startedAt,
4599
- playName: input.playName,
4600
- eventType: event.type
4601
- });
4602
- }
4603
4698
  const eventRunId = getEventPayload(event).runId;
4604
4699
  if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
4605
4700
  lastKnownWorkflowId = eventRunId;
@@ -4640,14 +4735,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
4640
4735
  });
4641
4736
  const finalStatus = getFinalStatusFromLiveEvent(event);
4642
4737
  if (finalStatus) {
4643
- recordCliTrace({
4644
- phase: "cli.start_stream_final_event",
4645
- ms: Date.now() - startedAt,
4646
- playName: input.playName,
4647
- runId: finalStatus.runId,
4648
- status: finalStatus.status,
4649
- eventCount
4650
- });
4651
4738
  return finalStatus;
4652
4739
  }
4653
4740
  }
@@ -4926,7 +5013,6 @@ function buildRunNextCommands(runId) {
4926
5013
  return {
4927
5014
  exportCsv: `deepline runs export ${runId} --out output.csv`,
4928
5015
  status: `deepline runs status ${runId} --json`,
4929
- fullStatus: `deepline runs status ${runId} --json --full`,
4930
5016
  logs: `deepline runs logs ${runId}`
4931
5017
  };
4932
5018
  }
@@ -4950,12 +5036,26 @@ function compactPlayStatus(status, options) {
4950
5036
  ...result !== void 0 ? { result } : {},
4951
5037
  ...status.resultView ? { resultView: status.resultView } : {},
4952
5038
  ...datasetStats ? { dataset_stats: datasetStats } : {},
4953
- ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 10) } : {},
5039
+ ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
4954
5040
  ...billing ? { billing } : {},
4955
5041
  ...status.run ? { run: status.run } : {},
4956
5042
  next: buildRunNextCommands(status.runId)
4957
5043
  };
4958
5044
  }
5045
+ function enrichPlayStatusWithDatasetStats(status) {
5046
+ const rowsInfo = extractCanonicalRowsInfo(status);
5047
+ if (!rowsInfo?.complete) {
5048
+ return status;
5049
+ }
5050
+ return {
5051
+ ...status,
5052
+ dataset_stats: buildDatasetStats(
5053
+ rowsInfo.rows,
5054
+ rowsInfo.totalRows,
5055
+ rowsInfo.columns
5056
+ )
5057
+ };
5058
+ }
4959
5059
  function formatDatasetStatsLines(datasetStats) {
4960
5060
  if (!datasetStats) {
4961
5061
  return [];
@@ -4965,10 +5065,10 @@ function formatDatasetStatsLines(datasetStats) {
4965
5065
  0,
4966
5066
  12
4967
5067
  )) {
4968
- const topValues = stat3.top_values ? `, ${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
4969
- const sample = stat3.sample_value !== void 0 ? `, sample=${JSON.stringify(stat3.sample_value)}` : "";
5068
+ const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
5069
+ const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
4970
5070
  lines.push(
4971
- ` ${column}: ${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
5071
+ ` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
4972
5072
  );
4973
5073
  }
4974
5074
  return lines;
@@ -4977,7 +5077,7 @@ function writePlayResult(status, jsonOutput, options) {
4977
5077
  if (jsonOutput) {
4978
5078
  process.stdout.write(
4979
5079
  `${JSON.stringify(
4980
- options?.fullJson ? status : compactPlayStatus(status, options)
5080
+ options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options)
4981
5081
  )}
4982
5082
  `
4983
5083
  );
@@ -5126,10 +5226,9 @@ function writeStartedPlayRun(input) {
5126
5226
  console.log(output);
5127
5227
  }
5128
5228
  function parsePlayRunOptions(args) {
5129
- const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--csv file.csv] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run <play-file.ts> [--input '{...}'] [--csv file.csv] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--csv file.csv] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force]\n deepline plays run --name <name> [--input '{...}'] [--csv file.csv] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json]";
5229
+ const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--out output.csv] [--tail-timeout-ms 30000] [--force] [--json] [--<input> value]";
5130
5230
  let filePath = null;
5131
5231
  let playName = null;
5132
- let csvPath = null;
5133
5232
  let input = null;
5134
5233
  let revisionId = null;
5135
5234
  let revisionSelector = null;
@@ -5150,10 +5249,6 @@ function parsePlayRunOptions(args) {
5150
5249
  playName = parseReferencedPlayTarget(args[++index]).playName;
5151
5250
  continue;
5152
5251
  }
5153
- if (arg === "--csv" && args[index + 1]) {
5154
- csvPath = (0, import_node_path8.resolve)(args[++index]);
5155
- continue;
5156
- }
5157
5252
  if ((arg === "--input" || arg === "-i") && args[index + 1]) {
5158
5253
  input = parseJsonInput(args[++index]);
5159
5254
  continue;
@@ -5203,8 +5298,13 @@ function parsePlayRunOptions(args) {
5203
5298
  continue;
5204
5299
  }
5205
5300
  if (arg.startsWith("--")) {
5206
- throw new Error(`Unexpected flag: ${arg}
5207
- ${usage}`);
5301
+ const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
5302
+ input ??= {};
5303
+ setDottedInputValue(input, path, parseInputFlagValue(value));
5304
+ if (!arg.includes("=")) {
5305
+ index += 1;
5306
+ }
5307
+ continue;
5208
5308
  }
5209
5309
  if (!arg.startsWith("--") && !filePath && !playName) {
5210
5310
  if (isFileTarget(arg) || looksLikeFilePath(arg)) {
@@ -5240,7 +5340,6 @@ ${usage}`);
5240
5340
  }
5241
5341
  return {
5242
5342
  target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
5243
- csvPath,
5244
5343
  input,
5245
5344
  revisionId,
5246
5345
  revisionSelector,
@@ -5315,34 +5414,12 @@ async function handleFileBackedRun(options) {
5315
5414
  const client = new DeeplineClient();
5316
5415
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5317
5416
  const absolutePlayPath = (0, import_node_path8.resolve)(options.target.path);
5318
- recordCliTrace({
5319
- phase: "cli.play_run_file_start",
5320
- playPath: absolutePlayPath,
5321
- watch: options.watch,
5322
- hasCsv: Boolean(options.csvPath),
5323
- force: options.force
5324
- });
5325
5417
  progress.phase("compiling play");
5326
- const readSourceStartedAt = Date.now();
5327
5418
  const sourceCode = (0, import_node_fs6.readFileSync)(absolutePlayPath, "utf-8");
5328
- recordCliTrace({
5329
- phase: "cli.read_play_source",
5330
- ms: Date.now() - readSourceStartedAt,
5331
- bytes: sourceCode.length,
5332
- playPath: absolutePlayPath
5333
- });
5334
5419
  let graph;
5335
5420
  try {
5336
- graph = await traceCliSpan(
5337
- "cli.bundle_play_graph",
5338
- { playPath: absolutePlayPath },
5339
- () => collectBundledPlayGraph(absolutePlayPath)
5340
- );
5341
- await traceCliSpan(
5342
- "cli.compile_play_manifest",
5343
- { playPath: absolutePlayPath, nodeCount: graph.nodes.size },
5344
- () => compileBundledPlayGraphManifests(client, graph)
5345
- );
5421
+ graph = await collectBundledPlayGraph(absolutePlayPath);
5422
+ await compileBundledPlayGraphManifests(client, graph);
5346
5423
  progress.phase("compiled play");
5347
5424
  } catch (error) {
5348
5425
  progress.fail();
@@ -5353,87 +5430,58 @@ async function handleFileBackedRun(options) {
5353
5430
  const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
5354
5431
  try {
5355
5432
  progress.phase("publishing imported plays");
5356
- await traceCliSpan(
5357
- "cli.publish_imported_plays",
5358
- { playName, nodeCount: graph.nodes.size },
5359
- () => publishImportedPlayDependencies(client, graph)
5360
- );
5433
+ await publishImportedPlayDependencies(client, graph);
5361
5434
  } catch (error) {
5362
5435
  progress.fail();
5363
5436
  console.error(error instanceof Error ? error.message : String(error));
5364
5437
  return 1;
5365
5438
  }
5366
5439
  const runtimeInput = options.input ? { ...options.input } : {};
5367
- const prepareFilesStartedAt = Date.now();
5368
5440
  const packagedFileUploads = bundleResult.packagedFiles.map(
5369
5441
  (file) => stageFile(file.logicalPath, file.absolutePath)
5370
5442
  );
5371
- const inputFileUpload = options.csvPath ? stageFile((0, import_node_path8.basename)(options.csvPath), options.csvPath) : packagedFileUploads[0] ?? null;
5372
- if (options.csvPath && typeof runtimeInput.file !== "string" && typeof runtimeInput.csv !== "string") {
5373
- runtimeInput.file = (0, import_node_path8.basename)(options.csvPath);
5374
- }
5375
- recordCliTrace({
5376
- phase: "cli.prepare_input_files",
5377
- ms: Date.now() - prepareFilesStartedAt,
5378
- playName,
5379
- packagedFileCount: packagedFileUploads.length,
5380
- hasInputFile: Boolean(inputFileUpload)
5443
+ const stagedFileInputs = await stageFileInputArgs({
5444
+ client,
5445
+ runtimeInput,
5446
+ bindings: fileInputBindingsFromStaticPipeline(
5447
+ requireCompilerManifest(bundleResult).staticPipeline
5448
+ ),
5449
+ progress
5381
5450
  });
5382
5451
  const startRequest = {
5383
5452
  name: playName,
5384
5453
  sourceCode: bundleResult.sourceCode,
5385
5454
  runtimeArtifact: bundleResult.artifact,
5386
5455
  compilerManifest: requireCompilerManifest(bundleResult),
5387
- inputFileUpload,
5388
5456
  packagedFileUploads,
5389
5457
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5458
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5459
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5390
5460
  ...options.force ? { force: true } : {}
5391
5461
  };
5392
5462
  if (options.watch) {
5393
5463
  progress.phase("starting run");
5394
- const finalStatus = await traceCliSpan(
5395
- "cli.start_and_watch",
5396
- { playName },
5397
- () => startAndWaitForPlayCompletionByStream({
5398
- client,
5399
- request: startRequest,
5400
- playName,
5401
- jsonOutput: options.jsonOutput,
5402
- emitLogs: options.emitLogs,
5403
- waitTimeoutMs: options.waitTimeoutMs,
5404
- progress
5405
- })
5406
- );
5407
- const exportStartedAt = Date.now();
5408
- const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5409
- recordCliTrace({
5410
- phase: "cli.export_rows",
5411
- ms: Date.now() - exportStartedAt,
5464
+ const finalStatus = await startAndWaitForPlayCompletionByStream({
5465
+ client,
5466
+ request: startRequest,
5412
5467
  playName,
5413
- exported: Boolean(exportedPath)
5468
+ jsonOutput: options.jsonOutput,
5469
+ emitLogs: options.emitLogs,
5470
+ waitTimeoutMs: options.waitTimeoutMs,
5471
+ progress
5414
5472
  });
5473
+ const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5415
5474
  if (finalStatus.status === "completed") {
5416
5475
  progress.complete();
5417
5476
  } else {
5418
5477
  progress.fail();
5419
5478
  }
5420
- recordCliTrace({
5421
- phase: "cli.write_play_result",
5422
- playName,
5423
- status: finalStatus.status,
5424
- runId: finalStatus.runId
5425
- });
5426
5479
  writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
5427
5480
  return finalStatus.status === "completed" ? 0 : 1;
5428
5481
  }
5429
5482
  progress.phase("starting run");
5430
- const started = await traceCliSpan(
5431
- "cli.start_run",
5432
- { playName },
5433
- () => client.startPlayRun(startRequest)
5434
- );
5435
- const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5436
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5483
+ const started = await client.startPlayRun(startRequest);
5484
+ const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5437
5485
  progress.phase(`loading play on ${dashboardUrl}`);
5438
5486
  progress.complete();
5439
5487
  writeStartedPlayRun({
@@ -5441,7 +5489,7 @@ async function handleFileBackedRun(options) {
5441
5489
  playName,
5442
5490
  status: started.status,
5443
5491
  statusUrl: started.statusUrl,
5444
- dashboardUrl,
5492
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5445
5493
  jsonOutput: options.jsonOutput,
5446
5494
  progress
5447
5495
  });
@@ -5467,9 +5515,8 @@ async function handleNamedRun(options) {
5467
5515
  }
5468
5516
  const client = new DeeplineClient();
5469
5517
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5470
- let stagedInputFile = null;
5471
5518
  progress.phase("loading play definition");
5472
- await assertCanonicalNamedPlayReference(client, options.target.name);
5519
+ const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
5473
5520
  progress.phase("selecting revision");
5474
5521
  const selectedRevisionId = await resolveNamedRunRevisionId({
5475
5522
  client,
@@ -5477,19 +5524,22 @@ async function handleNamedRun(options) {
5477
5524
  revisionId: options.revisionId,
5478
5525
  selector: options.revisionSelector
5479
5526
  });
5480
- if (options.csvPath) {
5481
- progress.phase("staging input file");
5482
- const [staged] = await client.stagePlayFiles([
5483
- stageFile((0, import_node_path8.basename)(options.csvPath), options.csvPath)
5484
- ]);
5485
- stagedInputFile = staged ?? null;
5486
- }
5487
5527
  const runtimeInput = options.input ? { ...options.input } : {};
5528
+ const stagedFileInputs = await stageFileInputArgs({
5529
+ client,
5530
+ runtimeInput,
5531
+ bindings: [
5532
+ ...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
5533
+ ...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
5534
+ ],
5535
+ progress
5536
+ });
5488
5537
  const startRequest = {
5489
5538
  name: options.target.name,
5490
5539
  ...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
5491
5540
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5492
- ...stagedInputFile ? { inputFile: stagedInputFile } : {},
5541
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5542
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5493
5543
  ...options.force ? { force: true } : {}
5494
5544
  };
5495
5545
  if (options.watch) {
@@ -5503,22 +5553,6 @@ async function handleNamedRun(options) {
5503
5553
  waitTimeoutMs: options.waitTimeoutMs,
5504
5554
  progress
5505
5555
  });
5506
- if (finalStatus.status !== "completed" && options.csvPath) {
5507
- progress.phase("generating csv wrapper play");
5508
- const generatedPlayPath = writeGeneratedCsvWrapperPlay(
5509
- options.target.name
5510
- );
5511
- progress.writeLogLine(
5512
- `Generated CSV wrapper play: ${generatedPlayPath}`
5513
- );
5514
- progress.phase("running generated csv wrapper play");
5515
- return handleFileBackedRun({
5516
- ...options,
5517
- target: { kind: "file", path: generatedPlayPath },
5518
- revisionId: null,
5519
- revisionSelector: null
5520
- });
5521
- }
5522
5556
  const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5523
5557
  if (finalStatus.status === "completed") {
5524
5558
  progress.complete();
@@ -5530,11 +5564,10 @@ async function handleNamedRun(options) {
5530
5564
  }
5531
5565
  progress.phase("starting run");
5532
5566
  const started = await client.startPlayRun(startRequest);
5533
- const fallbackDashboardUrl = buildPlayDashboardUrl(
5567
+ const dashboardUrl = buildPlayDashboardUrl(
5534
5568
  client.baseUrl,
5535
5569
  options.target.name
5536
5570
  );
5537
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5538
5571
  progress.phase(`loading play on ${dashboardUrl}`);
5539
5572
  progress.complete();
5540
5573
  writeStartedPlayRun({
@@ -5542,7 +5575,7 @@ async function handleNamedRun(options) {
5542
5575
  playName: started.name ?? options.target.name,
5543
5576
  status: started.status,
5544
5577
  statusUrl: started.statusUrl,
5545
- dashboardUrl,
5578
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5546
5579
  jsonOutput: options.jsonOutput,
5547
5580
  progress
5548
5581
  });
@@ -5761,8 +5794,9 @@ async function handlePlayGet(args) {
5761
5794
  return 1;
5762
5795
  }
5763
5796
  const client = new DeeplineClient();
5764
- const jsonOutput = argsWantJson(args);
5797
+ const explicitJson = args.includes("--json");
5765
5798
  const sourceOutput = args.includes("--source");
5799
+ const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
5766
5800
  let outPath = null;
5767
5801
  for (let index = 1; index < args.length; index += 1) {
5768
5802
  const arg = args[index];
@@ -5773,7 +5807,7 @@ async function handlePlayGet(args) {
5773
5807
  const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs6.readFileSync)((0, import_node_path8.resolve)(target), "utf-8"), (0, import_node_path8.resolve)(target)) : parseReferencedPlayTarget(target).playName;
5774
5808
  const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
5775
5809
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
5776
- const materializedFile = sourceOutput || outPath ? materializeRemotePlaySource({
5810
+ const materializedFile = outPath ? materializeRemotePlaySource({
5777
5811
  target,
5778
5812
  playName,
5779
5813
  sourceCode: resolvedSource,
@@ -5813,6 +5847,10 @@ async function handlePlayGet(args) {
5813
5847
  }
5814
5848
  return 0;
5815
5849
  }
5850
+ if (outPath && loadedMessage) {
5851
+ console.log(loadedMessage);
5852
+ return 0;
5853
+ }
5816
5854
  console.log(`Play: ${formatPlayReference(detail.play)}`);
5817
5855
  console.log(
5818
5856
  `Working version: ${detail.play.workingRevision?.version ?? "\u2014"}`
@@ -5979,6 +6017,20 @@ function printPlayDescription(play) {
5979
6017
  console.log(` ${line}`);
5980
6018
  }
5981
6019
  }
6020
+ if (play.csvInput) {
6021
+ console.log(" CSV input:");
6022
+ const rendered = JSON.stringify(play.csvInput, null, 2);
6023
+ for (const line of rendered.split("\n")) {
6024
+ console.log(` ${line}`);
6025
+ }
6026
+ }
6027
+ if (play.rowOutputSchema) {
6028
+ console.log(" Row output schema:");
6029
+ const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
6030
+ for (const line of rendered.split("\n")) {
6031
+ console.log(` ${line}`);
6032
+ }
6033
+ }
5982
6034
  console.log(` Run: ${play.runCommand}`);
5983
6035
  }
5984
6036
  async function handlePlaySearch(args) {
@@ -6118,6 +6170,45 @@ async function handlePlayPublish(args) {
6118
6170
  `);
6119
6171
  return result.success ? 0 : 1;
6120
6172
  }
6173
+ async function handlePlayDelete(args) {
6174
+ const playName = args[0];
6175
+ if (!playName) {
6176
+ console.error("Usage: deepline plays delete <play-name> --yes [--json]");
6177
+ return 1;
6178
+ }
6179
+ const confirmed = args.includes("--yes") || args.includes("-y") || args.includes("--force");
6180
+ if (!confirmed) {
6181
+ console.error(
6182
+ "Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records."
6183
+ );
6184
+ return 1;
6185
+ }
6186
+ const client = new DeeplineClient();
6187
+ let detail;
6188
+ try {
6189
+ detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
6190
+ } catch (error) {
6191
+ console.error(error instanceof Error ? error.message : String(error));
6192
+ return 1;
6193
+ }
6194
+ if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
6195
+ console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
6196
+ return 1;
6197
+ }
6198
+ const result = await client.deletePlay(
6199
+ parseReferencedPlayTarget(formatPlayReference(detail.play)).playName
6200
+ );
6201
+ if (argsWantJson(args)) {
6202
+ process.stdout.write(`${JSON.stringify(result)}
6203
+ `);
6204
+ return result.deleted ? 0 : 1;
6205
+ }
6206
+ process.stdout.write(
6207
+ `Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}
6208
+ `
6209
+ );
6210
+ return result.deleted ? 0 : 1;
6211
+ }
6121
6212
  function registerPlayCommands(program) {
6122
6213
  const play = program.command("plays").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
6123
6214
  "after",
@@ -6151,21 +6242,23 @@ Examples:
6151
6242
  ...options.json ? ["--json"] : []
6152
6243
  ]);
6153
6244
  });
6154
- play.command("run [target]").description("Run a play file or named play.").addHelpText(
6245
+ play.command("run [target]").description("Run a play file or named play.").allowUnknownOption(true).allowExcessArguments(true).addHelpText(
6155
6246
  "after",
6156
6247
  `
6157
6248
  Notes:
6158
- Local play files are bundled locally, then validated and executed in Deepline cloud.
6159
- Named plays run the stored live cloud revision.
6160
- Run performs server preflight automatically. Use \`deepline plays check <file>\`
6161
- to validate without starting a run.
6249
+ Local play files are bundled locally, then validated and executed in Deepline cloud.
6250
+ Named plays run the stored live cloud revision.
6251
+ Unknown --foo and --foo.bar flags are treated as play input args.
6252
+ File-like input args accept local paths; the CLI stages those files before submit.
6253
+ Run performs server preflight automatically. Use \`deepline plays check <file>\`
6254
+ to validate without starting a run.
6162
6255
 
6163
6256
  Examples:
6164
6257
  deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
6165
6258
  deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
6166
6259
  deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
6167
6260
  `
6168
- ).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("--csv <path>", "Attach a CSV file").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
6261
+ ).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
6169
6262
  "--revision-id <id>",
6170
6263
  "Run a specific saved revision instead of the live revision"
6171
6264
  ).option(
@@ -6174,12 +6267,21 @@ Examples:
6174
6267
  ).option("--watch", "Stream logs until completion").option(
6175
6268
  "--logs",
6176
6269
  "When output is non-interactive, stream play logs to stderr while waiting"
6177
- ).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options) => {
6270
+ ).option("--poll-interval-ms <ms>", "Polling interval while tailing").option("--tail-timeout-ms <ms>", "Timeout while tailing").option("--force", "Supersede any active runs for this play").option("--json", "Emit JSON output").action(async (target, options, command) => {
6271
+ const passthroughArgs = [...command.args];
6272
+ const explicitTarget = options.file || options.name;
6273
+ const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
6274
+ const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
6275
+ if (explicitTarget && typeof target === "string" && !targetIsInputFlag && !passthroughArgs.includes(target)) {
6276
+ passthroughArgs.push(target);
6277
+ }
6278
+ if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
6279
+ passthroughArgs.shift();
6280
+ }
6178
6281
  process.exitCode = await handlePlayRun([
6179
- ...target ? [target] : [],
6282
+ ...effectiveTarget ? [effectiveTarget] : [],
6180
6283
  ...options.file ? ["--file", options.file] : [],
6181
6284
  ...options.name ? ["--name", options.name] : [],
6182
- ...options.csv ? ["--csv", options.csv] : [],
6183
6285
  ...options.input ? ["--input", options.input] : [],
6184
6286
  ...options.live ? ["--live"] : [],
6185
6287
  ...options.latest ? ["--latest"] : [],
@@ -6190,7 +6292,8 @@ Examples:
6190
6292
  ...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
6191
6293
  ...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
6192
6294
  ...options.force ? ["--force"] : [],
6193
- ...options.json ? ["--json"] : []
6295
+ ...options.json ? ["--json"] : [],
6296
+ ...passthroughArgs
6194
6297
  ]);
6195
6298
  });
6196
6299
  play.command("get <target>").description("Fetch full play details.").addHelpText(
@@ -6204,12 +6307,10 @@ Notes:
6204
6307
  Examples:
6205
6308
  deepline plays get person-linkedin-to-email
6206
6309
  deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
6310
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
6311
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
6207
6312
  `
6208
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").addOption(
6209
- new import_commander.Option("--source", "Materialize or print the source code").hideHelp()
6210
- ).addOption(
6211
- new import_commander.Option("--out <path>", "Write source to a specific path").hideHelp()
6212
- ).action(async (target, options) => {
6313
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--source", "Print raw source code; combine with --out to write a file").option("--out <path>", "Write source to a specific path").action(async (target, options) => {
6213
6314
  process.exitCode = await handlePlayGet([
6214
6315
  target,
6215
6316
  ...options.json ? ["--json"] : [],
@@ -6269,7 +6370,7 @@ Examples:
6269
6370
  ...options.json ? ["--json"] : []
6270
6371
  ]);
6271
6372
  });
6272
- play.command("status").description("Show status for a play run.").option("--run-id <runId>", "Run id to inspect").option("--name <name>", "Inspect the latest run for a named play").option("--json", "Emit JSON output").option("--full", "With --json, emit the full raw status payload").action(async (options) => {
6373
+ play.command("status").description("Show status for a play run.").option("--run-id <runId>", "Run id to inspect").option("--name <name>", "Inspect the latest run for a named play").option("--json", "Emit JSON output").option("--full", "Debug only: with --json, emit the raw status payload").action(async (options) => {
6273
6374
  process.exitCode = await handlePlayStatus([
6274
6375
  ...options.runId ? ["--run-id", options.runId] : [],
6275
6376
  ...options.name ? ["--name", options.name] : [],
@@ -6300,6 +6401,13 @@ Examples:
6300
6401
  ...options.json ? ["--json"] : []
6301
6402
  ]);
6302
6403
  });
6404
+ play.command("delete <target>").description("Delete an org-owned play and its saved revisions/runs.").option("-y, --yes", "Confirm deletion").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
6405
+ process.exitCode = await handlePlayDelete([
6406
+ target,
6407
+ ...options.yes ? ["--yes"] : [],
6408
+ ...options.json ? ["--json"] : []
6409
+ ]);
6410
+ });
6303
6411
  const runs = program.command("runs").description("Inspect and export play runs.").addHelpText(
6304
6412
  "after",
6305
6413
  `
@@ -6309,7 +6417,7 @@ Examples:
6309
6417
  deepline runs logs play/my-play/run/20260501t000000-000
6310
6418
  `
6311
6419
  );
6312
- runs.command("status <runId>").description("Show compact status for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "With --json, emit the full raw status payload").action(async (runId, options) => {
6420
+ runs.command("status <runId>").description("Show compact status for a play run.").option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--full", "Debug only: with --json, emit the raw status payload").action(async (runId, options) => {
6313
6421
  process.exitCode = await handleRunStatus([
6314
6422
  runId,
6315
6423
  ...options.json ? ["--json"] : [],
@@ -6664,7 +6772,7 @@ function printToolDetails(tool, requestedToolId) {
6664
6772
  const operation = typeof tool.operation === "string" ? tool.operation : "";
6665
6773
  const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
6666
6774
  const displayName = titleCase(displayBase || String(tool.displayName || toolId));
6667
- const cost = isRecord2(tool.cost) ? tool.cost : null;
6775
+ const cost = isRecord3(tool.cost) ? tool.cost : null;
6668
6776
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
6669
6777
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
6670
6778
  const deeplineUsdPerCredit = numberField(tool, "deeplineUsdPerCredit", "deepline_usd_per_credit");
@@ -6709,7 +6817,7 @@ function printToolDetails(tool, requestedToolId) {
6709
6817
  if (stepContributions.length) {
6710
6818
  console.log(" step contributions:");
6711
6819
  for (const item of stepContributions) {
6712
- if (!isRecord2(item)) continue;
6820
+ if (!isRecord3(item)) continue;
6713
6821
  const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
6714
6822
  const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
6715
6823
  const high = typeof item.highCredits === "number" ? item.highCredits : null;
@@ -6796,12 +6904,12 @@ function printToolCost(input) {
6796
6904
  return false;
6797
6905
  }
6798
6906
  function toolInputFieldsForDisplay(inputSchema) {
6799
- if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord2);
6800
- const jsonSchema = isRecord2(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
6801
- const properties = isRecord2(jsonSchema.properties) ? jsonSchema.properties : {};
6907
+ if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord3);
6908
+ const jsonSchema = isRecord3(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
6909
+ const properties = isRecord3(jsonSchema.properties) ? jsonSchema.properties : {};
6802
6910
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
6803
6911
  return Object.entries(properties).map(([name, value]) => {
6804
- const property = isRecord2(value) ? value : {};
6912
+ const property = isRecord3(value) ? value : {};
6805
6913
  return {
6806
6914
  name,
6807
6915
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -6828,7 +6936,7 @@ function printJsonPreview(label, payload) {
6828
6936
  }
6829
6937
  function samplePayload(samples, key) {
6830
6938
  const entry = samples[key];
6831
- if (!isRecord2(entry)) return void 0;
6939
+ if (!isRecord3(entry)) return void 0;
6832
6940
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
6833
6941
  }
6834
6942
  function isPlayTool(tool) {
@@ -6849,7 +6957,7 @@ function formatDecimal(value) {
6849
6957
  function formatUsd(value) {
6850
6958
  return `$${formatDecimal(value)}`;
6851
6959
  }
6852
- function isRecord2(value) {
6960
+ function isRecord3(value) {
6853
6961
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6854
6962
  }
6855
6963
  function stringField(source, ...keys) {
@@ -6876,7 +6984,7 @@ function arrayField(source, ...keys) {
6876
6984
  function recordField(source, ...keys) {
6877
6985
  for (const key of keys) {
6878
6986
  const value = source[key];
6879
- if (isRecord2(value)) return value;
6987
+ if (isRecord3(value)) return value;
6880
6988
  }
6881
6989
  return {};
6882
6990
  }
@@ -6967,7 +7075,7 @@ async function executeTool(args) {
6967
7075
  }
6968
7076
  throw error;
6969
7077
  }
6970
- const rawResponse = await client.executeToolRaw(parsed.toolId, parsed.params);
7078
+ const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
6971
7079
  const listConversion = tryConvertToList(rawResponse, {
6972
7080
  listExtractorPaths: metadata.listExtractorPaths ?? []
6973
7081
  });
@@ -7138,6 +7246,54 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
7138
7246
  progress?.writeLine("SDK skills are up to date.") ?? process.stderr.write("SDK skills are up to date.\n");
7139
7247
  }
7140
7248
 
7249
+ // src/cli/trace.ts
7250
+ var cliTraceStartedAt = Date.now();
7251
+ function isTruthyEnv(value) {
7252
+ return value === "1" || value === "true" || value === "yes";
7253
+ }
7254
+ function isCliTraceEnabled() {
7255
+ return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
7256
+ }
7257
+ function recordCliTrace(event) {
7258
+ if (!isCliTraceEnabled()) {
7259
+ return;
7260
+ }
7261
+ const now = Date.now();
7262
+ const payload = {
7263
+ ts: now,
7264
+ source: "cli",
7265
+ sinceStartMs: now - cliTraceStartedAt,
7266
+ ...event
7267
+ };
7268
+ process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
7269
+ `);
7270
+ }
7271
+ async function traceCliSpan(phase, fields, run) {
7272
+ if (!isCliTraceEnabled()) {
7273
+ return run();
7274
+ }
7275
+ const startedAt = Date.now();
7276
+ try {
7277
+ const result = await run();
7278
+ recordCliTrace({
7279
+ phase,
7280
+ ms: Date.now() - startedAt,
7281
+ ok: true,
7282
+ ...fields
7283
+ });
7284
+ return result;
7285
+ } catch (error) {
7286
+ recordCliTrace({
7287
+ phase,
7288
+ ms: Date.now() - startedAt,
7289
+ ok: false,
7290
+ error: error instanceof Error ? error.message : String(error),
7291
+ ...fields
7292
+ });
7293
+ throw error;
7294
+ }
7295
+ }
7296
+
7141
7297
  // src/cli/index.ts
7142
7298
  function shouldPrintStartupPhase() {
7143
7299
  if (process.argv.includes("--json")) {
@@ -7159,7 +7315,7 @@ async function main() {
7159
7315
  if (printStartupPhase) {
7160
7316
  progress?.phase("loading deepline cli");
7161
7317
  }
7162
- const program = new import_commander2.Command();
7318
+ const program = new import_commander.Command();
7163
7319
  program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
7164
7320
  "after",
7165
7321
  `