deepline 0.1.93 → 0.1.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/index.ts
27
- var import_promises6 = require("fs/promises");
28
- var import_node_path18 = require("path");
27
+ var import_promises7 = require("fs/promises");
28
+ var import_node_path19 = require("path");
29
29
  var import_node_os11 = require("os");
30
30
  var import_commander3 = require("commander");
31
31
 
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.93",
232
+ version: "0.1.94",
233
233
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.93",
235
+ latest: "0.1.94",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -584,7 +584,7 @@ function decodeSseFrame(frame) {
584
584
  return parsed;
585
585
  }
586
586
  function sleep(ms) {
587
- return new Promise((resolve14) => setTimeout(resolve14, ms));
587
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
588
588
  }
589
589
 
590
590
  // src/stream-reconnect.ts
@@ -1200,7 +1200,7 @@ async function* observeRunEvents(options) {
1200
1200
  const logPageQuery = convexServer.makeFunctionReference(
1201
1201
  OBSERVER_LOG_PAGE_QUERY
1202
1202
  );
1203
- const client = new convexBrowser.ConvexClient(grant.convexUrl, {
1203
+ const client2 = new convexBrowser.ConvexClient(grant.convexUrl, {
1204
1204
  ...webSocketConstructor ? { webSocketConstructor } : {},
1205
1205
  unsavedChangesWarning: false
1206
1206
  });
@@ -1212,7 +1212,7 @@ async function* observeRunEvents(options) {
1212
1212
  wake = null;
1213
1213
  };
1214
1214
  let lastForcedRefreshAt = 0;
1215
- client.setAuth(async ({ forceRefreshToken }) => {
1215
+ client2.setAuth(async ({ forceRefreshToken }) => {
1216
1216
  const now = Date.now();
1217
1217
  if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
1218
1218
  return grant.token;
@@ -1239,7 +1239,7 @@ async function* observeRunEvents(options) {
1239
1239
  return null;
1240
1240
  }
1241
1241
  });
1242
- const unsubscribe = client.onUpdate(
1242
+ const unsubscribe = client2.onUpdate(
1243
1243
  snapshotQuery,
1244
1244
  { workflowId: runId },
1245
1245
  (run) => push({ kind: "run", run: run ?? null }),
@@ -1253,7 +1253,7 @@ async function* observeRunEvents(options) {
1253
1253
  const watchdog = setInterval(() => {
1254
1254
  const now = Date.now();
1255
1255
  try {
1256
- const connectionState = client.connectionState();
1256
+ const connectionState = client2.connectionState();
1257
1257
  if (connectionState.isWebSocketConnected) {
1258
1258
  disconnectedSince = null;
1259
1259
  warnedReconnecting = false;
@@ -1291,14 +1291,14 @@ async function* observeRunEvents(options) {
1291
1291
  try {
1292
1292
  for (; ; ) {
1293
1293
  if (queue.length === 0) {
1294
- const waitForItem = new Promise((resolve14) => {
1295
- wake = resolve14;
1294
+ const waitForItem = new Promise((resolve15) => {
1295
+ wake = resolve15;
1296
1296
  });
1297
1297
  if (!sawFirstSnapshot) {
1298
1298
  const timedOut = await Promise.race([
1299
1299
  waitForItem.then(() => false),
1300
1300
  new Promise(
1301
- (resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1301
+ (resolve15) => setTimeout(() => resolve15(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1302
1302
  )
1303
1303
  ]);
1304
1304
  if (timedOut && queue.length === 0) {
@@ -1334,7 +1334,7 @@ async function* observeRunEvents(options) {
1334
1334
  const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
1335
1335
  if (gap && diffState.lastLogSeq > 0) {
1336
1336
  const backfilled = await backfillLogGap({
1337
- queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
1337
+ queryLogPage: (afterSeq, limit) => client2.query(logPageQuery, {
1338
1338
  workflowId: runId,
1339
1339
  afterSeq,
1340
1340
  limit
@@ -1384,7 +1384,7 @@ async function* observeRunEvents(options) {
1384
1384
  unsubscribe();
1385
1385
  } catch {
1386
1386
  }
1387
- await client.close().catch(() => void 0);
1387
+ await client2.close().catch(() => void 0);
1388
1388
  }
1389
1389
  }
1390
1390
 
@@ -1395,7 +1395,7 @@ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
1395
1395
  var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
1396
1396
  var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
1397
1397
  function sleep2(ms) {
1398
- return new Promise((resolve14) => setTimeout(resolve14, ms));
1398
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
1399
1399
  }
1400
1400
  function isTransientCompileManifestError(error) {
1401
1401
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
@@ -2298,6 +2298,93 @@ var DeeplineClient = class {
2298
2298
  );
2299
2299
  return response.runs ?? [];
2300
2300
  }
2301
+ // ---------------------------------------------------------------------------
2302
+ // Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
2303
+ // `/api/v2/workflows/*` API so the SDK CLI keeps existing cloud workflows
2304
+ // working while users migrate them to plays via `workflows transform`. Kept
2305
+ // intentionally minimal — workflows are a deprecated surface.
2306
+ // ---------------------------------------------------------------------------
2307
+ /** List the org's workflows. `GET /api/v2/workflows`. */
2308
+ async listWorkflows(options) {
2309
+ const params = new URLSearchParams();
2310
+ if (typeof options?.limit === "number") {
2311
+ params.set("limit", String(options.limit));
2312
+ }
2313
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2314
+ return this.http.get(`/api/v2/workflows${query}`);
2315
+ }
2316
+ /**
2317
+ * Fetch a single workflow (including its published-revision config — the
2318
+ * input to `compileWorkflowConfigToPlay`). `GET /api/v2/workflows/:id`.
2319
+ */
2320
+ async getWorkflow(id) {
2321
+ return this.http.get(`/api/v2/workflows/${encodeURIComponent(id)}`);
2322
+ }
2323
+ /** Delete a workflow. `DELETE /api/v2/workflows/:id`. */
2324
+ async deleteWorkflow(id) {
2325
+ return this.http.delete(`/api/v2/workflows/${encodeURIComponent(id)}`);
2326
+ }
2327
+ /** Turn a workflow off. `POST /api/v2/workflows/:id/disable`. */
2328
+ async disableWorkflow(id) {
2329
+ return this.http.post(
2330
+ `/api/v2/workflows/${encodeURIComponent(id)}/disable`,
2331
+ {}
2332
+ );
2333
+ }
2334
+ /** Turn a workflow back on. `POST /api/v2/workflows/:id/enable`. */
2335
+ async enableWorkflow(id) {
2336
+ return this.http.post(
2337
+ `/api/v2/workflows/${encodeURIComponent(id)}/enable`,
2338
+ {}
2339
+ );
2340
+ }
2341
+ /** Create/update a workflow from config. `POST /api/v2/workflows/apply`. */
2342
+ async applyWorkflow(body) {
2343
+ return this.http.post("/api/v2/workflows/apply", body);
2344
+ }
2345
+ /** Validate a workflow config without saving. `POST /api/v2/workflows/lint`. */
2346
+ async lintWorkflow(body) {
2347
+ return this.http.post("/api/v2/workflows/lint", body);
2348
+ }
2349
+ /** Fetch live workflow request schemas. `GET /api/v2/workflows/schema`. */
2350
+ async getWorkflowSchema(subject) {
2351
+ const params = new URLSearchParams();
2352
+ if (subject) params.set("subject", subject);
2353
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2354
+ return this.http.get(`/api/v2/workflows/schema${query}`);
2355
+ }
2356
+ /** Queue a workflow run. `POST /api/v2/workflows/call`. */
2357
+ async callWorkflow(body) {
2358
+ return this.http.post("/api/v2/workflows/call", body);
2359
+ }
2360
+ /** List a workflow's runs. `GET /api/v2/workflows/:id/runs`. */
2361
+ async listWorkflowRuns(id, options) {
2362
+ const params = new URLSearchParams();
2363
+ if (typeof options?.limit === "number") {
2364
+ params.set("limit", String(options.limit));
2365
+ }
2366
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2367
+ return this.http.get(
2368
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs${query}`
2369
+ );
2370
+ }
2371
+ /** Fetch one workflow run. `GET /api/v2/workflows/:id/runs/:runId`. */
2372
+ async getWorkflowRun(id, runId) {
2373
+ return this.http.get(
2374
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
2375
+ runId
2376
+ )}`
2377
+ );
2378
+ }
2379
+ /** Cancel a workflow run. `POST /api/v2/workflows/:id/runs/:runId/cancel`. */
2380
+ async cancelWorkflowRun(id, runId) {
2381
+ return this.http.post(
2382
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
2383
+ runId
2384
+ )}/cancel`,
2385
+ {}
2386
+ );
2387
+ }
2301
2388
  /**
2302
2389
  * Get a run by id using the public runs resource model.
2303
2390
  *
@@ -3185,6 +3272,9 @@ function openInBrowser(url) {
3185
3272
  } catch {
3186
3273
  }
3187
3274
  }
3275
+ function sleep3(ms) {
3276
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
3277
+ }
3188
3278
  function collectLocalEnvInfo() {
3189
3279
  return {
3190
3280
  os: `${process.platform} ${process.arch}`,
@@ -3222,14 +3312,38 @@ function parseMaybeJsonObject(value) {
3222
3312
  return value;
3223
3313
  }
3224
3314
  }
3315
+ function failureMessageFromRecord(value) {
3316
+ const status = typeof value.status === "string" ? value.status.trim().toLowerCase() : "";
3317
+ const directError = typeof value.error === "string" ? value.error.trim() : typeof value.last_error === "string" ? value.last_error.trim() : "";
3318
+ const result = value.result && typeof value.result === "object" && !Array.isArray(value.result) ? value.result : null;
3319
+ const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
3320
+ if (!directError && !resultError && status !== "error" && status !== "failed") {
3321
+ return null;
3322
+ }
3323
+ return directError || resultError || `Column status: ${status}`;
3324
+ }
3225
3325
  function flattenObjectColumns(row) {
3226
3326
  const flattened = {};
3227
3327
  for (const [key, rawValue] of Object.entries(row)) {
3228
3328
  const value = parseMaybeJsonObject(rawValue);
3329
+ if (key === "_metadata") {
3330
+ flattened[key] = value && typeof value === "object" ? JSON.stringify(value) : value;
3331
+ continue;
3332
+ }
3229
3333
  if (value && typeof value === "object" && !Array.isArray(value)) {
3230
- for (const [nestedKey, nestedValue] of Object.entries(
3231
- value
3232
- )) {
3334
+ const record = value;
3335
+ const hasMatchedEnvelope = Object.prototype.hasOwnProperty.call(record, "matched_result") || Object.prototype.hasOwnProperty.call(record, "matchedResult");
3336
+ if (hasMatchedEnvelope) {
3337
+ flattened[key] = JSON.stringify(record);
3338
+ } else {
3339
+ const failureMessage = failureMessageFromRecord(record);
3340
+ if (failureMessage) {
3341
+ flattened[key] = failureMessage;
3342
+ } else if (Object.prototype.hasOwnProperty.call(record, "result")) {
3343
+ flattened[key] = JSON.stringify(record);
3344
+ }
3345
+ }
3346
+ for (const [nestedKey, nestedValue] of Object.entries(record)) {
3233
3347
  flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
3234
3348
  }
3235
3349
  continue;
@@ -3485,8 +3599,8 @@ function buildCandidateUrls2(url) {
3485
3599
  return [url];
3486
3600
  }
3487
3601
  }
3488
- function sleep3(ms) {
3489
- return new Promise((resolve14) => setTimeout(resolve14, ms));
3602
+ function sleep4(ms) {
3603
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
3490
3604
  }
3491
3605
  function printDeeplineLogo() {
3492
3606
  if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
@@ -3589,7 +3703,7 @@ async function handleRegister(args) {
3589
3703
  return EXIT_AUTH;
3590
3704
  }
3591
3705
  if (s >= 500 || s === 0 || s === 400) {
3592
- await sleep3(2e3);
3706
+ await sleep4(2e3);
3593
3707
  continue;
3594
3708
  }
3595
3709
  if (s >= 400) {
@@ -3619,7 +3733,7 @@ async function handleRegister(args) {
3619
3733
  );
3620
3734
  return EXIT_AUTH;
3621
3735
  }
3622
- await sleep3(2e3);
3736
+ await sleep4(2e3);
3623
3737
  }
3624
3738
  }
3625
3739
  async function handleWait(args) {
@@ -3656,7 +3770,7 @@ async function handleWait(args) {
3656
3770
  return EXIT_AUTH;
3657
3771
  }
3658
3772
  if (status >= 500 || status === 0 || status === 400) {
3659
- await sleep3(2e3);
3773
+ await sleep4(2e3);
3660
3774
  continue;
3661
3775
  }
3662
3776
  if (status >= 400) {
@@ -3684,7 +3798,7 @@ async function handleWait(args) {
3684
3798
  console.error("That approval link expired. Run: deepline auth register");
3685
3799
  return EXIT_AUTH;
3686
3800
  }
3687
- await sleep3(2e3);
3801
+ await sleep4(2e3);
3688
3802
  }
3689
3803
  console.error(
3690
3804
  "Still pending. Approve the browser link, then run: deepline auth wait"
@@ -4500,6 +4614,9 @@ function isRecord3(value) {
4500
4614
  function isSerializedDataset(value) {
4501
4615
  return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
4502
4616
  }
4617
+ function isPackagedDatasetOutput(value) {
4618
+ return isRecord3(value) && value.kind === "dataset" && isRecord3(value.preview) && Array.isArray(value.preview.rows);
4619
+ }
4503
4620
  function pathParts(path) {
4504
4621
  return path.split(".").map((part) => part.trim()).filter(Boolean);
4505
4622
  }
@@ -4547,6 +4664,12 @@ function inferColumns(rows) {
4547
4664
  }
4548
4665
  return columns;
4549
4666
  }
4667
+ function columnsFromDatasetSummary(summary) {
4668
+ if (!isRecord3(summary) || !isRecord3(summary.columnStats)) {
4669
+ return [];
4670
+ }
4671
+ return Object.keys(summary.columnStats).filter((column) => column);
4672
+ }
4550
4673
  function canonicalRowsInfoFromCandidate(input2) {
4551
4674
  const candidate = input2;
4552
4675
  if (isSerializedDataset(candidate.value)) {
@@ -4569,6 +4692,28 @@ function canonicalRowsInfoFromCandidate(input2) {
4569
4692
  tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
4570
4693
  };
4571
4694
  }
4695
+ if (isPackagedDatasetOutput(candidate.value)) {
4696
+ const rawRows = rowArray(candidate.value.preview?.rows) ?? [];
4697
+ const totalRows2 = readNumber(candidate.value.preview?.totalRows) ?? readNumber(candidate.value.rowCount) ?? rawRows.length;
4698
+ const explicitColumns = Array.isArray(candidate.value.columns) ? candidate.value.columns.filter(
4699
+ (column) => typeof column === "string"
4700
+ ) : [];
4701
+ const rawColumns = explicitColumns.length > 0 ? explicitColumns : columnsFromDatasetSummary(candidate.value.summary);
4702
+ const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
4703
+ rows: rawRows,
4704
+ columns: rawColumns.length > 0 ? rawColumns : inferColumns(rawRows)
4705
+ });
4706
+ return {
4707
+ rows: rows2,
4708
+ totalRows: totalRows2,
4709
+ columns,
4710
+ columnsExplicit: rawColumns.length > 0,
4711
+ complete: rows2.length === totalRows2,
4712
+ source: candidate.source,
4713
+ datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
4714
+ tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
4715
+ };
4716
+ }
4572
4717
  if (candidate.serializedOnly) {
4573
4718
  return null;
4574
4719
  }
@@ -4601,6 +4746,14 @@ function collectDatasetCandidates(input2) {
4601
4746
  });
4602
4747
  return;
4603
4748
  }
4749
+ if (isPackagedDatasetOutput(input2.value)) {
4750
+ input2.output.push({
4751
+ source: input2.path,
4752
+ value: input2.value,
4753
+ total: input2.total
4754
+ });
4755
+ return;
4756
+ }
4604
4757
  if (!isRecord3(input2.value)) {
4605
4758
  return;
4606
4759
  }
@@ -4673,6 +4826,19 @@ function collectCanonicalRowsInfos(statusOrResult) {
4673
4826
  }
4674
4827
  );
4675
4828
  }
4829
+ if (Array.isArray(result.steps)) {
4830
+ result.steps.forEach((step, index) => {
4831
+ if (!isRecord3(step) || !isRecord3(step.output)) {
4832
+ return;
4833
+ }
4834
+ const source = typeof step.output.path === "string" ? step.output.path : typeof step.id === "string" ? `steps.${step.id}.output` : `steps.${index}.output`;
4835
+ candidates.push({
4836
+ source,
4837
+ value: step.output,
4838
+ total: step.output.rowCount ?? (isRecord3(step.output.preview) ? step.output.preview.totalRows : void 0) ?? (isRecord3(step.progress) ? step.progress.total : void 0)
4839
+ });
4840
+ });
4841
+ }
4676
4842
  collectDatasetCandidates({
4677
4843
  value: result,
4678
4844
  path: "result",
@@ -4682,12 +4848,14 @@ function collectCanonicalRowsInfos(statusOrResult) {
4682
4848
  const seen = /* @__PURE__ */ new Set();
4683
4849
  const infos = [];
4684
4850
  for (const candidate of candidates) {
4685
- if (seen.has(candidate.source)) {
4686
- continue;
4687
- }
4688
- seen.add(candidate.source);
4689
4851
  const info = canonicalRowsInfoFromCandidate(candidate);
4690
4852
  if (info) {
4853
+ if (info.source) {
4854
+ if (seen.has(info.source)) {
4855
+ continue;
4856
+ }
4857
+ seen.add(info.source);
4858
+ }
4691
4859
  infos.push(info);
4692
4860
  }
4693
4861
  }
@@ -4708,15 +4876,17 @@ function collectSerializedDatasetRowsInfos(statusOrResult) {
4708
4876
  const seen = /* @__PURE__ */ new Set();
4709
4877
  const infos = [];
4710
4878
  for (const candidate of candidates) {
4711
- if (seen.has(candidate.source)) {
4712
- continue;
4713
- }
4714
- seen.add(candidate.source);
4715
4879
  const info = canonicalRowsInfoFromCandidate({
4716
4880
  ...candidate,
4717
4881
  serializedOnly: true
4718
4882
  });
4719
4883
  if (info) {
4884
+ if (info.source) {
4885
+ if (seen.has(info.source)) {
4886
+ continue;
4887
+ }
4888
+ seen.add(info.source);
4889
+ }
4720
4890
  infos.push(info);
4721
4891
  }
4722
4892
  }
@@ -5226,10 +5396,10 @@ async function handleDbQuery(args) {
5226
5396
  }
5227
5397
  const jsonOutput = argsWantJson(args);
5228
5398
  const explicitJsonOutput = args.includes("--json");
5229
- const client = new DeeplineClient();
5399
+ const client2 = new DeeplineClient();
5230
5400
  let result;
5231
5401
  try {
5232
- result = await client.queryCustomerDb({ sql, maxRows });
5402
+ result = await client2.queryCustomerDb({ sql, maxRows });
5233
5403
  } catch (error) {
5234
5404
  console.error(formatDbQueryError(sql, error));
5235
5405
  return 1;
@@ -5472,23 +5642,28 @@ function shannonEntropy(value) {
5472
5642
  return entropy - p * Math.log2(p);
5473
5643
  }, 0);
5474
5644
  }
5475
- function validatePlaySourceHasNoInlineSecrets(input2) {
5645
+ function collectInlineSecretFindings(sourceCode) {
5476
5646
  const findings = [];
5477
- for (const match of input2.sourceCode.matchAll(SECRET_ENV_PATTERN)) {
5647
+ for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
5478
5648
  findings.push(`process.env.${match[1]}`);
5479
5649
  }
5480
- if (PRIVATE_KEY_PATTERN.test(input2.sourceCode)) findings.push("private key block");
5481
- if (BEARER_LITERAL_PATTERN.test(input2.sourceCode)) findings.push("bearer token literal");
5482
- if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(input2.sourceCode)) {
5650
+ if (PRIVATE_KEY_PATTERN.test(sourceCode)) findings.push("private key block");
5651
+ if (BEARER_LITERAL_PATTERN.test(sourceCode))
5652
+ findings.push("bearer token literal");
5653
+ if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
5483
5654
  findings.push("secret-looking assignment literal");
5484
5655
  }
5485
- for (const match of input2.sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
5656
+ for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
5486
5657
  const literal = match[1] ?? "";
5487
5658
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
5488
5659
  findings.push("high-entropy string literal");
5489
5660
  break;
5490
5661
  }
5491
5662
  }
5663
+ return [...new Set(findings)];
5664
+ }
5665
+ function validatePlaySourceHasNoInlineSecrets(input2) {
5666
+ const findings = collectInlineSecretFindings(input2.sourceCode);
5492
5667
  if (!findings.length) return;
5493
5668
  throw new Error(
5494
5669
  [
@@ -8429,13 +8604,13 @@ export default definePlay(${jsString(input2.options.name)}, async (ctx, input: I
8429
8604
 
8430
8605
  `;
8431
8606
  }
8432
- async function describePlayMaybe(client, playRef) {
8433
- return playRef ? client.describePlay(playRef, { compact: true }) : null;
8607
+ async function describePlayMaybe(client2, playRef) {
8608
+ return playRef ? client2.describePlay(playRef, { compact: true }) : null;
8434
8609
  }
8435
- function loadTools(client, providers) {
8436
- return Promise.all(providers.map((provider) => client.getTool(provider)));
8610
+ function loadTools(client2, providers) {
8611
+ return Promise.all(providers.map((provider) => client2.getTool(provider)));
8437
8612
  }
8438
- async function loadBootstrapContracts(client, options) {
8613
+ async function loadBootstrapContracts(client2, options) {
8439
8614
  const [
8440
8615
  sourceTools,
8441
8616
  sourcePlay,
@@ -8445,13 +8620,13 @@ async function loadBootstrapContracts(client, options) {
8445
8620
  emailTools,
8446
8621
  phoneTools
8447
8622
  ] = await Promise.all([
8448
- loadTools(client, sourceProviders(options)),
8449
- describePlayMaybe(client, sourcePlayRef(options)),
8450
- describePlayMaybe(client, stagePlayRef(options.people)),
8451
- describePlayMaybe(client, stagePlayRef(options.email)),
8452
- describePlayMaybe(client, stagePlayRef(options.phone)),
8453
- loadTools(client, stageProviders(options.email)),
8454
- loadTools(client, stageProviders(options.phone))
8623
+ loadTools(client2, sourceProviders(options)),
8624
+ describePlayMaybe(client2, sourcePlayRef(options)),
8625
+ describePlayMaybe(client2, stagePlayRef(options.people)),
8626
+ describePlayMaybe(client2, stagePlayRef(options.email)),
8627
+ describePlayMaybe(client2, stagePlayRef(options.phone)),
8628
+ loadTools(client2, stageProviders(options.email)),
8629
+ loadTools(client2, stageProviders(options.phone))
8455
8630
  ]);
8456
8631
  const contracts = {
8457
8632
  sourceTools,
@@ -8500,8 +8675,8 @@ function renderPlayBootstrapError(error) {
8500
8675
  }
8501
8676
  async function runPlayBootstrap(args) {
8502
8677
  const options = parsePlayBootstrapOptions(args);
8503
- const client = new DeeplineClient();
8504
- const contracts = await loadBootstrapContracts(client, options);
8678
+ const client2 = new DeeplineClient();
8679
+ const contracts = await loadBootstrapContracts(client2, options);
8505
8680
  const csvContext = loadCsvContext(options.from);
8506
8681
  const source = generateBootstrapPlaySource({
8507
8682
  options,
@@ -8907,8 +9082,8 @@ function traceCliSync(phase, fields, run) {
8907
9082
  throw error;
8908
9083
  }
8909
9084
  }
8910
- function sleep4(ms) {
8911
- return new Promise((resolve14) => setTimeout(resolve14, ms));
9085
+ function sleep5(ms) {
9086
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
8912
9087
  }
8913
9088
  function parseReferencedPlayTarget2(target) {
8914
9089
  const trimmed = target.trim();
@@ -8930,9 +9105,9 @@ function buildBarePrebuiltReferenceError(input2) {
8930
9105
  `Prebuilt play "${input2.requested}" must be referenced as "${input2.reference}". Use the prebuilt/ namespace anywhere you run, describe, get, or link to Deepline-managed plays.`
8931
9106
  );
8932
9107
  }
8933
- async function assertCanonicalNamedPlayReference(client, target) {
9108
+ async function assertCanonicalNamedPlayReference(client2, target) {
8934
9109
  const parsed = parseReferencedPlayTarget2(target);
8935
- const detail = await client.getPlay(parsed.playName);
9110
+ const detail = await client2.getPlay(parsed.playName);
8936
9111
  if (detail.play.ownerType === "deepline" && !isPrebuiltReferenceTarget(target)) {
8937
9112
  throw buildBarePrebuiltReferenceError({
8938
9113
  requested: target,
@@ -9010,9 +9185,9 @@ To make your own version:
9010
9185
  5. Your play will then live under your workspace namespace.`
9011
9186
  );
9012
9187
  }
9013
- async function ensureEditableRemotePlay(client, target) {
9188
+ async function ensureEditableRemotePlay(client2, target) {
9014
9189
  const parsed = parseReferencedPlayTarget2(target);
9015
- const detail = await client.getPlay(parsed.playName);
9190
+ const detail = await client2.getPlay(parsed.playName);
9016
9191
  if (detail.play.ownerType === "deepline") {
9017
9192
  throw buildReadonlyPrebuiltPlayError(formatPlayReference(detail.play));
9018
9193
  }
@@ -9291,7 +9466,7 @@ async function collectBundledPlayGraph(entryFile, profile = null) {
9291
9466
  const root = await visit(entryFile);
9292
9467
  return { root, nodes };
9293
9468
  }
9294
- async function compileBundledPlayGraphManifests(client, graph) {
9469
+ async function compileBundledPlayGraphManifests(client2, graph) {
9295
9470
  const compiling = /* @__PURE__ */ new Map();
9296
9471
  const compileNode = (node) => {
9297
9472
  const existing = compiling.get(node.filePath);
@@ -9307,7 +9482,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
9307
9482
  await compileNode(child);
9308
9483
  }
9309
9484
  const name = node.playName ?? extractPlayName(node.sourceCode, node.filePath);
9310
- node.compilerManifest = await client.compilePlayManifest({
9485
+ node.compilerManifest = await client2.compilePlayManifest({
9311
9486
  name,
9312
9487
  sourceCode: node.sourceCode,
9313
9488
  sourceFiles: node.sourceFiles,
@@ -9341,7 +9516,7 @@ function requireCompilerManifest(node) {
9341
9516
  }
9342
9517
  return node.compilerManifest;
9343
9518
  }
9344
- async function publishImportedPlayDependencies(client, graph) {
9519
+ async function publishImportedPlayDependencies(client2, graph) {
9345
9520
  const published = /* @__PURE__ */ new Set();
9346
9521
  const publishNode = async (filePath, skipPublish) => {
9347
9522
  const absolutePath = normalizePlayPath(filePath);
@@ -9360,7 +9535,7 @@ async function publishImportedPlayDependencies(client, graph) {
9360
9535
  `Imported play ${absolutePath} must export definePlay(...) so it can be published for runtime composition.`
9361
9536
  );
9362
9537
  }
9363
- await client.registerPlayArtifact({
9538
+ await client2.registerPlayArtifact({
9364
9539
  name: node.playName,
9365
9540
  sourceCode: node.sourceCode,
9366
9541
  sourceFiles: node.sourceFiles,
@@ -9936,7 +10111,7 @@ async function waitForPlayCompletionByStream(input2) {
9936
10111
  reason
9937
10112
  });
9938
10113
  const remainingBeforeSleep = remainingWaitMs();
9939
- await sleep4(
10114
+ await sleep5(
9940
10115
  remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
9941
10116
  );
9942
10117
  }
@@ -9961,7 +10136,7 @@ async function startAndWaitForPlayCompletionByStream(input2) {
9961
10136
  attempt: attempt + 1,
9962
10137
  reason: playStatusErrorText(status)
9963
10138
  });
9964
- await sleep4(retryDelayMs);
10139
+ await sleep5(retryDelayMs);
9965
10140
  }
9966
10141
  throw new DeeplineError(
9967
10142
  `Play ${input2.playName} did not start after retrying transient start failures.`,
@@ -11010,7 +11185,7 @@ async function resolvePlayRunOutputStatus(input2) {
11010
11185
  full: input2.fullJson
11011
11186
  });
11012
11187
  for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
11013
- await sleep4(250);
11188
+ await sleep5(250);
11014
11189
  refreshedStatus = await input2.client.getPlayStatus(runId, {
11015
11190
  billing: false,
11016
11191
  full: input2.fullJson
@@ -11133,7 +11308,7 @@ async function fetchBackingDatasetRows(input2) {
11133
11308
  source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
11134
11309
  };
11135
11310
  }
11136
- async function exportPlayStatusRows(client, status, outPath, options = {}) {
11311
+ async function exportPlayStatusRows(client2, status, outPath, options = {}) {
11137
11312
  if (!outPath) {
11138
11313
  return null;
11139
11314
  }
@@ -11168,7 +11343,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
11168
11343
  let fetchedRowsInfo = null;
11169
11344
  try {
11170
11345
  fetchedRowsInfo = await fetchBackingDatasetRows({
11171
- client,
11346
+ client: client2,
11172
11347
  status,
11173
11348
  rowsInfo
11174
11349
  });
@@ -11187,7 +11362,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
11187
11362
  return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
11188
11363
  }
11189
11364
  if (attempt < attempts && retryDelayMs > 0) {
11190
- await sleep4(retryDelayMs);
11365
+ await sleep5(retryDelayMs);
11191
11366
  }
11192
11367
  }
11193
11368
  if (!rowsInfo.complete) {
@@ -11705,12 +11880,12 @@ function toolGetterHintFromMetadata(toolId, tool) {
11705
11880
  raw: checkHintExpression(toolResponse.raw) || "result.toolResponse.raw"
11706
11881
  };
11707
11882
  }
11708
- async function buildToolGetterHints(client, staticPipeline) {
11883
+ async function buildToolGetterHints(client2, staticPipeline) {
11709
11884
  const toolIds = collectStaticPipelineToolIds(staticPipeline);
11710
11885
  return Promise.all(
11711
11886
  toolIds.map(async (toolId) => {
11712
11887
  try {
11713
- const tool = await client.getTool(toolId);
11888
+ const tool = await client2.getTool(toolId);
11714
11889
  return toolGetterHintFromMetadata(toolId, tool);
11715
11890
  } catch (error) {
11716
11891
  return {
@@ -11750,10 +11925,10 @@ function printToolGetterHints(hints) {
11750
11925
  async function handlePlayCheck(args) {
11751
11926
  const options = parsePlayCheckOptions(args);
11752
11927
  if (!isFileTarget(options.target)) {
11753
- const client2 = new DeeplineClient();
11928
+ const client3 = new DeeplineClient();
11754
11929
  try {
11755
- await assertCanonicalNamedPlayReference(client2, options.target);
11756
- const play = await client2.describePlay(
11930
+ await assertCanonicalNamedPlayReference(client3, options.target);
11931
+ const play = await client3.describePlay(
11757
11932
  parseReferencedPlayTarget2(options.target).playName,
11758
11933
  { compact: true }
11759
11934
  );
@@ -11835,8 +12010,8 @@ async function handlePlayCheck(args) {
11835
12010
  }
11836
12011
  return 0;
11837
12012
  }
11838
- const client = new DeeplineClient();
11839
- const result = await client.checkPlayArtifact({
12013
+ const client2 = new DeeplineClient();
12014
+ const result = await client2.checkPlayArtifact({
11840
12015
  name: playName,
11841
12016
  sourceCode: graph.root.sourceCode,
11842
12017
  sourceFiles: graph.root.sourceFiles,
@@ -11848,7 +12023,7 @@ async function handlePlayCheck(args) {
11848
12023
  errors: result.errors,
11849
12024
  sourceCode: graph.root.sourceCode
11850
12025
  }),
11851
- toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
12026
+ toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client2, result.staticPipeline)
11852
12027
  };
11853
12028
  if (options.jsonOutput) {
11854
12029
  process.stdout.write(
@@ -11874,7 +12049,7 @@ async function handleFileBackedRun(options) {
11874
12049
  if (options.target.kind !== "file") {
11875
12050
  throw new Error("Expected a file-backed play run target.");
11876
12051
  }
11877
- const client = new DeeplineClient();
12052
+ const client2 = new DeeplineClient();
11878
12053
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
11879
12054
  const absolutePlayPath = (0, import_node_path12.resolve)(options.target.path);
11880
12055
  progress.phase("compiling play");
@@ -11894,7 +12069,7 @@ async function handleFileBackedRun(options) {
11894
12069
  await traceCliSpan(
11895
12070
  "cli.play_file_compile_manifests",
11896
12071
  { targetKind: "file" },
11897
- () => compileBundledPlayGraphManifests(client, graph)
12072
+ () => compileBundledPlayGraphManifests(client2, graph)
11898
12073
  );
11899
12074
  progress.phase("compiled play");
11900
12075
  } catch (error) {
@@ -11912,7 +12087,7 @@ async function handleFileBackedRun(options) {
11912
12087
  await traceCliSpan(
11913
12088
  "cli.play_file_publish_imports",
11914
12089
  { targetKind: "file" },
11915
- () => publishImportedPlayDependencies(client, graph)
12090
+ () => publishImportedPlayDependencies(client2, graph)
11916
12091
  );
11917
12092
  } catch (error) {
11918
12093
  progress.fail();
@@ -11933,7 +12108,7 @@ async function handleFileBackedRun(options) {
11933
12108
  bindingCount: fileInputBindings.length
11934
12109
  },
11935
12110
  () => stageFileInputArgs({
11936
- client,
12111
+ client: client2,
11937
12112
  runtimeInput,
11938
12113
  bindings: fileInputBindings,
11939
12114
  progress
@@ -11958,7 +12133,7 @@ async function handleFileBackedRun(options) {
11958
12133
  "cli.play_start_watch",
11959
12134
  { targetKind: "file", playName },
11960
12135
  () => startAndWaitForPlayCompletionByStream({
11961
- client,
12136
+ client: client2,
11962
12137
  request: startRequest,
11963
12138
  playName,
11964
12139
  jsonOutput: options.jsonOutput,
@@ -11977,7 +12152,7 @@ async function handleFileBackedRun(options) {
11977
12152
  }
11978
12153
  const outputStatus = withTerminalPlayIdentity(
11979
12154
  await resolvePlayRunOutputStatus({
11980
- client,
12155
+ client: client2,
11981
12156
  status: finalStatus,
11982
12157
  fullJson: options.fullJson,
11983
12158
  jsonOutput: options.jsonOutput
@@ -11997,11 +12172,11 @@ async function handleFileBackedRun(options) {
11997
12172
  const started = await traceCliSpan(
11998
12173
  "cli.play_start_unwatched",
11999
12174
  { targetKind: "file", playName },
12000
- () => client.startPlayRun(startRequest).catch((error) => {
12175
+ () => client2.startPlayRun(startRequest).catch((error) => {
12001
12176
  throw normalizePlayStartError(error, playName);
12002
12177
  })
12003
12178
  );
12004
- const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
12179
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
12005
12180
  openPlayDashboard({
12006
12181
  dashboardUrl: resolvedDashboardUrl,
12007
12182
  noOpen: options.noOpen
@@ -12037,7 +12212,7 @@ async function handleNamedRun(options) {
12037
12212
  if (options.target.kind !== "name") {
12038
12213
  throw new Error("Expected a named play run target.");
12039
12214
  }
12040
- const client = new DeeplineClient();
12215
+ const client2 = new DeeplineClient();
12041
12216
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
12042
12217
  const playName = options.target.name;
12043
12218
  const runtimeInput = options.input ? { ...options.input } : {};
@@ -12050,7 +12225,7 @@ async function handleNamedRun(options) {
12050
12225
  return traceCliSpan(
12051
12226
  "cli.play_load_definition",
12052
12227
  { targetKind: "name", playName, skipped: false },
12053
- () => assertCanonicalNamedPlayReference(client, playName)
12228
+ () => assertCanonicalNamedPlayReference(client2, playName)
12054
12229
  );
12055
12230
  })() : (recordCliTrace({
12056
12231
  phase: "cli.play_load_definition",
@@ -12070,7 +12245,7 @@ async function handleNamedRun(options) {
12070
12245
  hasExplicitRevisionId: Boolean(options.revisionId)
12071
12246
  },
12072
12247
  () => resolveNamedRunRevisionId({
12073
- client,
12248
+ client: client2,
12074
12249
  playName,
12075
12250
  revisionId: options.revisionId,
12076
12251
  selector: options.revisionSelector
@@ -12088,7 +12263,7 @@ async function handleNamedRun(options) {
12088
12263
  bindingCount: fileInputBindings.length
12089
12264
  },
12090
12265
  () => stageFileInputArgs({
12091
- client,
12266
+ client: client2,
12092
12267
  runtimeInput,
12093
12268
  bindings: fileInputBindings,
12094
12269
  progress
@@ -12109,7 +12284,7 @@ async function handleNamedRun(options) {
12109
12284
  "cli.play_start_watch",
12110
12285
  { targetKind: "name", playName },
12111
12286
  () => startAndWaitForPlayCompletionByStream({
12112
- client,
12287
+ client: client2,
12113
12288
  request: startRequest,
12114
12289
  playName,
12115
12290
  jsonOutput: options.jsonOutput,
@@ -12128,7 +12303,7 @@ async function handleNamedRun(options) {
12128
12303
  }
12129
12304
  const outputStatus = withTerminalPlayIdentity(
12130
12305
  await resolvePlayRunOutputStatus({
12131
- client,
12306
+ client: client2,
12132
12307
  status: finalStatus,
12133
12308
  fullJson: options.fullJson,
12134
12309
  jsonOutput: options.jsonOutput
@@ -12148,11 +12323,11 @@ async function handleNamedRun(options) {
12148
12323
  const started = await traceCliSpan(
12149
12324
  "cli.play_start_unwatched",
12150
12325
  { targetKind: "name", playName },
12151
- () => client.startPlayRun(startRequest).catch((error) => {
12326
+ () => client2.startPlayRun(startRequest).catch((error) => {
12152
12327
  throw normalizePlayStartError(error, playName);
12153
12328
  })
12154
12329
  );
12155
- const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
12330
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
12156
12331
  openPlayDashboard({
12157
12332
  dashboardUrl: resolvedDashboardUrl,
12158
12333
  noOpen: options.noOpen
@@ -12226,8 +12401,8 @@ async function handleRunGet(args) {
12226
12401
  console.error(error instanceof Error ? error.message : usage);
12227
12402
  return 1;
12228
12403
  }
12229
- const client = new DeeplineClient();
12230
- const status = await client.runs.get(runId, {
12404
+ const client2 = new DeeplineClient();
12405
+ const status = await client2.runs.get(runId, {
12231
12406
  full: args.includes("--full")
12232
12407
  });
12233
12408
  writePlayResult(status, argsWantJson(args), {
@@ -12257,8 +12432,8 @@ async function handleRunsList(args) {
12257
12432
  console.error(usage);
12258
12433
  return 1;
12259
12434
  }
12260
- const client = new DeeplineClient();
12261
- const runs = (await client.runs.list({
12435
+ const client2 = new DeeplineClient();
12436
+ const runs = (await client2.runs.list({
12262
12437
  play: playName,
12263
12438
  ...statusFilter ? { status: statusFilter } : {}
12264
12439
  })).map((run) => ({
@@ -12311,9 +12486,9 @@ async function handleRunTail(args) {
12311
12486
  return 1;
12312
12487
  }
12313
12488
  }
12314
- const client = new DeeplineClient();
12489
+ const client2 = new DeeplineClient();
12315
12490
  const jsonOutput = argsWantJson(args);
12316
- const status = await client.runs.tail(runId, {
12491
+ const status = await client2.runs.tail(runId, {
12317
12492
  // Human mode only: in --json mode emit nothing non-protocol.
12318
12493
  onReconnect: jsonOutput ? void 0 : ({ reason }) => {
12319
12494
  process.stderr.write(
@@ -12346,9 +12521,9 @@ async function handleRunLogs(args) {
12346
12521
  outPath = (0, import_node_path12.resolve)(args[++index]);
12347
12522
  }
12348
12523
  }
12349
- const client = new DeeplineClient();
12524
+ const client2 = new DeeplineClient();
12350
12525
  if (outPath) {
12351
- const result2 = await client.runs.logs(runId, { all: true });
12526
+ const result2 = await client2.runs.logs(runId, { all: true });
12352
12527
  const logs = result2.entries;
12353
12528
  (0, import_node_fs10.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
12354
12529
  printCommandEnvelope(
@@ -12377,7 +12552,7 @@ async function handleRunLogs(args) {
12377
12552
  );
12378
12553
  return 0;
12379
12554
  }
12380
- const result = await client.runs.logs(runId, { limit });
12555
+ const result = await client2.runs.logs(runId, { limit });
12381
12556
  printCommandEnvelope(
12382
12557
  {
12383
12558
  runId: result.runId,
@@ -12417,8 +12592,8 @@ async function handleRunStop(args) {
12417
12592
  reason = args[++index];
12418
12593
  }
12419
12594
  }
12420
- const client = new DeeplineClient();
12421
- const result = await client.runs.stop(runId, { reason });
12595
+ const client2 = new DeeplineClient();
12596
+ const result = await client2.runs.stop(runId, { reason });
12422
12597
  const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
12423
12598
  if (stopNotConfirmed) {
12424
12599
  const detail = typeof result.error === "string" && result.error.trim() ? result.error.trim() : result.staleSchedulerState === true ? "scheduler state for the run is stale" : "the server did not confirm the stop";
@@ -12490,9 +12665,9 @@ async function handleRunExport(args) {
12490
12665
  console.error(usage);
12491
12666
  return 1;
12492
12667
  }
12493
- const client = new DeeplineClient();
12494
- const status = await client.getPlayStatus(runId, { full: true });
12495
- const exportResult = await exportPlayStatusRows(client, status, outPath, {
12668
+ const client2 = new DeeplineClient();
12669
+ const status = await client2.getPlayStatus(runId, { full: true });
12670
+ const exportResult = await exportPlayStatusRows(client2, status, outPath, {
12496
12671
  datasetPath
12497
12672
  });
12498
12673
  const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
@@ -12545,7 +12720,7 @@ async function handlePlayGet(args) {
12545
12720
  );
12546
12721
  return 2;
12547
12722
  }
12548
- const client = new DeeplineClient();
12723
+ const client2 = new DeeplineClient();
12549
12724
  const explicitJson = args.includes("--json");
12550
12725
  const sourceOutput = args.includes("--source");
12551
12726
  const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
@@ -12557,7 +12732,7 @@ async function handlePlayGet(args) {
12557
12732
  }
12558
12733
  }
12559
12734
  const playName = isFileTarget(target) ? extractPlayName((0, import_node_fs10.readFileSync)((0, import_node_path12.resolve)(target), "utf-8"), (0, import_node_path12.resolve)(target)) : parseReferencedPlayTarget2(target).playName;
12560
- const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
12735
+ const detail = isFileTarget(target) ? await client2.getPlay(playName) : await assertCanonicalNamedPlayReference(client2, target);
12561
12736
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
12562
12737
  const materializedFile = outPath ? materializeRemotePlaySource({
12563
12738
  target,
@@ -12637,10 +12812,10 @@ async function handlePlayVersions(args) {
12637
12812
  console.error("Usage: deepline play versions --name <name> [--json]");
12638
12813
  return 1;
12639
12814
  }
12640
- const client = new DeeplineClient();
12815
+ const client2 = new DeeplineClient();
12641
12816
  const jsonOutput = argsWantJson(args);
12642
- await assertCanonicalNamedPlayReference(client, playName);
12643
- const versions = await client.listPlayVersions(
12817
+ await assertCanonicalNamedPlayReference(client2, playName);
12818
+ const versions = await client2.listPlayVersions(
12644
12819
  parseReferencedPlayTarget2(playName).playName
12645
12820
  );
12646
12821
  if (jsonOutput) {
@@ -12659,8 +12834,8 @@ async function handlePlayVersions(args) {
12659
12834
  }
12660
12835
  async function handlePlayList(args) {
12661
12836
  const jsonOutput = argsWantJson(args);
12662
- const client = new DeeplineClient();
12663
- const plays = await client.listPlays();
12837
+ const client2 = new DeeplineClient();
12838
+ const plays = await client2.listPlays();
12664
12839
  if (jsonOutput) {
12665
12840
  process.stdout.write(`${JSON.stringify(plays)}
12666
12841
  `);
@@ -12843,8 +13018,8 @@ async function handlePlaySearch(args) {
12843
13018
  console.error(error instanceof Error ? error.message : String(error));
12844
13019
  return 1;
12845
13020
  }
12846
- const client = new DeeplineClient();
12847
- const plays = await client.searchPlays({
13021
+ const client2 = new DeeplineClient();
13022
+ const plays = await client2.searchPlays({
12848
13023
  query: options.query,
12849
13024
  compact: options.compact,
12850
13025
  scope: options.scope
@@ -12936,8 +13111,8 @@ async function handlePlayGrep(args) {
12936
13111
  }
12937
13112
  }
12938
13113
  const compact = args.includes("--compact");
12939
- const client = new DeeplineClient();
12940
- const plays = (await client.listPlays({
13114
+ const client2 = new DeeplineClient();
13115
+ const plays = (await client2.listPlays({
12941
13116
  grep: query,
12942
13117
  grepMode: mode
12943
13118
  })).filter(
@@ -12994,9 +13169,9 @@ async function handlePlayDescribe(args) {
12994
13169
  );
12995
13170
  return 2;
12996
13171
  }
12997
- const client = new DeeplineClient();
12998
- await assertCanonicalNamedPlayReference(client, playName);
12999
- const play = await client.describePlay(
13172
+ const client2 = new DeeplineClient();
13173
+ await assertCanonicalNamedPlayReference(client2, playName);
13174
+ const play = await client2.describePlay(
13000
13175
  parseReferencedPlayTarget2(playName).playName,
13001
13176
  {
13002
13177
  compact: args.includes("--compact")
@@ -13033,7 +13208,7 @@ async function handlePlayPublish(args) {
13033
13208
  console.error("Choose only one live target: --latest or --revision-id.");
13034
13209
  return 1;
13035
13210
  }
13036
- const client = new DeeplineClient();
13211
+ const client2 = new DeeplineClient();
13037
13212
  if (isFileTarget(playName)) {
13038
13213
  if (revisionId || useLatest) {
13039
13214
  console.error(
@@ -13051,12 +13226,12 @@ async function handlePlayPublish(args) {
13051
13226
  await traceCliSpan(
13052
13227
  "cli.play_publish_compile_manifests",
13053
13228
  { targetKind: "file", nodeCount: graph.nodes.size },
13054
- () => compileBundledPlayGraphManifests(client, graph)
13229
+ () => compileBundledPlayGraphManifests(client2, graph)
13055
13230
  );
13056
13231
  await traceCliSpan(
13057
13232
  "cli.play_publish_imports",
13058
13233
  { targetKind: "file", nodeCount: graph.nodes.size },
13059
- () => publishImportedPlayDependencies(client, graph)
13234
+ () => publishImportedPlayDependencies(client2, graph)
13060
13235
  );
13061
13236
  } catch (error) {
13062
13237
  console.error(error instanceof Error ? error.message : String(error));
@@ -13070,7 +13245,7 @@ async function handlePlayPublish(args) {
13070
13245
  playName: rootPlayName,
13071
13246
  artifactHash: graph.root.artifact.artifactHash
13072
13247
  },
13073
- () => client.registerPlayArtifact({
13248
+ () => client2.registerPlayArtifact({
13074
13249
  name: rootPlayName,
13075
13250
  sourceCode: graph.root.sourceCode,
13076
13251
  sourceFiles: graph.root.sourceFiles,
@@ -13094,7 +13269,7 @@ async function handlePlayPublish(args) {
13094
13269
  }
13095
13270
  const resolvedName = parseReferencedPlayTarget2(playName).playName;
13096
13271
  if (useLatest) {
13097
- const versions = await client.listPlayVersions(resolvedName);
13272
+ const versions = await client2.listPlayVersions(resolvedName);
13098
13273
  const latest = versions[0];
13099
13274
  if (!latest?._id) {
13100
13275
  console.error(`No saved revisions found for ${resolvedName}.`);
@@ -13103,12 +13278,12 @@ async function handlePlayPublish(args) {
13103
13278
  revisionId = latest._id;
13104
13279
  }
13105
13280
  try {
13106
- await ensureEditableRemotePlay(client, resolvedName);
13281
+ await ensureEditableRemotePlay(client2, resolvedName);
13107
13282
  } catch (error) {
13108
13283
  console.error(error instanceof Error ? error.message : String(error));
13109
13284
  return 1;
13110
13285
  }
13111
- const result = await client.publishPlayVersion(
13286
+ const result = await client2.publishPlayVersion(
13112
13287
  resolvedName,
13113
13288
  revisionId ? { revisionId } : {}
13114
13289
  );
@@ -13129,10 +13304,10 @@ async function handlePlayDelete(args) {
13129
13304
  );
13130
13305
  return 1;
13131
13306
  }
13132
- const client = new DeeplineClient();
13307
+ const client2 = new DeeplineClient();
13133
13308
  let detail;
13134
13309
  try {
13135
- detail = await client.getPlay(parseReferencedPlayTarget2(playName).playName);
13310
+ detail = await client2.getPlay(parseReferencedPlayTarget2(playName).playName);
13136
13311
  } catch (error) {
13137
13312
  console.error(error instanceof Error ? error.message : String(error));
13138
13313
  return 1;
@@ -13143,7 +13318,7 @@ async function handlePlayDelete(args) {
13143
13318
  );
13144
13319
  return 1;
13145
13320
  }
13146
- const result = await client.deletePlay(
13321
+ const result = await client2.deletePlay(
13147
13322
  parseReferencedPlayTarget2(formatPlayReference(detail.play)).playName
13148
13323
  );
13149
13324
  if (argsWantJson(args)) {
@@ -13808,11 +13983,11 @@ async function handlePlaySharePublish(args) {
13808
13983
  return 2;
13809
13984
  }
13810
13985
  const name = parseReferencedPlayTarget2(target).playName;
13811
- const client = new DeeplineClient();
13986
+ const client2 = new DeeplineClient();
13812
13987
  let revisionId = shareFlagValue(args, "--revision-id");
13813
13988
  let resolvedVersion;
13814
13989
  if (!revisionId) {
13815
- const versions = await client.listPlayVersions(name);
13990
+ const versions = await client2.listPlayVersions(name);
13816
13991
  if (versions.length === 0) {
13817
13992
  console.error(
13818
13993
  `No saved revisions for ${name}. Run \`deepline plays set-live ${name}\` first.`
@@ -13833,7 +14008,7 @@ async function handlePlaySharePublish(args) {
13833
14008
  resolvedVersion = chosen.version;
13834
14009
  }
13835
14010
  if (!args.includes("--no-run-check")) {
13836
- const runs = await client.listRuns({ play: name });
14011
+ const runs = await client2.listRuns({ play: name });
13837
14012
  const hasCompleted = runs.some(
13838
14013
  (r) => String(r.status).toLowerCase() === "completed"
13839
14014
  );
@@ -13872,7 +14047,7 @@ async function handlePlaySharePublish(args) {
13872
14047
  }
13873
14048
  return 0;
13874
14049
  }
13875
- const status = await client.publishSharePage(name, request);
14050
+ const status = await client2.publishSharePage(name, request);
13876
14051
  if (argsWantJson(args)) {
13877
14052
  process.stdout.write(`${JSON.stringify(status)}
13878
14053
  `);
@@ -13920,11 +14095,11 @@ async function handlePlayShareRegenerate(args) {
13920
14095
  return 2;
13921
14096
  }
13922
14097
  const name = parseReferencedPlayTarget2(target).playName;
13923
- const client = new DeeplineClient();
14098
+ const client2 = new DeeplineClient();
13924
14099
  let revisionId = shareFlagValue(args, "--revision-id");
13925
14100
  const versionFlag = shareFlagValue(args, "--version");
13926
14101
  if (!revisionId && versionFlag) {
13927
- const versions = await client.listPlayVersions(name);
14102
+ const versions = await client2.listPlayVersions(name);
13928
14103
  const chosen = versions.find((r) => r.version === Number(versionFlag));
13929
14104
  if (!chosen?._id) {
13930
14105
  console.error(`Version ${versionFlag} not found for ${name}.`);
@@ -13932,7 +14107,7 @@ async function handlePlayShareRegenerate(args) {
13932
14107
  }
13933
14108
  revisionId = chosen._id;
13934
14109
  }
13935
- const status = await client.regenerateSharePage(
14110
+ const status = await client2.regenerateSharePage(
13936
14111
  name,
13937
14112
  revisionId ? { revisionId } : {}
13938
14113
  );
@@ -13969,52 +14144,158 @@ async function handlePlayShareUnpublish(args) {
13969
14144
  return 0;
13970
14145
  }
13971
14146
 
13972
- // src/cli/enrich-play-compiler.ts
13973
- var RESERVED_WORDS = /* @__PURE__ */ new Set([
13974
- "break",
13975
- "case",
13976
- "catch",
13977
- "class",
13978
- "const",
13979
- "continue",
13980
- "debugger",
13981
- "default",
13982
- "delete",
13983
- "do",
13984
- "else",
13985
- "export",
13986
- "extends",
13987
- "finally",
13988
- "for",
13989
- "function",
13990
- "if",
13991
- "import",
13992
- "in",
13993
- "instanceof",
13994
- "new",
13995
- "return",
13996
- "super",
13997
- "switch",
13998
- "this",
13999
- "throw",
14000
- "try",
14001
- "typeof",
14002
- "var",
14003
- "void",
14004
- "while",
14005
- "with",
14006
- "yield"
14147
+ // ../shared_libs/plays/tool-codegen.ts
14148
+ var KNOWN_GETTER_NAMES = /* @__PURE__ */ new Set([
14149
+ "company_domain",
14150
+ "company_linkedin_url",
14151
+ "company_name",
14152
+ "domain",
14153
+ "email",
14154
+ "email_status",
14155
+ "first_name",
14156
+ "full_name",
14157
+ "job_change",
14158
+ "job_change_status",
14159
+ "job_changed",
14160
+ "last_name",
14161
+ "linkedin",
14162
+ "personal_email",
14163
+ "phone",
14164
+ "phone_status",
14165
+ "status",
14166
+ "title"
14007
14167
  ]);
14168
+ function renderToolCodegenString(value) {
14169
+ return JSON.stringify(value);
14170
+ }
14171
+ function renderToolRowPathExpression(path, rowName = "row") {
14172
+ const parts = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean);
14173
+ if (parts.length === 0) return rowName;
14174
+ return parts.reduce((expr, part, index) => {
14175
+ if (index === 0) return `${rowName}[${renderToolCodegenString(part)}]`;
14176
+ return `(${expr} as Record<string, unknown> | null | undefined)?.[${renderToolCodegenString(part)}]`;
14177
+ }, rowName);
14178
+ }
14179
+ function renderToolPayloadExpression(value, rowName = "row") {
14180
+ if (Array.isArray(value)) {
14181
+ return `[${value.map((entry) => renderToolPayloadExpression(entry, rowName)).join(", ")}]`;
14182
+ }
14183
+ if (value && typeof value === "object") {
14184
+ const entries = Object.entries(value).sort(
14185
+ ([left], [right]) => left.localeCompare(right)
14186
+ );
14187
+ return `{ ${entries.map(
14188
+ ([key, entry]) => `${renderToolCodegenString(key)}: ${renderToolPayloadExpression(entry, rowName)}`
14189
+ ).join(", ")} }`;
14190
+ }
14191
+ if (typeof value !== "string") return JSON.stringify(value);
14192
+ const exact = value.match(/^\{\{\s*([^{}]+?)\s*\}\}$/);
14193
+ if (exact) return renderToolRowPathExpression(exact[1] ?? "", rowName);
14194
+ const pieces = [];
14195
+ let cursor = 0;
14196
+ for (const match of value.matchAll(/\{\{\s*([^{}]+?)\s*\}\}/g)) {
14197
+ const index = match.index ?? 0;
14198
+ if (index > cursor) {
14199
+ pieces.push(renderToolCodegenString(value.slice(cursor, index)));
14200
+ }
14201
+ pieces.push(
14202
+ `String(${renderToolRowPathExpression(match[1] ?? "", rowName)} ?? '')`
14203
+ );
14204
+ cursor = index + match[0].length;
14205
+ }
14206
+ if (pieces.length === 0) return renderToolCodegenString(value);
14207
+ if (cursor < value.length) {
14208
+ pieces.push(renderToolCodegenString(value.slice(cursor)));
14209
+ }
14210
+ return pieces.join(" + ");
14211
+ }
14212
+ function renderExtractedValueGetterExpression(toolResultVar, targetKey) {
14213
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(targetKey) ? `${toolResultVar}.extractedValues.${targetKey}?.get()` : `${toolResultVar}.extractedValues[${renderToolCodegenString(targetKey)}]?.get()`;
14214
+ }
14215
+ function getterFromLegacyPath(path) {
14216
+ const last = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean).at(-1);
14217
+ if (!last) return null;
14218
+ const normalized = last.replace(/[^A-Za-z0-9_]/g, "_");
14219
+ return KNOWN_GETTER_NAMES.has(normalized) ? normalized : null;
14220
+ }
14221
+ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
14222
+ const source = extractJs?.trim();
14223
+ if (!source) {
14224
+ const aliasGetter = fallbackAlias?.replace(/[^A-Za-z0-9_]/g, "_");
14225
+ return aliasGetter && KNOWN_GETTER_NAMES.has(aliasGetter) ? aliasGetter : null;
14226
+ }
14227
+ const directExtract = source.match(
14228
+ /\bextract\(\s*["'][^"']+["']\s*,\s*output_data\s*,\s*["']([^"']+)["']\s*\)/
14229
+ );
14230
+ if (directExtract?.[1]) {
14231
+ return getterFromLegacyPath(
14232
+ directExtract[1] === "linkedin_url" ? "linkedin" : directExtract[1]
14233
+ );
14234
+ }
14235
+ const pick = source.match(
14236
+ /\b(?:pick|extract|target)\(\s*(\[[\s\S]*?\]|["'][^"']+["'])\s*\)/
14237
+ );
14238
+ if (!pick?.[1]) return null;
14239
+ try {
14240
+ const parsed = JSON.parse(pick[1].replace(/'/g, '"'));
14241
+ const paths = Array.isArray(parsed) ? parsed : [parsed];
14242
+ for (const path of paths) {
14243
+ if (typeof path !== "string") continue;
14244
+ const getter = getterFromLegacyPath(path);
14245
+ if (getter) return getter;
14246
+ }
14247
+ } catch {
14248
+ return null;
14249
+ }
14250
+ return null;
14251
+ }
14252
+
14253
+ // src/cli/user-code-safety.ts
14254
+ var FORBIDDEN = [
14255
+ // Non-deterministic — breaks replay.
14256
+ { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
14257
+ { pattern: /\bDate\s*\.\s*now\b/, reason: "Date.now()" },
14258
+ { pattern: /\bnew\s+Date\s*\(\s*\)/, reason: "new Date() with no argument" },
14259
+ { pattern: /\bperformance\s*\.\s*now\b/, reason: "performance.now()" },
14260
+ {
14261
+ pattern: /\bcrypto\s*\.\s*(?:randomUUID|getRandomValues)\b/,
14262
+ reason: "crypto random"
14263
+ },
14264
+ // Sandbox escape / I/O.
14265
+ { pattern: /(?<!\.)\bfetch\s*\(/, reason: "fetch()" },
14266
+ { pattern: /(?<!\.)\bimport\s*\(/, reason: "dynamic import()" },
14267
+ { pattern: /(?<!\.)\brequire\s*\(/, reason: "require()" },
14268
+ { pattern: /(?<!\.)\beval\s*\(/, reason: "eval()" },
14269
+ {
14270
+ pattern: /(?<!\.)\bnew\s+Function\b|(?<!\.)\bFunction\s*\(/,
14271
+ reason: "the Function constructor"
14272
+ },
14273
+ { pattern: /(?<!\.)\bprocess\b/, reason: "process" },
14274
+ { pattern: /(?<!\.)\bglobalThis\b/, reason: "globalThis" },
14275
+ { pattern: /(?<!\.)\b(?:window|self)\b/, reason: "window/self" },
14276
+ { pattern: /(?<!\.)\bXMLHttpRequest\b/, reason: "XMLHttpRequest" },
14277
+ { pattern: /(?<!\.)\bWebAssembly\b/, reason: "WebAssembly" }
14278
+ ];
14279
+ function assertUserCodeIsSafe(code, label) {
14280
+ if (typeof code !== "string" || !code.trim()) return;
14281
+ for (const { pattern, reason } of FORBIDDEN) {
14282
+ if (pattern.test(code)) {
14283
+ throw new Error(
14284
+ `${label} uses ${reason}, which is not allowed in play code: it breaks deterministic replay or escapes the sandbox. Remove it and compute the value from the row/result instead.`
14285
+ );
14286
+ }
14287
+ }
14288
+ }
14289
+
14290
+ // src/cli/enrich-play-compiler.ts
14008
14291
  function isWaterfall(command) {
14009
14292
  return "with_waterfall" in command;
14010
14293
  }
14011
- function safeIdentifier2(value, fallback) {
14012
- const cleaned = value.replace(/[^A-Za-z0-9_$]/g, "_");
14013
- const prefixed = /^[A-Za-z_$]/.test(cleaned) ? cleaned : `_${cleaned}`;
14014
- if (!prefixed || RESERVED_WORDS.has(prefixed)) {
14015
- return fallback;
14016
- }
14017
- return prefixed;
14294
+ function configHasRunJavascript(config) {
14295
+ const walk = (commands) => commands.some(
14296
+ (command) => isWaterfall(command) ? walk(command.commands) : command.tool === "run_javascript"
14297
+ );
14298
+ return walk(config.commands ?? []);
14018
14299
  }
14019
14300
  function stringLiteral(value) {
14020
14301
  return JSON.stringify(value);
@@ -14041,12 +14322,23 @@ function commandCallId(command) {
14041
14322
  function normalizeAlias(value) {
14042
14323
  return value.toLowerCase().replace(/[^a-z0-9]/g, "");
14043
14324
  }
14044
- function renderExecuteStep(command, options = { force: false }) {
14325
+ function renderExecuteStep(command, options = {
14326
+ force: false
14327
+ }) {
14328
+ if (command.play) {
14329
+ return renderPlayStep(command, options);
14330
+ }
14331
+ if (command.tool === "run_javascript" && options.inlineRunJavascript) {
14332
+ return renderInlineJavascriptStep(command, options);
14333
+ }
14334
+ if (options.idiomaticGetters) {
14335
+ return renderIdiomaticExecuteStep(command, options);
14336
+ }
14045
14337
  const alias = stringLiteral(command.alias);
14046
14338
  const callId = stringLiteral(commandCallId(command));
14047
14339
  const tool = stringLiteral(command.tool);
14048
14340
  const payload = stableJson(command.payload ?? {});
14049
- const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, target }) => { const input = row; const context = row;
14341
+ const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, extractList, target, get }) => { const input = row; const context = row;
14050
14342
  ${indent(renderJavascriptBody(command.extract_js), 6)}
14051
14343
  }` : "null";
14052
14344
  const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
@@ -14056,6 +14348,8 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14056
14348
  description: ${stringLiteral(command.description)}` : "";
14057
14349
  const force = options.force ? `,
14058
14350
  force: true` : "";
14351
+ const legacyEnvelope = options.legacyEnvelope ? `,
14352
+ legacyEnvelope: true` : "";
14059
14353
  return [
14060
14354
  `async (row, stepCtx) => {`,
14061
14355
  ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
@@ -14067,253 +14361,842 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14067
14361
  ` extract: ${extractJs},`,
14068
14362
  ` runIf: ${runIfJs},`,
14069
14363
  ` row,`,
14070
- ` stepCtx${description}${force}`,
14364
+ ` stepCtx${description}${force}${legacyEnvelope}`,
14365
+ ` });`,
14366
+ `}`
14367
+ ].join("\n");
14368
+ }
14369
+ function renderIdiomaticExecuteStep(command, options) {
14370
+ const callId = stringLiteral(commandCallId(command));
14371
+ const tool = stringLiteral(command.tool);
14372
+ const input2 = renderToolPayloadExpression(command.payload ?? {});
14373
+ const getter = getterFromLegacyExtractJs(command.extract_js, command.alias);
14374
+ const extraction = getter ? `${renderExtractedValueGetterExpression("result", getter)} ?? null` : "result";
14375
+ const runIfLines = command.run_if_js ? [
14376
+ ` if (`,
14377
+ ` !((row: Record<string, any>) => {`,
14378
+ ` const input = row;`,
14379
+ ` const context = row;`,
14380
+ indent(renderJavascriptBody(command.run_if_js), 6),
14381
+ ` })(row as Record<string, any>)`,
14382
+ ` ) return null;`
14383
+ ] : [];
14384
+ return [
14385
+ `async (row, ctx) => {`,
14386
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14387
+ ...runIfLines,
14388
+ ` const result: any = await ctx.tools.execute({`,
14389
+ ` id: ${callId},`,
14390
+ ` tool: ${tool},`,
14391
+ ` input: ${input2} as any,`,
14392
+ ` description: ${stringLiteral((command.description ?? "").trim() || `Run ${command.alias} via ${command.tool}.`)},`,
14393
+ ...options.force ? [` staleAfterSeconds: 1,`] : [],
14394
+ ` });`,
14395
+ ` return ${extraction};`,
14396
+ `}`
14397
+ ].join("\n");
14398
+ }
14399
+ function renderInlineJavascriptStep(command, options) {
14400
+ const code = typeof command.payload?.code === "string" ? command.payload.code : "return null;";
14401
+ const runIfLines = command.run_if_js ? [
14402
+ ` if (!((row: Record<string, any>) => { const input = row; const context = row;`,
14403
+ indent(renderJavascriptBody(command.run_if_js), 4),
14404
+ ` })(row as Record<string, any>)) return null;`
14405
+ ] : [];
14406
+ return [
14407
+ `async (row) => {`,
14408
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14409
+ ...runIfLines,
14410
+ ` return ((row: Record<string, any>, input: Record<string, any>, context: Record<string, any>) => {`,
14411
+ indent(renderJavascriptBody(code), 4),
14412
+ ` })(row as Record<string, any>, row as Record<string, any>, row as Record<string, any>);`,
14413
+ `}`
14414
+ ].join("\n");
14415
+ }
14416
+ function renderPlayStep(command, options) {
14417
+ const alias = stringLiteral(command.alias);
14418
+ const callId = stringLiteral(commandCallId(command));
14419
+ const playRef = stringLiteral(command.play?.ref ?? command.tool);
14420
+ const payload = stableJson(command.payload ?? {});
14421
+ const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
14422
+ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14423
+ }` : "null";
14424
+ const runIfLines = command.run_if_js ? [` const __dlRunIf = ${runIfJs};`, ` if (!__dlRunIf(row)) return null;`] : [];
14425
+ const playOptions = [
14426
+ ` description: ${stringLiteral(command.description ?? command.alias)}`,
14427
+ ...options.force ? [` staleAfterSeconds: 1`] : []
14428
+ ].join(",\n");
14429
+ return [
14430
+ `async (row, stepCtx) => {`,
14431
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14432
+ ...runIfLines,
14433
+ ` const payload = __dlTemplate(${payload}, row) as Record<string, unknown>;`,
14434
+ ` const result = await stepCtx.runPlay(${callId}, ${playRef}, payload, {`,
14435
+ playOptions,
14071
14436
  ` });`,
14437
+ ` return __dlPlayResultValue(${alias}, result);`,
14072
14438
  `}`
14073
14439
  ].join("\n");
14074
14440
  }
14075
14441
  function renderJavascriptBody(source) {
14442
+ assertUserCodeIsSafe(source, "play step code");
14076
14443
  const trimmed = source.trim();
14444
+ if (/^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed)) {
14445
+ return `return (${trimmed});`;
14446
+ }
14077
14447
  if (trimmed && !trimmed.includes("\n") && !trimmed.includes(";") && !/\breturn\b/.test(trimmed)) {
14078
14448
  return `return (${trimmed});`;
14079
14449
  }
14080
14450
  return source;
14081
14451
  }
14082
- function renderWaterfallProgram(command, index, forceAliases) {
14083
- const variableName = safeIdentifier2(
14084
- `${command.with_waterfall}_${index}_waterfall`,
14085
- `waterfall_${index}`
14452
+ function renderColumnStep(alias, resolverSource, force) {
14453
+ const resolver = indent(resolverSource, 8);
14454
+ return [
14455
+ ` .withColumn(${stringLiteral(alias)},`,
14456
+ force ? `${resolver},` : resolver,
14457
+ ...force ? [` { staleAfterSeconds: 1 }`] : [],
14458
+ ` )`
14459
+ ].join("\n");
14460
+ }
14461
+ function metadataMode(command) {
14462
+ return /\bextractList\s*\(/.test(command.extract_js ?? "") ? "list" : "scalar";
14463
+ }
14464
+ function metadataEntryForCommand(command, waterfallGroupId) {
14465
+ const entry = {
14466
+ tool_id: command.tool
14467
+ };
14468
+ if (command.play?.ref) {
14469
+ entry.play_ref = command.play.ref;
14470
+ entry.kind = "play_call";
14471
+ }
14472
+ if (command.operation) {
14473
+ entry.operation = command.operation;
14474
+ }
14475
+ if (command.extract_js?.trim()) {
14476
+ entry.extract_js = command.extract_js.trim();
14477
+ }
14478
+ if (waterfallGroupId) {
14479
+ entry.waterfall = {
14480
+ group_id: waterfallGroupId,
14481
+ mode: metadataMode(command)
14482
+ };
14483
+ }
14484
+ return entry;
14485
+ }
14486
+ function collectMetadataColumns(commands, waterfallGroupId) {
14487
+ const columns = {};
14488
+ for (const command of commands) {
14489
+ if (isWaterfall(command)) {
14490
+ Object.assign(
14491
+ columns,
14492
+ collectMetadataColumns(command.commands, command.with_waterfall)
14493
+ );
14494
+ continue;
14495
+ }
14496
+ if (command.disabled) {
14497
+ continue;
14498
+ }
14499
+ columns[normalizeAlias(command.alias)] = metadataEntryForCommand(
14500
+ command,
14501
+ waterfallGroupId
14502
+ );
14503
+ }
14504
+ return columns;
14505
+ }
14506
+ function renderMetadataColumnStep(config) {
14507
+ const columns = collectMetadataColumns(config.commands);
14508
+ if (Object.keys(columns).length === 0) {
14509
+ return "";
14510
+ }
14511
+ return [
14512
+ ` .withColumn('_metadata',`,
14513
+ ` (row) => __dlMergeMetadata(row._metadata, ${stableJson({ columns })}),`,
14514
+ ` { staleAfterSeconds: 1 }`,
14515
+ ` )`
14516
+ ].join("\n");
14517
+ }
14518
+ function renderWaterfallColumns(command, forceAliases, inlineRunJavascript, idiomaticGetters) {
14519
+ const activeChildren = command.commands.filter(
14520
+ (nested) => !isWaterfall(nested) && !nested.disabled
14086
14521
  );
14087
- const stepLines = command.commands.map((nested, stepIndex) => {
14522
+ const minResults = typeof command.min_results === "number" ? Math.max(1, Math.trunc(command.min_results)) : 1;
14523
+ const columnSteps = activeChildren.map((nested, stepIndex) => {
14088
14524
  if (isWaterfall(nested)) {
14089
14525
  throw new Error("Nested with_waterfall blocks are not supported.");
14090
14526
  }
14091
14527
  if (nested.disabled) {
14092
14528
  return null;
14093
14529
  }
14094
- const priorAliases = command.commands.slice(0, stepIndex).filter((prior) => !isWaterfall(prior)).filter((prior) => !prior.disabled).map((prior) => prior.alias);
14095
- const minResults = typeof command.min_results === "number" ? Math.max(1, Math.trunc(command.min_results)) : 1;
14096
- return [
14097
- ` .step(${stringLiteral(nested.alias)},`,
14098
- indent(
14099
- renderExecuteStep(nested, {
14100
- force: forceAliases.has(normalizeAlias(nested.alias)),
14101
- precheck: priorAliases.length > 0 ? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})` : void 0
14102
- }),
14103
- 4
14104
- ),
14105
- ` )`
14106
- ].join("\n");
14530
+ const priorAliases = activeChildren.slice(0, stepIndex).map((prior) => prior.alias);
14531
+ const force = forceAliases.has(normalizeAlias(nested.alias));
14532
+ return renderColumnStep(
14533
+ nested.alias,
14534
+ renderExecuteStep(nested, {
14535
+ force,
14536
+ precheck: priorAliases.length > 0 ? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})` : void 0,
14537
+ legacyEnvelope: Boolean(nested.extract_js),
14538
+ inlineRunJavascript,
14539
+ idiomaticGetters
14540
+ }),
14541
+ force
14542
+ );
14107
14543
  }).filter((line) => line !== null);
14108
- const aliases = command.commands.filter((nested) => !isWaterfall(nested)).filter((nested) => !nested.disabled).map((nested) => nested.alias);
14544
+ const aliases = activeChildren.map((nested) => nested.alias);
14545
+ const forceParent = forceAliases.has(normalizeAlias(command.with_waterfall)) || activeChildren.some(
14546
+ (nested) => forceAliases.has(normalizeAlias(nested.alias))
14547
+ );
14109
14548
  const returnExpr = typeof command.min_results === "number" ? `__dlFirstMinResults(row, ${stableJson(aliases)}, ${Math.max(
14110
14549
  1,
14111
14550
  Math.trunc(command.min_results)
14112
14551
  )})` : `__dlFirstMeaningful(row, ${stableJson(aliases)})`;
14113
- return {
14114
- variableName,
14115
- hasSteps: stepLines.length > 0,
14116
- source: [
14117
- `const ${variableName} = steps<Record<string, unknown>>()`,
14118
- ...stepLines,
14119
- ` .return((row) => ${returnExpr});`
14120
- ].join("\n")
14121
- };
14552
+ if (columnSteps.length === 0) {
14553
+ return [];
14554
+ }
14555
+ return [
14556
+ ...columnSteps,
14557
+ renderColumnStep(
14558
+ command.with_waterfall,
14559
+ `(row) => ${returnExpr}`,
14560
+ forceParent
14561
+ ),
14562
+ renderColumnStep(
14563
+ `${command.with_waterfall}_source`,
14564
+ typeof command.min_results === "number" ? `(row) => __dlContributingAliases(row, ${stableJson(aliases)})` : `(row) => __dlFirstMeaningfulAlias(row, ${stableJson(aliases)})`,
14565
+ forceParent
14566
+ )
14567
+ ];
14122
14568
  }
14123
14569
  function compileEnrichConfigToPlaySource(config, options = {}) {
14124
14570
  const playName = options.playName ?? "deepline-enrich-v1-compat";
14125
14571
  const mapName = options.mapName ?? "deepline_enrich_rows";
14572
+ const inlineRunJavascript = options.inlineRunJavascript ?? false;
14573
+ const idiomaticGetters = options.idiomaticGetters ?? false;
14126
14574
  const forceAliases = new Set(
14127
14575
  [...options.forceAliases ?? []].map((alias) => normalizeAlias(alias))
14128
14576
  );
14129
- const waterfalls = [];
14130
- const mapSteps = [];
14131
- config.commands.forEach((command, index) => {
14577
+ const columnSteps = [];
14578
+ config.commands.forEach((command) => {
14132
14579
  if (isWaterfall(command)) {
14133
- const rendered = renderWaterfallProgram(command, index, forceAliases);
14134
- if (!rendered.hasSteps) {
14135
- return;
14136
- }
14137
- waterfalls.push({
14138
- alias: command.with_waterfall,
14139
- variableName: rendered.variableName,
14140
- source: rendered.source
14141
- });
14142
- mapSteps.push(
14143
- ` .step(${stringLiteral(command.with_waterfall)}, ${rendered.variableName})`
14580
+ columnSteps.push(
14581
+ ...renderWaterfallColumns(
14582
+ command,
14583
+ forceAliases,
14584
+ inlineRunJavascript,
14585
+ idiomaticGetters
14586
+ )
14144
14587
  );
14145
14588
  return;
14146
14589
  }
14147
14590
  if (command.disabled) {
14148
14591
  return;
14149
14592
  }
14150
- mapSteps.push(
14151
- [
14152
- ` .step(${stringLiteral(command.alias)},`,
14153
- indent(
14154
- renderExecuteStep(command, {
14155
- force: forceAliases.has(normalizeAlias(command.alias))
14156
- }),
14157
- 8
14158
- ),
14159
- ` )`
14160
- ].join("\n")
14593
+ const force = forceAliases.has(normalizeAlias(command.alias));
14594
+ columnSteps.push(
14595
+ renderColumnStep(
14596
+ command.alias,
14597
+ renderExecuteStep(command, {
14598
+ force,
14599
+ inlineRunJavascript,
14600
+ idiomaticGetters
14601
+ }),
14602
+ force
14603
+ )
14161
14604
  );
14162
14605
  });
14163
- const waterfallSource = waterfalls.map((entry) => indent(entry.source, 4));
14164
- const mapStepSource = mapSteps.length > 0 ? mapSteps.join("\n") : ` .step('noop', (row) => row)`;
14606
+ const columnStepSource = columnSteps.length > 0 ? columnSteps.join("\n") : ` .withColumn('noop', () => null)`;
14607
+ const metadataColumnSource = renderMetadataColumnStep(config);
14608
+ const body = [
14609
+ `export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
14610
+ ` const sourceRows = await ctx.csv<Record<string, unknown>>(input.file);`,
14611
+ ` const rowStart = __dlNonNegativeInteger(input.rowStart, 0);`,
14612
+ ` const rowEndExclusive = Number.isFinite(input.rowEnd) ? Math.max(rowStart, __dlWholeNumber(input.rowEnd, rowStart) + 1) : undefined;`,
14613
+ ` const rows = sourceRows.slice(rowStart, rowEndExclusive, { key: 'deepline_enrich_selected_rows', sourceLabel: 'Selected enrich rows' });`,
14614
+ ` const enriched = await ctx`,
14615
+ ` .dataset(${stringLiteral(mapName)}, rows)`,
14616
+ columnStepSource,
14617
+ ...metadataColumnSource ? [metadataColumnSource] : [],
14618
+ ` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
14619
+ ` return { rows: enriched, count: await enriched.count() };`,
14620
+ `});`
14621
+ ];
14622
+ const helpers = idiomaticGetters ? selectUsedHelpers(helperSource(), body.join("\n")) : helperSource();
14165
14623
  return [
14166
- `import { definePlay, steps } from 'deepline';`,
14624
+ ...inlineRunJavascript && configHasRunJavascript(config) ? ["// @ts-nocheck", "/* eslint-disable */", ""] : [],
14625
+ `import { definePlay } from 'deepline';`,
14167
14626
  ``,
14168
14627
  `type EnrichInput = { file: string; rowStart?: number | null; rowEnd?: number | null };`,
14169
14628
  ``,
14170
- helperSource(),
14629
+ helpers,
14171
14630
  ``,
14172
- `export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
14173
- ` const allRows = await ctx.csv<Record<string, unknown>>(input.file);`,
14174
- ` const rowStart = Number.isFinite(input.rowStart) ? Math.max(0, Math.trunc(Number(input.rowStart))) : 0;`,
14175
- ` const rowEnd = Number.isFinite(input.rowEnd) ? Math.max(rowStart, Math.trunc(Number(input.rowEnd))) : allRows.length;`,
14176
- ` const rows = allRows.slice(rowStart, rowEnd);`,
14177
- ...waterfallSource,
14178
- ` const enriched = await ctx`,
14179
- ` .map(${stringLiteral(mapName)}, rows)`,
14180
- mapStepSource,
14181
- ` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
14182
- ` return { rows: enriched, count: await enriched.count() };`,
14183
- `});`,
14631
+ ...body,
14184
14632
  ``
14185
14633
  ].join("\n");
14186
14634
  }
14635
+ function selectUsedHelpers(helperBlock, referenceSource) {
14636
+ const referencesSymbol = (source, symbol) => new RegExp(`\\b${symbol}\\b`).test(source);
14637
+ const blocks = helperBlock.split("\n\n").map((source) => source.trim()).filter(Boolean).map((source) => ({
14638
+ name: source.match(/(?:function|type|const)\s+(__[A-Za-z]\w*)/)?.[1] ?? "",
14639
+ source
14640
+ }));
14641
+ const used = /* @__PURE__ */ new Set();
14642
+ for (const { name } of blocks) {
14643
+ if (name && referencesSymbol(referenceSource, name)) used.add(name);
14644
+ }
14645
+ let changed = true;
14646
+ while (changed) {
14647
+ changed = false;
14648
+ const usedSource = blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n");
14649
+ for (const { name } of blocks) {
14650
+ if (!name || used.has(name)) continue;
14651
+ if (referencesSymbol(usedSource, name)) {
14652
+ used.add(name);
14653
+ changed = true;
14654
+ }
14655
+ }
14656
+ }
14657
+ return blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n\n");
14658
+ }
14187
14659
  function helperSource() {
14188
14660
  return [
14189
- `function __dlGetByPath(root: unknown, path: string): unknown {`,
14190
- ` return String(path || '')`,
14191
- ` .replace(/\\[(\\d+)\\]/g, '.$1')`,
14192
- ` .split('.')`,
14193
- ` .map((part) => part.trim())`,
14194
- ` .filter(Boolean)`,
14195
- ` .reduce((cursor: unknown, part: string) => {`,
14196
- ` if (!cursor || typeof cursor !== 'object') return undefined;`,
14197
- ` const record = cursor as Record<string, unknown>;`,
14198
- ` if (part in record) return record[part];`,
14199
- ` const data = record.data;`,
14200
- ` return data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
14201
- ` }, root);`,
14661
+ `function __dlWholeNumber(value: unknown, fallback: number): number {`,
14662
+ ` const numeric = Number(value);`,
14663
+ ` if (!Number.isFinite(numeric)) return fallback;`,
14664
+ ` return Math.floor(numeric);`,
14202
14665
  `}`,
14203
14666
  ``,
14204
- `function __dlMeaningful(value: unknown): boolean {`,
14205
- ` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
14667
+ `function __dlNonNegativeInteger(value: unknown, fallback: number): number {`,
14668
+ ` return Math.max(0, __dlWholeNumber(value, fallback));`,
14206
14669
  `}`,
14207
14670
  ``,
14208
- `function __dlRawToolOutput(result: unknown): unknown {`,
14209
- ` if (!result || typeof result !== 'object') return result;`,
14210
- ` const record = result as Record<string, unknown>;`,
14211
- ` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
14671
+ `function __dlAtLeastOneInteger(value: number): number {`,
14672
+ ` return Math.max(1, Math.floor(Number(value)));`,
14212
14673
  `}`,
14213
14674
  ``,
14214
- `function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
14215
- ` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
14216
- ` if (value && typeof value === 'object') {`,
14217
- ` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
14675
+ `function __dlRecord(value: unknown): value is Record<string, unknown> {`,
14676
+ ` return Boolean(value && typeof value === 'object' && !Array.isArray(value));`,
14677
+ `}`,
14678
+ ``,
14679
+ `function __dlParseMetadata(value: unknown): Record<string, unknown> | null {`,
14680
+ ` if (__dlRecord(value)) return value;`,
14681
+ ` if (typeof value !== 'string') return null;`,
14682
+ ` const trimmed = value.trim();`,
14683
+ ` if (!trimmed) return null;`,
14684
+ ` const candidates = [trimmed];`,
14685
+ ` if (trimmed.includes('\\\\"')) candidates.push(trimmed.replace(/\\\\"/g, '"'));`,
14686
+ ` for (const candidate of candidates) {`,
14687
+ ` try {`,
14688
+ ` const parsed = JSON.parse(candidate);`,
14689
+ ` if (__dlRecord(parsed)) return parsed;`,
14690
+ ` if (typeof parsed === 'string') {`,
14691
+ ` const nested = JSON.parse(parsed);`,
14692
+ ` if (__dlRecord(nested)) return nested;`,
14693
+ ` }`,
14694
+ ` } catch {}`,
14218
14695
  ` }`,
14219
- ` if (typeof value !== 'string') return value;`,
14220
- ` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
14221
- ` if (exact) return __dlGetByPath(row, exact[1] || '');`,
14222
- ` return value.replace(/\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g, (_match, path) => {`,
14223
- ` const replacement = __dlGetByPath(row, String(path || ''));`,
14224
- ` return replacement === null || replacement === undefined ? '' : String(replacement);`,
14225
- ` });`,
14696
+ ` return null;`,
14697
+ `}`,
14698
+ ``,
14699
+ `function __dlMergeMetadata(existing: unknown, patch: Record<string, unknown>): Record<string, unknown> {`,
14700
+ ` const base = __dlParseMetadata(existing) ?? {};`,
14701
+ ` const baseColumns = __dlRecord(base.columns) ? base.columns : {};`,
14702
+ ` const patchColumns = __dlRecord(patch.columns) ? patch.columns : {};`,
14703
+ ` return {`,
14704
+ ` ...base,`,
14705
+ ` ...patch,`,
14706
+ ` columns: {`,
14707
+ ` ...baseColumns,`,
14708
+ ` ...patchColumns,`,
14709
+ ` },`,
14710
+ ` };`,
14711
+ `}`,
14712
+ ``,
14713
+ `function __dlPathParts(path: string): string[] {`,
14714
+ ` const source = String(path || '');`,
14715
+ ` const parts: string[] = [];`,
14716
+ ` let current = '';`,
14717
+ ` for (let index = 0; index < source.length; index += 1) {`,
14718
+ ` const char = source.slice(index, index + 1);`,
14719
+ ` if (char === '.' || char === '[' || char === ']') {`,
14720
+ ` const trimmed = current.trim();`,
14721
+ ` if (trimmed) parts.push(trimmed);`,
14722
+ ` current = '';`,
14723
+ ` continue;`,
14724
+ ` }`,
14725
+ ` current += char;`,
14726
+ ` }`,
14727
+ ` const trimmed = current.trim();`,
14728
+ ` if (trimmed) parts.push(trimmed);`,
14729
+ ` return parts;`,
14730
+ `}`,
14731
+ ``,
14732
+ `function __dlGetByPath(root: unknown, path: string): unknown {`,
14733
+ ` let cursor = root;`,
14734
+ ` for (const part of __dlPathParts(path)) {`,
14735
+ ` if (!cursor || typeof cursor !== 'object') return undefined;`,
14736
+ ` const record = cursor as Record<string, unknown>;`,
14737
+ ` if (part in record) {`,
14738
+ ` cursor = record[part];`,
14739
+ ` continue;`,
14740
+ ` }`,
14741
+ ` const data = record.data;`,
14742
+ ` cursor = data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
14743
+ ` }`,
14744
+ ` return cursor;`,
14745
+ `}`,
14746
+ ``,
14747
+ `function __dlMeaningful(value: unknown): boolean {`,
14748
+ ` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
14749
+ ` const record = value as Record<string, unknown>;`,
14750
+ ` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
14751
+ ` if (status === 'error' || status === 'failed') return false;`,
14752
+ ` if (typeof record.error === 'string' && record.error.trim()) return false;`,
14753
+ ` const result = record.result;`,
14754
+ ` if (result && typeof result === 'object' && !Array.isArray(result)) {`,
14755
+ ` const resultRecord = result as Record<string, unknown>;`,
14756
+ ` if (typeof resultRecord.error === 'string' && resultRecord.error.trim()) return false;`,
14757
+ ` if (typeof resultRecord.message === 'string' && resultRecord.message.trim()) return false;`,
14758
+ ` }`,
14759
+ ` if ('matched_result' in record) return __dlMeaningful(record.matched_result);`,
14760
+ ` }`,
14761
+ ` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
14762
+ `}`,
14763
+ ``,
14764
+ `function __dlErrorPayload(value: unknown): boolean {`,
14765
+ ` if (!value || typeof value !== 'object' || Array.isArray(value)) return false;`,
14766
+ ` const record = value as Record<string, unknown>;`,
14767
+ ` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
14768
+ ` const result = record.result;`,
14769
+ ` const resultError = result && typeof result === 'object' && !Array.isArray(result) ? (result as Record<string, unknown>).error : null;`,
14770
+ ` return status === 'error' || status === 'failed' || (typeof record.error === 'string' && record.error.trim() !== '') || (typeof resultError === 'string' && resultError.trim() !== '');`,
14771
+ `}`,
14772
+ ``,
14773
+ `function __dlRawToolOutput(result: unknown): unknown {`,
14774
+ ` if (!result || typeof result !== 'object') return result;`,
14775
+ ` const record = result as Record<string, unknown>;`,
14776
+ ` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
14777
+ `}`,
14778
+ ``,
14779
+ `function __dlPushCandidate(candidates: unknown[], value: unknown): void {`,
14780
+ ` if (value === null || value === undefined) return;`,
14781
+ ` if (!candidates.includes(value)) candidates.push(value);`,
14782
+ `}`,
14783
+ ``,
14784
+ `function __dlRawToolCandidates(raw: unknown): unknown[] {`,
14785
+ ` const candidates: unknown[] = [raw];`,
14786
+ ` if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return candidates;`,
14787
+ ` const record = raw as Record<string, unknown>;`,
14788
+ ` __dlPushCandidate(candidates, record.data);`,
14789
+ ` __dlPushCandidate(candidates, record.result);`,
14790
+ ` __dlPushCandidate(candidates, record.output);`,
14791
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'result.data'));`,
14792
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.body'));`,
14793
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolResponse.raw'));`,
14794
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolOutput.raw'));`,
14795
+ ` return candidates;`,
14796
+ `}`,
14797
+ ``,
14798
+ `function __dlLegacyResultData(value: unknown): unknown {`,
14799
+ ` if (!value || typeof value !== 'object' || Array.isArray(value)) return value;`,
14800
+ ` const record = value as Record<string, unknown>;`,
14801
+ ` return 'data' in record ? record.data : value;`,
14802
+ `}`,
14803
+ ``,
14804
+ `function __dlLegacyOutputData(result: unknown, raw: unknown): unknown {`,
14805
+ ` const rawRecord = raw && typeof raw === 'object' && !Array.isArray(raw) ? (raw as Record<string, unknown>) : null;`,
14806
+ ` const data = rawRecord && 'data' in rawRecord ? rawRecord.data : raw;`,
14807
+ ` const existingResult = rawRecord && rawRecord.result && typeof rawRecord.result === 'object' && !Array.isArray(rawRecord.result) ? (rawRecord.result as Record<string, unknown>) : null;`,
14808
+ ` const resultData = existingResult && 'data' in existingResult ? __dlLegacyResultData(existingResult.data) : __dlLegacyResultData(data);`,
14809
+ ` const resultObject = resultData && typeof resultData === 'object' && !Array.isArray(resultData) ? (resultData as Record<string, unknown>) : {};`,
14810
+ ` return {`,
14811
+ ` ...(rawRecord ?? {}),`,
14812
+ ` data,`,
14813
+ ` result: { ...resultObject, ...(existingResult ?? {}), data: resultData },`,
14814
+ ` raw,`,
14815
+ ` toolResponse: { raw },`,
14816
+ ` originalResult: result,`,
14817
+ ` };`,
14818
+ `}`,
14819
+ ``,
14820
+ `function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
14821
+ ` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
14822
+ ` if (value && typeof value === 'object') {`,
14823
+ ` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
14824
+ ` }`,
14825
+ ` if (typeof value !== 'string') return value;`,
14826
+ ` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
14827
+ ` if (exact) return __dlGetByPath(row, exact[1] || '');`,
14828
+ ` let rendered = '';`,
14829
+ ` let cursor = 0;`,
14830
+ ` while (cursor < value.length) {`,
14831
+ ` const open = value.indexOf('{{', cursor);`,
14832
+ ` if (open < 0) {`,
14833
+ ` rendered += value.slice(cursor);`,
14834
+ ` break;`,
14835
+ ` }`,
14836
+ ` const close = value.indexOf('}}', open + 2);`,
14837
+ ` if (close < 0) {`,
14838
+ ` rendered += value.slice(cursor);`,
14839
+ ` break;`,
14840
+ ` }`,
14841
+ ` rendered += value.slice(cursor, open);`,
14842
+ ` const path = value.slice(open + 2, close).trim();`,
14843
+ ` const replacement = __dlGetByPath(row, path);`,
14844
+ ` rendered += replacement === null || replacement === undefined ? '' : String(replacement);`,
14845
+ ` cursor = close + 2;`,
14846
+ ` }`,
14847
+ ` return rendered;`,
14226
14848
  `}`,
14227
14849
  ``,
14228
14850
  `function __dlStableRowKey(row: Record<string, unknown>, index: number): string {`,
14229
- ` for (const key of ['id', 'ID', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN']) {`,
14851
+ ` for (const key of ['id', 'ID', 'row_key', 'ROW_KEY', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN', 'name', 'Name']) {`,
14230
14852
  ` const value = row[key];`,
14231
14853
  ` if (__dlMeaningful(value)) return String(value);`,
14232
14854
  ` }`,
14233
14855
  ` return String(index);`,
14234
14856
  `}`,
14235
14857
  ``,
14858
+ `function __dlScalarValue(value: unknown): unknown {`,
14859
+ ` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
14860
+ ` const record = value as Record<string, unknown>;`,
14861
+ ` if ('matched_result' in record) return __dlScalarValue(record.matched_result);`,
14862
+ ` for (const key of ['value', 'email', 'output', 'result', 'data']) {`,
14863
+ ` const nested = record[key];`,
14864
+ ` if (__dlMeaningful(nested)) return __dlScalarValue(nested);`,
14865
+ ` }`,
14866
+ ` }`,
14867
+ ` return value;`,
14868
+ `}`,
14869
+ ``,
14236
14870
  `function __dlFirstMeaningful(row: Record<string, unknown>, aliases: string[]): unknown {`,
14237
14871
  ` for (const alias of aliases) {`,
14238
14872
  ` const value = row[alias];`,
14239
- ` if (__dlMeaningful(value)) return value;`,
14873
+ ` if (__dlMeaningful(value)) return __dlScalarValue(value);`,
14874
+ ` }`,
14875
+ ` return null;`,
14876
+ `}`,
14877
+ ``,
14878
+ `function __dlFirstMeaningfulAlias(row: Record<string, unknown>, aliases: string[]): string | null {`,
14879
+ ` for (const alias of aliases) {`,
14880
+ ` if (__dlMeaningful(row[alias])) return alias;`,
14240
14881
  ` }`,
14241
14882
  ` return null;`,
14242
14883
  `}`,
14243
14884
  ``,
14885
+ `function __dlListValue(value: unknown): unknown[] {`,
14886
+ ` if (Array.isArray(value)) return value.filter(__dlMeaningful);`,
14887
+ ` if (value && typeof value === 'object') {`,
14888
+ ` const record = value as Record<string, unknown>;`,
14889
+ ` if ('matched_result' in record) return __dlListValue(record.matched_result);`,
14890
+ ` for (const key of ['result', 'value', 'data']) {`,
14891
+ ` const nested = record[key];`,
14892
+ ` const nestedList = __dlListValue(nested);`,
14893
+ ` if (nestedList.length > 0) return nestedList;`,
14894
+ ` }`,
14895
+ ` return __dlMeaningful(value) ? [value] : [];`,
14896
+ ` }`,
14897
+ ` return __dlMeaningful(value) ? [value] : [];`,
14898
+ `}`,
14899
+ ``,
14244
14900
  `function __dlFirstMinResults(row: Record<string, unknown>, aliases: string[], minResults: number): unknown {`,
14245
14901
  ` const values: unknown[] = [];`,
14246
14902
  ` for (const alias of aliases) {`,
14247
- ` const value = row[alias];`,
14248
- ` if (Array.isArray(value)) values.push(...value.filter(__dlMeaningful));`,
14249
- ` else if (__dlMeaningful(value)) values.push(value);`,
14250
- ` if (values.length >= minResults) return values.slice(0, minResults);`,
14903
+ ` values.push(...__dlListValue(row[alias]));`,
14904
+ ` if (values.length >= minResults) return values;`,
14251
14905
  ` }`,
14252
14906
  ` return values.length > 0 ? values : null;`,
14253
14907
  `}`,
14254
14908
  ``,
14909
+ `function __dlContributingAliases(row: Record<string, unknown>, aliases: string[]): string[] | null {`,
14910
+ ` const sources: string[] = [];`,
14911
+ ` for (const alias of aliases) {`,
14912
+ ` if (__dlListValue(row[alias]).length > 0) sources.push(alias);`,
14913
+ ` }`,
14914
+ ` return sources.length > 0 ? sources : null;`,
14915
+ `}`,
14916
+ ``,
14255
14917
  `function __dlWaterfallSatisfied(row: Record<string, unknown>, aliases: string[], minResults: number): boolean {`,
14256
14918
  ` let count = 0;`,
14257
14919
  ` for (const alias of aliases) {`,
14258
- ` const value = row[alias];`,
14259
- ` if (Array.isArray(value)) count += value.filter(__dlMeaningful).length;`,
14260
- ` else if (__dlMeaningful(value)) count += 1;`,
14261
- ` if (count >= Math.max(1, Math.trunc(minResults))) return true;`,
14920
+ ` count += __dlListValue(row[alias]).length;`,
14921
+ ` if (count >= __dlAtLeastOneInteger(minResults)) return true;`,
14262
14922
  ` }`,
14263
14923
  ` return false;`,
14264
14924
  `}`,
14265
14925
  ``,
14266
- `function __dlExtract(alias: string, result: unknown, row: Record<string, unknown>, extractor: ((args: { row: Record<string, unknown>; result: unknown; data: unknown; raw: unknown; pick: (paths: string[] | string) => unknown; extract: (paths: string[] | string) => unknown; target: (paths: string[] | string) => unknown }) => unknown) | null): unknown {`,
14926
+ `function __dlKeyPaths(key: string): string[] {`,
14927
+ ` const normalized = String(key || '').trim();`,
14928
+ ` if (normalized === 'email') return ['email', 'email_address', 'person.email', 'contact.email', 'data.email', 'result.data.email'];`,
14929
+ ` if (normalized === 'personal_email') return ['personal_email', 'email', 'email_address', 'data.personal_email', 'data.email'];`,
14930
+ ` if (normalized === 'phone') return ['phone', 'phone_number', 'mobile_phone', 'mobile_phone_number', 'data.phone'];`,
14931
+ ` if (normalized === 'linkedin') return ['linkedin', 'linkedin_url', 'linkedin_profile', 'profile_url', 'person.linkedin', 'person.linkedin_url'];`,
14932
+ ` if (normalized === 'full_name') return ['full_name', 'name', 'person.full_name', 'person.name'];`,
14933
+ ` if (normalized === 'first_name') return ['first_name', 'person.first_name'];`,
14934
+ ` if (normalized === 'last_name') return ['last_name', 'person.last_name'];`,
14935
+ ` if (normalized === 'title') return ['title', 'job_title', 'current_title', 'headline', 'person.title'];`,
14936
+ ` if (normalized === 'company_name') return ['company_name', 'company.name', 'organization.name'];`,
14937
+ ` if (normalized === 'company_domain') return ['company_domain', 'domain', 'company.domain', 'organization.domain'];`,
14938
+ ` if (normalized === 'status') return ['status', 'verdict', 'state'];`,
14939
+ ` if (normalized === 'email_status') return ['email_status', 'status', 'verdict', 'data.email_status'];`,
14940
+ ` return [normalized];`,
14941
+ `}`,
14942
+ ``,
14943
+ `function __dlSelectorKeys(selector: unknown): string[] | null {`,
14944
+ ` if (!selector || typeof selector !== 'object' || Array.isArray(selector)) return null;`,
14945
+ ` const keys = (selector as { keys?: unknown }).keys;`,
14946
+ ` if (!Array.isArray(keys) || keys.length === 0) return null;`,
14947
+ ` const out: string[] = [];`,
14948
+ ` for (const key of keys) {`,
14949
+ ` if (typeof key === 'string' && key.trim()) out.push(key.trim());`,
14950
+ ` }`,
14951
+ ` return out.length > 0 ? out : null;`,
14952
+ `}`,
14953
+ ``,
14954
+ `function __dlFirstByPaths(payload: unknown, paths: string[] | string): unknown {`,
14955
+ ` const candidates = Array.isArray(paths) ? paths : [paths];`,
14956
+ ` for (const path of candidates) {`,
14957
+ ` for (const candidate of __dlRawToolCandidates(payload)) {`,
14958
+ ` const value = __dlGetByPath(candidate, String(path));`,
14959
+ ` if (__dlMeaningful(value)) return value;`,
14960
+ ` }`,
14961
+ ` }`,
14962
+ ` return null;`,
14963
+ `}`,
14964
+ ``,
14965
+ `function __dlExtractTarget(payload: unknown, key: string): unknown {`,
14966
+ ` return __dlFirstByPaths(payload, __dlKeyPaths(key));`,
14967
+ `}`,
14968
+ ``,
14969
+ `function __dlExtractedValue(result: unknown, key: string): unknown {`,
14970
+ ` if (!result || typeof result !== 'object' || Array.isArray(result)) return undefined;`,
14971
+ ` const extractedValues = (result as Record<string, unknown>).extractedValues;`,
14972
+ ` if (!extractedValues || typeof extractedValues !== 'object' || Array.isArray(extractedValues)) return undefined;`,
14973
+ ` const accessor = (extractedValues as Record<string, unknown>)[key];`,
14974
+ ` if (!accessor || typeof accessor !== 'object' || Array.isArray(accessor)) return undefined;`,
14975
+ ` const record = accessor as Record<string, unknown>;`,
14976
+ ` const get = record.get;`,
14977
+ ` if (typeof get === 'function') {`,
14978
+ ` try {`,
14979
+ ` return (get as () => unknown)();`,
14980
+ ` } catch {`,
14981
+ ` return undefined;`,
14982
+ ` }`,
14983
+ ` }`,
14984
+ ` return record.value;`,
14985
+ `}`,
14986
+ ``,
14987
+ `function __dlLegacyExtractorPayload(payload: unknown, raw: unknown): unknown {`,
14988
+ ` if (payload === undefined || payload === null) return raw;`,
14989
+ ` if (payload && typeof payload === 'object' && !Array.isArray(payload) && Object.keys(payload as Record<string, unknown>).length === 0) return raw;`,
14990
+ ` return payload;`,
14991
+ `}`,
14992
+ ``,
14993
+ `function __dlLegacyMatchedEnvelope(value: unknown, raw: unknown): unknown {`,
14994
+ ` if (value && typeof value === 'object' && !Array.isArray(value) && 'matched_result' in (value as Record<string, unknown>)) return value;`,
14995
+ ` const legacyOutput = __dlLegacyOutputData(undefined, raw) as Record<string, unknown>;`,
14996
+ ` const legacyResult = legacyOutput.result && typeof legacyOutput.result === 'object' && !Array.isArray(legacyOutput.result) ? legacyOutput.result : raw;`,
14997
+ ` return { matched_result: value, result: legacyResult };`,
14998
+ `}`,
14999
+ ``,
15000
+ `function __dlString(value: unknown): string | null {`,
15001
+ ` return typeof value === 'string' && value.trim() ? value.trim() : null;`,
15002
+ `}`,
15003
+ ``,
15004
+ `// NOTE: email_status is NOT normalized here. It is materialized once by the`,
15005
+ `// provider's emailStatus({...}) contract via buildEmailStatus and read back`,
15006
+ `// through __dlExtractedValue(result, 'email_status') below. There is no`,
15007
+ `// legacy string coarsening \u2014 see CONTEXT.md "Provider Email Status Contract".`,
15008
+ ``,
15009
+ `function __dlListPathCandidates(selector: unknown): string[] {`,
15010
+ ` const paths: string[] = [];`,
15011
+ ` const push = (path: string) => { if (path && !paths.includes(path)) paths.push(path); };`,
15012
+ ` if (Array.isArray(selector)) {`,
15013
+ ` for (const path of selector) push(String(path));`,
15014
+ ` } else if (typeof selector === 'string') {`,
15015
+ ` push(selector);`,
15016
+ ` }`,
15017
+ ` for (const path of ['data', 'data.people', 'data.persons', 'data.contacts', 'data.results', 'data.leads', 'data.items', 'result.data', 'result.people', 'result.persons', 'result.contacts', 'result.results', 'result.leads', 'result.items', 'people', 'persons', 'contacts', 'results', 'leads', 'items', 'output.body', 'body']) push(path);`,
15018
+ ` return paths;`,
15019
+ `}`,
15020
+ ``,
15021
+ `function __dlFindList(payload: unknown, selector: unknown): unknown[] {`,
15022
+ ` if (Array.isArray(payload)) return payload;`,
15023
+ ` for (const path of __dlListPathCandidates(selector)) {`,
15024
+ ` for (const candidate of __dlRawToolCandidates(payload)) {`,
15025
+ ` const value = __dlGetByPath(candidate, path);`,
15026
+ ` if (Array.isArray(value)) return value;`,
15027
+ ` }`,
15028
+ ` }`,
15029
+ ` const queue = __dlRawToolCandidates(payload);`,
15030
+ ` const seen: unknown[] = [];`,
15031
+ ` for (let index = 0; index < queue.length; index += 1) {`,
15032
+ ` const current = queue[index];`,
15033
+ ` if (!current || typeof current !== 'object' || seen.includes(current)) continue;`,
15034
+ ` seen.push(current);`,
15035
+ ` for (const value of Object.values(current as Record<string, unknown>)) {`,
15036
+ ` if (Array.isArray(value)) return value;`,
15037
+ ` if (value && typeof value === 'object') queue.push(value);`,
15038
+ ` }`,
15039
+ ` }`,
15040
+ ` return [];`,
15041
+ `}`,
15042
+ ``,
15043
+ `function __dlDeriveFullName(row: Record<string, unknown>): string | null {`,
15044
+ ` const existing = __dlFirstByPaths(row, ['full_name', 'name']);`,
15045
+ ` if (typeof existing === 'string' && existing.trim()) return existing.trim();`,
15046
+ ` const first = __dlFirstByPaths(row, ['first_name', 'firstName']);`,
15047
+ ` const last = __dlFirstByPaths(row, ['last_name', 'lastName']);`,
15048
+ ` const parts = [first, last].filter((part): part is string => typeof part === 'string').map((part) => part.trim()).filter(Boolean);`,
15049
+ ` return parts.length > 0 ? parts.join(' ') : null;`,
15050
+ `}`,
15051
+ ``,
15052
+ `function __dlProjectListRows(rows: unknown[], keys: string[], payload: unknown): Array<Record<string, unknown>> {`,
15053
+ ` const projected: Array<Record<string, unknown>> = [];`,
15054
+ ` for (const row of rows) {`,
15055
+ ` const record = row && typeof row === 'object' && !Array.isArray(row) ? (row as Record<string, unknown>) : { value: row };`,
15056
+ ` const out: Record<string, unknown> = {};`,
15057
+ ` for (const key of keys) {`,
15058
+ ` let value = __dlExtractTarget(record, key);`,
15059
+ ` if (!__dlMeaningful(value) && key === 'full_name') value = __dlDeriveFullName(record);`,
15060
+ ` if (!__dlMeaningful(value)) value = __dlExtractTarget(payload, key);`,
15061
+ ` out[key] = value === undefined ? null : value;`,
15062
+ ` }`,
15063
+ ` if (Object.values(out).some(__dlMeaningful)) projected.push(out);`,
15064
+ ` }`,
15065
+ ` return projected;`,
15066
+ `}`,
15067
+ ``,
15068
+ `type __DlExtractorHelpers = { row: Record<string, unknown>; result: unknown; data: unknown; raw: unknown; pick: (paths: string[] | string) => unknown; extract: (...args: unknown[]) => unknown; extractList: (...args: unknown[]) => unknown[]; target: (paths: string[] | string) => unknown; get: (path: string) => unknown };`,
15069
+ ``,
15070
+ `function __dlErrorMessage(error: unknown): string {`,
15071
+ ` if (error instanceof Error && error.message) return error.message;`,
15072
+ ` if (typeof error === 'string' && error.trim()) return error.trim();`,
15073
+ ` return 'Extractor failed';`,
15074
+ `}`,
15075
+ ``,
15076
+ `function __dlExtractorFailure(error: unknown): unknown {`,
15077
+ ` const message = __dlErrorMessage(error);`,
15078
+ ` return { matched_result: null, result: { error: message, message } };`,
15079
+ `}`,
15080
+ ``,
15081
+ `function __dlExtract(alias: string, result: unknown, row: Record<string, unknown>, extractor: ((args: __DlExtractorHelpers) => unknown) | null, legacyEnvelope = false): unknown {`,
14267
15082
  ` const raw = __dlRawToolOutput(result);`,
14268
15083
  ` if (!extractor) return raw;`,
14269
15084
  ` const pick = (paths: string[] | string) => {`,
14270
15085
  ` const candidates = Array.isArray(paths) ? paths : [paths];`,
14271
15086
  ` for (const path of candidates) {`,
14272
- ` const value = __dlGetByPath(raw, String(path));`,
14273
- ` if (__dlMeaningful(value)) return value;`,
15087
+ ` if (typeof path === 'string') {`,
15088
+ ` const extractedValue = __dlExtractedValue(result, path);`,
15089
+ ` if (__dlMeaningful(extractedValue)) return extractedValue;`,
15090
+ ` }`,
15091
+ ` for (const candidate of __dlRawToolCandidates(raw)) {`,
15092
+ ` const value = __dlGetByPath(candidate, String(path));`,
15093
+ ` if (__dlMeaningful(value)) return value;`,
15094
+ ` }`,
14274
15095
  ` }`,
14275
15096
  ` return null;`,
14276
15097
  ` };`,
14277
- ` const extracted = extractor({ row, result, data: raw, raw, pick, extract: pick, target: pick });`,
14278
- ` if (extracted && typeof extracted === 'object' && !Array.isArray(extracted) && alias in (extracted as Record<string, unknown>)) {`,
14279
- ` return (extracted as Record<string, unknown>)[alias];`,
15098
+ ` const extract = (...args: unknown[]): unknown => {`,
15099
+ ` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
15100
+ ` const selector = args.length >= 3 ? args[2] : args[0];`,
15101
+ ` if (selector === undefined) return payload;`,
15102
+ ` const keys = __dlSelectorKeys(selector);`,
15103
+ ` if (keys) return Object.fromEntries(keys.map((key) => [key, __dlExtractTarget(payload, key) ?? null]));`,
15104
+ ` if (Array.isArray(selector)) return __dlFirstByPaths(payload, selector.map(String));`,
15105
+ ` if (typeof selector === 'string') {`,
15106
+ ` const extractedValue = __dlExtractedValue(result, selector);`,
15107
+ ` if (__dlMeaningful(extractedValue)) return __dlLegacyMatchedEnvelope(extractedValue, payload);`,
15108
+ ` return __dlExtractTarget(payload, selector) ?? __dlFirstByPaths(payload, selector);`,
15109
+ ` }`,
15110
+ ` return selector;`,
15111
+ ` };`,
15112
+ ` const extractList = (...args: unknown[]): unknown[] => {`,
15113
+ ` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
15114
+ ` const selector = args.length >= 3 ? args[2] : args[0];`,
15115
+ ` const rows = __dlFindList(payload, selector);`,
15116
+ ` const keys = __dlSelectorKeys(selector);`,
15117
+ ` return keys ? __dlProjectListRows(rows, keys, payload) : rows;`,
15118
+ ` };`,
15119
+ ` const get = (path: string): unknown => __dlExtractedValue(result, path) ?? __dlFirstByPaths(raw, path);`,
15120
+ ` let resolved: unknown;`,
15121
+ ` try {`,
15122
+ ` const extracted = extractor({ row, result, data: raw, raw, pick, extract, extractList, target: pick, get });`,
15123
+ ` resolved = typeof extracted === 'function' ? (extracted as (outputData: unknown) => unknown)(__dlLegacyOutputData(result, raw)) : extracted;`,
15124
+ ` } catch (error) {`,
15125
+ ` return __dlExtractorFailure(error);`,
15126
+ ` }`,
15127
+ ` if (resolved && typeof resolved === 'object' && !Array.isArray(resolved) && alias in (resolved as Record<string, unknown>)) {`,
15128
+ ` const aliasValue = (resolved as Record<string, unknown>)[alias];`,
15129
+ ` return legacyEnvelope && __dlMeaningful(aliasValue) ? __dlLegacyMatchedEnvelope(aliasValue, raw) : aliasValue;`,
15130
+ ` }`,
15131
+ ` if (Array.isArray(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
15132
+ ` if ((resolved === null || resolved === undefined) && __dlErrorPayload(raw)) return raw;`,
15133
+ ` if (legacyEnvelope && __dlMeaningful(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
15134
+ ` return resolved === undefined ? raw : resolved;`,
15135
+ `}`,
15136
+ ``,
15137
+ `function __dlMergeContextRecord(existing: unknown, defaults: Record<string, unknown>): Record<string, unknown> {`,
15138
+ ` if (!existing || typeof existing !== 'object' || Array.isArray(existing)) return { ...defaults };`,
15139
+ ` return { ...defaults, ...(existing as Record<string, unknown>) };`,
15140
+ `}`,
15141
+ ``,
15142
+ `function __dlRuntimePayload(tool: string, payload: Record<string, unknown>, row: Record<string, unknown>): Record<string, unknown> {`,
15143
+ ` if (tool !== 'run_javascript') return payload;`,
15144
+ ` return {`,
15145
+ ` ...payload,`,
15146
+ ` row: __dlMergeContextRecord(payload.row, row),`,
15147
+ ` input: __dlMergeContextRecord(payload.input, row),`,
15148
+ ` context: __dlMergeContextRecord(payload.context, row),`,
15149
+ ` };`,
15150
+ `}`,
15151
+ ``,
15152
+ `function __dlAliasCandidates(alias: string): string[] {`,
15153
+ ` const aliases: string[] = [];`,
15154
+ ` for (const candidate of [alias, alias.replace(/-/g, '_'), alias.replace(/_/g, '-')]) {`,
15155
+ ` if (candidate && !aliases.includes(candidate)) aliases.push(candidate);`,
15156
+ ` }`,
15157
+ ` return aliases;`,
15158
+ `}`,
15159
+ ``,
15160
+ `function __dlPlayResultValue(alias: string, result: unknown): unknown {`,
15161
+ ` if (!result || typeof result !== 'object' || Array.isArray(result)) return result;`,
15162
+ ` const record = result as Record<string, unknown>;`,
15163
+ ` const aliases = __dlAliasCandidates(alias);`,
15164
+ ` for (const key of aliases) {`,
15165
+ ` if (key in record && __dlMeaningful(record[key])) return __dlScalarValue(record[key]);`,
14280
15166
  ` }`,
14281
- ` return extracted === undefined ? raw : extracted;`,
15167
+ ` const values = Object.values(record).filter(__dlMeaningful);`,
15168
+ ` if (values.length === 1) return __dlScalarValue(values[0]);`,
15169
+ ` return result;`,
14282
15170
  `}`,
14283
15171
  ``,
14284
- `async function __dlRunCommand(input: { alias: string; callId: string; tool: string; payload: Record<string, unknown>; extract: ((args: { row: Record<string, unknown>; result: unknown; data: unknown; raw: unknown; pick: (paths: string[] | string) => unknown; extract: (paths: string[] | string) => unknown; target: (paths: string[] | string) => unknown }) => unknown) | null; runIf: ((row: Record<string, unknown>) => unknown) | null; row: Record<string, unknown>; stepCtx: { tools: { execute: (request: Record<string, unknown>) => Promise<unknown> } }; description?: string; force?: boolean }): Promise<unknown> {`,
15172
+ `async function __dlRunCommand(input: { alias: string; callId: string; tool: string; payload: Record<string, unknown>; extract: ((args: __DlExtractorHelpers) => unknown) | null; runIf: ((row: Record<string, unknown>) => unknown) | null; row: Record<string, unknown>; stepCtx: { tools: { execute: (request: Record<string, unknown>) => Promise<unknown> } }; description?: string; force?: boolean; legacyEnvelope?: boolean }): Promise<unknown> {`,
14285
15173
  ` if (input.runIf) {`,
14286
15174
  ` const shouldRun = input.runIf(input.row);`,
14287
15175
  ` if (!shouldRun) return null;`,
14288
15176
  ` }`,
15177
+ ` const payload = __dlRuntimePayload(input.tool, __dlTemplate(input.payload, input.row) as Record<string, unknown>, input.row);`,
14289
15178
  ` const result = await input.stepCtx.tools.execute({`,
14290
15179
  ` id: input.callId,`,
14291
15180
  ` tool: input.tool,`,
14292
- ` input: __dlTemplate(input.payload, input.row) as Record<string, unknown>,`,
15181
+ ` input: payload,`,
14293
15182
  ` ...(input.description ? { description: input.description } : {}),`,
14294
- ` ...(input.force ? { staleAfterSeconds: 0 } : {}),`,
15183
+ ` ...(input.force ? { staleAfterSeconds: 1 } : {}),`,
14295
15184
  ` });`,
14296
- ` return __dlExtract(input.alias, result, input.row, input.extract);`,
15185
+ ` return __dlExtract(input.alias, result, input.row, input.extract, Boolean(input.legacyEnvelope));`,
14297
15186
  `}`
14298
15187
  ].join("\n");
14299
15188
  }
14300
15189
 
14301
15190
  // src/cli/commands/enrich.ts
15191
+ var ENRICH_EXPORT_PAGE_SIZE = 5e3;
15192
+ var EXIT_SERVER2 = 5;
15193
+ var ENRICH_DEBUG_T0 = Date.now();
14302
15194
  var PLAN_SHAPING_OPTION_NAMES = [
14303
15195
  "with",
14304
15196
  "withWaterfall",
14305
15197
  "minResults",
14306
15198
  "endWaterfall"
14307
15199
  ];
14308
- var ENRICH_DEPRECATION_NOTICE = {
14309
- status: "deprecated",
14310
- message: "The enrich compatibility command is deprecated. This run generates a temporary .play.ts file and executes it through plays run.",
14311
- generatedPlayFile: "Temporary compatibility play file; deleted after the command exits.",
14312
- printGeneratedPlayCommand: "deepline enrich <same args> --dry-run > enrich.play.ts",
14313
- recommendedCommand: `deepline plays run enrich.play.ts --input '{"file":"<input.csv>"}'`
14314
- };
14315
- var ENRICH_DEPRECATION_TEXT = `${ENRICH_DEPRECATION_NOTICE.message} Print the generated play with: ${ENRICH_DEPRECATION_NOTICE.printGeneratedPlayCommand}. Then run: ${ENRICH_DEPRECATION_NOTICE.recommendedCommand}
14316
- `;
14317
15200
  function optionWasProvided(args, ...flags) {
14318
15201
  return args.some(
14319
15202
  (arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`))
@@ -14325,9 +15208,103 @@ function normalizeAlias2(value) {
14325
15208
  function hasPlanShapingArgs(args) {
14326
15209
  return optionWasProvided(args, "--with") || optionWasProvided(args, "--with-waterfall") || optionWasProvided(args, "--min-results") || optionWasProvided(args, "--end-waterfall");
14327
15210
  }
14328
- function printDeprecationNotice(options) {
14329
- if (!options.json) {
14330
- process.stderr.write(ENRICH_DEPRECATION_TEXT);
15211
+ function enrichDebugEnabled(env = process.env) {
15212
+ const raw = String(env.DEEPLINE_DEBUG_ENRICH ?? "").trim().toLowerCase();
15213
+ if (["1", "true", "yes", "on"].includes(raw)) {
15214
+ return true;
15215
+ }
15216
+ const nodeEnv = String(env.NODE_ENV ?? "").trim().toLowerCase();
15217
+ const deeplineEnv = String(env.DEEPLINE_ENV ?? "").trim().toLowerCase();
15218
+ return nodeEnv === "development" || ["dev", "development"].includes(deeplineEnv);
15219
+ }
15220
+ function decodeStringLiteral(raw) {
15221
+ try {
15222
+ const parsed = JSON.parse(raw);
15223
+ return typeof parsed === "string" ? parsed : null;
15224
+ } catch {
15225
+ const quote = raw[0];
15226
+ if ((quote === "'" || quote === '"') && raw[raw.length - 1] === quote) {
15227
+ return raw.slice(1, -1).replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
15228
+ }
15229
+ return null;
15230
+ }
15231
+ }
15232
+ function addStringArrayLiteralTargets(targets, arrayLiteral) {
15233
+ for (const match of arrayLiteral.matchAll(/(["'])(?:\\.|(?!\1).)*\1/g)) {
15234
+ const parsed = decodeStringLiteral(match[0] ?? "");
15235
+ if (parsed?.trim()) {
15236
+ targets.add(parsed.trim());
15237
+ }
15238
+ }
15239
+ }
15240
+ function extractExtractorTargetKeys(source) {
15241
+ const targets = /* @__PURE__ */ new Set();
15242
+ for (const match of source.matchAll(/\bkeys\s*:\s*(\[[^\]]*\])/g)) {
15243
+ addStringArrayLiteralTargets(targets, match[1] ?? "");
15244
+ }
15245
+ for (const match of source.matchAll(
15246
+ /\btarget\s*\(\s*(["'][^"']+["'])\s*\)/g
15247
+ )) {
15248
+ const parsed = decodeStringLiteral(match[1] ?? "");
15249
+ if (parsed?.trim()) {
15250
+ targets.add(parsed.trim());
15251
+ }
15252
+ }
15253
+ for (const match of source.matchAll(
15254
+ /\bextract\s*\(\s*["'][^"']+["']\s*,[^,]*,\s*(["'][^"']+["'])\s*\)/g
15255
+ )) {
15256
+ const parsed = decodeStringLiteral(match[1] ?? "");
15257
+ if (parsed?.trim()) {
15258
+ targets.add(parsed.trim());
15259
+ }
15260
+ }
15261
+ return [...targets].sort();
15262
+ }
15263
+ function flattenEnrichStepCommands(commands) {
15264
+ const steps = [];
15265
+ for (const command of commands) {
15266
+ if ("with_waterfall" in command) {
15267
+ steps.push(...flattenEnrichStepCommands(command.commands));
15268
+ continue;
15269
+ }
15270
+ if (!command.disabled) {
15271
+ steps.push(command);
15272
+ }
15273
+ }
15274
+ return steps;
15275
+ }
15276
+ function buildEnrichDebugValidationLines(config) {
15277
+ return flattenEnrichStepCommands(config.commands).filter(
15278
+ (command) => typeof command.extract_js === "string" && command.extract_js.trim()
15279
+ ).map((command) => {
15280
+ const source = command.extract_js ?? "";
15281
+ const targets = extractExtractorTargetKeys(source);
15282
+ const mode = /\bextractList\s*\(/.test(source) ? "list" : "scalar";
15283
+ return [
15284
+ "validate extractor",
15285
+ `column=${command.alias}`,
15286
+ `tool_id=${command.tool || "(none)"}`,
15287
+ `mode=${mode}`,
15288
+ "has_result_sample=false",
15289
+ "sample_keys=<none>",
15290
+ `targets=${JSON.stringify(targets)}`
15291
+ ].join(" ");
15292
+ });
15293
+ }
15294
+ function emitEnrichDebug(message) {
15295
+ if (!enrichDebugEnabled()) {
15296
+ return;
15297
+ }
15298
+ const now = /* @__PURE__ */ new Date();
15299
+ const elapsedMs = Date.now() - ENRICH_DEBUG_T0;
15300
+ process.stderr.write(
15301
+ `[deepline:enrich] ${now.toISOString()} +${elapsedMs}ms ${message}
15302
+ `
15303
+ );
15304
+ }
15305
+ function emitEnrichDebugValidationLines(config) {
15306
+ for (const line of buildEnrichDebugValidationLines(config)) {
15307
+ emitEnrichDebug(line);
14331
15308
  }
14332
15309
  }
14333
15310
  function expandAtFilePath(rawPath) {
@@ -14464,15 +15441,18 @@ async function buildPlanArgs(args) {
14464
15441
  "--csv",
14465
15442
  "--output",
14466
15443
  "--config",
15444
+ "--name",
14467
15445
  "--rows",
14468
- "--with-force"
15446
+ "--with-force",
15447
+ "--timeout"
14469
15448
  ]);
14470
15449
  const localBooleanOptions = /* @__PURE__ */ new Set([
14471
15450
  "--dry-run",
14472
15451
  "--json",
14473
15452
  "--force",
14474
15453
  "--all",
14475
- "--in-place"
15454
+ "--in-place",
15455
+ "--no-open"
14476
15456
  ]);
14477
15457
  const planArgs = [];
14478
15458
  for (let index = 0; index < args.length; index += 1) {
@@ -14538,7 +15518,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14538
15518
  const output2 = (0, import_node_path13.resolve)(outputPath);
14539
15519
  if (input2 === output2) {
14540
15520
  throw new Error(
14541
- "--output must be a different path from --input. --in-place is not supported by this V2 enrich runner yet."
15521
+ "--input and --output must be different files when not using --in-place."
14542
15522
  );
14543
15523
  }
14544
15524
  try {
@@ -14548,7 +15528,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14548
15528
  ]);
14549
15529
  if (inputInfo.dev === outputInfo.dev && inputInfo.ino === outputInfo.ino) {
14550
15530
  throw new Error(
14551
- "--output must be a different file from --input. --in-place is not supported by this V2 enrich runner yet."
15531
+ "--input and --output must be different files when not using --in-place."
14552
15532
  );
14553
15533
  }
14554
15534
  } catch (error) {
@@ -14562,6 +15542,18 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14562
15542
  throw error;
14563
15543
  }
14564
15544
  }
15545
+ async function regularFileExists(path) {
15546
+ try {
15547
+ const info = await (0, import_promises5.stat)((0, import_node_path13.resolve)(path));
15548
+ return info.isFile();
15549
+ } catch (error) {
15550
+ const code = error && typeof error === "object" ? error.code : void 0;
15551
+ if (code === "ENOENT") {
15552
+ return false;
15553
+ }
15554
+ throw error;
15555
+ }
15556
+ }
14565
15557
  async function readConfig(path) {
14566
15558
  const source = await (0, import_promises5.readFile)((0, import_node_path13.resolve)(path), "utf8");
14567
15559
  let parsed;
@@ -14584,27 +15576,30 @@ function parseRows(value, all) {
14584
15576
  const trimmed = value.trim();
14585
15577
  const range = trimmed.match(/^(\d*)\s*:\s*(\d*)$/);
14586
15578
  if (range) {
14587
- if (!range[1] && !range[2]) {
15579
+ if (!range[1]) {
14588
15580
  throw new Error(
14589
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15581
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14590
15582
  );
14591
15583
  }
14592
15584
  const start = range[1] ? Number.parseInt(range[1], 10) : 0;
14593
15585
  const end = range[2] ? Number.parseInt(range[2], 10) : null;
15586
+ if (end !== null && end < start) {
15587
+ throw new Error("Invalid --rows range: end must be >= start.");
15588
+ }
14594
15589
  return { rowStart: start, rowEnd: end };
14595
15590
  }
14596
15591
  if (!/^\d+$/.test(trimmed)) {
14597
15592
  throw new Error(
14598
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15593
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14599
15594
  );
14600
15595
  }
14601
15596
  const single = Number.parseInt(trimmed, 10);
14602
15597
  if (!Number.isFinite(single) || single < 0) {
14603
15598
  throw new Error(
14604
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15599
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14605
15600
  );
14606
15601
  }
14607
- return { rowStart: single, rowEnd: single + 1 };
15602
+ return { rowStart: single, rowEnd: single };
14608
15603
  }
14609
15604
  function summarizePlan(config, playSource) {
14610
15605
  const steps = [];
@@ -14686,9 +15681,9 @@ function parseWithForceAliases(values) {
14686
15681
  return aliases;
14687
15682
  }
14688
15683
  function resolveForceAliases(config, options) {
14689
- const { allAliases, scalarAliases, waterfallGroups } = collectCommandAliases(config);
15684
+ const { allAliases, waterfallGroups } = collectCommandAliases(config);
14690
15685
  if (options.force) {
14691
- return new Set(scalarAliases);
15686
+ return new Set(allAliases);
14692
15687
  }
14693
15688
  const requested = parseWithForceAliases(options.withForce);
14694
15689
  const unknown = [...requested].filter((alias) => !allAliases.has(alias));
@@ -14697,62 +15692,701 @@ function resolveForceAliases(config, options) {
14697
15692
  `--with-force references unknown --with column alias(es): ${unknown.sort().join(", ")}.`
14698
15693
  );
14699
15694
  }
14700
- const resolved = /* @__PURE__ */ new Set();
14701
- for (const alias of requested) {
14702
- const children = waterfallGroups.get(alias);
14703
- if (children) {
14704
- for (const child of children) {
14705
- resolved.add(child);
15695
+ const resolved = /* @__PURE__ */ new Set();
15696
+ for (const alias of requested) {
15697
+ const children = waterfallGroups.get(alias);
15698
+ if (children) {
15699
+ resolved.add(alias);
15700
+ for (const child of children) {
15701
+ resolved.add(child);
15702
+ }
15703
+ } else {
15704
+ resolved.add(alias);
15705
+ }
15706
+ }
15707
+ return resolved;
15708
+ }
15709
+ function isPersistedFailureCell(value) {
15710
+ const raw = String(value ?? "").trim();
15711
+ if (!raw) {
15712
+ return false;
15713
+ }
15714
+ if (cellFailureError(value)) {
15715
+ return true;
15716
+ }
15717
+ return raw.includes("Error:") || raw.includes("Traceback");
15718
+ }
15719
+ function isPersistedFailureStatusCell(value) {
15720
+ const raw = String(value ?? "").trim().toLowerCase();
15721
+ return raw === "error" || raw === "failed" || raw === "failure";
15722
+ }
15723
+ function stripFailureCompanionSuffix(key) {
15724
+ const lowerKey = key.toLowerCase();
15725
+ for (const suffix of [".error", "__error"]) {
15726
+ if (lowerKey.endsWith(suffix)) {
15727
+ return { alias: key.slice(0, -suffix.length), kind: "error" };
15728
+ }
15729
+ }
15730
+ for (const suffix of [".status", "__status"]) {
15731
+ if (lowerKey.endsWith(suffix)) {
15732
+ return { alias: key.slice(0, -suffix.length), kind: "status" };
15733
+ }
15734
+ }
15735
+ return null;
15736
+ }
15737
+ function collectFailedInputAliases(config, sourceCsvPath, rows) {
15738
+ const { scalarAliases } = collectCommandAliases(config);
15739
+ if (scalarAliases.size === 0) {
15740
+ return /* @__PURE__ */ new Set();
15741
+ }
15742
+ const inputRows = readCsvRows(sourceCsvPath);
15743
+ if (inputRows.length === 0) {
15744
+ return /* @__PURE__ */ new Set();
15745
+ }
15746
+ const start = rows.rowStart ?? 0;
15747
+ const maxEnd = Math.max(start, inputRows.length - 1);
15748
+ const inclusiveEnd = rows.rowEnd === null || rows.rowEnd === void 0 ? maxEnd : Math.min(maxEnd, rows.rowEnd);
15749
+ const failedAliases = /* @__PURE__ */ new Set();
15750
+ for (let index = start; index <= inclusiveEnd; index += 1) {
15751
+ const row = inputRows[index];
15752
+ if (!row) {
15753
+ continue;
15754
+ }
15755
+ for (const [key, value] of Object.entries(row)) {
15756
+ const alias = normalizeAlias2(key);
15757
+ if (scalarAliases.has(alias) && isPersistedFailureCell(value)) {
15758
+ failedAliases.add(alias);
15759
+ continue;
15760
+ }
15761
+ const companion = stripFailureCompanionSuffix(key);
15762
+ if (!companion) {
15763
+ continue;
15764
+ }
15765
+ const companionAlias = normalizeAlias2(companion.alias);
15766
+ if (!scalarAliases.has(companionAlias)) {
15767
+ continue;
15768
+ }
15769
+ if (companion.kind === "error" ? isPersistedFailureCell(value) : isPersistedFailureStatusCell(value)) {
15770
+ failedAliases.add(companionAlias);
15771
+ }
15772
+ }
15773
+ }
15774
+ return failedAliases;
15775
+ }
15776
+ function parseJsonOutput(stdout) {
15777
+ const trimmed = stdout.trim();
15778
+ if (!trimmed) return null;
15779
+ try {
15780
+ return JSON.parse(trimmed);
15781
+ } catch {
15782
+ const start = trimmed.lastIndexOf("\n{");
15783
+ if (start >= 0) {
15784
+ return JSON.parse(trimmed.slice(start + 1));
15785
+ }
15786
+ throw new Error(
15787
+ "The generated play completed but did not emit parseable JSON."
15788
+ );
15789
+ }
15790
+ }
15791
+ function extractRunIdFromPlayOutput(stdout) {
15792
+ const runIdLine = stdout.match(/^\s*run id:\s*(play\/\S+\/run\/\S+)\s*$/im);
15793
+ if (runIdLine?.[1]) {
15794
+ return runIdLine[1];
15795
+ }
15796
+ return stdout.match(/\b(play\/\S+\/run\/\S+)/)?.[1] ?? null;
15797
+ }
15798
+ async function resolveWatchedGeneratedPlayStatus(input2) {
15799
+ const runId = extractRunIdFromPlayOutput(input2.stdout);
15800
+ if (!runId) {
15801
+ if (input2.exitCode === 0) {
15802
+ throw new Error(
15803
+ "The generated play completed but did not print a run id to inspect."
15804
+ );
15805
+ }
15806
+ return null;
15807
+ }
15808
+ return input2.client.runs.get(runId, { full: true });
15809
+ }
15810
+ function isRecord6(value) {
15811
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
15812
+ }
15813
+ async function captureStdout(run, options = {}) {
15814
+ const originalWrite = process.stdout.write.bind(process.stdout);
15815
+ let stdout = "";
15816
+ process.stdout.write = ((chunk, ..._args) => {
15817
+ stdout += typeof chunk === "string" ? chunk : String(chunk);
15818
+ if (options.passthrough) {
15819
+ return originalWrite(typeof chunk === "string" ? chunk : String(chunk));
15820
+ }
15821
+ return true;
15822
+ });
15823
+ try {
15824
+ const result = await run();
15825
+ return { result, stdout };
15826
+ } finally {
15827
+ process.stdout.write = originalWrite;
15828
+ }
15829
+ }
15830
+ function isPlayStartStreamEndedError(error) {
15831
+ return error instanceof DeeplineError && error.code === "PLAY_START_STREAM_ENDED" || error instanceof Error && error.message.includes(
15832
+ "Play start stream ended before a terminal status"
15833
+ );
15834
+ }
15835
+ async function runGeneratedEnrichPlay(runArgs, options = {}) {
15836
+ for (let attempt = 0; attempt < 2; attempt += 1) {
15837
+ try {
15838
+ return await captureStdout(() => handlePlayRun(runArgs), {
15839
+ passthrough: options.passthroughStdout
15840
+ });
15841
+ } catch (error) {
15842
+ if (attempt === 0 && isPlayStartStreamEndedError(error)) {
15843
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
15844
+ continue;
15845
+ }
15846
+ throw error;
15847
+ }
15848
+ }
15849
+ return captureStdout(() => handlePlayRun(runArgs), {
15850
+ passthrough: options.passthroughStdout
15851
+ });
15852
+ }
15853
+ async function writeOutputCsv(outputPath, status, options) {
15854
+ let rowsInfo = extractCanonicalRowsInfo(status);
15855
+ if (!rowsInfo) {
15856
+ throw new Error("The generated play did not return row-shaped output.");
15857
+ }
15858
+ if (!rowsInfo.complete && options?.client) {
15859
+ rowsInfo = await fetchBackingRowsForCsvExport({
15860
+ client: options.client,
15861
+ status,
15862
+ rowsInfo
15863
+ }) ?? rowsInfo;
15864
+ }
15865
+ assertCompleteRowsForCsvExport(rowsInfo, status, outputPath);
15866
+ const merged = mergeRowsForCsvExport(rowsInfo.rows, options);
15867
+ const columns = orderEnrichCsvColumns(
15868
+ dataExportColumns(merged.rows, [
15869
+ ...merged.preferredColumns,
15870
+ ...rowsInfo.columns
15871
+ ]),
15872
+ options?.config
15873
+ );
15874
+ await (0, import_promises5.writeFile)(
15875
+ (0, import_node_path13.resolve)(outputPath),
15876
+ csvStringFromRows(merged.rows, columns),
15877
+ "utf8"
15878
+ );
15879
+ return {
15880
+ rows: merged.rows.length,
15881
+ path: (0, import_node_path13.resolve)(outputPath),
15882
+ enrichedRows: rowsInfo.rows
15883
+ };
15884
+ }
15885
+ function recordField(value, key) {
15886
+ return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
15887
+ }
15888
+ function extractRunId(status) {
15889
+ const candidates = [
15890
+ recordField(status, "runId"),
15891
+ recordField(recordField(status, "run"), "id")
15892
+ ];
15893
+ for (const candidate of candidates) {
15894
+ if (typeof candidate === "string" && candidate.trim()) {
15895
+ return candidate.trim();
15896
+ }
15897
+ }
15898
+ return null;
15899
+ }
15900
+ function extractPlayName2(status) {
15901
+ const run = recordField(status, "run");
15902
+ const candidates = [
15903
+ recordField(status, "playName"),
15904
+ recordField(status, "name"),
15905
+ recordField(run, "playName"),
15906
+ recordField(run, "name")
15907
+ ];
15908
+ for (const candidate of candidates) {
15909
+ if (typeof candidate === "string" && candidate.trim()) {
15910
+ return candidate.trim();
15911
+ }
15912
+ }
15913
+ return null;
15914
+ }
15915
+ function exportableSheetRow2(row) {
15916
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
15917
+ return null;
15918
+ }
15919
+ const record = row;
15920
+ const data = record.data;
15921
+ if (data && typeof data === "object" && !Array.isArray(data)) {
15922
+ return data;
15923
+ }
15924
+ const fallback = { ...record };
15925
+ for (const key of [
15926
+ "key",
15927
+ "status",
15928
+ "cellMeta",
15929
+ "inputIndex",
15930
+ "runId",
15931
+ "error",
15932
+ "stage",
15933
+ "provider",
15934
+ "seq",
15935
+ "createdAt",
15936
+ "updatedAt"
15937
+ ]) {
15938
+ delete fallback[key];
15939
+ }
15940
+ return fallback;
15941
+ }
15942
+ function collectHardFailureAliases(config) {
15943
+ const aliases = [];
15944
+ const seen = /* @__PURE__ */ new Set();
15945
+ for (const command of config.commands) {
15946
+ if ("with_waterfall" in command) {
15947
+ continue;
15948
+ }
15949
+ if (command.disabled) {
15950
+ continue;
15951
+ }
15952
+ const alias = command.alias.trim();
15953
+ if (!alias || seen.has(alias)) {
15954
+ continue;
15955
+ }
15956
+ seen.add(alias);
15957
+ aliases.push(alias);
15958
+ }
15959
+ return aliases;
15960
+ }
15961
+ function cellFailureError(value) {
15962
+ const parsed = parseMaybeJsonObject(value);
15963
+ if (!isRecord6(parsed)) {
15964
+ return null;
15965
+ }
15966
+ const status = typeof parsed.status === "string" ? parsed.status.trim().toLowerCase() : "";
15967
+ const directError = typeof parsed.error === "string" ? parsed.error.trim() : typeof parsed.last_error === "string" ? parsed.last_error.trim() : "";
15968
+ const result = isRecord6(parsed.result) ? parsed.result : null;
15969
+ const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
15970
+ if (!directError && !resultError && status !== "error" && status !== "failed") {
15971
+ return null;
15972
+ }
15973
+ const message = directError || resultError || `Column status: ${status}`;
15974
+ return {
15975
+ message,
15976
+ ...typeof parsed.operation === "string" && parsed.operation.trim() ? { operation: parsed.operation.trim() } : {},
15977
+ ...typeof parsed.provider === "string" && parsed.provider.trim() ? { provider: parsed.provider.trim() } : {}
15978
+ };
15979
+ }
15980
+ function buildEnrichWaterfallSummaryLines(config, rows) {
15981
+ if (rows.length === 0) {
15982
+ return [];
15983
+ }
15984
+ const lines = [];
15985
+ for (const command of config.commands) {
15986
+ if (!("with_waterfall" in command)) {
15987
+ continue;
15988
+ }
15989
+ for (const child of command.commands) {
15990
+ if ("with_waterfall" in child || child.disabled) {
15991
+ continue;
15992
+ }
15993
+ const failures = rows.filter(
15994
+ (row) => cellFailureError(row[child.alias])
15995
+ ).length;
15996
+ if (failures > 0) {
15997
+ lines.push(
15998
+ `Column '${child.alias}' had high failure rate (${failures}/${rows.length}); continuing waterfall group '${command.with_waterfall}'.`
15999
+ );
16000
+ }
16001
+ }
16002
+ }
16003
+ return lines;
16004
+ }
16005
+ function collectEnrichFailureJobs(input2) {
16006
+ const aliases = collectHardFailureAliases(input2.config);
16007
+ if (aliases.length === 0 || input2.rows.length === 0) {
16008
+ return [];
16009
+ }
16010
+ const rowOffset = input2.rowStart ?? 0;
16011
+ const jobs = [];
16012
+ input2.rows.forEach((row, rowIndex) => {
16013
+ aliases.forEach((alias, aliasIndex) => {
16014
+ const failure = cellFailureError(row[alias]);
16015
+ if (!failure) {
16016
+ return;
16017
+ }
16018
+ const rowId = rowOffset + rowIndex;
16019
+ jobs.push({
16020
+ job_id: `row-${rowId}-${alias}`,
16021
+ row_id: rowId,
16022
+ col_index: aliasIndex,
16023
+ column: alias,
16024
+ status: "failed",
16025
+ last_error: failure.message,
16026
+ error: failure.message,
16027
+ ...failure.operation ? { operation: failure.operation } : {},
16028
+ ...failure.provider ? { provider: failure.provider } : {}
16029
+ });
16030
+ });
16031
+ });
16032
+ return jobs;
16033
+ }
16034
+ function summarizeFailedJobError(value) {
16035
+ const text = String(value ?? "").trim();
16036
+ return text.length > 500 ? `${text.slice(0, 497)}...` : text;
16037
+ }
16038
+ function formatFailureReportCommand(reportPath) {
16039
+ const quoted = reportPath.replace(/'/g, `'\\''`);
16040
+ return `Inspect failed jobs: jq -r '.jobs[] | [.job_id,.row_id,.col_index,.column,.status,.last_error] | @tsv' '${quoted}'`;
16041
+ }
16042
+ async function persistEnrichFailureReport(input2) {
16043
+ if (input2.jobs.length === 0) {
16044
+ return null;
16045
+ }
16046
+ const stateDir = (0, import_node_path13.join)((0, import_node_os7.homedir)(), ".local", "deepline", "runtime", "state");
16047
+ await (0, import_promises5.mkdir)(stateDir, { recursive: true });
16048
+ const reportPath = (0, import_node_path13.join)(
16049
+ stateDir,
16050
+ `run-block-failures-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
16051
+ );
16052
+ const report = {
16053
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
16054
+ api_url: input2.apiUrl,
16055
+ output_csv: input2.outputPath ?? "",
16056
+ summary: {
16057
+ failed_count: input2.jobs.length,
16058
+ canceled_count: 0,
16059
+ pending_count: 0,
16060
+ missing_count: 0,
16061
+ total_jobs: input2.jobs.length
16062
+ },
16063
+ jobs: input2.jobs,
16064
+ failed_jobs: input2.jobs,
16065
+ canceled_jobs: [],
16066
+ pending_jobs: [],
16067
+ missing_job_ids: []
16068
+ };
16069
+ if (input2.rows.rowStart !== null && input2.rows.rowEnd !== null) {
16070
+ report.rows = { start: input2.rows.rowStart, end: input2.rows.rowEnd };
16071
+ }
16072
+ await (0, import_promises5.writeFile)(reportPath, `${JSON.stringify(report, null, 2)}
16073
+ `, "utf8");
16074
+ return reportPath;
16075
+ }
16076
+ async function maybeEmitEnrichFailureReport(input2) {
16077
+ const jobs = collectEnrichFailureJobs({
16078
+ config: input2.config,
16079
+ rows: input2.rows,
16080
+ rowStart: input2.rowRange.rowStart
16081
+ });
16082
+ if (jobs.length === 0) {
16083
+ return null;
16084
+ }
16085
+ const reportPath = await persistEnrichFailureReport({
16086
+ jobs,
16087
+ apiUrl: input2.client.baseUrl,
16088
+ outputPath: input2.outputPath,
16089
+ rows: input2.rowRange
16090
+ });
16091
+ process.stderr.write("Execution failed.\n");
16092
+ process.stderr.write("Failure preview (up to 5 failed jobs):\n");
16093
+ process.stderr.write("job_id row_id col_index column status error\n");
16094
+ for (const job of jobs.slice(0, 5)) {
16095
+ process.stderr.write(
16096
+ `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
16097
+ `
16098
+ );
16099
+ }
16100
+ if (jobs.length > 5) {
16101
+ process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
16102
+ `);
16103
+ }
16104
+ if (reportPath) {
16105
+ process.stderr.write(`Full failure report: ${reportPath}
16106
+ `);
16107
+ process.stderr.write(`${formatFailureReportCommand(reportPath)}
16108
+ `);
16109
+ process.stderr.write("Enrichment failed. See failure report above.\n");
16110
+ return { path: reportPath, jobs };
16111
+ }
16112
+ process.stderr.write("Enrichment failed.\n");
16113
+ return { path: "", jobs };
16114
+ }
16115
+ function mergePreferredColumns(preferredColumns, rows) {
16116
+ const columns = [];
16117
+ const seen = /* @__PURE__ */ new Set();
16118
+ for (const column of preferredColumns) {
16119
+ if (!column || seen.has(column)) {
16120
+ continue;
16121
+ }
16122
+ seen.add(column);
16123
+ columns.push(column);
16124
+ }
16125
+ for (const row of rows) {
16126
+ for (const column of Object.keys(row)) {
16127
+ if (seen.has(column)) {
16128
+ continue;
16129
+ }
16130
+ seen.add(column);
16131
+ columns.push(column);
16132
+ }
16133
+ }
16134
+ return columns;
16135
+ }
16136
+ async function fetchBackingRowsForCsvExport(input2) {
16137
+ const runId = extractRunId(input2.status);
16138
+ const playName = extractPlayName2(input2.status);
16139
+ const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
16140
+ if (!runId || !playName || !tableNamespace) {
16141
+ return null;
16142
+ }
16143
+ const sheetRows = [];
16144
+ let offset = 0;
16145
+ let expectedTotal = input2.rowsInfo.totalRows;
16146
+ while (true) {
16147
+ const page = await input2.client.runs.exportDatasetRows({
16148
+ playName,
16149
+ tableNamespace,
16150
+ runId,
16151
+ limit: ENRICH_EXPORT_PAGE_SIZE,
16152
+ offset
16153
+ });
16154
+ sheetRows.push(...page.rows);
16155
+ const summaryTotal = page.summary?.stats?.total;
16156
+ if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
16157
+ expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
16158
+ }
16159
+ if (page.rows.length < ENRICH_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
16160
+ break;
16161
+ }
16162
+ offset += page.rows.length;
16163
+ }
16164
+ const rows = sheetRows.map(exportableSheetRow2).filter((row) => Boolean(row));
16165
+ if (rows.length < input2.rowsInfo.totalRows) {
16166
+ return null;
16167
+ }
16168
+ return {
16169
+ ...input2.rowsInfo,
16170
+ rows,
16171
+ columns: mergePreferredColumns(
16172
+ input2.rowsInfo.columnsExplicit ? input2.rowsInfo.columns : [],
16173
+ rows
16174
+ ),
16175
+ totalRows: rows.length,
16176
+ complete: true,
16177
+ source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
16178
+ };
16179
+ }
16180
+ function mergeRowsForCsvExport(enrichedRows, options) {
16181
+ const rows = dataExportRows(normalizeEnrichRowsForCsvExport(enrichedRows));
16182
+ const range = options?.rows;
16183
+ if (range?.rowStart === null || range?.rowStart === void 0) {
16184
+ return { rows, preferredColumns: [] };
16185
+ }
16186
+ if (!options?.sourceCsvPath) {
16187
+ return { rows, preferredColumns: [] };
16188
+ }
16189
+ const parsedBaseRows = readCsvRows(options.sourceCsvPath);
16190
+ const preferredColumns = Object.keys(parsedBaseRows[0] ?? {});
16191
+ const baseRows = parsedBaseRows.map(
16192
+ (row) => ({ ...row })
16193
+ );
16194
+ const start = Math.max(0, range.rowStart);
16195
+ const maxEnd = Math.max(start, baseRows.length - 1);
16196
+ const inclusiveEnd = range.rowEnd === null ? maxEnd : Math.min(maxEnd, range.rowEnd);
16197
+ const merged = [...baseRows];
16198
+ let selectedIndex = 0;
16199
+ for (let rowIndex = start; rowIndex <= inclusiveEnd; rowIndex += 1) {
16200
+ const enriched = rows[selectedIndex++];
16201
+ if (!enriched) {
16202
+ break;
16203
+ }
16204
+ const base = merged[rowIndex] ?? {};
16205
+ merged[rowIndex] = {
16206
+ ...base,
16207
+ ...enriched
16208
+ };
16209
+ const metadata = mergeLegacyMetadataCell(
16210
+ base._metadata,
16211
+ enriched._metadata
16212
+ );
16213
+ if (metadata !== void 0) {
16214
+ merged[rowIndex]._metadata = metadata;
16215
+ }
16216
+ }
16217
+ return { rows: merged, preferredColumns };
16218
+ }
16219
+ function collectConfigScalarAliasOrder(config) {
16220
+ const aliases = [];
16221
+ const seen = /* @__PURE__ */ new Set();
16222
+ const addAlias = (alias) => {
16223
+ const normalized = normalizeAlias2(alias);
16224
+ if (!normalized || seen.has(normalized)) {
16225
+ return;
16226
+ }
16227
+ seen.add(normalized);
16228
+ aliases.push(alias);
16229
+ };
16230
+ for (const command of config.commands) {
16231
+ if ("with_waterfall" in command) {
16232
+ for (const child of command.commands) {
16233
+ if ("with_waterfall" in child || child.disabled) {
16234
+ continue;
16235
+ }
16236
+ addAlias(child.alias);
16237
+ }
16238
+ continue;
16239
+ }
16240
+ if (!command.disabled) {
16241
+ addAlias(command.alias);
16242
+ }
16243
+ }
16244
+ return aliases;
16245
+ }
16246
+ function orderEnrichCsvColumns(columns, config) {
16247
+ if (!config || columns.length < 2) {
16248
+ return columns;
16249
+ }
16250
+ const aliasOrder = collectConfigScalarAliasOrder(config);
16251
+ if (aliasOrder.length < 2) {
16252
+ return columns;
16253
+ }
16254
+ const normalizedAliasOrder = aliasOrder.map(normalizeAlias2);
16255
+ const requestedColumnByAlias = /* @__PURE__ */ new Map();
16256
+ for (const column of columns) {
16257
+ const normalizedColumn = normalizeAlias2(column);
16258
+ if (normalizedAliasOrder.includes(normalizedColumn) && !requestedColumnByAlias.has(normalizedColumn)) {
16259
+ requestedColumnByAlias.set(normalizedColumn, column);
16260
+ }
16261
+ }
16262
+ const requestedColumns = normalizedAliasOrder.map((alias) => requestedColumnByAlias.get(alias)).filter((column) => Boolean(column));
16263
+ if (requestedColumns.length < 2) {
16264
+ return columns;
16265
+ }
16266
+ const requestedSet = new Set(requestedColumns);
16267
+ const firstRequestedIndex = columns.findIndex(
16268
+ (column) => requestedSet.has(column)
16269
+ );
16270
+ if (firstRequestedIndex < 0) {
16271
+ return columns;
16272
+ }
16273
+ const withoutRequested = columns.filter(
16274
+ (column) => !requestedSet.has(column)
16275
+ );
16276
+ withoutRequested.splice(firstRequestedIndex, 0, ...requestedColumns);
16277
+ return withoutRequested;
16278
+ }
16279
+ function isNonEmptyCsvCell(value) {
16280
+ return value !== null && value !== void 0 && String(value).trim() !== "";
16281
+ }
16282
+ function normalizeEnrichRowsForCsvExport(rows) {
16283
+ return rows.map((row) => {
16284
+ const metadata = legacyMetadataFromRow(row);
16285
+ if (!metadata) {
16286
+ return row;
16287
+ }
16288
+ const normalized = { ...row, _metadata: metadata };
16289
+ delete normalized.metadata;
16290
+ delete normalized["metadata.columns"];
16291
+ delete normalized.metadata__columns;
16292
+ for (const key of Object.keys(normalized)) {
16293
+ if (key.startsWith("metadata.columns.") || key.startsWith("metadata__columns.")) {
16294
+ delete normalized[key];
16295
+ }
16296
+ }
16297
+ return normalized;
16298
+ });
16299
+ }
16300
+ function legacyMetadataFromRow(row) {
16301
+ const direct = parseLegacyMetadataCell(row._metadata);
16302
+ if (direct && isRecord6(direct.columns)) {
16303
+ return direct;
16304
+ }
16305
+ const relocated = parseLegacyMetadataCell(row.metadata);
16306
+ if (relocated && isRecord6(relocated.columns)) {
16307
+ return relocated;
16308
+ }
16309
+ const flattenedColumns = parseLegacyMetadataCell(row["metadata.columns"]);
16310
+ if (flattenedColumns) {
16311
+ return { columns: flattenedColumns };
16312
+ }
16313
+ const flattenedUnderscoreColumns = parseLegacyMetadataCell(
16314
+ row.metadata__columns
16315
+ );
16316
+ if (flattenedUnderscoreColumns) {
16317
+ return { columns: flattenedUnderscoreColumns };
16318
+ }
16319
+ return null;
16320
+ }
16321
+ function parseLegacyMetadataCell(value) {
16322
+ const parsed = parseMaybeJsonObject(value);
16323
+ if (isRecord6(parsed)) {
16324
+ return parsed;
16325
+ }
16326
+ if (typeof value !== "string") {
16327
+ return null;
16328
+ }
16329
+ const trimmed = value.trim();
16330
+ if (!trimmed) {
16331
+ return null;
16332
+ }
16333
+ const candidates = [trimmed];
16334
+ if (trimmed.includes('\\"')) {
16335
+ candidates.push(trimmed.replace(/\\"/g, '"'));
16336
+ }
16337
+ for (const candidate of candidates) {
16338
+ try {
16339
+ const decoded = JSON.parse(candidate);
16340
+ if (isRecord6(decoded)) {
16341
+ return decoded;
14706
16342
  }
14707
- } else {
14708
- resolved.add(alias);
16343
+ if (typeof decoded === "string") {
16344
+ const nested = JSON.parse(decoded);
16345
+ if (isRecord6(nested)) {
16346
+ return nested;
16347
+ }
16348
+ }
16349
+ } catch {
14709
16350
  }
14710
16351
  }
14711
- return resolved;
16352
+ return null;
14712
16353
  }
14713
- function parseJsonOutput(stdout) {
14714
- const trimmed = stdout.trim();
14715
- if (!trimmed) return null;
14716
- try {
14717
- return JSON.parse(trimmed);
14718
- } catch {
14719
- const start = trimmed.lastIndexOf("\n{");
14720
- if (start >= 0) {
14721
- return JSON.parse(trimmed.slice(start + 1));
14722
- }
14723
- throw new Error(
14724
- "The generated play completed but did not emit parseable JSON."
14725
- );
16354
+ function mergeLegacyMetadataRecords(base, enriched) {
16355
+ const baseColumns = isRecord6(base.columns) ? base.columns : null;
16356
+ const enrichedColumns = isRecord6(enriched.columns) ? enriched.columns : null;
16357
+ const merged = {
16358
+ ...base,
16359
+ ...enriched
16360
+ };
16361
+ if (baseColumns || enrichedColumns) {
16362
+ merged.columns = {
16363
+ ...baseColumns ?? {},
16364
+ ...enrichedColumns ?? {}
16365
+ };
14726
16366
  }
16367
+ return merged;
14727
16368
  }
14728
- async function captureStdout(run) {
14729
- const originalWrite = process.stdout.write.bind(process.stdout);
14730
- let stdout = "";
14731
- process.stdout.write = ((chunk, ..._args) => {
14732
- stdout += typeof chunk === "string" ? chunk : String(chunk);
14733
- return true;
14734
- });
14735
- try {
14736
- const result = await run();
14737
- return { result, stdout };
14738
- } finally {
14739
- process.stdout.write = originalWrite;
16369
+ function mergeLegacyMetadataCell(base, enriched) {
16370
+ const baseRecord = parseLegacyMetadataCell(base);
16371
+ const enrichedRecord = parseLegacyMetadataCell(enriched);
16372
+ if (baseRecord && enrichedRecord) {
16373
+ return JSON.stringify(
16374
+ mergeLegacyMetadataRecords(baseRecord, enrichedRecord)
16375
+ );
14740
16376
  }
14741
- }
14742
- async function writeOutputCsv(outputPath, status) {
14743
- const rowsInfo = extractCanonicalRowsInfo(status);
14744
- if (!rowsInfo) {
14745
- throw new Error("The generated play did not return row-shaped output.");
16377
+ if (enrichedRecord) {
16378
+ return JSON.stringify(enrichedRecord);
14746
16379
  }
14747
- assertCompleteRowsForCsvExport(rowsInfo, status, outputPath);
14748
- const rows = dataExportRows(rowsInfo.rows);
14749
- const columns = dataExportColumns(rows, rowsInfo.columns);
14750
- await (0, import_promises5.writeFile)(
14751
- (0, import_node_path13.resolve)(outputPath),
14752
- csvStringFromRows(rows, columns),
14753
- "utf8"
14754
- );
14755
- return { rows: rows.length, path: (0, import_node_path13.resolve)(outputPath) };
16380
+ if (baseRecord) {
16381
+ return JSON.stringify(baseRecord);
16382
+ }
16383
+ if (isNonEmptyCsvCell(enriched)) {
16384
+ return enriched;
16385
+ }
16386
+ if (isNonEmptyCsvCell(base)) {
16387
+ return base;
16388
+ }
16389
+ return void 0;
14756
16390
  }
14757
16391
  function assertCompleteRowsForCsvExport(rowsInfo, status, outputPath) {
14758
16392
  if (rowsInfo.complete) {
@@ -14773,13 +16407,26 @@ async function compileConfig(input2) {
14773
16407
  );
14774
16408
  }
14775
16409
  const config = await readConfig(input2.options.config);
14776
- return (await input2.client.compileEnrichPlan({ config })).config;
16410
+ return (await input2.client.compileEnrichPlan({
16411
+ config,
16412
+ native_play_materialization: "inline_prebuilt"
16413
+ })).config;
14777
16414
  }
14778
16415
  const planArgs = await buildPlanArgs(input2.args);
14779
- return (await input2.client.compileEnrichPlan({ plan_args: planArgs })).config;
16416
+ return (await input2.client.compileEnrichPlan({
16417
+ plan_args: planArgs,
16418
+ native_play_materialization: "inline_prebuilt"
16419
+ })).config;
14780
16420
  }
14781
16421
  function registerEnrichCommand(program) {
14782
- program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").option("--input <path>", "Input CSV path.").option("--csv <path>", "Alias for --input.").option("--output <path>", "Output CSV path.").option("--config <path>", "JSON enrich config.").option(
16422
+ program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").configureHelp({
16423
+ optionTerm(option) {
16424
+ return option.long === "--timeout" ? "--timeout SECONDS" : option.flags;
16425
+ }
16426
+ }).option("--input <path>", "Input CSV path.").option("--csv <path>", "Alias for --input.").option("--output <path>", "Output CSV path.").option("--config <path>", "JSON enrich config.").requiredOption(
16427
+ "--name <name>",
16428
+ "Name for the compiled enrich play. Reuse a name to keep iterating on the same play; use a new name for a distinct enrichment so runs stay separate in the run ledger and share URLs."
16429
+ ).option(
14783
16430
  "--with <json>",
14784
16431
  "Add a scalar enrich command.",
14785
16432
  (value, previous = []) => [...previous, value]
@@ -14792,70 +16439,77 @@ function registerEnrichCommand(program) {
14792
16439
  "Minimum list results for the current waterfall."
14793
16440
  ).option("--end-waterfall", "End the current waterfall group.").option(
14794
16441
  "--rows <range>",
14795
- "Zero-based row number or end-exclusive range, e.g. 0:10."
16442
+ "Zero-based row number or inclusive range, e.g. 0:10."
14796
16443
  ).option("--all", "Run all rows.").option(
14797
16444
  "--dry-run",
14798
16445
  "Compile and print the generated plan without starting a run."
14799
- ).option("--json", "Emit JSON.").option("--force", "Force rerun for all enrich aliases.").option(
16446
+ ).option("--json", "Emit JSON.").option("--no-open", "Do not open the play page in a browser.").option("--force", "Force rerun for all enrich aliases.").option(
14800
16447
  "--with-force <aliases>",
14801
16448
  "Force rerun for selected aliases.",
14802
16449
  (value, previous = []) => [...previous, value]
14803
- ).option(
14804
- "--in-place",
14805
- "Not supported by the V2 enrich compatibility runner yet."
16450
+ ).option("--in-place", "Write enriched output back to the input CSV.").option(
16451
+ "--timeout <SECONDS>",
16452
+ "API read timeout for enrich status/API requests."
14806
16453
  ).action(async (options, _command) => {
14807
- if (options.inPlace) {
14808
- throw new Error(
14809
- "--in-place is not supported by this V2 enrich runner yet. Use --output instead."
14810
- );
16454
+ if (options.inPlace && options.dryRun) {
16455
+ throw new Error("--in-place is not supported with --dry-run.");
14811
16456
  }
14812
16457
  const inputCsv = options.input ?? options.csv;
14813
16458
  if (!inputCsv) {
14814
16459
  throw new Error("Missing required --input <csv> (or --csv <csv>).");
14815
16460
  }
14816
16461
  await assertInputCsvExists(inputCsv);
14817
- if (options.output) {
16462
+ if (options.output && !options.inPlace) {
14818
16463
  await assertSafeOutputPath(inputCsv, options.output);
14819
16464
  }
14820
16465
  if (!options.config && !hasPlanShapingArgs(currentEnrichArgs())) {
14821
16466
  throw new Error("Pass --config or at least one --with enrich spec.");
14822
16467
  }
14823
- const client = new DeeplineClient();
16468
+ const client2 = new DeeplineClient();
14824
16469
  const args = currentEnrichArgs();
14825
- const config = await compileConfig({ client, args, options });
14826
- const forceAliases = resolveForceAliases(config, options);
14827
- const playSource = compileEnrichConfigToPlaySource(config, {
14828
- forceAliases
14829
- });
14830
- const summary = summarizePlan(config, playSource);
14831
- printDeprecationNotice(options);
16470
+ const config = await compileConfig({ client: client2, args, options });
16471
+ emitEnrichDebugValidationLines(config);
14832
16472
  if (options.dryRun) {
16473
+ const forceAliases2 = resolveForceAliases(config, options);
16474
+ const playSource2 = compileEnrichConfigToPlaySource(config, {
16475
+ forceAliases: forceAliases2,
16476
+ playName: options.name
16477
+ });
16478
+ const summary = summarizePlan(config, playSource2);
14833
16479
  if (options.json) {
14834
16480
  printJson({
14835
16481
  dryRun: true,
14836
- deprecation: ENRICH_DEPRECATION_NOTICE,
14837
16482
  input: (0, import_node_path13.resolve)(inputCsv),
14838
16483
  output: options.output ? (0, import_node_path13.resolve)(options.output) : null,
14839
16484
  plan: summary
14840
16485
  });
14841
16486
  return;
14842
16487
  }
14843
- process.stdout.write(`${playSource}
16488
+ process.stdout.write(`${playSource2}
14844
16489
  `);
14845
16490
  return;
14846
16491
  }
14847
16492
  const rows = parseRows(options.rows, options.all);
14848
- if (options.output && options.rows) {
14849
- throw new Error(
14850
- "CSV export with --rows is not supported yet because it would write only the selected rows. Run without --rows or omit --output."
14851
- );
16493
+ const outputPath = options.inPlace ? inputCsv : options.output;
16494
+ const sourceCsvPath = !options.inPlace && outputPath && await regularFileExists(outputPath) ? outputPath : inputCsv;
16495
+ const forceAliases = resolveForceAliases(config, options);
16496
+ for (const alias of collectFailedInputAliases(
16497
+ config,
16498
+ sourceCsvPath,
16499
+ rows
16500
+ )) {
16501
+ forceAliases.add(alias);
14852
16502
  }
16503
+ const playSource = compileEnrichConfigToPlaySource(config, {
16504
+ forceAliases,
16505
+ playName: options.name
16506
+ });
14853
16507
  const tempDir = await (0, import_promises5.mkdtemp)((0, import_node_path13.join)((0, import_node_os7.tmpdir)(), "deepline-enrich-play-"));
14854
16508
  const tempPlay = (0, import_node_path13.join)(tempDir, "deepline-enrich.play.ts");
14855
16509
  try {
14856
16510
  await (0, import_promises5.writeFile)(tempPlay, playSource, "utf8");
14857
16511
  const runtimeInput = {
14858
- file: (0, import_node_path13.resolve)(inputCsv),
16512
+ file: (0, import_node_path13.resolve)(sourceCsvPath),
14859
16513
  ...rows.rowStart !== null ? { rowStart: rows.rowStart } : {},
14860
16514
  ...rows.rowEnd !== null ? { rowEnd: rows.rowEnd } : {}
14861
16515
  };
@@ -14864,40 +16518,91 @@ function registerEnrichCommand(program) {
14864
16518
  tempPlay,
14865
16519
  "--input",
14866
16520
  JSON.stringify(runtimeInput),
14867
- "--watch",
14868
- "--no-open",
14869
- "--json"
16521
+ "--watch"
14870
16522
  ];
14871
- const captured = await captureStdout(() => handlePlayRun(runArgs));
14872
- const status = parseJsonOutput(captured.stdout);
16523
+ if (options.noOpen) {
16524
+ runArgs.push("--no-open");
16525
+ }
16526
+ if (options.json) {
16527
+ runArgs.push("--json");
16528
+ } else {
16529
+ runArgs.push("--logs");
16530
+ }
16531
+ const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : null;
16532
+ if (timeoutSeconds !== null && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
16533
+ runArgs.push("--tail-timeout-ms", String(timeoutSeconds * 1e3));
16534
+ }
16535
+ const captured = await runGeneratedEnrichPlay(runArgs, {
16536
+ passthroughStdout: !options.json
16537
+ });
16538
+ const status = options.json ? parseJsonOutput(captured.stdout) : await resolveWatchedGeneratedPlayStatus({
16539
+ client: client2,
16540
+ stdout: captured.stdout,
16541
+ exitCode: captured.result
16542
+ });
14873
16543
  if (captured.result !== 0) {
14874
16544
  if (options.json) {
14875
16545
  printJson({
14876
- deprecation: ENRICH_DEPRECATION_NOTICE,
14877
16546
  result: status
14878
16547
  });
14879
- } else {
14880
- process.stdout.write(captured.stdout);
14881
16548
  }
14882
16549
  process.exitCode = captured.result;
14883
16550
  return;
14884
16551
  }
14885
- const exportResult = options.output ? await writeOutputCsv(options.output, status) : null;
16552
+ const exportResult = outputPath ? await writeOutputCsv(outputPath, status, {
16553
+ client: client2,
16554
+ config,
16555
+ sourceCsvPath,
16556
+ rows
16557
+ }) : null;
16558
+ const rowsForFailureReport = exportResult?.enrichedRows ?? extractCanonicalRowsInfo(status)?.rows ?? [];
14886
16559
  if (options.json) {
16560
+ const failureReport2 = await maybeEmitEnrichFailureReport({
16561
+ config,
16562
+ rows: rowsForFailureReport,
16563
+ rowRange: rows,
16564
+ client: client2,
16565
+ outputPath: exportResult?.path ?? outputPath ?? null
16566
+ });
14887
16567
  printJson({
14888
- ok: true,
14889
- deprecation: ENRICH_DEPRECATION_NOTICE,
16568
+ ok: !failureReport2,
14890
16569
  run: status,
14891
- output: exportResult
16570
+ output: exportResult ? { rows: exportResult.rows, path: exportResult.path } : null,
16571
+ ...failureReport2 ? {
16572
+ failure_report: {
16573
+ path: failureReport2.path,
16574
+ jobs: failureReport2.jobs.length
16575
+ }
16576
+ } : {}
14892
16577
  });
16578
+ if (failureReport2) {
16579
+ process.exitCode = EXIT_SERVER2;
16580
+ }
14893
16581
  return;
14894
16582
  }
14895
- process.stdout.write(captured.stdout);
14896
16583
  if (exportResult) {
14897
16584
  process.stderr.write(
14898
16585
  `Wrote ${exportResult.rows} row(s) to ${exportResult.path}
14899
16586
  `
14900
16587
  );
16588
+ const waterfallSummaryLines = buildEnrichWaterfallSummaryLines(
16589
+ config,
16590
+ exportResult.enrichedRows
16591
+ );
16592
+ if (waterfallSummaryLines.length > 0) {
16593
+ process.stdout.write(`${waterfallSummaryLines.join("\n")}
16594
+ `);
16595
+ }
16596
+ }
16597
+ const failureReport = await maybeEmitEnrichFailureReport({
16598
+ config,
16599
+ rows: rowsForFailureReport,
16600
+ rowRange: rows,
16601
+ client: client2,
16602
+ outputPath: exportResult?.path ?? outputPath ?? null
16603
+ });
16604
+ if (failureReport) {
16605
+ process.exitCode = EXIT_SERVER2;
14901
16606
  }
14902
16607
  } finally {
14903
16608
  await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
@@ -15137,7 +16842,7 @@ async function readHiddenLine(prompt) {
15137
16842
  if (typeof import_node_process.stdin.setRawMode === "function") import_node_process.stdin.setRawMode(true);
15138
16843
  let value = "";
15139
16844
  import_node_process.stdin.resume();
15140
- return await new Promise((resolve14, reject) => {
16845
+ return await new Promise((resolve15, reject) => {
15141
16846
  let settled = false;
15142
16847
  const cleanup = () => {
15143
16848
  import_node_process.stdin.off("data", onData);
@@ -15152,7 +16857,7 @@ async function readHiddenLine(prompt) {
15152
16857
  settled = true;
15153
16858
  import_node_process.stdout.write("\n");
15154
16859
  cleanup();
15155
- resolve14(line);
16860
+ resolve15(line);
15156
16861
  };
15157
16862
  const fail = (error) => {
15158
16863
  if (settled) return;
@@ -15216,8 +16921,8 @@ function preventShellHistoryLeak(forbidden) {
15216
16921
  }
15217
16922
  }
15218
16923
  async function handleList(options) {
15219
- const client = new DeeplineClient();
15220
- const secrets = await client.listSecrets();
16924
+ const client2 = new DeeplineClient();
16925
+ const secrets = await client2.listSecrets();
15221
16926
  printCommandEnvelope(
15222
16927
  {
15223
16928
  secrets,
@@ -15236,8 +16941,8 @@ async function handleList(options) {
15236
16941
  }
15237
16942
  async function handleCheck(nameInput, options) {
15238
16943
  const name = normalizeSecretName(nameInput);
15239
- const client = new DeeplineClient();
15240
- const secret = await client.checkSecret(name);
16944
+ const client2 = new DeeplineClient();
16945
+ const secret = await client2.checkSecret(name);
15241
16946
  printCommandEnvelope(
15242
16947
  {
15243
16948
  ok: Boolean(secret),
@@ -15545,12 +17250,12 @@ function matchesGrepQuery(value, query, mode) {
15545
17250
  return terms.every((term) => haystack.includes(term));
15546
17251
  }
15547
17252
  async function listTools(args) {
15548
- const client = new DeeplineClient();
17253
+ const client2 = new DeeplineClient();
15549
17254
  const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
15550
17255
  const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
15551
17256
  const compact = !args.includes("--full");
15552
17257
  const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
15553
- const items = (await client.listTools({
17258
+ const items = (await client2.listTools({
15554
17259
  ...categoryFilter ? { categories: categoryFilter } : {},
15555
17260
  compact
15556
17261
  })).map(toListedTool).filter(
@@ -15597,8 +17302,8 @@ async function searchTools(queryInput, options = {}) {
15597
17302
  console.error("Usage: deepline tools search <query> [--json]");
15598
17303
  return 1;
15599
17304
  }
15600
- const client = new DeeplineClient();
15601
- const result = await client.searchTools({
17305
+ const client2 = new DeeplineClient();
17306
+ const result = await client2.searchTools({
15602
17307
  query,
15603
17308
  categories: options.categories,
15604
17309
  searchTerms: options.searchTerms,
@@ -15621,10 +17326,10 @@ async function grepTools(queryInput, options = {}) {
15621
17326
  console.error("Usage: deepline tools grep <query> [--json]");
15622
17327
  return 1;
15623
17328
  }
15624
- const client = new DeeplineClient();
17329
+ const client2 = new DeeplineClient();
15625
17330
  const requestedCategories = options.categories ? options.categories.split(",").map((item) => item.trim()).filter(Boolean) : [];
15626
17331
  const mode = options.mode ?? "all";
15627
- const tools = (await client.listTools({
17332
+ const tools = (await client2.listTools({
15628
17333
  grep: query,
15629
17334
  grepMode: mode,
15630
17335
  ...options.categories ? { categories: options.categories } : {},
@@ -15694,12 +17399,12 @@ function compactTool(tool) {
15694
17399
  function playIdentifiers(play) {
15695
17400
  return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
15696
17401
  }
15697
- async function findPlayForToolId(client, toolId) {
17402
+ async function findPlayForToolId(client2, toolId) {
15698
17403
  const requested = toolId.trim();
15699
17404
  if (!requested) {
15700
17405
  return null;
15701
17406
  }
15702
- const plays = await client.searchPlays({ query: requested, compact: true });
17407
+ const plays = await client2.searchPlays({ query: requested, compact: true });
15703
17408
  return plays.find((play) => playIdentifiers(play).includes(requested)) ?? null;
15704
17409
  }
15705
17410
  function playAliasToolErrorMessage(toolId, play) {
@@ -15714,7 +17419,7 @@ function printPlayAliasToolError(toolId, play) {
15714
17419
  function isPlayLikeTool(tool) {
15715
17420
  const record = tool;
15716
17421
  if (record.isPlay === true || record.is_play === true) return true;
15717
- const playExpansion = recordField(record, "playExpansion", "play_expansion");
17422
+ const playExpansion = recordField2(record, "playExpansion", "play_expansion");
15718
17423
  if (Object.keys(playExpansion).length > 0) return true;
15719
17424
  const toolId = typeof record.toolId === "string" ? record.toolId : "";
15720
17425
  return toolId.endsWith("_waterfall");
@@ -15948,12 +17653,12 @@ async function getTool(toolId, options = {}) {
15948
17653
  console.error("Usage: deepline tools get <toolId> [--json]");
15949
17654
  return 1;
15950
17655
  }
15951
- const client = new DeeplineClient();
17656
+ const client2 = new DeeplineClient();
15952
17657
  let tool;
15953
17658
  try {
15954
- tool = await client.getTool(toolId);
17659
+ tool = await client2.getTool(toolId);
15955
17660
  } catch (error) {
15956
- const play = await findPlayForToolId(client, toolId);
17661
+ const play = await findPlayForToolId(client2, toolId);
15957
17662
  if (play) {
15958
17663
  printPlayAliasToolError(toolId, play);
15959
17664
  return 2;
@@ -16020,10 +17725,10 @@ async function getTool(toolId, options = {}) {
16020
17725
  function toolContractJsonForDescribe(tool, requestedToolId) {
16021
17726
  const toolId = String(tool.toolId || requestedToolId);
16022
17727
  const inputFields = toolInputFieldsForDisplay(
16023
- recordField(tool, "inputSchema", "input_schema")
17728
+ recordField2(tool, "inputSchema", "input_schema")
16024
17729
  );
16025
- const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
16026
- const toolExecutionResult = recordField(
17730
+ const usageGuidance = recordField2(tool, "usageGuidance", "usage_guidance");
17731
+ const toolExecutionResult = recordField2(
16027
17732
  usageGuidance,
16028
17733
  "toolExecutionResult",
16029
17734
  "tool_execution_result"
@@ -16034,7 +17739,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
16034
17739
  const extractedValues = extractionContractEntries(
16035
17740
  arrayField(toolExecutionResult, "extractedValues", "extracted_values")
16036
17741
  );
16037
- const cost = recordField(tool, "cost");
17742
+ const cost = recordField2(tool, "cost");
16038
17743
  const deeplineCredits = numberField(
16039
17744
  tool,
16040
17745
  "deeplineCreditsPerPricingUnit",
@@ -16088,7 +17793,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
16088
17793
  }
16089
17794
  function extractionContractEntries(entries) {
16090
17795
  return entries.flatMap((entry) => {
16091
- if (!isRecord6(entry)) return [];
17796
+ if (!isRecord7(entry)) return [];
16092
17797
  const name = stringField(entry, "name");
16093
17798
  const expression = stringField(entry, "expression");
16094
17799
  return name && expression ? [{ name, expression }] : [];
@@ -16096,8 +17801,8 @@ function extractionContractEntries(entries) {
16096
17801
  }
16097
17802
  function printCompactToolContract(tool, requestedToolId) {
16098
17803
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16099
- const cost = isRecord6(contract.cost) ? contract.cost : {};
16100
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17804
+ const cost = isRecord7(contract.cost) ? contract.cost : {};
17805
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16101
17806
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16102
17807
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16103
17808
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
@@ -16114,7 +17819,7 @@ function printCompactToolContract(tool, requestedToolId) {
16114
17819
  console.log("");
16115
17820
  console.log("Inputs:");
16116
17821
  for (const field of inputFields) {
16117
- if (!isRecord6(field)) continue;
17822
+ if (!isRecord7(field)) continue;
16118
17823
  const name = stringField(field, "name");
16119
17824
  if (!name) continue;
16120
17825
  const required = field.required ? "*" : "";
@@ -16127,7 +17832,7 @@ function printCompactToolContract(tool, requestedToolId) {
16127
17832
  }
16128
17833
  console.log("");
16129
17834
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
16130
- const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
17835
+ const starterScript = isRecord7(contract.starterScript) ? contract.starterScript : {};
16131
17836
  const starterPath = stringField(starterScript, "path");
16132
17837
  if (starterPath) {
16133
17838
  console.log("");
@@ -16141,14 +17846,14 @@ function printCompactToolContract(tool, requestedToolId) {
16141
17846
  console.log("Getters:");
16142
17847
  if (listGetters.length) console.log("Lists:");
16143
17848
  for (const entry of listGetters) {
16144
- if (isRecord6(entry))
17849
+ if (isRecord7(entry))
16145
17850
  console.log(
16146
17851
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16147
17852
  );
16148
17853
  }
16149
17854
  if (valueGetters.length) console.log("Values:");
16150
17855
  for (const entry of valueGetters) {
16151
- if (isRecord6(entry))
17856
+ if (isRecord7(entry))
16152
17857
  console.log(
16153
17858
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16154
17859
  );
@@ -16161,7 +17866,7 @@ function printCompactToolContract(tool, requestedToolId) {
16161
17866
  }
16162
17867
  function printToolPricingOnly(tool, requestedToolId, options = {}) {
16163
17868
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16164
- const cost = isRecord6(contract.cost) ? contract.cost : {};
17869
+ const cost = isRecord7(contract.cost) ? contract.cost : {};
16165
17870
  const pricingModel = stringField(cost, "pricingModel") || "unknown";
16166
17871
  const billingMode = stringField(cost, "billingMode") || "unknown";
16167
17872
  const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
@@ -16181,7 +17886,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
16181
17886
  }
16182
17887
  console.log("Inputs:");
16183
17888
  for (const field of inputFields) {
16184
- if (!isRecord6(field)) continue;
17889
+ if (!isRecord7(field)) continue;
16185
17890
  const name = stringField(field, "name");
16186
17891
  if (!name) continue;
16187
17892
  const required = field.required ? "*" : "";
@@ -16207,27 +17912,27 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
16207
17912
  ` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
16208
17913
  );
16209
17914
  console.log("});");
16210
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17915
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16211
17916
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16212
17917
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16213
- const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
17918
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord7);
16214
17919
  if (firstGetter) {
16215
17920
  const name = stringField(firstGetter, "name") || "value";
16216
17921
  const expression = stringField(firstGetter, "expression");
16217
17922
  if (expression)
16218
17923
  console.log(
16219
- `const ${safeIdentifier3(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
17924
+ `const ${safeIdentifier2(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
16220
17925
  );
16221
17926
  }
16222
17927
  console.log("```");
16223
17928
  if (options.includeSamples !== false) {
16224
- const samples = recordField(tool, "samples");
17929
+ const samples = recordField2(tool, "samples");
16225
17930
  printSamples(samples);
16226
17931
  }
16227
17932
  }
16228
17933
  function printToolGettersOnly(tool, requestedToolId) {
16229
17934
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16230
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17935
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16231
17936
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16232
17937
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16233
17938
  console.log(`Getters: ${contract.toolId}`);
@@ -16240,7 +17945,7 @@ function printToolGettersOnly(tool, requestedToolId) {
16240
17945
  if (listGetters.length) {
16241
17946
  console.log("Lists:");
16242
17947
  for (const entry of listGetters) {
16243
- if (isRecord6(entry))
17948
+ if (isRecord7(entry))
16244
17949
  console.log(
16245
17950
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16246
17951
  );
@@ -16249,7 +17954,7 @@ function printToolGettersOnly(tool, requestedToolId) {
16249
17954
  if (valueGetters.length) {
16250
17955
  console.log("Values:");
16251
17956
  for (const entry of valueGetters) {
16252
- if (isRecord6(entry))
17957
+ if (isRecord7(entry))
16253
17958
  console.log(
16254
17959
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16255
17960
  );
@@ -16275,7 +17980,7 @@ function sampleValueForField(field) {
16275
17980
  function samplePayloadForInputFields(fields) {
16276
17981
  return Object.fromEntries(
16277
17982
  fields.slice(0, 4).flatMap((field) => {
16278
- if (!isRecord6(field)) return [];
17983
+ if (!isRecord7(field)) return [];
16279
17984
  const name = stringField(field, "name");
16280
17985
  if (!name) return [];
16281
17986
  return [[name, sampleValueForField(field)]];
@@ -16285,7 +17990,7 @@ function samplePayloadForInputFields(fields) {
16285
17990
  function stableStepIdForTool(toolId) {
16286
17991
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
16287
17992
  }
16288
- function safeIdentifier3(name) {
17993
+ function safeIdentifier2(name) {
16289
17994
  const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
16290
17995
  return cleaned || "value";
16291
17996
  }
@@ -16298,7 +18003,7 @@ function playResultExpression(entry) {
16298
18003
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
16299
18004
  const toolId = String(tool.toolId || requestedToolId);
16300
18005
  const inputFields = toolInputFieldsForDisplay(
16301
- recordField(tool, "inputSchema", "input_schema")
18006
+ recordField2(tool, "inputSchema", "input_schema")
16302
18007
  );
16303
18008
  const starterScript = seedToolListScript({
16304
18009
  toolId,
@@ -16325,7 +18030,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
16325
18030
  provider: tool.provider,
16326
18031
  displayName: tool.displayName,
16327
18032
  usageGuidance: usageGuidanceWithAccessDefaults(
16328
- recordField(tool, "usageGuidance", "usage_guidance")
18033
+ recordField2(tool, "usageGuidance", "usage_guidance")
16329
18034
  ),
16330
18035
  runtimeOutputHelp: {
16331
18036
  contract: "tools describe shows declared schema and Deepline getters; it is not an observed provider response.",
@@ -16350,7 +18055,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
16350
18055
  }
16351
18056
  function usageGuidanceWithAccessDefaults(usageGuidance) {
16352
18057
  if (Object.keys(usageGuidance).length === 0) return usageGuidance;
16353
- const existingAccess = recordField(usageGuidance, "access");
18058
+ const existingAccess = recordField2(usageGuidance, "access");
16354
18059
  return {
16355
18060
  ...usageGuidance,
16356
18061
  access: {
@@ -16383,18 +18088,18 @@ function listedToolDescription(tool) {
16383
18088
  }
16384
18089
  function formatListedToolCost(tool) {
16385
18090
  const record = tool;
16386
- const pricing = recordField(record, "pricing");
18091
+ const pricing = recordField2(record, "pricing");
16387
18092
  const displayText = stringField(pricing, "displayText", "display_text");
16388
18093
  return displayText ? `Cost: ${displayText}` : "";
16389
18094
  }
16390
18095
  function toolInputFieldsForDisplay(inputSchema) {
16391
18096
  if (Array.isArray(inputSchema.fields))
16392
- return inputSchema.fields.filter(isRecord6);
16393
- const jsonSchema = isRecord6(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
16394
- const properties = isRecord6(jsonSchema.properties) ? jsonSchema.properties : {};
18097
+ return inputSchema.fields.filter(isRecord7);
18098
+ const jsonSchema = isRecord7(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
18099
+ const properties = isRecord7(jsonSchema.properties) ? jsonSchema.properties : {};
16395
18100
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
16396
18101
  return Object.entries(properties).map(([name, value]) => {
16397
- const property = isRecord6(value) ? value : {};
18102
+ const property = isRecord7(value) ? value : {};
16398
18103
  return {
16399
18104
  name,
16400
18105
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -16423,15 +18128,15 @@ function printJsonPreview(label, payload) {
16423
18128
  }
16424
18129
  function samplePayload(samples, key) {
16425
18130
  const entry = samples[key];
16426
- if (!isRecord6(entry)) return void 0;
18131
+ if (!isRecord7(entry)) return void 0;
16427
18132
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
16428
18133
  }
16429
18134
  function commandEnvelopeFromRawResponse(rawResponse) {
16430
- return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
18135
+ return isRecord7(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
16431
18136
  }
16432
18137
  function listExtractorPathsFromUsageGuidance(tool) {
16433
18138
  const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
16434
- const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord6(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
18139
+ const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord7(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
16435
18140
  return extractedLists.flatMap((entry) => {
16436
18141
  const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
16437
18142
  if (!Array.isArray(paths)) return [];
@@ -16447,7 +18152,7 @@ function formatDecimal(value) {
16447
18152
  function formatUsd(value) {
16448
18153
  return `$${formatDecimal(value)}`;
16449
18154
  }
16450
- function isRecord6(value) {
18155
+ function isRecord7(value) {
16451
18156
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16452
18157
  }
16453
18158
  function stringField(source, ...keys) {
@@ -16471,10 +18176,10 @@ function arrayField(source, ...keys) {
16471
18176
  }
16472
18177
  return [];
16473
18178
  }
16474
- function recordField(source, ...keys) {
18179
+ function recordField2(source, ...keys) {
16475
18180
  for (const key of keys) {
16476
18181
  const value = source[key];
16477
- if (isRecord6(value)) return value;
18182
+ if (isRecord7(value)) return value;
16478
18183
  }
16479
18184
  return {};
16480
18185
  }
@@ -16540,7 +18245,7 @@ function parseJsonObjectArgument(raw, flagName) {
16540
18245
  }
16541
18246
  throw invalidJsonError(flagName, message);
16542
18247
  }
16543
- if (!isRecord6(parsed)) {
18248
+ if (!isRecord7(parsed)) {
16544
18249
  throw invalidJsonError(flagName, "expected an object.");
16545
18250
  }
16546
18251
  return parsed;
@@ -16663,7 +18368,7 @@ function buildToolExecuteBaseEnvelope(input2) {
16663
18368
  kind: summaryEntries.length > 0 ? "object" : "raw",
16664
18369
  summary: input2.summary
16665
18370
  };
16666
- const envelopeHasCanonicalOutput = isRecord6(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
18371
+ const envelopeHasCanonicalOutput = isRecord7(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
16667
18372
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
16668
18373
  const actions = input2.listConversion ? [
16669
18374
  {
@@ -16718,12 +18423,12 @@ async function executeTool(args) {
16718
18423
  }
16719
18424
  return 1;
16720
18425
  }
16721
- const client = new DeeplineClient();
18426
+ const client2 = new DeeplineClient();
16722
18427
  let metadata;
16723
18428
  try {
16724
- metadata = await client.getTool(parsed.toolId);
18429
+ metadata = await client2.getTool(parsed.toolId);
16725
18430
  } catch (error) {
16726
- const play = await findPlayForToolId(client, parsed.toolId);
18431
+ const play = await findPlayForToolId(client2, parsed.toolId);
16727
18432
  if (play) {
16728
18433
  if (argsWantJson(args)) {
16729
18434
  printJsonError(
@@ -16747,7 +18452,7 @@ async function executeTool(args) {
16747
18452
  }
16748
18453
  return 2;
16749
18454
  }
16750
- const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
18455
+ const rawResponse = await client2.executeTool(parsed.toolId, parsed.params);
16751
18456
  const listConversion = tryConvertToList(rawResponse, {
16752
18457
  listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
16753
18458
  });
@@ -16772,7 +18477,7 @@ async function executeTool(args) {
16772
18477
  {
16773
18478
  ...baseEnvelope,
16774
18479
  local: {
16775
- ...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
18480
+ ...isRecord7(baseEnvelope.local) ? baseEnvelope.local : {},
16776
18481
  payload_file: jsonPath
16777
18482
  }
16778
18483
  },
@@ -16893,10 +18598,499 @@ async function executeTool(args) {
16893
18598
  return 0;
16894
18599
  }
16895
18600
 
18601
+ // src/cli/commands/workflow.ts
18602
+ var import_promises6 = require("fs/promises");
18603
+ var import_node_path16 = require("path");
18604
+
18605
+ // src/cli/workflow-to-play.ts
18606
+ var import_node_crypto4 = require("crypto");
18607
+ var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
18608
+ var HITL_SLACK_TOOL = "slack_message_with_hitl";
18609
+ var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
18610
+ var UNSUPPORTED_REASON_HINT = {
18611
+ unsafe_user_code: "a step's JavaScript (extract_js / run_if_js / run_javascript) uses non-deterministic or sandbox-escaping code (e.g. new Date(), Date.now(), Math.random(), fetch, require). Plays replay deterministically inside a sandbox, so the compiler rejects it \u2014 rewrite it to compute from the row/result before migrating.",
18612
+ hitl: "human-in-the-loop / wait-for-signal steps have no play-runtime equivalent yet.",
18613
+ sub_workflow: "sub-workflow calls have no play-runtime equivalent yet (a future ctx.runPlay bridge).",
18614
+ nested_waterfall: "nested with_waterfall blocks are not supported by the play compiler.",
18615
+ inline_secret: 'a step inlines a hardcoded secret (API key / token / private key). Plays forbid inline secrets \u2014 move it to a dashboard secret and read it with ctx.secrets.get("NAME") before migrating.'
18616
+ };
18617
+ var UnsupportedWorkflowCommandError = class extends Error {
18618
+ status = 400;
18619
+ unsupported;
18620
+ constructor(unsupported) {
18621
+ super(
18622
+ `Workflow uses ${unsupported.length} command${unsupported.length === 1 ? "" : "s"} that cannot run as a play yet: ${unsupported.map((entry) => `${entry.alias} (${entry.reason})`).join(", ")}.`
18623
+ );
18624
+ this.name = "UnsupportedWorkflowCommandError";
18625
+ this.unsupported = unsupported;
18626
+ }
18627
+ };
18628
+ function isWaterfall2(command) {
18629
+ return "with_waterfall" in command;
18630
+ }
18631
+ function stepInlinedText(command) {
18632
+ return [
18633
+ JSON.stringify(command.payload ?? {}),
18634
+ command.extract_js ?? "",
18635
+ command.run_if_js ?? ""
18636
+ ].join("\n");
18637
+ }
18638
+ function stepUserCode(command) {
18639
+ const code = [];
18640
+ if (command.extract_js) code.push(command.extract_js);
18641
+ if (command.run_if_js) code.push(command.run_if_js);
18642
+ if (command.tool === "run_javascript" && typeof command.payload?.code === "string") {
18643
+ code.push(command.payload.code);
18644
+ }
18645
+ return code;
18646
+ }
18647
+ function hasUnsafeUserCode(command) {
18648
+ return stepUserCode(command).some((code) => {
18649
+ try {
18650
+ assertUserCodeIsSafe(code, "play step code");
18651
+ return false;
18652
+ } catch {
18653
+ return true;
18654
+ }
18655
+ });
18656
+ }
18657
+ function classifyUnsupportedCommand(command) {
18658
+ const { tool } = command;
18659
+ if (tool === HITL_SLACK_TOOL || tool === HITL_WAIT_FOR_SIGNAL_TOOL) {
18660
+ return "hitl";
18661
+ }
18662
+ if (tool.startsWith(SUB_WORKFLOW_TOOL_PREFIX)) return "sub_workflow";
18663
+ if (collectInlineSecretFindings(stepInlinedText(command)).length > 0) {
18664
+ return "inline_secret";
18665
+ }
18666
+ if (hasUnsafeUserCode(command)) return "unsafe_user_code";
18667
+ return null;
18668
+ }
18669
+ function collectStepOffenders(command, offenders) {
18670
+ if (command.disabled) return;
18671
+ const reason = classifyUnsupportedCommand(command);
18672
+ if (reason) {
18673
+ offenders.push({ alias: command.alias, tool: command.tool, reason });
18674
+ }
18675
+ }
18676
+ function findUnsupportedWorkflowCommands(config) {
18677
+ const offenders = [];
18678
+ for (const command of config.commands ?? []) {
18679
+ if (!isWaterfall2(command)) {
18680
+ collectStepOffenders(command, offenders);
18681
+ continue;
18682
+ }
18683
+ for (const child of command.commands ?? []) {
18684
+ if (isWaterfall2(child)) {
18685
+ offenders.push({
18686
+ alias: command.with_waterfall,
18687
+ tool: "with_waterfall",
18688
+ reason: "nested_waterfall"
18689
+ });
18690
+ continue;
18691
+ }
18692
+ collectStepOffenders(child, offenders);
18693
+ }
18694
+ }
18695
+ return offenders;
18696
+ }
18697
+ function validateWorkflowConfigForPlay(config) {
18698
+ const offenders = findUnsupportedWorkflowCommands(config);
18699
+ if (offenders.length > 0) {
18700
+ throw new UnsupportedWorkflowCommandError(offenders);
18701
+ }
18702
+ }
18703
+ function describeUnsupportedReason(reason) {
18704
+ return UNSUPPORTED_REASON_HINT[reason];
18705
+ }
18706
+ var MAX_PLAY_NAME_LENGTH = 39;
18707
+ function sanitizePlayNameSegment(value) {
18708
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
18709
+ }
18710
+ function deriveWorkflowPlayName(workflowName) {
18711
+ const base = sanitizePlayNameSegment(workflowName) || "workflow";
18712
+ const suffix = (0, import_node_crypto4.createHash)("sha256").update(workflowName).digest("hex").slice(0, 8);
18713
+ const reserved = suffix.length + 1;
18714
+ const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
18715
+ let name = `${base.slice(0, allowedBase)}_${suffix}`;
18716
+ if (name.startsWith("prebuilt")) {
18717
+ name = `wf_${name}`.slice(0, MAX_PLAY_NAME_LENGTH);
18718
+ }
18719
+ return name;
18720
+ }
18721
+ function compileWorkflowConfigToPlay(config, options) {
18722
+ validateWorkflowConfigForPlay(config);
18723
+ const playName = deriveWorkflowPlayName(options.workflowName);
18724
+ const sourceCode = compileEnrichConfigToPlaySource(config, {
18725
+ playName,
18726
+ inlineRunJavascript: true,
18727
+ idiomaticGetters: true
18728
+ });
18729
+ const warnings = [];
18730
+ if (typeof config.cost_cap_usd_per_run === "number") {
18731
+ warnings.push(
18732
+ `cost_cap_usd_per_run ($${config.cost_cap_usd_per_run}) was not carried into the play \u2014 credits are not USD. Set billing.maxCreditsPerRun manually after checking your credit conversion.`
18733
+ );
18734
+ }
18735
+ return { playName, sourceCode, warnings };
18736
+ }
18737
+
18738
+ // src/cli/commands/workflow.ts
18739
+ var PLAY_EQUIVALENT = {
18740
+ list: "deepline plays list",
18741
+ get: "deepline plays get <name>",
18742
+ call: "deepline plays run <name>",
18743
+ apply: "deepline plays publish <file>",
18744
+ lint: "deepline plays check <file>",
18745
+ schema: "deepline plays describe <name>",
18746
+ runs: "deepline runs list --play <name>",
18747
+ run: "deepline runs get <run-id>",
18748
+ tail: "deepline runs tail <run-id>",
18749
+ cancel: "deepline runs stop <run-id>",
18750
+ delete: "deepline plays delete <name>"
18751
+ };
18752
+ var MIGRATE_HINT = "deepline workflows transform <id>";
18753
+ var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
18754
+ function withJsonOption(command) {
18755
+ return command.option("--json", JSON_OPTION_DESCRIPTION);
18756
+ }
18757
+ var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
18758
+ "completed",
18759
+ "succeeded",
18760
+ "success",
18761
+ "failed",
18762
+ "error",
18763
+ "errored",
18764
+ "cancelled",
18765
+ "canceled",
18766
+ "done"
18767
+ ]);
18768
+ function migrationDeprecation(command) {
18769
+ const use = PLAY_EQUIVALENT[command];
18770
+ if (!use) return null;
18771
+ return { use, migrate: MIGRATE_HINT };
18772
+ }
18773
+ function noticeToStderr(command) {
18774
+ const deprecation = migrationDeprecation(command);
18775
+ if (!deprecation) return;
18776
+ process.stderr.write(
18777
+ `note: \`deepline workflows ${command}\` is a legacy surface \u2014 workflows are becoming plays.
18778
+ equivalent: \`${deprecation.use}\`
18779
+ migrate this workflow: \`${MIGRATE_HINT}\`
18780
+ `
18781
+ );
18782
+ }
18783
+ function emitPassThrough(command, payload, options) {
18784
+ const json = shouldEmitJson(options.json);
18785
+ const deprecation = migrationDeprecation(command);
18786
+ if (json) {
18787
+ const enriched = payload && typeof payload === "object" && !Array.isArray(payload) ? { ...payload, deprecation } : { result: payload, deprecation };
18788
+ printJson(enriched);
18789
+ return;
18790
+ }
18791
+ printJson(payload);
18792
+ noticeToStderr(command);
18793
+ }
18794
+ function client() {
18795
+ return new DeeplineClient();
18796
+ }
18797
+ function readStatus(payload) {
18798
+ if (!payload || typeof payload !== "object") return null;
18799
+ const record = payload;
18800
+ if (typeof record.status === "string") return record.status;
18801
+ const run = record.run;
18802
+ if (run && typeof run === "object") {
18803
+ const status = run.status;
18804
+ if (typeof status === "string") return status;
18805
+ }
18806
+ return null;
18807
+ }
18808
+ async function readJsonOption(payload, file) {
18809
+ if (file) {
18810
+ const raw = await (0, import_promises6.readFile)((0, import_node_path16.resolve)(file), "utf8");
18811
+ return JSON.parse(raw);
18812
+ }
18813
+ if (payload) {
18814
+ return JSON.parse(payload);
18815
+ }
18816
+ throw new Error("Provide --payload <json> or --file <path>.");
18817
+ }
18818
+ async function transformOne(api, workflowId, outDir, publish) {
18819
+ const { workflow } = await api.getWorkflow(workflowId);
18820
+ if (!workflow) {
18821
+ return {
18822
+ ok: false,
18823
+ workflowId,
18824
+ workflowName: workflowId,
18825
+ reason: "Workflow not found."
18826
+ };
18827
+ }
18828
+ const revision = workflow.current_published_revision;
18829
+ if (!revision) {
18830
+ return {
18831
+ ok: false,
18832
+ workflowId,
18833
+ workflowName: workflow.name,
18834
+ reason: "Workflow has no published revision to transform."
18835
+ };
18836
+ }
18837
+ try {
18838
+ const compiled = compileWorkflowConfigToPlay(
18839
+ // The API returns the config as `unknown`; it is structurally the
18840
+ // EnrichCompiledConfig the compiler expects. Cast at this single boundary.
18841
+ revision.config,
18842
+ { workflowName: workflow.name, version: revision.version }
18843
+ );
18844
+ const file = (0, import_node_path16.join)((0, import_node_path16.resolve)(outDir), `${compiled.playName}.play.ts`);
18845
+ await (0, import_promises6.mkdir)((0, import_node_path16.dirname)(file), { recursive: true });
18846
+ await (0, import_promises6.writeFile)(file, compiled.sourceCode, "utf8");
18847
+ let published = false;
18848
+ if (publish) {
18849
+ const code = await handlePlayPublish([file]);
18850
+ if (code !== 0) {
18851
+ return {
18852
+ ok: false,
18853
+ workflowId,
18854
+ workflowName: workflow.name,
18855
+ reason: `Wrote ${file} but publish failed (exit ${code}).`
18856
+ };
18857
+ }
18858
+ published = true;
18859
+ }
18860
+ return {
18861
+ ok: true,
18862
+ workflowId,
18863
+ workflowName: workflow.name,
18864
+ playName: compiled.playName,
18865
+ file,
18866
+ warnings: compiled.warnings,
18867
+ published
18868
+ };
18869
+ } catch (error) {
18870
+ if (error instanceof UnsupportedWorkflowCommandError) {
18871
+ return {
18872
+ ok: false,
18873
+ workflowId,
18874
+ workflowName: workflow.name,
18875
+ reason: error.unsupported.map((u) => `${u.alias} (${describeUnsupportedReason(u.reason)})`).join("; "),
18876
+ unsupported: error.unsupported
18877
+ };
18878
+ }
18879
+ throw error;
18880
+ }
18881
+ }
18882
+ function renderTransformReport(outcomes) {
18883
+ const lines = [];
18884
+ for (const outcome of outcomes) {
18885
+ if (outcome.ok) {
18886
+ lines.push(
18887
+ `\u2705 ${outcome.workflowName} \u2192 ${outcome.file}` + (outcome.published ? " (published)" : ` (next: deepline plays publish ${outcome.file})`)
18888
+ );
18889
+ for (const warning of outcome.warnings) {
18890
+ lines.push(` \u26A0\uFE0F ${warning}`);
18891
+ }
18892
+ } else {
18893
+ lines.push(`\u26A0\uFE0F ${outcome.workflowName} \u2014 skipped: ${outcome.reason}`);
18894
+ }
18895
+ }
18896
+ const ok = outcomes.filter((o) => o.ok).length;
18897
+ const total = outcomes.length;
18898
+ const manual = total - ok;
18899
+ lines.push("");
18900
+ lines.push(
18901
+ `${ok}/${total} transformed` + (manual > 0 ? `, ${manual} need manual attention` : "")
18902
+ );
18903
+ return `${lines.join("\n")}
18904
+ `;
18905
+ }
18906
+ async function handleTransform(id, options) {
18907
+ const api = client();
18908
+ const outDir = options.out ?? "./plays";
18909
+ const publish = options.publish === true;
18910
+ let ids;
18911
+ if (options.all) {
18912
+ const { workflows } = await api.listWorkflows();
18913
+ ids = workflows.map((w) => w.id);
18914
+ } else if (id) {
18915
+ ids = [id];
18916
+ } else {
18917
+ throw new Error("Provide a workflow <id> or --all.");
18918
+ }
18919
+ const outcomes = [];
18920
+ for (const workflowId of ids) {
18921
+ outcomes.push(await transformOne(api, workflowId, outDir, publish));
18922
+ }
18923
+ printCommandEnvelope(
18924
+ { outcomes, deprecation: null },
18925
+ {
18926
+ json: shouldEmitJson(options.json),
18927
+ text: renderTransformReport(outcomes)
18928
+ }
18929
+ );
18930
+ return outcomes.every((o) => o.ok) ? 0 : 1;
18931
+ }
18932
+ async function handleList2(options) {
18933
+ const limit = options.limit ? Number(options.limit) : void 0;
18934
+ const result = await client().listWorkflows({
18935
+ limit: Number.isFinite(limit) ? limit : void 0
18936
+ });
18937
+ emitPassThrough("list", result, options);
18938
+ }
18939
+ async function handleGet(id, options) {
18940
+ emitPassThrough("get", await client().getWorkflow(id), options);
18941
+ }
18942
+ async function handleDelete(id, options) {
18943
+ emitPassThrough("delete", await client().deleteWorkflow(id), options);
18944
+ }
18945
+ async function handleDisable(id, options) {
18946
+ const result = await client().disableWorkflow(id);
18947
+ printJson(result);
18948
+ }
18949
+ async function handleEnable(id, options) {
18950
+ const result = await client().enableWorkflow(id);
18951
+ printJson(result);
18952
+ }
18953
+ async function handleApply(options) {
18954
+ const body = await readJsonOption(options.payload, options.file);
18955
+ emitPassThrough("apply", await client().applyWorkflow(body), options);
18956
+ }
18957
+ async function handleLint(options) {
18958
+ const body = await readJsonOption(options.payload, options.file);
18959
+ emitPassThrough("lint", await client().lintWorkflow(body), options);
18960
+ }
18961
+ async function handleSchema(options) {
18962
+ emitPassThrough(
18963
+ "schema",
18964
+ await client().getWorkflowSchema(options.subject),
18965
+ options
18966
+ );
18967
+ }
18968
+ async function handleCall(options) {
18969
+ const body = {};
18970
+ if (options.workflowId) body.workflow_id = options.workflowId;
18971
+ if (options.workflowName) body.workflow_name = options.workflowName;
18972
+ const input2 = options.payload ? JSON.parse(options.payload) : {};
18973
+ if (options.mode) input2.__execution_mode = options.mode;
18974
+ body.input = input2;
18975
+ emitPassThrough("call", await client().callWorkflow(body), options);
18976
+ }
18977
+ async function handleRuns(id, options) {
18978
+ const limit = options.limit ? Number(options.limit) : void 0;
18979
+ emitPassThrough(
18980
+ "runs",
18981
+ await client().listWorkflowRuns(id, {
18982
+ limit: Number.isFinite(limit) ? limit : void 0
18983
+ }),
18984
+ options
18985
+ );
18986
+ }
18987
+ async function handleRun(id, runId, options) {
18988
+ emitPassThrough("run", await client().getWorkflowRun(id, runId), options);
18989
+ }
18990
+ async function handleCancel(id, runId, options) {
18991
+ emitPassThrough(
18992
+ "cancel",
18993
+ await client().cancelWorkflowRun(id, runId),
18994
+ options
18995
+ );
18996
+ }
18997
+ async function handleTail(id, runId, options) {
18998
+ const api = client();
18999
+ const intervalMs = Math.max(500, Number(options.intervalMs ?? 2e3) || 2e3);
19000
+ const deadline = Date.now() + 10 * 6e4;
19001
+ for (; ; ) {
19002
+ const run = await api.getWorkflowRun(id, runId);
19003
+ emitPassThrough("tail", run, options);
19004
+ const status = readStatus(run);
19005
+ if (status && TERMINAL_RUN_STATUSES.has(status) || Date.now() > deadline) {
19006
+ return;
19007
+ }
19008
+ await sleep3(intervalMs);
19009
+ }
19010
+ }
19011
+ function registerWorkflowCommands(program) {
19012
+ const workflows = program.command("workflows").description(
19013
+ "Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play."
19014
+ ).addHelpText(
19015
+ "after",
19016
+ `
19017
+ Workflows are a legacy surface. They still run in the cloud and these commands
19018
+ keep working, but new work belongs in plays. Migrate a workflow with:
19019
+
19020
+ deepline workflows transform <id> # write a reviewable .play.ts
19021
+ deepline workflows transform --all --publish # migrate + publish them all
19022
+
19023
+ Equivalents: list\u2192plays list \xB7 call\u2192plays run \xB7 apply\u2192plays publish \xB7
19024
+ lint\u2192plays check \xB7 runs\u2192runs list.
19025
+ `
19026
+ );
19027
+ withJsonOption(
19028
+ workflows.command("transform [id]").description("Compile a workflow into a reviewable V2 play (.play.ts).").option("--all", "Transform every workflow in the workspace").option("--out <dir>", "Output directory for generated plays", "./plays").option("--publish", "Publish each clean play immediately")
19029
+ ).addHelpText(
19030
+ "after",
19031
+ `
19032
+ Examples:
19033
+ deepline workflows transform wf_123 --out ./plays
19034
+ deepline workflows transform --all
19035
+ deepline workflows transform --all --publish --json
19036
+
19037
+ Notes:
19038
+ HITL, run_javascript steps, and sub-workflow calls cannot run as a play yet;
19039
+ those workflows are reported as needing manual attention and skipped.
19040
+ `
19041
+ ).action(async (id, options) => {
19042
+ process.exitCode = await handleTransform(id, options);
19043
+ });
19044
+ withJsonOption(
19045
+ workflows.command("list").description("List workspace workflows.").option("--limit <n>", "Max workflows to return")
19046
+ ).action(handleList2);
19047
+ withJsonOption(
19048
+ workflows.command("get <id>").description(
19049
+ "Fetch a workflow, including its published-revision config."
19050
+ )
19051
+ ).action(handleGet);
19052
+ withJsonOption(
19053
+ workflows.command("disable <id>").description("Turn a workflow off.")
19054
+ ).action(handleDisable);
19055
+ withJsonOption(
19056
+ workflows.command("enable <id>").description("Turn a workflow back on.")
19057
+ ).action(handleEnable);
19058
+ withJsonOption(
19059
+ workflows.command("delete <id>").description("Delete a workflow and its revisions/runs.")
19060
+ ).action(handleDelete);
19061
+ withJsonOption(
19062
+ workflows.command("apply").description("Create or update a workflow from config.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
19063
+ ).action(handleApply);
19064
+ withJsonOption(
19065
+ workflows.command("lint").description("Validate a workflow config without saving.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
19066
+ ).action(handleLint);
19067
+ withJsonOption(
19068
+ workflows.command("schema").description("Fetch live workflow request schemas.").option(
19069
+ "--subject <subject>",
19070
+ "Schema subject (apply|call|trigger|all|\u2026)"
19071
+ )
19072
+ ).action(handleSchema);
19073
+ withJsonOption(
19074
+ workflows.command("call").description("Queue a workflow run.").option("--workflow-id <id>", "Workflow id").option("--workflow-name <name>", "Workflow name").option("--payload <json>", "Run input JSON").option("--mode <mode>", "live | dry_run | smoke_test")
19075
+ ).action(handleCall);
19076
+ withJsonOption(
19077
+ workflows.command("runs <id>").description("List a workflow\u2019s runs.").option("--limit <n>", "Max runs to return")
19078
+ ).action(handleRuns);
19079
+ withJsonOption(
19080
+ workflows.command("run <id> <runId>").description("Fetch a single workflow run.")
19081
+ ).action(handleRun);
19082
+ withJsonOption(
19083
+ workflows.command("tail <id> <runId>").description("Poll a workflow run until it reaches a terminal status.").option("--interval-ms <n>", "Poll interval in ms (default 2000)")
19084
+ ).action(handleTail);
19085
+ withJsonOption(
19086
+ workflows.command("cancel <id> <runId>").description("Cancel a running workflow run.")
19087
+ ).action(handleCancel);
19088
+ }
19089
+
16896
19090
  // src/cli/commands/update.ts
16897
19091
  var import_node_child_process = require("child_process");
16898
19092
  var import_node_fs13 = require("fs");
16899
- var import_node_path16 = require("path");
19093
+ var import_node_path17 = require("path");
16900
19094
  function posixShellQuote(value) {
16901
19095
  return `'${value.replace(/'/g, `'\\''`)}'`;
16902
19096
  }
@@ -16915,19 +19109,19 @@ function buildSourceUpdateCommand(sourceRoot) {
16915
19109
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
16916
19110
  }
16917
19111
  function findRepoBackedSdkRoot(startPath) {
16918
- let current = (0, import_node_path16.resolve)(startPath);
19112
+ let current = (0, import_node_path17.resolve)(startPath);
16919
19113
  while (true) {
16920
- if ((0, import_node_fs13.existsSync)((0, import_node_path16.join)(current, "sdk", "package.json")) && (0, import_node_fs13.existsSync)((0, import_node_path16.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
19114
+ if ((0, import_node_fs13.existsSync)((0, import_node_path17.join)(current, "sdk", "package.json")) && (0, import_node_fs13.existsSync)((0, import_node_path17.join)(current, "sdk", "bin", "deepline-dev.ts"))) {
16921
19115
  return current;
16922
19116
  }
16923
- const parent = (0, import_node_path16.dirname)(current);
19117
+ const parent = (0, import_node_path17.dirname)(current);
16924
19118
  if (parent === current) return null;
16925
19119
  current = parent;
16926
19120
  }
16927
19121
  }
16928
19122
  function resolveUpdatePlan() {
16929
- const entrypoint = process.argv[1] ? (0, import_node_path16.resolve)(process.argv[1]) : "";
16930
- const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path16.dirname)(entrypoint)) : null;
19123
+ const entrypoint = process.argv[1] ? (0, import_node_path17.resolve)(process.argv[1]) : "";
19124
+ const sourceRoot = entrypoint ? findRepoBackedSdkRoot((0, import_node_path17.dirname)(entrypoint)) : null;
16931
19125
  if (sourceRoot) {
16932
19126
  return {
16933
19127
  kind: "source",
@@ -17192,7 +19386,7 @@ function unknownCommandNameFromMessage(message) {
17192
19386
  var import_node_child_process2 = require("child_process");
17193
19387
  var import_node_fs14 = require("fs");
17194
19388
  var import_node_os10 = require("os");
17195
- var import_node_path17 = require("path");
19389
+ var import_node_path18 = require("path");
17196
19390
  var CHECK_TIMEOUT_MS2 = 3e3;
17197
19391
  var SDK_SKILL_NAME = "deepline-plays";
17198
19392
  var attemptedSync = false;
@@ -17212,14 +19406,14 @@ function readPluginSkillsVersion() {
17212
19406
  const dir = activePluginSkillsDir();
17213
19407
  if (!dir) return "";
17214
19408
  try {
17215
- return (0, import_node_fs14.readFileSync)((0, import_node_path17.join)(dir, ".version"), "utf-8").trim();
19409
+ return (0, import_node_fs14.readFileSync)((0, import_node_path18.join)(dir, ".version"), "utf-8").trim();
17216
19410
  } catch {
17217
19411
  return "";
17218
19412
  }
17219
19413
  }
17220
19414
  function sdkSkillsVersionPath(baseUrl) {
17221
19415
  const home = process.env.HOME?.trim() || (0, import_node_os10.homedir)();
17222
- return (0, import_node_path17.join)(
19416
+ return (0, import_node_path18.join)(
17223
19417
  home,
17224
19418
  ".local",
17225
19419
  "deepline",
@@ -17241,16 +19435,16 @@ function readLocalSkillsVersion(baseUrl) {
17241
19435
  }
17242
19436
  function writeLocalSkillsVersion(baseUrl, version) {
17243
19437
  const path = sdkSkillsVersionPath(baseUrl);
17244
- (0, import_node_fs14.mkdirSync)((0, import_node_path17.dirname)(path), { recursive: true });
19438
+ (0, import_node_fs14.mkdirSync)((0, import_node_path18.dirname)(path), { recursive: true });
17245
19439
  (0, import_node_fs14.writeFileSync)(path, `${version}
17246
19440
  `, "utf-8");
17247
19441
  }
17248
19442
  function installedSdkSkillHasStalePositionalExecuteExamples() {
17249
19443
  const home = process.env.HOME?.trim() || (0, import_node_os10.homedir)();
17250
19444
  const pluginSkillsDir = activePluginSkillsDir();
17251
- const roots = pluginSkillsDir ? [(0, import_node_path17.join)(pluginSkillsDir, SDK_SKILL_NAME)] : [
17252
- (0, import_node_path17.join)(home, ".claude", "skills", SDK_SKILL_NAME),
17253
- (0, import_node_path17.join)(home, ".agents", "skills", SDK_SKILL_NAME)
19445
+ const roots = pluginSkillsDir ? [(0, import_node_path18.join)(pluginSkillsDir, SDK_SKILL_NAME)] : [
19446
+ (0, import_node_path18.join)(home, ".claude", "skills", SDK_SKILL_NAME),
19447
+ (0, import_node_path18.join)(home, ".agents", "skills", SDK_SKILL_NAME)
17254
19448
  ];
17255
19449
  const staleMarkers = [
17256
19450
  "ctx.tools.execute(key",
@@ -17261,7 +19455,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
17261
19455
  ];
17262
19456
  const scan = (dir) => {
17263
19457
  for (const entry of (0, import_node_fs14.readdirSync)(dir)) {
17264
- const path = (0, import_node_path17.join)(dir, entry);
19458
+ const path = (0, import_node_path18.join)(dir, entry);
17265
19459
  const stat4 = (0, import_node_fs14.statSync)(path);
17266
19460
  if (stat4.isDirectory()) {
17267
19461
  if (scan(path)) return true;
@@ -17347,7 +19541,7 @@ function resolveSkillsInstallCommands(baseUrl) {
17347
19541
  return [npxInstall];
17348
19542
  }
17349
19543
  function runOneSkillsInstall(install) {
17350
- return new Promise((resolve14) => {
19544
+ return new Promise((resolve15) => {
17351
19545
  const child = (0, import_node_child_process2.spawn)(install.command, install.args, {
17352
19546
  stdio: ["ignore", "ignore", "pipe"],
17353
19547
  env: process.env
@@ -17357,7 +19551,7 @@ function runOneSkillsInstall(install) {
17357
19551
  stderr += chunk.toString("utf-8");
17358
19552
  });
17359
19553
  child.on("error", (error) => {
17360
- resolve14({
19554
+ resolve15({
17361
19555
  ok: false,
17362
19556
  detail: `failed to start ${install.command}: ${error.message}`,
17363
19557
  manualCommand: install.manualCommand
@@ -17365,11 +19559,11 @@ function runOneSkillsInstall(install) {
17365
19559
  });
17366
19560
  child.on("close", (code) => {
17367
19561
  if (code === 0) {
17368
- resolve14({ ok: true, detail: "", manualCommand: install.manualCommand });
19562
+ resolve15({ ok: true, detail: "", manualCommand: install.manualCommand });
17369
19563
  return;
17370
19564
  }
17371
19565
  const detail = stderr.trim();
17372
- resolve14({
19566
+ resolve15({
17373
19567
  ok: false,
17374
19568
  detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
17375
19569
  manualCommand: install.manualCommand
@@ -17457,10 +19651,10 @@ function shouldDeferSkillsSyncForCommand() {
17457
19651
  return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
17458
19652
  }
17459
19653
  async function runPlayRunnerHealthCheck() {
17460
- const dir = await (0, import_promises6.mkdtemp)((0, import_node_path18.join)((0, import_node_os11.tmpdir)(), "deepline-health-play-"));
17461
- const file = (0, import_node_path18.join)(dir, "health-check.play.ts");
19654
+ const dir = await (0, import_promises7.mkdtemp)((0, import_node_path19.join)((0, import_node_os11.tmpdir)(), "deepline-health-play-"));
19655
+ const file = (0, import_node_path19.join)(dir, "health-check.play.ts");
17462
19656
  try {
17463
- await (0, import_promises6.writeFile)(
19657
+ await (0, import_promises7.writeFile)(
17464
19658
  file,
17465
19659
  [
17466
19660
  "import { definePlay } from 'deepline';",
@@ -17508,7 +19702,7 @@ async function runPlayRunnerHealthCheck() {
17508
19702
  }
17509
19703
  };
17510
19704
  } finally {
17511
- await (0, import_promises6.rm)(dir, { recursive: true, force: true });
19705
+ await (0, import_promises7.rm)(dir, { recursive: true, force: true });
17512
19706
  }
17513
19707
  }
17514
19708
  function pickString(value, ...keys) {
@@ -17557,14 +19751,12 @@ async function runPreflightCheck() {
17557
19751
  connected: false,
17558
19752
  error: preflightErrorMessage(error)
17559
19753
  })),
17560
- http.get("/api/v2/billing/balance").catch(
17561
- (error) => ({
17562
- balance: null,
17563
- balance_display: "unavailable",
17564
- balance_status: "unknown",
17565
- error: preflightErrorMessage(error)
17566
- })
17567
- )
19754
+ http.get("/api/v2/billing/balance").catch((error) => ({
19755
+ balance: null,
19756
+ balance_display: "unavailable",
19757
+ balance_status: "unknown",
19758
+ error: preflightErrorMessage(error)
19759
+ }))
17568
19760
  ]) : [
17569
19761
  {
17570
19762
  status: "not_connected",
@@ -17700,6 +19892,7 @@ Exit codes:
17700
19892
  registerAuthCommands(program);
17701
19893
  registerToolsCommands(program);
17702
19894
  registerPlayCommands(program);
19895
+ registerWorkflowCommands(program);
17703
19896
  registerSecretsCommands(program);
17704
19897
  registerBillingCommands(program);
17705
19898
  registerOrgCommands(program);
@@ -17752,8 +19945,8 @@ Examples:
17752
19945
  `);
17753
19946
  return;
17754
19947
  }
17755
- const client = new DeeplineClient();
17756
- const data = await client.health();
19948
+ const client2 = new DeeplineClient();
19949
+ const data = await client2.health();
17757
19950
  process.stdout.write(`${JSON.stringify(data, null, 2)}
17758
19951
  `);
17759
19952
  } catch (error) {