deepline 0.1.11 → 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 -307
  7. package/dist/index.d.ts +250 -307
  8. package/dist/index.js +174 -315
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +174 -314
  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 -140
  20. package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
  21. package/dist/repo/sdk/src/tool-output.ts +0 -146
  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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { Command as Command2 } from "commander";
4
+ import { Command } from "commander";
5
5
 
6
6
  // src/config.ts
7
7
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
@@ -169,7 +169,7 @@ function resolveConfig(options) {
169
169
  }
170
170
 
171
171
  // src/version.ts
172
- var SDK_VERSION = "0.1.11";
172
+ var SDK_VERSION = "0.1.12";
173
173
  var SDK_API_CONTRACT = "2026-04-plays-v1";
174
174
 
175
175
  // ../shared_libs/play-runtime/coordinator-headers.ts
@@ -444,6 +444,9 @@ function sleep(ms) {
444
444
 
445
445
  // src/client.ts
446
446
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
447
+ function isRecord(value) {
448
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
449
+ }
447
450
  function normalizePlayStatus(raw) {
448
451
  const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
449
452
  const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
@@ -496,12 +499,27 @@ var DeeplineClient = class {
496
499
  ).filter((field) => Boolean(field?.name)) : [];
497
500
  return fields.length > 0 ? { fields } : schema;
498
501
  }
499
- playRunCommand(name) {
500
- return `deepline plays run ${name} --input '{...}' --watch`;
502
+ schemaMetadata(schema, key) {
503
+ if (!isRecord(schema)) return null;
504
+ const value = schema[key];
505
+ return isRecord(value) ? value : null;
506
+ }
507
+ playRunCommand(play, options) {
508
+ const target = play.reference || play.name;
509
+ if (options?.csvInput) {
510
+ const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
511
+ return `deepline plays run ${target} --${inputField} leads.csv --watch`;
512
+ }
513
+ return `deepline plays run ${target} --input '{...}' --watch`;
501
514
  }
502
515
  summarizePlayListItem(play, options) {
503
516
  const aliases = play.aliases?.length ? play.aliases : [play.name];
504
- const runCommand = this.playRunCommand(play.name);
517
+ const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
518
+ const rowOutputSchema = this.schemaMetadata(
519
+ play.outputSchema,
520
+ "rowOutputSchema"
521
+ );
522
+ const runCommand = this.playRunCommand(play, { csvInput });
505
523
  return {
506
524
  name: play.name,
507
525
  ...play.reference ? { reference: play.reference } : {},
@@ -513,6 +531,8 @@ var DeeplineClient = class {
513
531
  aliases,
514
532
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
515
533
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
534
+ ...csvInput ? { csvInput } : {},
535
+ ...rowOutputSchema ? { rowOutputSchema } : {},
516
536
  runCommand,
517
537
  examples: [runCommand],
518
538
  currentPublishedVersion: play.currentPublishedVersion ?? null,
@@ -579,50 +599,14 @@ var DeeplineClient = class {
579
599
  );
580
600
  }
581
601
  /**
582
- * Execute a tool and return the extracted result.
602
+ * Execute a tool and return the standard execution envelope.
583
603
  *
584
- * Sends the input payload to the tool and returns the `.result` field from the
585
- * response. For the full response envelope (including job_id, credits, etc.),
586
- * use {@link executeToolRaw}.
587
- *
588
- * @param toolId - Tool identifier (e.g. `"test_company_search"`)
589
- * @param input - Tool-specific input parameters
590
- * @returns The tool's output (shape varies by tool)
591
- * @throws {@link DeeplineError} if the tool execution fails
592
- *
593
- * @example
594
- * ```typescript
595
- * const company = await client.executeTool('test_company_search', {
596
- * domain: 'stripe.com',
597
- * });
598
- * console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
599
- * ```
604
+ * The `result.data` field contains the provider payload. `result.meta`
605
+ * contains provider/upstream metadata such as HTTP status or paging details.
606
+ * Top-level fields such as `status`, `job_id`, and `billing` describe the
607
+ * Deepline execution.
600
608
  */
601
609
  async executeTool(toolId, input) {
602
- const res = await this.http.post(
603
- `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
604
- { payload: input }
605
- );
606
- return res.result ?? res;
607
- }
608
- /**
609
- * Execute a tool and return the full response envelope.
610
- *
611
- * Unlike {@link executeTool}, this returns the complete API response including
612
- * `job_id`, `status`, `credits`, and the raw `result` object.
613
- *
614
- * @param toolId - Tool identifier
615
- * @param input - Tool-specific input parameters
616
- * @returns Full response with job metadata and result
617
- *
618
- * @example
619
- * ```typescript
620
- * const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
621
- * console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
622
- * console.log(`Result:`, raw.result);
623
- * ```
624
- */
625
- async executeToolRaw(toolId, input) {
626
610
  return this.http.post(
627
611
  `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
628
612
  { payload: input }
@@ -1953,18 +1937,61 @@ function registerBillingCommands(program) {
1953
1937
  // src/cli/dataset-stats.ts
1954
1938
  import { writeFileSync as writeFileSync3 } from "fs";
1955
1939
  import { resolve as resolve3 } from "path";
1956
- function isRecord(value) {
1940
+ var CSV_PROJECTED_FIELDS_KEY = "__deeplineCsvProjectedFields";
1941
+ function csvProjectedFields(row) {
1942
+ const serialized = row[CSV_PROJECTED_FIELDS_KEY];
1943
+ if (!Array.isArray(serialized)) {
1944
+ return /* @__PURE__ */ new Set();
1945
+ }
1946
+ return new Set(
1947
+ serialized.filter((field) => typeof field === "string")
1948
+ );
1949
+ }
1950
+ function stripCsvProjectionFields(row) {
1951
+ const projectedFields = csvProjectedFields(row);
1952
+ if (projectedFields.size === 0 && !(CSV_PROJECTED_FIELDS_KEY in row)) {
1953
+ return row;
1954
+ }
1955
+ const stripped = { ...row };
1956
+ for (const field of projectedFields) {
1957
+ delete stripped[field];
1958
+ }
1959
+ delete stripped[CSV_PROJECTED_FIELDS_KEY];
1960
+ return stripped;
1961
+ }
1962
+ function stripCsvProjectionColumns(columns, rows) {
1963
+ const projectedFields = /* @__PURE__ */ new Set();
1964
+ let hasProjectionMetadata = false;
1965
+ for (const row of rows) {
1966
+ for (const field of csvProjectedFields(row)) {
1967
+ projectedFields.add(field);
1968
+ }
1969
+ hasProjectionMetadata ||= CSV_PROJECTED_FIELDS_KEY in row;
1970
+ }
1971
+ if (!hasProjectionMetadata && projectedFields.size === 0) {
1972
+ return columns;
1973
+ }
1974
+ return columns.filter(
1975
+ (column) => column !== CSV_PROJECTED_FIELDS_KEY && !projectedFields.has(column)
1976
+ );
1977
+ }
1978
+ function sanitizeCsvProjectionInfo(input) {
1979
+ const columns = stripCsvProjectionColumns(input.columns, input.rows);
1980
+ const rows = input.rows.map(stripCsvProjectionFields);
1981
+ return { rows, columns };
1982
+ }
1983
+ function isRecord2(value) {
1957
1984
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1958
1985
  }
1959
1986
  function isSerializedDataset(value) {
1960
- return isRecord(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
1987
+ return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
1961
1988
  }
1962
1989
  function rowArray(value) {
1963
1990
  if (!Array.isArray(value)) {
1964
1991
  return null;
1965
1992
  }
1966
1993
  const rows = value.filter(
1967
- (row) => isRecord(row)
1994
+ (row) => isRecord2(row)
1968
1995
  );
1969
1996
  return rows.length === value.length ? rows : null;
1970
1997
  }
@@ -1988,12 +2015,12 @@ function inferColumns(rows) {
1988
2015
  return columns;
1989
2016
  }
1990
2017
  function extractCanonicalRowsInfo(statusOrResult) {
1991
- const root = isRecord(statusOrResult) ? statusOrResult : null;
1992
- const result = isRecord(root?.result) ? root.result : root;
2018
+ const root = isRecord2(statusOrResult) ? statusOrResult : null;
2019
+ const result = isRecord2(root?.result) ? root.result : root;
1993
2020
  if (!result) {
1994
2021
  return null;
1995
2022
  }
1996
- const metadata = isRecord(result._metadata) ? result._metadata : null;
2023
+ const metadata = isRecord2(result._metadata) ? result._metadata : null;
1997
2024
  const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
1998
2025
  const candidates = [
1999
2026
  { source: "result.contacts", value: result.contacts, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
@@ -2001,8 +2028,8 @@ function extractCanonicalRowsInfo(statusOrResult) {
2001
2028
  { source: "result.rows", value: result.rows, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count },
2002
2029
  { source: "result.results", value: result.results, total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count }
2003
2030
  ];
2004
- if (isRecord(result.output)) {
2005
- const outputMetadata = isRecord(result.output._metadata) ? result.output._metadata : null;
2031
+ if (isRecord2(result.output)) {
2032
+ const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
2006
2033
  const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
2007
2034
  candidates.push(
2008
2035
  { source: "result.output.contacts", value: result.output.contacts, total: outputTotalFromMetadata ?? result.output.totalRows ?? result.output.rowCount ?? result.output.count },
@@ -2013,12 +2040,17 @@ function extractCanonicalRowsInfo(statusOrResult) {
2013
2040
  }
2014
2041
  for (const candidate of candidates) {
2015
2042
  if (isSerializedDataset(candidate.value)) {
2016
- const rows2 = rowArray(candidate.value.preview) ?? [];
2017
- const totalRows2 = readNumber(candidate.value.count) ?? rows2.length;
2043
+ const rawRows = rowArray(candidate.value.preview) ?? [];
2044
+ const totalRows2 = readNumber(candidate.value.count) ?? rawRows.length;
2045
+ const rawColumns = Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rawRows);
2046
+ const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
2047
+ rows: rawRows,
2048
+ columns: rawColumns
2049
+ });
2018
2050
  return {
2019
2051
  rows: rows2,
2020
2052
  totalRows: totalRows2,
2021
- columns: Array.isArray(candidate.value.columns) && candidate.value.columns.every((column) => typeof column === "string") ? candidate.value.columns : inferColumns(rows2),
2053
+ columns,
2022
2054
  complete: rows2.length === totalRows2,
2023
2055
  source: candidate.source
2024
2056
  };
@@ -2028,10 +2060,14 @@ function extractCanonicalRowsInfo(statusOrResult) {
2028
2060
  continue;
2029
2061
  }
2030
2062
  const totalRows = readNumber(candidate.total) ?? rows.length;
2031
- return {
2063
+ const sanitized = sanitizeCsvProjectionInfo({
2032
2064
  rows,
2065
+ columns: inferColumns(rows)
2066
+ });
2067
+ return {
2068
+ rows: sanitized.rows,
2033
2069
  totalRows,
2034
- columns: inferColumns(rows),
2070
+ columns: sanitized.columns,
2035
2071
  complete: rows.length === totalRows,
2036
2072
  source: candidate.source
2037
2073
  };
@@ -2079,13 +2115,13 @@ function summarizeSampleValue(value, depth = 0) {
2079
2115
  if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
2080
2116
  if (depth >= 3) {
2081
2117
  if (Array.isArray(parsed)) return [];
2082
- if (isRecord(parsed)) return {};
2118
+ if (isRecord2(parsed)) return {};
2083
2119
  return compactScalar(parsed);
2084
2120
  }
2085
2121
  if (Array.isArray(parsed)) {
2086
2122
  return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
2087
2123
  }
2088
- if (isRecord(parsed)) {
2124
+ if (isRecord2(parsed)) {
2089
2125
  const out = {};
2090
2126
  for (const [key, nested] of Object.entries(parsed)) {
2091
2127
  if (["__dl", "meta", "metadata"].includes(key)) {
@@ -2115,7 +2151,7 @@ function compactCell(value) {
2115
2151
  }
2116
2152
  return `[${parsed.length} items]`;
2117
2153
  }
2118
- if (isRecord(parsed)) {
2154
+ if (isRecord2(parsed)) {
2119
2155
  for (const key of ["matched_result", "output"]) {
2120
2156
  if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
2121
2157
  return compactCell(parsed[key]);
@@ -2134,14 +2170,15 @@ function compactCell(value) {
2134
2170
  return compactScalar(parsed, 120);
2135
2171
  }
2136
2172
  function buildDatasetStats(rows, totalRows = rows.length, columns = inferColumns(rows)) {
2173
+ const sanitized = sanitizeCsvProjectionInfo({ rows, columns });
2137
2174
  const columnStats = {};
2138
- for (const column of columns) {
2175
+ for (const column of sanitized.columns) {
2139
2176
  let nonEmpty = 0;
2140
2177
  let empty = 0;
2141
2178
  let sampleValue;
2142
2179
  let sampleValueType = null;
2143
2180
  const valueCounts = /* @__PURE__ */ new Map();
2144
- for (const row of rows) {
2181
+ for (const row of sanitized.rows) {
2145
2182
  const raw = row[column];
2146
2183
  const value = compactCell(raw);
2147
2184
  if (value) {
@@ -2193,10 +2230,14 @@ function writeCanonicalRowsCsv(rowsInfo, outPath) {
2193
2230
  `Run output only includes ${rowsInfo.rows.length} preview row(s) of ${rowsInfo.totalRows}; cannot export a complete CSV from this status payload yet.`
2194
2231
  );
2195
2232
  }
2233
+ const sanitized = sanitizeCsvProjectionInfo({
2234
+ rows: rowsInfo.rows,
2235
+ columns: rowsInfo.columns
2236
+ });
2196
2237
  const resolved = resolve3(outPath);
2197
2238
  writeFileSync3(
2198
2239
  resolved,
2199
- csvStringFromRows(rowsInfo.rows, rowsInfo.columns),
2240
+ csvStringFromRows(sanitized.rows, sanitized.columns),
2200
2241
  "utf-8"
2201
2242
  );
2202
2243
  return resolved;
@@ -2488,14 +2529,12 @@ function registerOrgCommands(program) {
2488
2529
  import { createHash as createHash3 } from "crypto";
2489
2530
  import {
2490
2531
  existsSync as existsSync4,
2491
- mkdirSync as mkdirSync3,
2492
2532
  readFileSync as readFileSync3,
2493
2533
  readdirSync,
2494
2534
  realpathSync,
2495
2535
  writeFileSync as writeFileSync4
2496
2536
  } from "fs";
2497
2537
  import { basename as basename3, dirname as dirname6, join as join6, resolve as resolve7 } from "path";
2498
- import { Option } from "commander";
2499
2538
 
2500
2539
  // src/plays/bundle-play-file.ts
2501
2540
  import { tmpdir as tmpdir2 } from "os";
@@ -2579,6 +2618,14 @@ var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
2579
2618
  var NODE_BUILTIN_SET = new Set(
2580
2619
  builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
2581
2620
  );
2621
+ function assertValidExportName(exportName) {
2622
+ if (exportName === "default") return;
2623
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
2624
+ throw new Error(
2625
+ `Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`
2626
+ );
2627
+ }
2628
+ }
2582
2629
  function sha256(value) {
2583
2630
  return createHash("sha256").update(value).digest("hex");
2584
2631
  }
@@ -2773,6 +2820,29 @@ function workersPlayEntryAliasPlugin(playFilePath) {
2773
2820
  }
2774
2821
  };
2775
2822
  }
2823
+ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
2824
+ return {
2825
+ name: "deepline-workers-named-play-entry-alias",
2826
+ setup(buildContext) {
2827
+ buildContext.onResolve(
2828
+ { filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
2829
+ () => ({
2830
+ path: `${playFilePath}.${exportName}.entry.ts`,
2831
+ namespace: "deepline-named-play-entry"
2832
+ })
2833
+ );
2834
+ buildContext.onLoad(
2835
+ { filter: /.*/, namespace: "deepline-named-play-entry" },
2836
+ () => ({
2837
+ contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};
2838
+ `,
2839
+ loader: "ts",
2840
+ resolveDir: dirname3(playFilePath)
2841
+ })
2842
+ );
2843
+ }
2844
+ };
2845
+ }
2776
2846
  function workersNodeBuiltinStubPlugin() {
2777
2847
  const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
2778
2848
  return {
@@ -3204,11 +3274,20 @@ function getBundleSizeError(filePath, bundledCode, artifactKind) {
3204
3274
  }
3205
3275
  return null;
3206
3276
  }
3207
- async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter) {
3277
+ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter, exportName) {
3208
3278
  const sdkAliasPlugin = localSdkAliasPlugin(adapter);
3209
3279
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3280
+ const namedExportShim = exportName === "default" ? null : `export { ${exportName} as default } from ${JSON.stringify(entryFile)};
3281
+ `;
3210
3282
  const result = await build({
3211
- entryPoints: [entryFile],
3283
+ ...namedExportShim ? {
3284
+ stdin: {
3285
+ contents: namedExportShim,
3286
+ resolveDir: dirname3(entryFile),
3287
+ sourcefile: `${basename(entryFile)}.${exportName}.entry.ts`,
3288
+ loader: "ts"
3289
+ }
3290
+ } : { entryPoints: [entryFile] },
3212
3291
  absWorkingDir: adapter.projectRoot,
3213
3292
  bundle: true,
3214
3293
  format: "cjs",
@@ -3236,10 +3315,10 @@ async function runEsbuildForCjsNode(entryFile, importedPlayDependencies, adapter
3236
3315
  outputExtension: "cjs"
3237
3316
  };
3238
3317
  }
3239
- async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter) {
3318
+ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies, adapter, exportName) {
3240
3319
  const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
3241
3320
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
3242
- const playEntryAlias = workersPlayEntryAliasPlugin(playEntryFile);
3321
+ const playEntryAlias = exportName === "default" ? workersPlayEntryAliasPlugin(playEntryFile) : workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
3243
3322
  const result = await build({
3244
3323
  // Entry is the Workers harness; it imports the play via the virtual
3245
3324
  // `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
@@ -3310,10 +3389,16 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
3310
3389
  async function bundlePlayFile(filePath, options) {
3311
3390
  const adapter = options.adapter;
3312
3391
  const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
3392
+ const exportName = options.exportName?.trim() || "default";
3393
+ assertValidExportName(exportName);
3313
3394
  const absolutePath = await normalizeLocalPath(filePath);
3314
3395
  adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
3315
3396
  try {
3316
3397
  const analysis = await analyzeSourceGraph(absolutePath, adapter);
3398
+ analysis.graphHash = sha256(
3399
+ `${analysis.graphHash}
3400
+ entry-export:${exportName}`
3401
+ );
3317
3402
  if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
3318
3403
  const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
3319
3404
  analysis.graphHash = sha256(
@@ -3365,7 +3450,7 @@ workers-harness:${harnessFingerprint}`
3365
3450
  errors: typecheckErrors
3366
3451
  };
3367
3452
  }
3368
- const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
3453
+ const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
3369
3454
  if (Array.isArray(buildOutcome)) {
3370
3455
  return {
3371
3456
  success: false,
@@ -3375,7 +3460,8 @@ workers-harness:${harnessFingerprint}`
3375
3460
  }
3376
3461
  const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
3377
3462
  const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
3378
- const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${basename(absolutePath).replace(/\.[^.]+$/, "")}.${outputExtension}`;
3463
+ const virtualBaseName = exportName === "default" ? basename(absolutePath).replace(/\.[^.]+$/, "") : `${basename(absolutePath).replace(/\.[^.]+$/, "")}.${exportName}`;
3464
+ const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
3379
3465
  const executableCode = `${bundledCode}
3380
3466
  //# sourceMappingURL=${basename(virtualFilename)}.map
3381
3467
  `;
@@ -3758,7 +3844,7 @@ function createSdkPlayBundlingAdapter() {
3758
3844
  sdkSourceRoot: SDK_SOURCE_ROOT,
3759
3845
  sdkPackageJson: SDK_PACKAGE_JSON,
3760
3846
  sdkEntryFile: SDK_ENTRY_FILE,
3761
- sdkTypesEntryFile: existsSync3(SDK_TYPES_ENTRY_FILE) ? SDK_TYPES_ENTRY_FILE : SDK_ENTRY_FILE,
3847
+ sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : existsSync3(SDK_TYPES_ENTRY_FILE) ? SDK_TYPES_ENTRY_FILE : SDK_ENTRY_FILE,
3762
3848
  sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
3763
3849
  workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
3764
3850
  workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
@@ -3769,6 +3855,7 @@ function createSdkPlayBundlingAdapter() {
3769
3855
  async function bundlePlayFile2(filePath, options = {}) {
3770
3856
  return bundlePlayFile(filePath, {
3771
3857
  target: options.target ?? defaultPlayBundleTarget(),
3858
+ exportName: options.exportName,
3772
3859
  adapter: createSdkPlayBundlingAdapter()
3773
3860
  });
3774
3861
  }
@@ -3918,54 +4005,6 @@ function createCliProgress(enabled) {
3918
4005
  return progress;
3919
4006
  }
3920
4007
 
3921
- // src/cli/trace.ts
3922
- var cliTraceStartedAt = Date.now();
3923
- function isTruthyEnv(value) {
3924
- return value === "1" || value === "true" || value === "yes";
3925
- }
3926
- function isCliTraceEnabled() {
3927
- return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
3928
- }
3929
- function recordCliTrace(event) {
3930
- if (!isCliTraceEnabled()) {
3931
- return;
3932
- }
3933
- const now = Date.now();
3934
- const payload = {
3935
- ts: now,
3936
- source: "cli",
3937
- sinceStartMs: now - cliTraceStartedAt,
3938
- ...event
3939
- };
3940
- process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
3941
- `);
3942
- }
3943
- async function traceCliSpan(phase, fields, run) {
3944
- if (!isCliTraceEnabled()) {
3945
- return run();
3946
- }
3947
- const startedAt = Date.now();
3948
- try {
3949
- const result = await run();
3950
- recordCliTrace({
3951
- phase,
3952
- ms: Date.now() - startedAt,
3953
- ok: true,
3954
- ...fields
3955
- });
3956
- return result;
3957
- } catch (error) {
3958
- recordCliTrace({
3959
- phase,
3960
- ms: Date.now() - startedAt,
3961
- ok: false,
3962
- error: error instanceof Error ? error.message : String(error),
3963
- ...fields
3964
- });
3965
- throw error;
3966
- }
3967
- }
3968
-
3969
4008
  // src/cli/commands/play.ts
3970
4009
  function parseReferencedPlayTarget(target) {
3971
4010
  const trimmed = target.trim();
@@ -4013,67 +4052,6 @@ function defaultMaterializedPlayPath(reference) {
4013
4052
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
4014
4053
  return resolve7(`${safeName || "play"}.play.ts`);
4015
4054
  }
4016
- function sanitizeGeneratedPlayName(value) {
4017
- return value.trim().toLowerCase().replace(/^prebuilt\//, "").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "play";
4018
- }
4019
- function buildGeneratedCsvWrapperSource(input) {
4020
- return `import { definePlay } from 'deepline';
4021
-
4022
- export default definePlay(
4023
- ${JSON.stringify(input.wrapperName)},
4024
- async (ctx, input: Record<string, unknown> & { file: string }) => {
4025
- const rows = await ctx.csv<Record<string, unknown>>(input.file);
4026
- const constants = Object.fromEntries(
4027
- Object.entries(input).filter(([key]) => key !== 'file'),
4028
- );
4029
-
4030
- const mappedRows = await ctx
4031
- .map('csv_rows', rows, {
4032
- key: (row, index) =>
4033
- String(
4034
- row.id ??
4035
- row.lead_id ??
4036
- row.email ??
4037
- row.linkedin_url ??
4038
- row.domain ??
4039
- index,
4040
- ),
4041
- })
4042
- .step('result', (row, rowCtx) =>
4043
- rowCtx.runPlay(
4044
- 'row_play',
4045
- ${JSON.stringify(input.playRef)},
4046
- {
4047
- ...constants,
4048
- ...row,
4049
- },
4050
- {
4051
- description: 'Run the source play for this CSV row.',
4052
- },
4053
- ),
4054
- )
4055
- .run({ description: 'Run the source play once per CSV row.' });
4056
-
4057
- return { rows: mappedRows };
4058
- },
4059
- );
4060
- `;
4061
- }
4062
- function writeGeneratedCsvWrapperPlay(playRef) {
4063
- const baseName = sanitizeGeneratedPlayName(
4064
- parseReferencedPlayTarget(playRef).unqualifiedPlayName
4065
- );
4066
- const wrapperName = `${baseName}-csv`;
4067
- const outputDir = resolve7(".deepline", "generated");
4068
- const outputPath = join6(outputDir, `${wrapperName}.play.ts`);
4069
- mkdirSync3(outputDir, { recursive: true });
4070
- writeFileSync4(
4071
- outputPath,
4072
- buildGeneratedCsvWrapperSource({ wrapperName, playRef }),
4073
- "utf-8"
4074
- );
4075
- return outputPath;
4076
- }
4077
4055
  function materializeRemotePlaySource(input) {
4078
4056
  if (isFileTarget(input.target)) {
4079
4057
  return null;
@@ -4139,7 +4117,10 @@ function looksLikeFilePath(target) {
4139
4117
  if (target.trim().toLowerCase().startsWith("prebuilt/")) {
4140
4118
  return false;
4141
4119
  }
4142
- return target.includes("/") || target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4120
+ if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/") || target.startsWith("~/")) {
4121
+ return true;
4122
+ }
4123
+ return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
4143
4124
  }
4144
4125
  function parsePositiveInteger2(value, flagName) {
4145
4126
  const parsed = Number.parseInt(value, 10);
@@ -4156,6 +4137,133 @@ function parseJsonInput(raw) {
4156
4137
  }
4157
4138
  return parsed;
4158
4139
  }
4140
+ function parseInputFieldFlag(rawFlag, nextArg) {
4141
+ const flag = rawFlag.slice(2);
4142
+ const equalsIndex = flag.indexOf("=");
4143
+ if (equalsIndex > 0) {
4144
+ const path = flag.slice(0, equalsIndex).trim();
4145
+ const value = flag.slice(equalsIndex + 1);
4146
+ if (!path) {
4147
+ throw new Error(`Invalid play input flag: ${rawFlag}`);
4148
+ }
4149
+ return { path, value };
4150
+ }
4151
+ if (!nextArg || nextArg.startsWith("--")) {
4152
+ throw new Error(`Play input flag ${rawFlag} requires a value.`);
4153
+ }
4154
+ return { path: flag, value: nextArg };
4155
+ }
4156
+ function parseInputFlagValue(raw) {
4157
+ const trimmed = raw.trim();
4158
+ if (!trimmed) return "";
4159
+ if (trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed.startsWith("{") || trimmed.startsWith("[") || /^-?\d+(\.\d+)?$/.test(trimmed)) {
4160
+ try {
4161
+ return JSON.parse(trimmed);
4162
+ } catch {
4163
+ return raw;
4164
+ }
4165
+ }
4166
+ return raw;
4167
+ }
4168
+ function getDottedInputValue(input, path) {
4169
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4170
+ let cursor = input;
4171
+ for (const part of parts) {
4172
+ if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) {
4173
+ return void 0;
4174
+ }
4175
+ cursor = cursor[part];
4176
+ }
4177
+ return cursor;
4178
+ }
4179
+ function setDottedInputValue(input, path, value) {
4180
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
4181
+ if (parts.length === 0) {
4182
+ throw new Error(`Invalid play input flag path: ${path}`);
4183
+ }
4184
+ let cursor = input;
4185
+ for (const part of parts.slice(0, -1)) {
4186
+ const existing = cursor[part];
4187
+ if (existing !== void 0 && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
4188
+ throw new Error(
4189
+ `Cannot set --${path}; input.${part} is already a non-object value.`
4190
+ );
4191
+ }
4192
+ if (!existing) {
4193
+ cursor[part] = {};
4194
+ }
4195
+ cursor = cursor[part];
4196
+ }
4197
+ cursor[parts[parts.length - 1]] = value;
4198
+ }
4199
+ function schemaMetadata(schema, key) {
4200
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
4201
+ const value = schema[key];
4202
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
4203
+ }
4204
+ function stringMetadata(metadata, key) {
4205
+ const value = metadata?.[key];
4206
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4207
+ }
4208
+ function inputFieldFromCsvArg(csvArg) {
4209
+ if (typeof csvArg !== "string") return null;
4210
+ const match = /^input\.([A-Za-z_$][\w$]*)$/.exec(csvArg.trim());
4211
+ return match?.[1] ?? null;
4212
+ }
4213
+ function fileInputBindingsFromPlaySchema(inputSchema) {
4214
+ const csvInput = schemaMetadata(inputSchema, "csvInput");
4215
+ if (!csvInput) return [];
4216
+ return [
4217
+ {
4218
+ inputPath: stringMetadata(csvInput, "inputField") ?? "csv"
4219
+ }
4220
+ ];
4221
+ }
4222
+ function fileInputBindingsFromStaticPipeline(staticPipeline) {
4223
+ if (!staticPipeline || typeof staticPipeline !== "object" || Array.isArray(staticPipeline)) {
4224
+ return [];
4225
+ }
4226
+ const inputField = inputFieldFromCsvArg(
4227
+ staticPipeline.csvArg
4228
+ );
4229
+ return inputField ? [{ inputPath: inputField }] : [];
4230
+ }
4231
+ function isLocalFilePathValue(value) {
4232
+ if (typeof value !== "string" || !value.trim()) return false;
4233
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
4234
+ return existsSync4(resolve7(value));
4235
+ }
4236
+ async function stageFileInputArgs(input) {
4237
+ const uniqueBindings = [
4238
+ ...new Map(input.bindings.map((binding) => [binding.inputPath, binding])).values()
4239
+ ];
4240
+ const localFiles = uniqueBindings.flatMap((binding) => {
4241
+ const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
4242
+ if (!isLocalFilePathValue(value)) return [];
4243
+ const absolutePath = resolve7(value);
4244
+ return [{ binding, absolutePath, logicalPath: basename3(absolutePath) }];
4245
+ });
4246
+ if (localFiles.length === 0) {
4247
+ return { inputFile: null, packagedFiles: [] };
4248
+ }
4249
+ input.progress.phase(
4250
+ localFiles.length === 1 ? "staging input file" : "staging input files"
4251
+ );
4252
+ const staged = await input.client.stagePlayFiles(
4253
+ localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
4254
+ );
4255
+ for (const [index, file] of localFiles.entries()) {
4256
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, file.logicalPath);
4257
+ const stagedFile = staged[index];
4258
+ if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
4259
+ setDottedInputValue(input.runtimeInput, file.binding.inputPath, stagedFile.logicalPath);
4260
+ }
4261
+ }
4262
+ return {
4263
+ inputFile: staged[0] ?? null,
4264
+ packagedFiles: staged.slice(1)
4265
+ };
4266
+ }
4159
4267
  function stageFile(logicalPath, absolutePath) {
4160
4268
  const buffer = readFileSync3(absolutePath);
4161
4269
  return {
@@ -4564,24 +4672,10 @@ async function startAndWaitForPlayCompletionByStream(input) {
4564
4672
  },
4565
4673
  Math.max(1, input.waitTimeoutMs)
4566
4674
  );
4567
- recordCliTrace({
4568
- phase: "cli.start_stream_request",
4569
- playName: input.playName
4570
- });
4571
4675
  try {
4572
- let eventCount = 0;
4573
4676
  for await (const event of input.client.startPlayRunStream(input.request, {
4574
4677
  signal: controller.signal
4575
4678
  })) {
4576
- eventCount += 1;
4577
- if (eventCount === 1) {
4578
- recordCliTrace({
4579
- phase: "cli.start_stream_first_event",
4580
- ms: Date.now() - startedAt,
4581
- playName: input.playName,
4582
- eventType: event.type
4583
- });
4584
- }
4585
4679
  const eventRunId = getEventPayload(event).runId;
4586
4680
  if (typeof eventRunId === "string" && eventRunId && eventRunId !== "pending") {
4587
4681
  lastKnownWorkflowId = eventRunId;
@@ -4622,14 +4716,6 @@ async function startAndWaitForPlayCompletionByStream(input) {
4622
4716
  });
4623
4717
  const finalStatus = getFinalStatusFromLiveEvent(event);
4624
4718
  if (finalStatus) {
4625
- recordCliTrace({
4626
- phase: "cli.start_stream_final_event",
4627
- ms: Date.now() - startedAt,
4628
- playName: input.playName,
4629
- runId: finalStatus.runId,
4630
- status: finalStatus.status,
4631
- eventCount
4632
- });
4633
4719
  return finalStatus;
4634
4720
  }
4635
4721
  }
@@ -4908,7 +4994,6 @@ function buildRunNextCommands(runId) {
4908
4994
  return {
4909
4995
  exportCsv: `deepline runs export ${runId} --out output.csv`,
4910
4996
  status: `deepline runs status ${runId} --json`,
4911
- fullStatus: `deepline runs status ${runId} --json --full`,
4912
4997
  logs: `deepline runs logs ${runId}`
4913
4998
  };
4914
4999
  }
@@ -4932,12 +5017,26 @@ function compactPlayStatus(status, options) {
4932
5017
  ...result !== void 0 ? { result } : {},
4933
5018
  ...status.resultView ? { resultView: status.resultView } : {},
4934
5019
  ...datasetStats ? { dataset_stats: datasetStats } : {},
4935
- ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 10) } : {},
5020
+ ...rowsInfo ? { previewRows: rowsInfo.rows.slice(0, 5) } : {},
4936
5021
  ...billing ? { billing } : {},
4937
5022
  ...status.run ? { run: status.run } : {},
4938
5023
  next: buildRunNextCommands(status.runId)
4939
5024
  };
4940
5025
  }
5026
+ function enrichPlayStatusWithDatasetStats(status) {
5027
+ const rowsInfo = extractCanonicalRowsInfo(status);
5028
+ if (!rowsInfo?.complete) {
5029
+ return status;
5030
+ }
5031
+ return {
5032
+ ...status,
5033
+ dataset_stats: buildDatasetStats(
5034
+ rowsInfo.rows,
5035
+ rowsInfo.totalRows,
5036
+ rowsInfo.columns
5037
+ )
5038
+ };
5039
+ }
4941
5040
  function formatDatasetStatsLines(datasetStats) {
4942
5041
  if (!datasetStats) {
4943
5042
  return [];
@@ -4947,10 +5046,10 @@ function formatDatasetStatsLines(datasetStats) {
4947
5046
  0,
4948
5047
  12
4949
5048
  )) {
4950
- const topValues = stat3.top_values ? `, ${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
4951
- const sample = stat3.sample_value !== void 0 ? `, sample=${JSON.stringify(stat3.sample_value)}` : "";
5049
+ const topValues = stat3.top_values ? `, top_values=${Object.entries(stat3.top_values).slice(0, 3).map(([value, count]) => `${value}=${count}`).join(", ")}` : "";
5050
+ const sample = stat3.sample_value !== void 0 ? `, sample_value=${JSON.stringify(stat3.sample_value)}` : "";
4952
5051
  lines.push(
4953
- ` ${column}: ${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
5052
+ ` ${column}: non_empty=${stat3.non_empty}, unique=${stat3.unique}${topValues}${sample}`
4954
5053
  );
4955
5054
  }
4956
5055
  return lines;
@@ -4959,7 +5058,7 @@ function writePlayResult(status, jsonOutput, options) {
4959
5058
  if (jsonOutput) {
4960
5059
  process.stdout.write(
4961
5060
  `${JSON.stringify(
4962
- options?.fullJson ? status : compactPlayStatus(status, options)
5061
+ options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status, options)
4963
5062
  )}
4964
5063
  `
4965
5064
  );
@@ -5108,10 +5207,9 @@ function writeStartedPlayRun(input) {
5108
5207
  console.log(output);
5109
5208
  }
5110
5209
  function parsePlayRunOptions(args) {
5111
- 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]";
5210
+ 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]";
5112
5211
  let filePath = null;
5113
5212
  let playName = null;
5114
- let csvPath = null;
5115
5213
  let input = null;
5116
5214
  let revisionId = null;
5117
5215
  let revisionSelector = null;
@@ -5132,10 +5230,6 @@ function parsePlayRunOptions(args) {
5132
5230
  playName = parseReferencedPlayTarget(args[++index]).playName;
5133
5231
  continue;
5134
5232
  }
5135
- if (arg === "--csv" && args[index + 1]) {
5136
- csvPath = resolve7(args[++index]);
5137
- continue;
5138
- }
5139
5233
  if ((arg === "--input" || arg === "-i") && args[index + 1]) {
5140
5234
  input = parseJsonInput(args[++index]);
5141
5235
  continue;
@@ -5185,8 +5279,13 @@ function parsePlayRunOptions(args) {
5185
5279
  continue;
5186
5280
  }
5187
5281
  if (arg.startsWith("--")) {
5188
- throw new Error(`Unexpected flag: ${arg}
5189
- ${usage}`);
5282
+ const { path, value } = parseInputFieldFlag(arg, args[index + 1]);
5283
+ input ??= {};
5284
+ setDottedInputValue(input, path, parseInputFlagValue(value));
5285
+ if (!arg.includes("=")) {
5286
+ index += 1;
5287
+ }
5288
+ continue;
5190
5289
  }
5191
5290
  if (!arg.startsWith("--") && !filePath && !playName) {
5192
5291
  if (isFileTarget(arg) || looksLikeFilePath(arg)) {
@@ -5222,7 +5321,6 @@ ${usage}`);
5222
5321
  }
5223
5322
  return {
5224
5323
  target: filePath ? { kind: "file", path: filePath } : { kind: "name", name: playName },
5225
- csvPath,
5226
5324
  input,
5227
5325
  revisionId,
5228
5326
  revisionSelector,
@@ -5297,34 +5395,12 @@ async function handleFileBackedRun(options) {
5297
5395
  const client = new DeeplineClient();
5298
5396
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5299
5397
  const absolutePlayPath = resolve7(options.target.path);
5300
- recordCliTrace({
5301
- phase: "cli.play_run_file_start",
5302
- playPath: absolutePlayPath,
5303
- watch: options.watch,
5304
- hasCsv: Boolean(options.csvPath),
5305
- force: options.force
5306
- });
5307
5398
  progress.phase("compiling play");
5308
- const readSourceStartedAt = Date.now();
5309
5399
  const sourceCode = readFileSync3(absolutePlayPath, "utf-8");
5310
- recordCliTrace({
5311
- phase: "cli.read_play_source",
5312
- ms: Date.now() - readSourceStartedAt,
5313
- bytes: sourceCode.length,
5314
- playPath: absolutePlayPath
5315
- });
5316
5400
  let graph;
5317
5401
  try {
5318
- graph = await traceCliSpan(
5319
- "cli.bundle_play_graph",
5320
- { playPath: absolutePlayPath },
5321
- () => collectBundledPlayGraph(absolutePlayPath)
5322
- );
5323
- await traceCliSpan(
5324
- "cli.compile_play_manifest",
5325
- { playPath: absolutePlayPath, nodeCount: graph.nodes.size },
5326
- () => compileBundledPlayGraphManifests(client, graph)
5327
- );
5402
+ graph = await collectBundledPlayGraph(absolutePlayPath);
5403
+ await compileBundledPlayGraphManifests(client, graph);
5328
5404
  progress.phase("compiled play");
5329
5405
  } catch (error) {
5330
5406
  progress.fail();
@@ -5335,87 +5411,58 @@ async function handleFileBackedRun(options) {
5335
5411
  const playName = bundleResult.playName ?? extractPlayName(sourceCode, absolutePlayPath);
5336
5412
  try {
5337
5413
  progress.phase("publishing imported plays");
5338
- await traceCliSpan(
5339
- "cli.publish_imported_plays",
5340
- { playName, nodeCount: graph.nodes.size },
5341
- () => publishImportedPlayDependencies(client, graph)
5342
- );
5414
+ await publishImportedPlayDependencies(client, graph);
5343
5415
  } catch (error) {
5344
5416
  progress.fail();
5345
5417
  console.error(error instanceof Error ? error.message : String(error));
5346
5418
  return 1;
5347
5419
  }
5348
5420
  const runtimeInput = options.input ? { ...options.input } : {};
5349
- const prepareFilesStartedAt = Date.now();
5350
5421
  const packagedFileUploads = bundleResult.packagedFiles.map(
5351
5422
  (file) => stageFile(file.logicalPath, file.absolutePath)
5352
5423
  );
5353
- const inputFileUpload = options.csvPath ? stageFile(basename3(options.csvPath), options.csvPath) : packagedFileUploads[0] ?? null;
5354
- if (options.csvPath && typeof runtimeInput.file !== "string" && typeof runtimeInput.csv !== "string") {
5355
- runtimeInput.file = basename3(options.csvPath);
5356
- }
5357
- recordCliTrace({
5358
- phase: "cli.prepare_input_files",
5359
- ms: Date.now() - prepareFilesStartedAt,
5360
- playName,
5361
- packagedFileCount: packagedFileUploads.length,
5362
- hasInputFile: Boolean(inputFileUpload)
5424
+ const stagedFileInputs = await stageFileInputArgs({
5425
+ client,
5426
+ runtimeInput,
5427
+ bindings: fileInputBindingsFromStaticPipeline(
5428
+ requireCompilerManifest(bundleResult).staticPipeline
5429
+ ),
5430
+ progress
5363
5431
  });
5364
5432
  const startRequest = {
5365
5433
  name: playName,
5366
5434
  sourceCode: bundleResult.sourceCode,
5367
5435
  runtimeArtifact: bundleResult.artifact,
5368
5436
  compilerManifest: requireCompilerManifest(bundleResult),
5369
- inputFileUpload,
5370
5437
  packagedFileUploads,
5371
5438
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5439
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5440
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5372
5441
  ...options.force ? { force: true } : {}
5373
5442
  };
5374
5443
  if (options.watch) {
5375
5444
  progress.phase("starting run");
5376
- const finalStatus = await traceCliSpan(
5377
- "cli.start_and_watch",
5378
- { playName },
5379
- () => startAndWaitForPlayCompletionByStream({
5380
- client,
5381
- request: startRequest,
5382
- playName,
5383
- jsonOutput: options.jsonOutput,
5384
- emitLogs: options.emitLogs,
5385
- waitTimeoutMs: options.waitTimeoutMs,
5386
- progress
5387
- })
5388
- );
5389
- const exportStartedAt = Date.now();
5390
- const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5391
- recordCliTrace({
5392
- phase: "cli.export_rows",
5393
- ms: Date.now() - exportStartedAt,
5445
+ const finalStatus = await startAndWaitForPlayCompletionByStream({
5446
+ client,
5447
+ request: startRequest,
5394
5448
  playName,
5395
- exported: Boolean(exportedPath)
5449
+ jsonOutput: options.jsonOutput,
5450
+ emitLogs: options.emitLogs,
5451
+ waitTimeoutMs: options.waitTimeoutMs,
5452
+ progress
5396
5453
  });
5454
+ const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5397
5455
  if (finalStatus.status === "completed") {
5398
5456
  progress.complete();
5399
5457
  } else {
5400
5458
  progress.fail();
5401
5459
  }
5402
- recordCliTrace({
5403
- phase: "cli.write_play_result",
5404
- playName,
5405
- status: finalStatus.status,
5406
- runId: finalStatus.runId
5407
- });
5408
5460
  writePlayResult(finalStatus, options.jsonOutput, { exportedPath });
5409
5461
  return finalStatus.status === "completed" ? 0 : 1;
5410
5462
  }
5411
5463
  progress.phase("starting run");
5412
- const started = await traceCliSpan(
5413
- "cli.start_run",
5414
- { playName },
5415
- () => client.startPlayRun(startRequest)
5416
- );
5417
- const fallbackDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5418
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5464
+ const started = await client.startPlayRun(startRequest);
5465
+ const dashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
5419
5466
  progress.phase(`loading play on ${dashboardUrl}`);
5420
5467
  progress.complete();
5421
5468
  writeStartedPlayRun({
@@ -5423,7 +5470,7 @@ async function handleFileBackedRun(options) {
5423
5470
  playName,
5424
5471
  status: started.status,
5425
5472
  statusUrl: started.statusUrl,
5426
- dashboardUrl,
5473
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5427
5474
  jsonOutput: options.jsonOutput,
5428
5475
  progress
5429
5476
  });
@@ -5449,9 +5496,8 @@ async function handleNamedRun(options) {
5449
5496
  }
5450
5497
  const client = new DeeplineClient();
5451
5498
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
5452
- let stagedInputFile = null;
5453
5499
  progress.phase("loading play definition");
5454
- await assertCanonicalNamedPlayReference(client, options.target.name);
5500
+ const playDetail = await assertCanonicalNamedPlayReference(client, options.target.name);
5455
5501
  progress.phase("selecting revision");
5456
5502
  const selectedRevisionId = await resolveNamedRunRevisionId({
5457
5503
  client,
@@ -5459,19 +5505,22 @@ async function handleNamedRun(options) {
5459
5505
  revisionId: options.revisionId,
5460
5506
  selector: options.revisionSelector
5461
5507
  });
5462
- if (options.csvPath) {
5463
- progress.phase("staging input file");
5464
- const [staged] = await client.stagePlayFiles([
5465
- stageFile(basename3(options.csvPath), options.csvPath)
5466
- ]);
5467
- stagedInputFile = staged ?? null;
5468
- }
5469
5508
  const runtimeInput = options.input ? { ...options.input } : {};
5509
+ const stagedFileInputs = await stageFileInputArgs({
5510
+ client,
5511
+ runtimeInput,
5512
+ bindings: [
5513
+ ...fileInputBindingsFromPlaySchema(playDetail.play.inputSchema),
5514
+ ...fileInputBindingsFromStaticPipeline(playDetail.play.staticPipeline)
5515
+ ],
5516
+ progress
5517
+ });
5470
5518
  const startRequest = {
5471
5519
  name: options.target.name,
5472
5520
  ...selectedRevisionId ? { revisionId: selectedRevisionId } : {},
5473
5521
  ...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
5474
- ...stagedInputFile ? { inputFile: stagedInputFile } : {},
5522
+ ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
5523
+ ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
5475
5524
  ...options.force ? { force: true } : {}
5476
5525
  };
5477
5526
  if (options.watch) {
@@ -5485,22 +5534,6 @@ async function handleNamedRun(options) {
5485
5534
  waitTimeoutMs: options.waitTimeoutMs,
5486
5535
  progress
5487
5536
  });
5488
- if (finalStatus.status !== "completed" && options.csvPath) {
5489
- progress.phase("generating csv wrapper play");
5490
- const generatedPlayPath = writeGeneratedCsvWrapperPlay(
5491
- options.target.name
5492
- );
5493
- progress.writeLogLine(
5494
- `Generated CSV wrapper play: ${generatedPlayPath}`
5495
- );
5496
- progress.phase("running generated csv wrapper play");
5497
- return handleFileBackedRun({
5498
- ...options,
5499
- target: { kind: "file", path: generatedPlayPath },
5500
- revisionId: null,
5501
- revisionSelector: null
5502
- });
5503
- }
5504
5537
  const exportedPath = exportPlayStatusRows(finalStatus, options.outPath);
5505
5538
  if (finalStatus.status === "completed") {
5506
5539
  progress.complete();
@@ -5512,11 +5545,10 @@ async function handleNamedRun(options) {
5512
5545
  }
5513
5546
  progress.phase("starting run");
5514
5547
  const started = await client.startPlayRun(startRequest);
5515
- const fallbackDashboardUrl = buildPlayDashboardUrl(
5548
+ const dashboardUrl = buildPlayDashboardUrl(
5516
5549
  client.baseUrl,
5517
5550
  options.target.name
5518
5551
  );
5519
- const dashboardUrl = started.dashboardUrl ?? fallbackDashboardUrl;
5520
5552
  progress.phase(`loading play on ${dashboardUrl}`);
5521
5553
  progress.complete();
5522
5554
  writeStartedPlayRun({
@@ -5524,7 +5556,7 @@ async function handleNamedRun(options) {
5524
5556
  playName: started.name ?? options.target.name,
5525
5557
  status: started.status,
5526
5558
  statusUrl: started.statusUrl,
5527
- dashboardUrl,
5559
+ dashboardUrl: started.dashboardUrl ?? dashboardUrl,
5528
5560
  jsonOutput: options.jsonOutput,
5529
5561
  progress
5530
5562
  });
@@ -5743,8 +5775,9 @@ async function handlePlayGet(args) {
5743
5775
  return 1;
5744
5776
  }
5745
5777
  const client = new DeeplineClient();
5746
- const jsonOutput = argsWantJson(args);
5778
+ const explicitJson = args.includes("--json");
5747
5779
  const sourceOutput = args.includes("--source");
5780
+ const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
5748
5781
  let outPath = null;
5749
5782
  for (let index = 1; index < args.length; index += 1) {
5750
5783
  const arg = args[index];
@@ -5755,7 +5788,7 @@ async function handlePlayGet(args) {
5755
5788
  const playName = isFileTarget(target) ? extractPlayName(readFileSync3(resolve7(target), "utf-8"), resolve7(target)) : parseReferencedPlayTarget(target).playName;
5756
5789
  const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
5757
5790
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
5758
- const materializedFile = sourceOutput || outPath ? materializeRemotePlaySource({
5791
+ const materializedFile = outPath ? materializeRemotePlaySource({
5759
5792
  target,
5760
5793
  playName,
5761
5794
  sourceCode: resolvedSource,
@@ -5795,6 +5828,10 @@ async function handlePlayGet(args) {
5795
5828
  }
5796
5829
  return 0;
5797
5830
  }
5831
+ if (outPath && loadedMessage) {
5832
+ console.log(loadedMessage);
5833
+ return 0;
5834
+ }
5798
5835
  console.log(`Play: ${formatPlayReference(detail.play)}`);
5799
5836
  console.log(
5800
5837
  `Working version: ${detail.play.workingRevision?.version ?? "\u2014"}`
@@ -5961,6 +5998,20 @@ function printPlayDescription(play) {
5961
5998
  console.log(` ${line}`);
5962
5999
  }
5963
6000
  }
6001
+ if (play.csvInput) {
6002
+ console.log(" CSV input:");
6003
+ const rendered = JSON.stringify(play.csvInput, null, 2);
6004
+ for (const line of rendered.split("\n")) {
6005
+ console.log(` ${line}`);
6006
+ }
6007
+ }
6008
+ if (play.rowOutputSchema) {
6009
+ console.log(" Row output schema:");
6010
+ const rendered = JSON.stringify(play.rowOutputSchema, null, 2);
6011
+ for (const line of rendered.split("\n")) {
6012
+ console.log(` ${line}`);
6013
+ }
6014
+ }
5964
6015
  console.log(` Run: ${play.runCommand}`);
5965
6016
  }
5966
6017
  async function handlePlaySearch(args) {
@@ -6100,6 +6151,45 @@ async function handlePlayPublish(args) {
6100
6151
  `);
6101
6152
  return result.success ? 0 : 1;
6102
6153
  }
6154
+ async function handlePlayDelete(args) {
6155
+ const playName = args[0];
6156
+ if (!playName) {
6157
+ console.error("Usage: deepline plays delete <play-name> --yes [--json]");
6158
+ return 1;
6159
+ }
6160
+ const confirmed = args.includes("--yes") || args.includes("-y") || args.includes("--force");
6161
+ if (!confirmed) {
6162
+ console.error(
6163
+ "Refusing to delete without --yes. This deletes the org-owned play, its revisions, trigger bindings, and local run records."
6164
+ );
6165
+ return 1;
6166
+ }
6167
+ const client = new DeeplineClient();
6168
+ let detail;
6169
+ try {
6170
+ detail = await client.getPlay(parseReferencedPlayTarget(playName).playName);
6171
+ } catch (error) {
6172
+ console.error(error instanceof Error ? error.message : String(error));
6173
+ return 1;
6174
+ }
6175
+ if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
6176
+ console.error(`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`);
6177
+ return 1;
6178
+ }
6179
+ const result = await client.deletePlay(
6180
+ parseReferencedPlayTarget(formatPlayReference(detail.play)).playName
6181
+ );
6182
+ if (argsWantJson(args)) {
6183
+ process.stdout.write(`${JSON.stringify(result)}
6184
+ `);
6185
+ return result.deleted ? 0 : 1;
6186
+ }
6187
+ process.stdout.write(
6188
+ `Deleted ${result.name}: revisions=${result.deletedRevisionCount}, bindings=${result.deletedBindingCount}, runs=${result.deletedRunCount}
6189
+ `
6190
+ );
6191
+ return result.deleted ? 0 : 1;
6192
+ }
6103
6193
  function registerPlayCommands(program) {
6104
6194
  const play = program.command("plays").alias("play").description("Search, validate, run, and manage cloud plays.").addHelpText(
6105
6195
  "after",
@@ -6133,21 +6223,23 @@ Examples:
6133
6223
  ...options.json ? ["--json"] : []
6134
6224
  ]);
6135
6225
  });
6136
- play.command("run [target]").description("Run a play file or named play.").addHelpText(
6226
+ play.command("run [target]").description("Run a play file or named play.").allowUnknownOption(true).allowExcessArguments(true).addHelpText(
6137
6227
  "after",
6138
6228
  `
6139
6229
  Notes:
6140
- Local play files are bundled locally, then validated and executed in Deepline cloud.
6141
- Named plays run the stored live cloud revision.
6142
- Run performs server preflight automatically. Use \`deepline plays check <file>\`
6143
- to validate without starting a run.
6230
+ Local play files are bundled locally, then validated and executed in Deepline cloud.
6231
+ Named plays run the stored live cloud revision.
6232
+ Unknown --foo and --foo.bar flags are treated as play input args.
6233
+ File-like input args accept local paths; the CLI stages those files before submit.
6234
+ Run performs server preflight automatically. Use \`deepline plays check <file>\`
6235
+ to validate without starting a run.
6144
6236
 
6145
6237
  Examples:
6146
6238
  deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
6147
6239
  deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
6148
6240
  deepline plays run enrich.play.ts --csv leads.csv --watch --out leads-enriched.csv
6149
6241
  `
6150
- ).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(
6242
+ ).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(
6151
6243
  "--revision-id <id>",
6152
6244
  "Run a specific saved revision instead of the live revision"
6153
6245
  ).option(
@@ -6156,12 +6248,21 @@ Examples:
6156
6248
  ).option("--watch", "Stream logs until completion").option(
6157
6249
  "--logs",
6158
6250
  "When output is non-interactive, stream play logs to stderr while waiting"
6159
- ).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) => {
6251
+ ).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) => {
6252
+ const passthroughArgs = [...command.args];
6253
+ const explicitTarget = options.file || options.name;
6254
+ const targetIsInputFlag = typeof target === "string" && target.startsWith("--");
6255
+ const effectiveTarget = explicitTarget || targetIsInputFlag ? null : target;
6256
+ if (explicitTarget && typeof target === "string" && !targetIsInputFlag && !passthroughArgs.includes(target)) {
6257
+ passthroughArgs.push(target);
6258
+ }
6259
+ if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
6260
+ passthroughArgs.shift();
6261
+ }
6160
6262
  process.exitCode = await handlePlayRun([
6161
- ...target ? [target] : [],
6263
+ ...effectiveTarget ? [effectiveTarget] : [],
6162
6264
  ...options.file ? ["--file", options.file] : [],
6163
6265
  ...options.name ? ["--name", options.name] : [],
6164
- ...options.csv ? ["--csv", options.csv] : [],
6165
6266
  ...options.input ? ["--input", options.input] : [],
6166
6267
  ...options.live ? ["--live"] : [],
6167
6268
  ...options.latest ? ["--latest"] : [],
@@ -6172,7 +6273,8 @@ Examples:
6172
6273
  ...options.pollIntervalMs ? ["--poll-interval-ms", options.pollIntervalMs] : [],
6173
6274
  ...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
6174
6275
  ...options.force ? ["--force"] : [],
6175
- ...options.json ? ["--json"] : []
6276
+ ...options.json ? ["--json"] : [],
6277
+ ...passthroughArgs
6176
6278
  ]);
6177
6279
  });
6178
6280
  play.command("get <target>").description("Fetch full play details.").addHelpText(
@@ -6186,12 +6288,10 @@ Notes:
6186
6288
  Examples:
6187
6289
  deepline plays get person-linkedin-to-email
6188
6290
  deepline plays get person-linkedin-to-email --json | jq '.play.liveRevision'
6291
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
6292
+ deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
6189
6293
  `
6190
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").addOption(
6191
- new Option("--source", "Materialize or print the source code").hideHelp()
6192
- ).addOption(
6193
- new Option("--out <path>", "Write source to a specific path").hideHelp()
6194
- ).action(async (target, options) => {
6294
+ ).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) => {
6195
6295
  process.exitCode = await handlePlayGet([
6196
6296
  target,
6197
6297
  ...options.json ? ["--json"] : [],
@@ -6251,7 +6351,7 @@ Examples:
6251
6351
  ...options.json ? ["--json"] : []
6252
6352
  ]);
6253
6353
  });
6254
- 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) => {
6354
+ 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) => {
6255
6355
  process.exitCode = await handlePlayStatus([
6256
6356
  ...options.runId ? ["--run-id", options.runId] : [],
6257
6357
  ...options.name ? ["--name", options.name] : [],
@@ -6282,6 +6382,13 @@ Examples:
6282
6382
  ...options.json ? ["--json"] : []
6283
6383
  ]);
6284
6384
  });
6385
+ 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) => {
6386
+ process.exitCode = await handlePlayDelete([
6387
+ target,
6388
+ ...options.yes ? ["--yes"] : [],
6389
+ ...options.json ? ["--json"] : []
6390
+ ]);
6391
+ });
6285
6392
  const runs = program.command("runs").description("Inspect and export play runs.").addHelpText(
6286
6393
  "after",
6287
6394
  `
@@ -6291,7 +6398,7 @@ Examples:
6291
6398
  deepline runs logs play/my-play/run/20260501t000000-000
6292
6399
  `
6293
6400
  );
6294
- 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) => {
6401
+ 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) => {
6295
6402
  process.exitCode = await handleRunStatus([
6296
6403
  runId,
6297
6404
  ...options.json ? ["--json"] : [],
@@ -6315,7 +6422,7 @@ Examples:
6315
6422
  }
6316
6423
 
6317
6424
  // src/tool-output.ts
6318
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6425
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
6319
6426
  import { homedir as homedir3 } from "os";
6320
6427
  import { join as join7 } from "path";
6321
6428
  function isPlainObject(value) {
@@ -6393,7 +6500,7 @@ function tryConvertToList(payload, options) {
6393
6500
  }
6394
6501
  function ensureOutputDir() {
6395
6502
  const outputDir = join7(homedir3(), ".local", "share", "deepline", "data");
6396
- mkdirSync4(outputDir, { recursive: true });
6503
+ mkdirSync3(outputDir, { recursive: true });
6397
6504
  return outputDir;
6398
6505
  }
6399
6506
  function writeJsonOutputFile(payload, stem) {
@@ -6646,7 +6753,7 @@ function printToolDetails(tool, requestedToolId) {
6646
6753
  const operation = typeof tool.operation === "string" ? tool.operation : "";
6647
6754
  const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
6648
6755
  const displayName = titleCase(displayBase || String(tool.displayName || toolId));
6649
- const cost = isRecord2(tool.cost) ? tool.cost : null;
6756
+ const cost = isRecord3(tool.cost) ? tool.cost : null;
6650
6757
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
6651
6758
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
6652
6759
  const deeplineUsdPerCredit = numberField(tool, "deeplineUsdPerCredit", "deepline_usd_per_credit");
@@ -6691,7 +6798,7 @@ function printToolDetails(tool, requestedToolId) {
6691
6798
  if (stepContributions.length) {
6692
6799
  console.log(" step contributions:");
6693
6800
  for (const item of stepContributions) {
6694
- if (!isRecord2(item)) continue;
6801
+ if (!isRecord3(item)) continue;
6695
6802
  const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
6696
6803
  const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
6697
6804
  const high = typeof item.highCredits === "number" ? item.highCredits : null;
@@ -6778,12 +6885,12 @@ function printToolCost(input) {
6778
6885
  return false;
6779
6886
  }
6780
6887
  function toolInputFieldsForDisplay(inputSchema) {
6781
- if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord2);
6782
- const jsonSchema = isRecord2(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
6783
- const properties = isRecord2(jsonSchema.properties) ? jsonSchema.properties : {};
6888
+ if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord3);
6889
+ const jsonSchema = isRecord3(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
6890
+ const properties = isRecord3(jsonSchema.properties) ? jsonSchema.properties : {};
6784
6891
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
6785
6892
  return Object.entries(properties).map(([name, value]) => {
6786
- const property = isRecord2(value) ? value : {};
6893
+ const property = isRecord3(value) ? value : {};
6787
6894
  return {
6788
6895
  name,
6789
6896
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -6810,7 +6917,7 @@ function printJsonPreview(label, payload) {
6810
6917
  }
6811
6918
  function samplePayload(samples, key) {
6812
6919
  const entry = samples[key];
6813
- if (!isRecord2(entry)) return void 0;
6920
+ if (!isRecord3(entry)) return void 0;
6814
6921
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
6815
6922
  }
6816
6923
  function isPlayTool(tool) {
@@ -6831,7 +6938,7 @@ function formatDecimal(value) {
6831
6938
  function formatUsd(value) {
6832
6939
  return `$${formatDecimal(value)}`;
6833
6940
  }
6834
- function isRecord2(value) {
6941
+ function isRecord3(value) {
6835
6942
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6836
6943
  }
6837
6944
  function stringField(source, ...keys) {
@@ -6858,7 +6965,7 @@ function arrayField(source, ...keys) {
6858
6965
  function recordField(source, ...keys) {
6859
6966
  for (const key of keys) {
6860
6967
  const value = source[key];
6861
- if (isRecord2(value)) return value;
6968
+ if (isRecord3(value)) return value;
6862
6969
  }
6863
6970
  return {};
6864
6971
  }
@@ -6949,7 +7056,7 @@ async function executeTool(args) {
6949
7056
  }
6950
7057
  throw error;
6951
7058
  }
6952
- const rawResponse = await client.executeToolRaw(parsed.toolId, parsed.params);
7059
+ const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
6953
7060
  const listConversion = tryConvertToList(rawResponse, {
6954
7061
  listExtractorPaths: metadata.listExtractorPaths ?? []
6955
7062
  });
@@ -7005,7 +7112,7 @@ async function executeTool(args) {
7005
7112
 
7006
7113
  // src/cli/skills-sync.ts
7007
7114
  import { spawn } from "child_process";
7008
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync6 } from "fs";
7115
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync6 } from "fs";
7009
7116
  import { homedir as homedir4 } from "os";
7010
7117
  import { dirname as dirname7, join as join8 } from "path";
7011
7118
  var CHECK_TIMEOUT_MS2 = 3e3;
@@ -7031,7 +7138,7 @@ function readLocalSkillsVersion(baseUrl) {
7031
7138
  }
7032
7139
  function writeLocalSkillsVersion(baseUrl, version) {
7033
7140
  const path = sdkSkillsVersionPath(baseUrl);
7034
- mkdirSync5(dirname7(path), { recursive: true });
7141
+ mkdirSync4(dirname7(path), { recursive: true });
7035
7142
  writeFileSync6(path, `${version}
7036
7143
  `, "utf-8");
7037
7144
  }
@@ -7120,6 +7227,54 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
7120
7227
  progress?.writeLine("SDK skills are up to date.") ?? process.stderr.write("SDK skills are up to date.\n");
7121
7228
  }
7122
7229
 
7230
+ // src/cli/trace.ts
7231
+ var cliTraceStartedAt = Date.now();
7232
+ function isTruthyEnv(value) {
7233
+ return value === "1" || value === "true" || value === "yes";
7234
+ }
7235
+ function isCliTraceEnabled() {
7236
+ return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
7237
+ }
7238
+ function recordCliTrace(event) {
7239
+ if (!isCliTraceEnabled()) {
7240
+ return;
7241
+ }
7242
+ const now = Date.now();
7243
+ const payload = {
7244
+ ts: now,
7245
+ source: "cli",
7246
+ sinceStartMs: now - cliTraceStartedAt,
7247
+ ...event
7248
+ };
7249
+ process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}
7250
+ `);
7251
+ }
7252
+ async function traceCliSpan(phase, fields, run) {
7253
+ if (!isCliTraceEnabled()) {
7254
+ return run();
7255
+ }
7256
+ const startedAt = Date.now();
7257
+ try {
7258
+ const result = await run();
7259
+ recordCliTrace({
7260
+ phase,
7261
+ ms: Date.now() - startedAt,
7262
+ ok: true,
7263
+ ...fields
7264
+ });
7265
+ return result;
7266
+ } catch (error) {
7267
+ recordCliTrace({
7268
+ phase,
7269
+ ms: Date.now() - startedAt,
7270
+ ok: false,
7271
+ error: error instanceof Error ? error.message : String(error),
7272
+ ...fields
7273
+ });
7274
+ throw error;
7275
+ }
7276
+ }
7277
+
7123
7278
  // src/cli/index.ts
7124
7279
  function shouldPrintStartupPhase() {
7125
7280
  if (process.argv.includes("--json")) {
@@ -7141,7 +7296,7 @@ async function main() {
7141
7296
  if (printStartupPhase) {
7142
7297
  progress?.phase("loading deepline cli");
7143
7298
  }
7144
- const program = new Command2();
7299
+ const program = new Command();
7145
7300
  program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
7146
7301
  "after",
7147
7302
  `