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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile5 } from "fs/promises";
5
- import { join as join13 } from "path";
4
+ import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile6 } from "fs/promises";
5
+ import { join as join14 } from "path";
6
6
  import { tmpdir as tmpdir5 } from "os";
7
7
  import { Command as Command3 } from "commander";
8
8
 
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
206
206
 
207
207
  // src/release.ts
208
208
  var SDK_RELEASE = {
209
- version: "0.1.93",
209
+ version: "0.1.94",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.93",
212
+ latest: "0.1.94",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
@@ -561,7 +561,7 @@ function decodeSseFrame(frame) {
561
561
  return parsed;
562
562
  }
563
563
  function sleep(ms) {
564
- return new Promise((resolve14) => setTimeout(resolve14, ms));
564
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
565
565
  }
566
566
 
567
567
  // src/stream-reconnect.ts
@@ -1177,7 +1177,7 @@ async function* observeRunEvents(options) {
1177
1177
  const logPageQuery = convexServer.makeFunctionReference(
1178
1178
  OBSERVER_LOG_PAGE_QUERY
1179
1179
  );
1180
- const client = new convexBrowser.ConvexClient(grant.convexUrl, {
1180
+ const client2 = new convexBrowser.ConvexClient(grant.convexUrl, {
1181
1181
  ...webSocketConstructor ? { webSocketConstructor } : {},
1182
1182
  unsavedChangesWarning: false
1183
1183
  });
@@ -1189,7 +1189,7 @@ async function* observeRunEvents(options) {
1189
1189
  wake = null;
1190
1190
  };
1191
1191
  let lastForcedRefreshAt = 0;
1192
- client.setAuth(async ({ forceRefreshToken }) => {
1192
+ client2.setAuth(async ({ forceRefreshToken }) => {
1193
1193
  const now = Date.now();
1194
1194
  if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
1195
1195
  return grant.token;
@@ -1216,7 +1216,7 @@ async function* observeRunEvents(options) {
1216
1216
  return null;
1217
1217
  }
1218
1218
  });
1219
- const unsubscribe = client.onUpdate(
1219
+ const unsubscribe = client2.onUpdate(
1220
1220
  snapshotQuery,
1221
1221
  { workflowId: runId },
1222
1222
  (run) => push({ kind: "run", run: run ?? null }),
@@ -1230,7 +1230,7 @@ async function* observeRunEvents(options) {
1230
1230
  const watchdog = setInterval(() => {
1231
1231
  const now = Date.now();
1232
1232
  try {
1233
- const connectionState = client.connectionState();
1233
+ const connectionState = client2.connectionState();
1234
1234
  if (connectionState.isWebSocketConnected) {
1235
1235
  disconnectedSince = null;
1236
1236
  warnedReconnecting = false;
@@ -1268,14 +1268,14 @@ async function* observeRunEvents(options) {
1268
1268
  try {
1269
1269
  for (; ; ) {
1270
1270
  if (queue.length === 0) {
1271
- const waitForItem = new Promise((resolve14) => {
1272
- wake = resolve14;
1271
+ const waitForItem = new Promise((resolve15) => {
1272
+ wake = resolve15;
1273
1273
  });
1274
1274
  if (!sawFirstSnapshot) {
1275
1275
  const timedOut = await Promise.race([
1276
1276
  waitForItem.then(() => false),
1277
1277
  new Promise(
1278
- (resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1278
+ (resolve15) => setTimeout(() => resolve15(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1279
1279
  )
1280
1280
  ]);
1281
1281
  if (timedOut && queue.length === 0) {
@@ -1311,7 +1311,7 @@ async function* observeRunEvents(options) {
1311
1311
  const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
1312
1312
  if (gap && diffState.lastLogSeq > 0) {
1313
1313
  const backfilled = await backfillLogGap({
1314
- queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
1314
+ queryLogPage: (afterSeq, limit) => client2.query(logPageQuery, {
1315
1315
  workflowId: runId,
1316
1316
  afterSeq,
1317
1317
  limit
@@ -1361,7 +1361,7 @@ async function* observeRunEvents(options) {
1361
1361
  unsubscribe();
1362
1362
  } catch {
1363
1363
  }
1364
- await client.close().catch(() => void 0);
1364
+ await client2.close().catch(() => void 0);
1365
1365
  }
1366
1366
  }
1367
1367
 
@@ -1372,7 +1372,7 @@ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
1372
1372
  var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
1373
1373
  var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
1374
1374
  function sleep2(ms) {
1375
- return new Promise((resolve14) => setTimeout(resolve14, ms));
1375
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
1376
1376
  }
1377
1377
  function isTransientCompileManifestError(error) {
1378
1378
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
@@ -2275,6 +2275,93 @@ var DeeplineClient = class {
2275
2275
  );
2276
2276
  return response.runs ?? [];
2277
2277
  }
2278
+ // ---------------------------------------------------------------------------
2279
+ // Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
2280
+ // `/api/v2/workflows/*` API so the SDK CLI keeps existing cloud workflows
2281
+ // working while users migrate them to plays via `workflows transform`. Kept
2282
+ // intentionally minimal — workflows are a deprecated surface.
2283
+ // ---------------------------------------------------------------------------
2284
+ /** List the org's workflows. `GET /api/v2/workflows`. */
2285
+ async listWorkflows(options) {
2286
+ const params = new URLSearchParams();
2287
+ if (typeof options?.limit === "number") {
2288
+ params.set("limit", String(options.limit));
2289
+ }
2290
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2291
+ return this.http.get(`/api/v2/workflows${query}`);
2292
+ }
2293
+ /**
2294
+ * Fetch a single workflow (including its published-revision config — the
2295
+ * input to `compileWorkflowConfigToPlay`). `GET /api/v2/workflows/:id`.
2296
+ */
2297
+ async getWorkflow(id) {
2298
+ return this.http.get(`/api/v2/workflows/${encodeURIComponent(id)}`);
2299
+ }
2300
+ /** Delete a workflow. `DELETE /api/v2/workflows/:id`. */
2301
+ async deleteWorkflow(id) {
2302
+ return this.http.delete(`/api/v2/workflows/${encodeURIComponent(id)}`);
2303
+ }
2304
+ /** Turn a workflow off. `POST /api/v2/workflows/:id/disable`. */
2305
+ async disableWorkflow(id) {
2306
+ return this.http.post(
2307
+ `/api/v2/workflows/${encodeURIComponent(id)}/disable`,
2308
+ {}
2309
+ );
2310
+ }
2311
+ /** Turn a workflow back on. `POST /api/v2/workflows/:id/enable`. */
2312
+ async enableWorkflow(id) {
2313
+ return this.http.post(
2314
+ `/api/v2/workflows/${encodeURIComponent(id)}/enable`,
2315
+ {}
2316
+ );
2317
+ }
2318
+ /** Create/update a workflow from config. `POST /api/v2/workflows/apply`. */
2319
+ async applyWorkflow(body) {
2320
+ return this.http.post("/api/v2/workflows/apply", body);
2321
+ }
2322
+ /** Validate a workflow config without saving. `POST /api/v2/workflows/lint`. */
2323
+ async lintWorkflow(body) {
2324
+ return this.http.post("/api/v2/workflows/lint", body);
2325
+ }
2326
+ /** Fetch live workflow request schemas. `GET /api/v2/workflows/schema`. */
2327
+ async getWorkflowSchema(subject) {
2328
+ const params = new URLSearchParams();
2329
+ if (subject) params.set("subject", subject);
2330
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2331
+ return this.http.get(`/api/v2/workflows/schema${query}`);
2332
+ }
2333
+ /** Queue a workflow run. `POST /api/v2/workflows/call`. */
2334
+ async callWorkflow(body) {
2335
+ return this.http.post("/api/v2/workflows/call", body);
2336
+ }
2337
+ /** List a workflow's runs. `GET /api/v2/workflows/:id/runs`. */
2338
+ async listWorkflowRuns(id, options) {
2339
+ const params = new URLSearchParams();
2340
+ if (typeof options?.limit === "number") {
2341
+ params.set("limit", String(options.limit));
2342
+ }
2343
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2344
+ return this.http.get(
2345
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs${query}`
2346
+ );
2347
+ }
2348
+ /** Fetch one workflow run. `GET /api/v2/workflows/:id/runs/:runId`. */
2349
+ async getWorkflowRun(id, runId) {
2350
+ return this.http.get(
2351
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
2352
+ runId
2353
+ )}`
2354
+ );
2355
+ }
2356
+ /** Cancel a workflow run. `POST /api/v2/workflows/:id/runs/:runId/cancel`. */
2357
+ async cancelWorkflowRun(id, runId) {
2358
+ return this.http.post(
2359
+ `/api/v2/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(
2360
+ runId
2361
+ )}/cancel`,
2362
+ {}
2363
+ );
2364
+ }
2278
2365
  /**
2279
2366
  * Get a run by id using the public runs resource model.
2280
2367
  *
@@ -3174,6 +3261,9 @@ function openInBrowser(url) {
3174
3261
  } catch {
3175
3262
  }
3176
3263
  }
3264
+ function sleep3(ms) {
3265
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
3266
+ }
3177
3267
  function collectLocalEnvInfo() {
3178
3268
  return {
3179
3269
  os: `${process.platform} ${process.arch}`,
@@ -3211,14 +3301,38 @@ function parseMaybeJsonObject(value) {
3211
3301
  return value;
3212
3302
  }
3213
3303
  }
3304
+ function failureMessageFromRecord(value) {
3305
+ const status = typeof value.status === "string" ? value.status.trim().toLowerCase() : "";
3306
+ const directError = typeof value.error === "string" ? value.error.trim() : typeof value.last_error === "string" ? value.last_error.trim() : "";
3307
+ const result = value.result && typeof value.result === "object" && !Array.isArray(value.result) ? value.result : null;
3308
+ const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
3309
+ if (!directError && !resultError && status !== "error" && status !== "failed") {
3310
+ return null;
3311
+ }
3312
+ return directError || resultError || `Column status: ${status}`;
3313
+ }
3214
3314
  function flattenObjectColumns(row) {
3215
3315
  const flattened = {};
3216
3316
  for (const [key, rawValue] of Object.entries(row)) {
3217
3317
  const value = parseMaybeJsonObject(rawValue);
3318
+ if (key === "_metadata") {
3319
+ flattened[key] = value && typeof value === "object" ? JSON.stringify(value) : value;
3320
+ continue;
3321
+ }
3218
3322
  if (value && typeof value === "object" && !Array.isArray(value)) {
3219
- for (const [nestedKey, nestedValue] of Object.entries(
3220
- value
3221
- )) {
3323
+ const record = value;
3324
+ const hasMatchedEnvelope = Object.prototype.hasOwnProperty.call(record, "matched_result") || Object.prototype.hasOwnProperty.call(record, "matchedResult");
3325
+ if (hasMatchedEnvelope) {
3326
+ flattened[key] = JSON.stringify(record);
3327
+ } else {
3328
+ const failureMessage = failureMessageFromRecord(record);
3329
+ if (failureMessage) {
3330
+ flattened[key] = failureMessage;
3331
+ } else if (Object.prototype.hasOwnProperty.call(record, "result")) {
3332
+ flattened[key] = JSON.stringify(record);
3333
+ }
3334
+ }
3335
+ for (const [nestedKey, nestedValue] of Object.entries(record)) {
3222
3336
  flattened[`${key}.${nestedKey}`] = nestedValue && typeof nestedValue === "object" ? JSON.stringify(nestedValue) : nestedValue;
3223
3337
  }
3224
3338
  continue;
@@ -3474,8 +3588,8 @@ function buildCandidateUrls2(url) {
3474
3588
  return [url];
3475
3589
  }
3476
3590
  }
3477
- function sleep3(ms) {
3478
- return new Promise((resolve14) => setTimeout(resolve14, ms));
3591
+ function sleep4(ms) {
3592
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
3479
3593
  }
3480
3594
  function printDeeplineLogo() {
3481
3595
  if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
@@ -3578,7 +3692,7 @@ async function handleRegister(args) {
3578
3692
  return EXIT_AUTH;
3579
3693
  }
3580
3694
  if (s >= 500 || s === 0 || s === 400) {
3581
- await sleep3(2e3);
3695
+ await sleep4(2e3);
3582
3696
  continue;
3583
3697
  }
3584
3698
  if (s >= 400) {
@@ -3608,7 +3722,7 @@ async function handleRegister(args) {
3608
3722
  );
3609
3723
  return EXIT_AUTH;
3610
3724
  }
3611
- await sleep3(2e3);
3725
+ await sleep4(2e3);
3612
3726
  }
3613
3727
  }
3614
3728
  async function handleWait(args) {
@@ -3645,7 +3759,7 @@ async function handleWait(args) {
3645
3759
  return EXIT_AUTH;
3646
3760
  }
3647
3761
  if (status >= 500 || status === 0 || status === 400) {
3648
- await sleep3(2e3);
3762
+ await sleep4(2e3);
3649
3763
  continue;
3650
3764
  }
3651
3765
  if (status >= 400) {
@@ -3673,7 +3787,7 @@ async function handleWait(args) {
3673
3787
  console.error("That approval link expired. Run: deepline auth register");
3674
3788
  return EXIT_AUTH;
3675
3789
  }
3676
- await sleep3(2e3);
3790
+ await sleep4(2e3);
3677
3791
  }
3678
3792
  console.error(
3679
3793
  "Still pending. Approve the browser link, then run: deepline auth wait"
@@ -4489,6 +4603,9 @@ function isRecord3(value) {
4489
4603
  function isSerializedDataset(value) {
4490
4604
  return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
4491
4605
  }
4606
+ function isPackagedDatasetOutput(value) {
4607
+ return isRecord3(value) && value.kind === "dataset" && isRecord3(value.preview) && Array.isArray(value.preview.rows);
4608
+ }
4492
4609
  function pathParts(path) {
4493
4610
  return path.split(".").map((part) => part.trim()).filter(Boolean);
4494
4611
  }
@@ -4536,6 +4653,12 @@ function inferColumns(rows) {
4536
4653
  }
4537
4654
  return columns;
4538
4655
  }
4656
+ function columnsFromDatasetSummary(summary) {
4657
+ if (!isRecord3(summary) || !isRecord3(summary.columnStats)) {
4658
+ return [];
4659
+ }
4660
+ return Object.keys(summary.columnStats).filter((column) => column);
4661
+ }
4539
4662
  function canonicalRowsInfoFromCandidate(input2) {
4540
4663
  const candidate = input2;
4541
4664
  if (isSerializedDataset(candidate.value)) {
@@ -4558,6 +4681,28 @@ function canonicalRowsInfoFromCandidate(input2) {
4558
4681
  tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
4559
4682
  };
4560
4683
  }
4684
+ if (isPackagedDatasetOutput(candidate.value)) {
4685
+ const rawRows = rowArray(candidate.value.preview?.rows) ?? [];
4686
+ const totalRows2 = readNumber(candidate.value.preview?.totalRows) ?? readNumber(candidate.value.rowCount) ?? rawRows.length;
4687
+ const explicitColumns = Array.isArray(candidate.value.columns) ? candidate.value.columns.filter(
4688
+ (column) => typeof column === "string"
4689
+ ) : [];
4690
+ const rawColumns = explicitColumns.length > 0 ? explicitColumns : columnsFromDatasetSummary(candidate.value.summary);
4691
+ const { rows: rows2, columns } = sanitizeCsvProjectionInfo({
4692
+ rows: rawRows,
4693
+ columns: rawColumns.length > 0 ? rawColumns : inferColumns(rawRows)
4694
+ });
4695
+ return {
4696
+ rows: rows2,
4697
+ totalRows: totalRows2,
4698
+ columns,
4699
+ columnsExplicit: rawColumns.length > 0,
4700
+ complete: rows2.length === totalRows2,
4701
+ source: candidate.source,
4702
+ datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
4703
+ tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null
4704
+ };
4705
+ }
4561
4706
  if (candidate.serializedOnly) {
4562
4707
  return null;
4563
4708
  }
@@ -4590,6 +4735,14 @@ function collectDatasetCandidates(input2) {
4590
4735
  });
4591
4736
  return;
4592
4737
  }
4738
+ if (isPackagedDatasetOutput(input2.value)) {
4739
+ input2.output.push({
4740
+ source: input2.path,
4741
+ value: input2.value,
4742
+ total: input2.total
4743
+ });
4744
+ return;
4745
+ }
4593
4746
  if (!isRecord3(input2.value)) {
4594
4747
  return;
4595
4748
  }
@@ -4662,6 +4815,19 @@ function collectCanonicalRowsInfos(statusOrResult) {
4662
4815
  }
4663
4816
  );
4664
4817
  }
4818
+ if (Array.isArray(result.steps)) {
4819
+ result.steps.forEach((step, index) => {
4820
+ if (!isRecord3(step) || !isRecord3(step.output)) {
4821
+ return;
4822
+ }
4823
+ const source = typeof step.output.path === "string" ? step.output.path : typeof step.id === "string" ? `steps.${step.id}.output` : `steps.${index}.output`;
4824
+ candidates.push({
4825
+ source,
4826
+ value: step.output,
4827
+ total: step.output.rowCount ?? (isRecord3(step.output.preview) ? step.output.preview.totalRows : void 0) ?? (isRecord3(step.progress) ? step.progress.total : void 0)
4828
+ });
4829
+ });
4830
+ }
4665
4831
  collectDatasetCandidates({
4666
4832
  value: result,
4667
4833
  path: "result",
@@ -4671,12 +4837,14 @@ function collectCanonicalRowsInfos(statusOrResult) {
4671
4837
  const seen = /* @__PURE__ */ new Set();
4672
4838
  const infos = [];
4673
4839
  for (const candidate of candidates) {
4674
- if (seen.has(candidate.source)) {
4675
- continue;
4676
- }
4677
- seen.add(candidate.source);
4678
4840
  const info = canonicalRowsInfoFromCandidate(candidate);
4679
4841
  if (info) {
4842
+ if (info.source) {
4843
+ if (seen.has(info.source)) {
4844
+ continue;
4845
+ }
4846
+ seen.add(info.source);
4847
+ }
4680
4848
  infos.push(info);
4681
4849
  }
4682
4850
  }
@@ -4697,15 +4865,17 @@ function collectSerializedDatasetRowsInfos(statusOrResult) {
4697
4865
  const seen = /* @__PURE__ */ new Set();
4698
4866
  const infos = [];
4699
4867
  for (const candidate of candidates) {
4700
- if (seen.has(candidate.source)) {
4701
- continue;
4702
- }
4703
- seen.add(candidate.source);
4704
4868
  const info = canonicalRowsInfoFromCandidate({
4705
4869
  ...candidate,
4706
4870
  serializedOnly: true
4707
4871
  });
4708
4872
  if (info) {
4873
+ if (info.source) {
4874
+ if (seen.has(info.source)) {
4875
+ continue;
4876
+ }
4877
+ seen.add(info.source);
4878
+ }
4709
4879
  infos.push(info);
4710
4880
  }
4711
4881
  }
@@ -5215,10 +5385,10 @@ async function handleDbQuery(args) {
5215
5385
  }
5216
5386
  const jsonOutput = argsWantJson(args);
5217
5387
  const explicitJsonOutput = args.includes("--json");
5218
- const client = new DeeplineClient();
5388
+ const client2 = new DeeplineClient();
5219
5389
  let result;
5220
5390
  try {
5221
- result = await client.queryCustomerDb({ sql, maxRows });
5391
+ result = await client2.queryCustomerDb({ sql, maxRows });
5222
5392
  } catch (error) {
5223
5393
  console.error(formatDbQueryError(sql, error));
5224
5394
  return 1;
@@ -5372,7 +5542,14 @@ Examples:
5372
5542
  }
5373
5543
 
5374
5544
  // src/cli/commands/enrich.ts
5375
- import { mkdtemp, readFile as readFile3, rm, stat as stat3, writeFile as writeFile4 } from "fs/promises";
5545
+ import {
5546
+ mkdir as mkdir4,
5547
+ mkdtemp,
5548
+ readFile as readFile3,
5549
+ rm,
5550
+ stat as stat3,
5551
+ writeFile as writeFile4
5552
+ } from "fs/promises";
5376
5553
  import { homedir as homedir4, tmpdir as tmpdir3 } from "os";
5377
5554
  import { join as join8, resolve as resolve11 } from "path";
5378
5555
 
@@ -5474,23 +5651,28 @@ function shannonEntropy(value) {
5474
5651
  return entropy - p * Math.log2(p);
5475
5652
  }, 0);
5476
5653
  }
5477
- function validatePlaySourceHasNoInlineSecrets(input2) {
5654
+ function collectInlineSecretFindings(sourceCode) {
5478
5655
  const findings = [];
5479
- for (const match of input2.sourceCode.matchAll(SECRET_ENV_PATTERN)) {
5656
+ for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
5480
5657
  findings.push(`process.env.${match[1]}`);
5481
5658
  }
5482
- if (PRIVATE_KEY_PATTERN.test(input2.sourceCode)) findings.push("private key block");
5483
- if (BEARER_LITERAL_PATTERN.test(input2.sourceCode)) findings.push("bearer token literal");
5484
- if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(input2.sourceCode)) {
5659
+ if (PRIVATE_KEY_PATTERN.test(sourceCode)) findings.push("private key block");
5660
+ if (BEARER_LITERAL_PATTERN.test(sourceCode))
5661
+ findings.push("bearer token literal");
5662
+ if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
5485
5663
  findings.push("secret-looking assignment literal");
5486
5664
  }
5487
- for (const match of input2.sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
5665
+ for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
5488
5666
  const literal = match[1] ?? "";
5489
5667
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
5490
5668
  findings.push("high-entropy string literal");
5491
5669
  break;
5492
5670
  }
5493
5671
  }
5672
+ return [...new Set(findings)];
5673
+ }
5674
+ function validatePlaySourceHasNoInlineSecrets(input2) {
5675
+ const findings = collectInlineSecretFindings(input2.sourceCode);
5494
5676
  if (!findings.length) return;
5495
5677
  throw new Error(
5496
5678
  [
@@ -8438,13 +8620,13 @@ export default definePlay(${jsString(input2.options.name)}, async (ctx, input: I
8438
8620
 
8439
8621
  `;
8440
8622
  }
8441
- async function describePlayMaybe(client, playRef) {
8442
- return playRef ? client.describePlay(playRef, { compact: true }) : null;
8623
+ async function describePlayMaybe(client2, playRef) {
8624
+ return playRef ? client2.describePlay(playRef, { compact: true }) : null;
8443
8625
  }
8444
- function loadTools(client, providers) {
8445
- return Promise.all(providers.map((provider) => client.getTool(provider)));
8626
+ function loadTools(client2, providers) {
8627
+ return Promise.all(providers.map((provider) => client2.getTool(provider)));
8446
8628
  }
8447
- async function loadBootstrapContracts(client, options) {
8629
+ async function loadBootstrapContracts(client2, options) {
8448
8630
  const [
8449
8631
  sourceTools,
8450
8632
  sourcePlay,
@@ -8454,13 +8636,13 @@ async function loadBootstrapContracts(client, options) {
8454
8636
  emailTools,
8455
8637
  phoneTools
8456
8638
  ] = await Promise.all([
8457
- loadTools(client, sourceProviders(options)),
8458
- describePlayMaybe(client, sourcePlayRef(options)),
8459
- describePlayMaybe(client, stagePlayRef(options.people)),
8460
- describePlayMaybe(client, stagePlayRef(options.email)),
8461
- describePlayMaybe(client, stagePlayRef(options.phone)),
8462
- loadTools(client, stageProviders(options.email)),
8463
- loadTools(client, stageProviders(options.phone))
8639
+ loadTools(client2, sourceProviders(options)),
8640
+ describePlayMaybe(client2, sourcePlayRef(options)),
8641
+ describePlayMaybe(client2, stagePlayRef(options.people)),
8642
+ describePlayMaybe(client2, stagePlayRef(options.email)),
8643
+ describePlayMaybe(client2, stagePlayRef(options.phone)),
8644
+ loadTools(client2, stageProviders(options.email)),
8645
+ loadTools(client2, stageProviders(options.phone))
8464
8646
  ]);
8465
8647
  const contracts = {
8466
8648
  sourceTools,
@@ -8509,8 +8691,8 @@ function renderPlayBootstrapError(error) {
8509
8691
  }
8510
8692
  async function runPlayBootstrap(args) {
8511
8693
  const options = parsePlayBootstrapOptions(args);
8512
- const client = new DeeplineClient();
8513
- const contracts = await loadBootstrapContracts(client, options);
8694
+ const client2 = new DeeplineClient();
8695
+ const contracts = await loadBootstrapContracts(client2, options);
8514
8696
  const csvContext = loadCsvContext(options.from);
8515
8697
  const source = generateBootstrapPlaySource({
8516
8698
  options,
@@ -8916,8 +9098,8 @@ function traceCliSync(phase, fields, run) {
8916
9098
  throw error;
8917
9099
  }
8918
9100
  }
8919
- function sleep4(ms) {
8920
- return new Promise((resolve14) => setTimeout(resolve14, ms));
9101
+ function sleep5(ms) {
9102
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
8921
9103
  }
8922
9104
  function parseReferencedPlayTarget2(target) {
8923
9105
  const trimmed = target.trim();
@@ -8939,9 +9121,9 @@ function buildBarePrebuiltReferenceError(input2) {
8939
9121
  `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.`
8940
9122
  );
8941
9123
  }
8942
- async function assertCanonicalNamedPlayReference(client, target) {
9124
+ async function assertCanonicalNamedPlayReference(client2, target) {
8943
9125
  const parsed = parseReferencedPlayTarget2(target);
8944
- const detail = await client.getPlay(parsed.playName);
9126
+ const detail = await client2.getPlay(parsed.playName);
8945
9127
  if (detail.play.ownerType === "deepline" && !isPrebuiltReferenceTarget(target)) {
8946
9128
  throw buildBarePrebuiltReferenceError({
8947
9129
  requested: target,
@@ -9019,9 +9201,9 @@ To make your own version:
9019
9201
  5. Your play will then live under your workspace namespace.`
9020
9202
  );
9021
9203
  }
9022
- async function ensureEditableRemotePlay(client, target) {
9204
+ async function ensureEditableRemotePlay(client2, target) {
9023
9205
  const parsed = parseReferencedPlayTarget2(target);
9024
- const detail = await client.getPlay(parsed.playName);
9206
+ const detail = await client2.getPlay(parsed.playName);
9025
9207
  if (detail.play.ownerType === "deepline") {
9026
9208
  throw buildReadonlyPrebuiltPlayError(formatPlayReference(detail.play));
9027
9209
  }
@@ -9300,7 +9482,7 @@ async function collectBundledPlayGraph(entryFile, profile = null) {
9300
9482
  const root = await visit(entryFile);
9301
9483
  return { root, nodes };
9302
9484
  }
9303
- async function compileBundledPlayGraphManifests(client, graph) {
9485
+ async function compileBundledPlayGraphManifests(client2, graph) {
9304
9486
  const compiling = /* @__PURE__ */ new Map();
9305
9487
  const compileNode = (node) => {
9306
9488
  const existing = compiling.get(node.filePath);
@@ -9316,7 +9498,7 @@ async function compileBundledPlayGraphManifests(client, graph) {
9316
9498
  await compileNode(child);
9317
9499
  }
9318
9500
  const name = node.playName ?? extractPlayName(node.sourceCode, node.filePath);
9319
- node.compilerManifest = await client.compilePlayManifest({
9501
+ node.compilerManifest = await client2.compilePlayManifest({
9320
9502
  name,
9321
9503
  sourceCode: node.sourceCode,
9322
9504
  sourceFiles: node.sourceFiles,
@@ -9350,7 +9532,7 @@ function requireCompilerManifest(node) {
9350
9532
  }
9351
9533
  return node.compilerManifest;
9352
9534
  }
9353
- async function publishImportedPlayDependencies(client, graph) {
9535
+ async function publishImportedPlayDependencies(client2, graph) {
9354
9536
  const published = /* @__PURE__ */ new Set();
9355
9537
  const publishNode = async (filePath, skipPublish) => {
9356
9538
  const absolutePath = normalizePlayPath(filePath);
@@ -9369,7 +9551,7 @@ async function publishImportedPlayDependencies(client, graph) {
9369
9551
  `Imported play ${absolutePath} must export definePlay(...) so it can be published for runtime composition.`
9370
9552
  );
9371
9553
  }
9372
- await client.registerPlayArtifact({
9554
+ await client2.registerPlayArtifact({
9373
9555
  name: node.playName,
9374
9556
  sourceCode: node.sourceCode,
9375
9557
  sourceFiles: node.sourceFiles,
@@ -9945,7 +10127,7 @@ async function waitForPlayCompletionByStream(input2) {
9945
10127
  reason
9946
10128
  });
9947
10129
  const remainingBeforeSleep = remainingWaitMs();
9948
- await sleep4(
10130
+ await sleep5(
9949
10131
  remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
9950
10132
  );
9951
10133
  }
@@ -9970,7 +10152,7 @@ async function startAndWaitForPlayCompletionByStream(input2) {
9970
10152
  attempt: attempt + 1,
9971
10153
  reason: playStatusErrorText(status)
9972
10154
  });
9973
- await sleep4(retryDelayMs);
10155
+ await sleep5(retryDelayMs);
9974
10156
  }
9975
10157
  throw new DeeplineError(
9976
10158
  `Play ${input2.playName} did not start after retrying transient start failures.`,
@@ -11019,7 +11201,7 @@ async function resolvePlayRunOutputStatus(input2) {
11019
11201
  full: input2.fullJson
11020
11202
  });
11021
11203
  for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
11022
- await sleep4(250);
11204
+ await sleep5(250);
11023
11205
  refreshedStatus = await input2.client.getPlayStatus(runId, {
11024
11206
  billing: false,
11025
11207
  full: input2.fullJson
@@ -11142,7 +11324,7 @@ async function fetchBackingDatasetRows(input2) {
11142
11324
  source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
11143
11325
  };
11144
11326
  }
11145
- async function exportPlayStatusRows(client, status, outPath, options = {}) {
11327
+ async function exportPlayStatusRows(client2, status, outPath, options = {}) {
11146
11328
  if (!outPath) {
11147
11329
  return null;
11148
11330
  }
@@ -11177,7 +11359,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
11177
11359
  let fetchedRowsInfo = null;
11178
11360
  try {
11179
11361
  fetchedRowsInfo = await fetchBackingDatasetRows({
11180
- client,
11362
+ client: client2,
11181
11363
  status,
11182
11364
  rowsInfo
11183
11365
  });
@@ -11196,7 +11378,7 @@ async function exportPlayStatusRows(client, status, outPath, options = {}) {
11196
11378
  return { path: writeCanonicalRowsCsv(rowsInfo, outPath), rowsInfo };
11197
11379
  }
11198
11380
  if (attempt < attempts && retryDelayMs > 0) {
11199
- await sleep4(retryDelayMs);
11381
+ await sleep5(retryDelayMs);
11200
11382
  }
11201
11383
  }
11202
11384
  if (!rowsInfo.complete) {
@@ -11714,12 +11896,12 @@ function toolGetterHintFromMetadata(toolId, tool) {
11714
11896
  raw: checkHintExpression(toolResponse.raw) || "result.toolResponse.raw"
11715
11897
  };
11716
11898
  }
11717
- async function buildToolGetterHints(client, staticPipeline) {
11899
+ async function buildToolGetterHints(client2, staticPipeline) {
11718
11900
  const toolIds = collectStaticPipelineToolIds(staticPipeline);
11719
11901
  return Promise.all(
11720
11902
  toolIds.map(async (toolId) => {
11721
11903
  try {
11722
- const tool = await client.getTool(toolId);
11904
+ const tool = await client2.getTool(toolId);
11723
11905
  return toolGetterHintFromMetadata(toolId, tool);
11724
11906
  } catch (error) {
11725
11907
  return {
@@ -11759,10 +11941,10 @@ function printToolGetterHints(hints) {
11759
11941
  async function handlePlayCheck(args) {
11760
11942
  const options = parsePlayCheckOptions(args);
11761
11943
  if (!isFileTarget(options.target)) {
11762
- const client2 = new DeeplineClient();
11944
+ const client3 = new DeeplineClient();
11763
11945
  try {
11764
- await assertCanonicalNamedPlayReference(client2, options.target);
11765
- const play = await client2.describePlay(
11946
+ await assertCanonicalNamedPlayReference(client3, options.target);
11947
+ const play = await client3.describePlay(
11766
11948
  parseReferencedPlayTarget2(options.target).playName,
11767
11949
  { compact: true }
11768
11950
  );
@@ -11844,8 +12026,8 @@ async function handlePlayCheck(args) {
11844
12026
  }
11845
12027
  return 0;
11846
12028
  }
11847
- const client = new DeeplineClient();
11848
- const result = await client.checkPlayArtifact({
12029
+ const client2 = new DeeplineClient();
12030
+ const result = await client2.checkPlayArtifact({
11849
12031
  name: playName,
11850
12032
  sourceCode: graph.root.sourceCode,
11851
12033
  sourceFiles: graph.root.sourceFiles,
@@ -11857,7 +12039,7 @@ async function handlePlayCheck(args) {
11857
12039
  errors: result.errors,
11858
12040
  sourceCode: graph.root.sourceCode
11859
12041
  }),
11860
- toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
12042
+ toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client2, result.staticPipeline)
11861
12043
  };
11862
12044
  if (options.jsonOutput) {
11863
12045
  process.stdout.write(
@@ -11883,7 +12065,7 @@ async function handleFileBackedRun(options) {
11883
12065
  if (options.target.kind !== "file") {
11884
12066
  throw new Error("Expected a file-backed play run target.");
11885
12067
  }
11886
- const client = new DeeplineClient();
12068
+ const client2 = new DeeplineClient();
11887
12069
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
11888
12070
  const absolutePlayPath = resolve10(options.target.path);
11889
12071
  progress.phase("compiling play");
@@ -11903,7 +12085,7 @@ async function handleFileBackedRun(options) {
11903
12085
  await traceCliSpan(
11904
12086
  "cli.play_file_compile_manifests",
11905
12087
  { targetKind: "file" },
11906
- () => compileBundledPlayGraphManifests(client, graph)
12088
+ () => compileBundledPlayGraphManifests(client2, graph)
11907
12089
  );
11908
12090
  progress.phase("compiled play");
11909
12091
  } catch (error) {
@@ -11921,7 +12103,7 @@ async function handleFileBackedRun(options) {
11921
12103
  await traceCliSpan(
11922
12104
  "cli.play_file_publish_imports",
11923
12105
  { targetKind: "file" },
11924
- () => publishImportedPlayDependencies(client, graph)
12106
+ () => publishImportedPlayDependencies(client2, graph)
11925
12107
  );
11926
12108
  } catch (error) {
11927
12109
  progress.fail();
@@ -11942,7 +12124,7 @@ async function handleFileBackedRun(options) {
11942
12124
  bindingCount: fileInputBindings.length
11943
12125
  },
11944
12126
  () => stageFileInputArgs({
11945
- client,
12127
+ client: client2,
11946
12128
  runtimeInput,
11947
12129
  bindings: fileInputBindings,
11948
12130
  progress
@@ -11967,7 +12149,7 @@ async function handleFileBackedRun(options) {
11967
12149
  "cli.play_start_watch",
11968
12150
  { targetKind: "file", playName },
11969
12151
  () => startAndWaitForPlayCompletionByStream({
11970
- client,
12152
+ client: client2,
11971
12153
  request: startRequest,
11972
12154
  playName,
11973
12155
  jsonOutput: options.jsonOutput,
@@ -11986,7 +12168,7 @@ async function handleFileBackedRun(options) {
11986
12168
  }
11987
12169
  const outputStatus = withTerminalPlayIdentity(
11988
12170
  await resolvePlayRunOutputStatus({
11989
- client,
12171
+ client: client2,
11990
12172
  status: finalStatus,
11991
12173
  fullJson: options.fullJson,
11992
12174
  jsonOutput: options.jsonOutput
@@ -12006,11 +12188,11 @@ async function handleFileBackedRun(options) {
12006
12188
  const started = await traceCliSpan(
12007
12189
  "cli.play_start_unwatched",
12008
12190
  { targetKind: "file", playName },
12009
- () => client.startPlayRun(startRequest).catch((error) => {
12191
+ () => client2.startPlayRun(startRequest).catch((error) => {
12010
12192
  throw normalizePlayStartError(error, playName);
12011
12193
  })
12012
12194
  );
12013
- const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
12195
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
12014
12196
  openPlayDashboard({
12015
12197
  dashboardUrl: resolvedDashboardUrl,
12016
12198
  noOpen: options.noOpen
@@ -12046,7 +12228,7 @@ async function handleNamedRun(options) {
12046
12228
  if (options.target.kind !== "name") {
12047
12229
  throw new Error("Expected a named play run target.");
12048
12230
  }
12049
- const client = new DeeplineClient();
12231
+ const client2 = new DeeplineClient();
12050
12232
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
12051
12233
  const playName = options.target.name;
12052
12234
  const runtimeInput = options.input ? { ...options.input } : {};
@@ -12059,7 +12241,7 @@ async function handleNamedRun(options) {
12059
12241
  return traceCliSpan(
12060
12242
  "cli.play_load_definition",
12061
12243
  { targetKind: "name", playName, skipped: false },
12062
- () => assertCanonicalNamedPlayReference(client, playName)
12244
+ () => assertCanonicalNamedPlayReference(client2, playName)
12063
12245
  );
12064
12246
  })() : (recordCliTrace({
12065
12247
  phase: "cli.play_load_definition",
@@ -12079,7 +12261,7 @@ async function handleNamedRun(options) {
12079
12261
  hasExplicitRevisionId: Boolean(options.revisionId)
12080
12262
  },
12081
12263
  () => resolveNamedRunRevisionId({
12082
- client,
12264
+ client: client2,
12083
12265
  playName,
12084
12266
  revisionId: options.revisionId,
12085
12267
  selector: options.revisionSelector
@@ -12097,7 +12279,7 @@ async function handleNamedRun(options) {
12097
12279
  bindingCount: fileInputBindings.length
12098
12280
  },
12099
12281
  () => stageFileInputArgs({
12100
- client,
12282
+ client: client2,
12101
12283
  runtimeInput,
12102
12284
  bindings: fileInputBindings,
12103
12285
  progress
@@ -12118,7 +12300,7 @@ async function handleNamedRun(options) {
12118
12300
  "cli.play_start_watch",
12119
12301
  { targetKind: "name", playName },
12120
12302
  () => startAndWaitForPlayCompletionByStream({
12121
- client,
12303
+ client: client2,
12122
12304
  request: startRequest,
12123
12305
  playName,
12124
12306
  jsonOutput: options.jsonOutput,
@@ -12137,7 +12319,7 @@ async function handleNamedRun(options) {
12137
12319
  }
12138
12320
  const outputStatus = withTerminalPlayIdentity(
12139
12321
  await resolvePlayRunOutputStatus({
12140
- client,
12322
+ client: client2,
12141
12323
  status: finalStatus,
12142
12324
  fullJson: options.fullJson,
12143
12325
  jsonOutput: options.jsonOutput
@@ -12157,11 +12339,11 @@ async function handleNamedRun(options) {
12157
12339
  const started = await traceCliSpan(
12158
12340
  "cli.play_start_unwatched",
12159
12341
  { targetKind: "name", playName },
12160
- () => client.startPlayRun(startRequest).catch((error) => {
12342
+ () => client2.startPlayRun(startRequest).catch((error) => {
12161
12343
  throw normalizePlayStartError(error, playName);
12162
12344
  })
12163
12345
  );
12164
- const resolvedDashboardUrl = buildPlayDashboardUrl(client.baseUrl, playName);
12346
+ const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
12165
12347
  openPlayDashboard({
12166
12348
  dashboardUrl: resolvedDashboardUrl,
12167
12349
  noOpen: options.noOpen
@@ -12235,8 +12417,8 @@ async function handleRunGet(args) {
12235
12417
  console.error(error instanceof Error ? error.message : usage);
12236
12418
  return 1;
12237
12419
  }
12238
- const client = new DeeplineClient();
12239
- const status = await client.runs.get(runId, {
12420
+ const client2 = new DeeplineClient();
12421
+ const status = await client2.runs.get(runId, {
12240
12422
  full: args.includes("--full")
12241
12423
  });
12242
12424
  writePlayResult(status, argsWantJson(args), {
@@ -12266,8 +12448,8 @@ async function handleRunsList(args) {
12266
12448
  console.error(usage);
12267
12449
  return 1;
12268
12450
  }
12269
- const client = new DeeplineClient();
12270
- const runs = (await client.runs.list({
12451
+ const client2 = new DeeplineClient();
12452
+ const runs = (await client2.runs.list({
12271
12453
  play: playName,
12272
12454
  ...statusFilter ? { status: statusFilter } : {}
12273
12455
  })).map((run) => ({
@@ -12320,9 +12502,9 @@ async function handleRunTail(args) {
12320
12502
  return 1;
12321
12503
  }
12322
12504
  }
12323
- const client = new DeeplineClient();
12505
+ const client2 = new DeeplineClient();
12324
12506
  const jsonOutput = argsWantJson(args);
12325
- const status = await client.runs.tail(runId, {
12507
+ const status = await client2.runs.tail(runId, {
12326
12508
  // Human mode only: in --json mode emit nothing non-protocol.
12327
12509
  onReconnect: jsonOutput ? void 0 : ({ reason }) => {
12328
12510
  process.stderr.write(
@@ -12355,9 +12537,9 @@ async function handleRunLogs(args) {
12355
12537
  outPath = resolve10(args[++index]);
12356
12538
  }
12357
12539
  }
12358
- const client = new DeeplineClient();
12540
+ const client2 = new DeeplineClient();
12359
12541
  if (outPath) {
12360
- const result2 = await client.runs.logs(runId, { all: true });
12542
+ const result2 = await client2.runs.logs(runId, { all: true });
12361
12543
  const logs = result2.entries;
12362
12544
  writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
12363
12545
  printCommandEnvelope(
@@ -12386,7 +12568,7 @@ async function handleRunLogs(args) {
12386
12568
  );
12387
12569
  return 0;
12388
12570
  }
12389
- const result = await client.runs.logs(runId, { limit });
12571
+ const result = await client2.runs.logs(runId, { limit });
12390
12572
  printCommandEnvelope(
12391
12573
  {
12392
12574
  runId: result.runId,
@@ -12426,8 +12608,8 @@ async function handleRunStop(args) {
12426
12608
  reason = args[++index];
12427
12609
  }
12428
12610
  }
12429
- const client = new DeeplineClient();
12430
- const result = await client.runs.stop(runId, { reason });
12611
+ const client2 = new DeeplineClient();
12612
+ const result = await client2.runs.stop(runId, { reason });
12431
12613
  const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
12432
12614
  if (stopNotConfirmed) {
12433
12615
  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";
@@ -12499,9 +12681,9 @@ async function handleRunExport(args) {
12499
12681
  console.error(usage);
12500
12682
  return 1;
12501
12683
  }
12502
- const client = new DeeplineClient();
12503
- const status = await client.getPlayStatus(runId, { full: true });
12504
- const exportResult = await exportPlayStatusRows(client, status, outPath, {
12684
+ const client2 = new DeeplineClient();
12685
+ const status = await client2.getPlayStatus(runId, { full: true });
12686
+ const exportResult = await exportPlayStatusRows(client2, status, outPath, {
12505
12687
  datasetPath
12506
12688
  });
12507
12689
  const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
@@ -12554,7 +12736,7 @@ async function handlePlayGet(args) {
12554
12736
  );
12555
12737
  return 2;
12556
12738
  }
12557
- const client = new DeeplineClient();
12739
+ const client2 = new DeeplineClient();
12558
12740
  const explicitJson = args.includes("--json");
12559
12741
  const sourceOutput = args.includes("--source");
12560
12742
  const jsonOutput = sourceOutput ? explicitJson : argsWantJson(args);
@@ -12566,7 +12748,7 @@ async function handlePlayGet(args) {
12566
12748
  }
12567
12749
  }
12568
12750
  const playName = isFileTarget(target) ? extractPlayName(readFileSync6(resolve10(target), "utf-8"), resolve10(target)) : parseReferencedPlayTarget2(target).playName;
12569
- const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
12751
+ const detail = isFileTarget(target) ? await client2.getPlay(playName) : await assertCanonicalNamedPlayReference(client2, target);
12570
12752
  const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
12571
12753
  const materializedFile = outPath ? materializeRemotePlaySource({
12572
12754
  target,
@@ -12646,10 +12828,10 @@ async function handlePlayVersions(args) {
12646
12828
  console.error("Usage: deepline play versions --name <name> [--json]");
12647
12829
  return 1;
12648
12830
  }
12649
- const client = new DeeplineClient();
12831
+ const client2 = new DeeplineClient();
12650
12832
  const jsonOutput = argsWantJson(args);
12651
- await assertCanonicalNamedPlayReference(client, playName);
12652
- const versions = await client.listPlayVersions(
12833
+ await assertCanonicalNamedPlayReference(client2, playName);
12834
+ const versions = await client2.listPlayVersions(
12653
12835
  parseReferencedPlayTarget2(playName).playName
12654
12836
  );
12655
12837
  if (jsonOutput) {
@@ -12668,8 +12850,8 @@ async function handlePlayVersions(args) {
12668
12850
  }
12669
12851
  async function handlePlayList(args) {
12670
12852
  const jsonOutput = argsWantJson(args);
12671
- const client = new DeeplineClient();
12672
- const plays = await client.listPlays();
12853
+ const client2 = new DeeplineClient();
12854
+ const plays = await client2.listPlays();
12673
12855
  if (jsonOutput) {
12674
12856
  process.stdout.write(`${JSON.stringify(plays)}
12675
12857
  `);
@@ -12852,8 +13034,8 @@ async function handlePlaySearch(args) {
12852
13034
  console.error(error instanceof Error ? error.message : String(error));
12853
13035
  return 1;
12854
13036
  }
12855
- const client = new DeeplineClient();
12856
- const plays = await client.searchPlays({
13037
+ const client2 = new DeeplineClient();
13038
+ const plays = await client2.searchPlays({
12857
13039
  query: options.query,
12858
13040
  compact: options.compact,
12859
13041
  scope: options.scope
@@ -12945,8 +13127,8 @@ async function handlePlayGrep(args) {
12945
13127
  }
12946
13128
  }
12947
13129
  const compact = args.includes("--compact");
12948
- const client = new DeeplineClient();
12949
- const plays = (await client.listPlays({
13130
+ const client2 = new DeeplineClient();
13131
+ const plays = (await client2.listPlays({
12950
13132
  grep: query,
12951
13133
  grepMode: mode
12952
13134
  })).filter(
@@ -13003,9 +13185,9 @@ async function handlePlayDescribe(args) {
13003
13185
  );
13004
13186
  return 2;
13005
13187
  }
13006
- const client = new DeeplineClient();
13007
- await assertCanonicalNamedPlayReference(client, playName);
13008
- const play = await client.describePlay(
13188
+ const client2 = new DeeplineClient();
13189
+ await assertCanonicalNamedPlayReference(client2, playName);
13190
+ const play = await client2.describePlay(
13009
13191
  parseReferencedPlayTarget2(playName).playName,
13010
13192
  {
13011
13193
  compact: args.includes("--compact")
@@ -13042,7 +13224,7 @@ async function handlePlayPublish(args) {
13042
13224
  console.error("Choose only one live target: --latest or --revision-id.");
13043
13225
  return 1;
13044
13226
  }
13045
- const client = new DeeplineClient();
13227
+ const client2 = new DeeplineClient();
13046
13228
  if (isFileTarget(playName)) {
13047
13229
  if (revisionId || useLatest) {
13048
13230
  console.error(
@@ -13060,12 +13242,12 @@ async function handlePlayPublish(args) {
13060
13242
  await traceCliSpan(
13061
13243
  "cli.play_publish_compile_manifests",
13062
13244
  { targetKind: "file", nodeCount: graph.nodes.size },
13063
- () => compileBundledPlayGraphManifests(client, graph)
13245
+ () => compileBundledPlayGraphManifests(client2, graph)
13064
13246
  );
13065
13247
  await traceCliSpan(
13066
13248
  "cli.play_publish_imports",
13067
13249
  { targetKind: "file", nodeCount: graph.nodes.size },
13068
- () => publishImportedPlayDependencies(client, graph)
13250
+ () => publishImportedPlayDependencies(client2, graph)
13069
13251
  );
13070
13252
  } catch (error) {
13071
13253
  console.error(error instanceof Error ? error.message : String(error));
@@ -13079,7 +13261,7 @@ async function handlePlayPublish(args) {
13079
13261
  playName: rootPlayName,
13080
13262
  artifactHash: graph.root.artifact.artifactHash
13081
13263
  },
13082
- () => client.registerPlayArtifact({
13264
+ () => client2.registerPlayArtifact({
13083
13265
  name: rootPlayName,
13084
13266
  sourceCode: graph.root.sourceCode,
13085
13267
  sourceFiles: graph.root.sourceFiles,
@@ -13103,7 +13285,7 @@ async function handlePlayPublish(args) {
13103
13285
  }
13104
13286
  const resolvedName = parseReferencedPlayTarget2(playName).playName;
13105
13287
  if (useLatest) {
13106
- const versions = await client.listPlayVersions(resolvedName);
13288
+ const versions = await client2.listPlayVersions(resolvedName);
13107
13289
  const latest = versions[0];
13108
13290
  if (!latest?._id) {
13109
13291
  console.error(`No saved revisions found for ${resolvedName}.`);
@@ -13112,12 +13294,12 @@ async function handlePlayPublish(args) {
13112
13294
  revisionId = latest._id;
13113
13295
  }
13114
13296
  try {
13115
- await ensureEditableRemotePlay(client, resolvedName);
13297
+ await ensureEditableRemotePlay(client2, resolvedName);
13116
13298
  } catch (error) {
13117
13299
  console.error(error instanceof Error ? error.message : String(error));
13118
13300
  return 1;
13119
13301
  }
13120
- const result = await client.publishPlayVersion(
13302
+ const result = await client2.publishPlayVersion(
13121
13303
  resolvedName,
13122
13304
  revisionId ? { revisionId } : {}
13123
13305
  );
@@ -13138,10 +13320,10 @@ async function handlePlayDelete(args) {
13138
13320
  );
13139
13321
  return 1;
13140
13322
  }
13141
- const client = new DeeplineClient();
13323
+ const client2 = new DeeplineClient();
13142
13324
  let detail;
13143
13325
  try {
13144
- detail = await client.getPlay(parseReferencedPlayTarget2(playName).playName);
13326
+ detail = await client2.getPlay(parseReferencedPlayTarget2(playName).playName);
13145
13327
  } catch (error) {
13146
13328
  console.error(error instanceof Error ? error.message : String(error));
13147
13329
  return 1;
@@ -13152,7 +13334,7 @@ async function handlePlayDelete(args) {
13152
13334
  );
13153
13335
  return 1;
13154
13336
  }
13155
- const result = await client.deletePlay(
13337
+ const result = await client2.deletePlay(
13156
13338
  parseReferencedPlayTarget2(formatPlayReference(detail.play)).playName
13157
13339
  );
13158
13340
  if (argsWantJson(args)) {
@@ -13817,11 +13999,11 @@ async function handlePlaySharePublish(args) {
13817
13999
  return 2;
13818
14000
  }
13819
14001
  const name = parseReferencedPlayTarget2(target).playName;
13820
- const client = new DeeplineClient();
14002
+ const client2 = new DeeplineClient();
13821
14003
  let revisionId = shareFlagValue(args, "--revision-id");
13822
14004
  let resolvedVersion;
13823
14005
  if (!revisionId) {
13824
- const versions = await client.listPlayVersions(name);
14006
+ const versions = await client2.listPlayVersions(name);
13825
14007
  if (versions.length === 0) {
13826
14008
  console.error(
13827
14009
  `No saved revisions for ${name}. Run \`deepline plays set-live ${name}\` first.`
@@ -13842,7 +14024,7 @@ async function handlePlaySharePublish(args) {
13842
14024
  resolvedVersion = chosen.version;
13843
14025
  }
13844
14026
  if (!args.includes("--no-run-check")) {
13845
- const runs = await client.listRuns({ play: name });
14027
+ const runs = await client2.listRuns({ play: name });
13846
14028
  const hasCompleted = runs.some(
13847
14029
  (r) => String(r.status).toLowerCase() === "completed"
13848
14030
  );
@@ -13881,7 +14063,7 @@ async function handlePlaySharePublish(args) {
13881
14063
  }
13882
14064
  return 0;
13883
14065
  }
13884
- const status = await client.publishSharePage(name, request);
14066
+ const status = await client2.publishSharePage(name, request);
13885
14067
  if (argsWantJson(args)) {
13886
14068
  process.stdout.write(`${JSON.stringify(status)}
13887
14069
  `);
@@ -13929,11 +14111,11 @@ async function handlePlayShareRegenerate(args) {
13929
14111
  return 2;
13930
14112
  }
13931
14113
  const name = parseReferencedPlayTarget2(target).playName;
13932
- const client = new DeeplineClient();
14114
+ const client2 = new DeeplineClient();
13933
14115
  let revisionId = shareFlagValue(args, "--revision-id");
13934
14116
  const versionFlag = shareFlagValue(args, "--version");
13935
14117
  if (!revisionId && versionFlag) {
13936
- const versions = await client.listPlayVersions(name);
14118
+ const versions = await client2.listPlayVersions(name);
13937
14119
  const chosen = versions.find((r) => r.version === Number(versionFlag));
13938
14120
  if (!chosen?._id) {
13939
14121
  console.error(`Version ${versionFlag} not found for ${name}.`);
@@ -13941,7 +14123,7 @@ async function handlePlayShareRegenerate(args) {
13941
14123
  }
13942
14124
  revisionId = chosen._id;
13943
14125
  }
13944
- const status = await client.regenerateSharePage(
14126
+ const status = await client2.regenerateSharePage(
13945
14127
  name,
13946
14128
  revisionId ? { revisionId } : {}
13947
14129
  );
@@ -13978,52 +14160,158 @@ async function handlePlayShareUnpublish(args) {
13978
14160
  return 0;
13979
14161
  }
13980
14162
 
13981
- // src/cli/enrich-play-compiler.ts
13982
- var RESERVED_WORDS = /* @__PURE__ */ new Set([
13983
- "break",
13984
- "case",
13985
- "catch",
13986
- "class",
13987
- "const",
13988
- "continue",
13989
- "debugger",
13990
- "default",
13991
- "delete",
13992
- "do",
13993
- "else",
13994
- "export",
13995
- "extends",
13996
- "finally",
13997
- "for",
13998
- "function",
13999
- "if",
14000
- "import",
14001
- "in",
14002
- "instanceof",
14003
- "new",
14004
- "return",
14005
- "super",
14006
- "switch",
14007
- "this",
14008
- "throw",
14009
- "try",
14010
- "typeof",
14011
- "var",
14012
- "void",
14013
- "while",
14014
- "with",
14015
- "yield"
14163
+ // ../shared_libs/plays/tool-codegen.ts
14164
+ var KNOWN_GETTER_NAMES = /* @__PURE__ */ new Set([
14165
+ "company_domain",
14166
+ "company_linkedin_url",
14167
+ "company_name",
14168
+ "domain",
14169
+ "email",
14170
+ "email_status",
14171
+ "first_name",
14172
+ "full_name",
14173
+ "job_change",
14174
+ "job_change_status",
14175
+ "job_changed",
14176
+ "last_name",
14177
+ "linkedin",
14178
+ "personal_email",
14179
+ "phone",
14180
+ "phone_status",
14181
+ "status",
14182
+ "title"
14016
14183
  ]);
14184
+ function renderToolCodegenString(value) {
14185
+ return JSON.stringify(value);
14186
+ }
14187
+ function renderToolRowPathExpression(path, rowName = "row") {
14188
+ const parts = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean);
14189
+ if (parts.length === 0) return rowName;
14190
+ return parts.reduce((expr, part, index) => {
14191
+ if (index === 0) return `${rowName}[${renderToolCodegenString(part)}]`;
14192
+ return `(${expr} as Record<string, unknown> | null | undefined)?.[${renderToolCodegenString(part)}]`;
14193
+ }, rowName);
14194
+ }
14195
+ function renderToolPayloadExpression(value, rowName = "row") {
14196
+ if (Array.isArray(value)) {
14197
+ return `[${value.map((entry) => renderToolPayloadExpression(entry, rowName)).join(", ")}]`;
14198
+ }
14199
+ if (value && typeof value === "object") {
14200
+ const entries = Object.entries(value).sort(
14201
+ ([left], [right]) => left.localeCompare(right)
14202
+ );
14203
+ return `{ ${entries.map(
14204
+ ([key, entry]) => `${renderToolCodegenString(key)}: ${renderToolPayloadExpression(entry, rowName)}`
14205
+ ).join(", ")} }`;
14206
+ }
14207
+ if (typeof value !== "string") return JSON.stringify(value);
14208
+ const exact = value.match(/^\{\{\s*([^{}]+?)\s*\}\}$/);
14209
+ if (exact) return renderToolRowPathExpression(exact[1] ?? "", rowName);
14210
+ const pieces = [];
14211
+ let cursor = 0;
14212
+ for (const match of value.matchAll(/\{\{\s*([^{}]+?)\s*\}\}/g)) {
14213
+ const index = match.index ?? 0;
14214
+ if (index > cursor) {
14215
+ pieces.push(renderToolCodegenString(value.slice(cursor, index)));
14216
+ }
14217
+ pieces.push(
14218
+ `String(${renderToolRowPathExpression(match[1] ?? "", rowName)} ?? '')`
14219
+ );
14220
+ cursor = index + match[0].length;
14221
+ }
14222
+ if (pieces.length === 0) return renderToolCodegenString(value);
14223
+ if (cursor < value.length) {
14224
+ pieces.push(renderToolCodegenString(value.slice(cursor)));
14225
+ }
14226
+ return pieces.join(" + ");
14227
+ }
14228
+ function renderExtractedValueGetterExpression(toolResultVar, targetKey) {
14229
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(targetKey) ? `${toolResultVar}.extractedValues.${targetKey}?.get()` : `${toolResultVar}.extractedValues[${renderToolCodegenString(targetKey)}]?.get()`;
14230
+ }
14231
+ function getterFromLegacyPath(path) {
14232
+ const last = String(path || "").replace(/\[(\d+)\]/g, ".$1").split(".").map((part) => part.trim()).filter(Boolean).at(-1);
14233
+ if (!last) return null;
14234
+ const normalized = last.replace(/[^A-Za-z0-9_]/g, "_");
14235
+ return KNOWN_GETTER_NAMES.has(normalized) ? normalized : null;
14236
+ }
14237
+ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
14238
+ const source = extractJs?.trim();
14239
+ if (!source) {
14240
+ const aliasGetter = fallbackAlias?.replace(/[^A-Za-z0-9_]/g, "_");
14241
+ return aliasGetter && KNOWN_GETTER_NAMES.has(aliasGetter) ? aliasGetter : null;
14242
+ }
14243
+ const directExtract = source.match(
14244
+ /\bextract\(\s*["'][^"']+["']\s*,\s*output_data\s*,\s*["']([^"']+)["']\s*\)/
14245
+ );
14246
+ if (directExtract?.[1]) {
14247
+ return getterFromLegacyPath(
14248
+ directExtract[1] === "linkedin_url" ? "linkedin" : directExtract[1]
14249
+ );
14250
+ }
14251
+ const pick = source.match(
14252
+ /\b(?:pick|extract|target)\(\s*(\[[\s\S]*?\]|["'][^"']+["'])\s*\)/
14253
+ );
14254
+ if (!pick?.[1]) return null;
14255
+ try {
14256
+ const parsed = JSON.parse(pick[1].replace(/'/g, '"'));
14257
+ const paths = Array.isArray(parsed) ? parsed : [parsed];
14258
+ for (const path of paths) {
14259
+ if (typeof path !== "string") continue;
14260
+ const getter = getterFromLegacyPath(path);
14261
+ if (getter) return getter;
14262
+ }
14263
+ } catch {
14264
+ return null;
14265
+ }
14266
+ return null;
14267
+ }
14268
+
14269
+ // src/cli/user-code-safety.ts
14270
+ var FORBIDDEN = [
14271
+ // Non-deterministic — breaks replay.
14272
+ { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
14273
+ { pattern: /\bDate\s*\.\s*now\b/, reason: "Date.now()" },
14274
+ { pattern: /\bnew\s+Date\s*\(\s*\)/, reason: "new Date() with no argument" },
14275
+ { pattern: /\bperformance\s*\.\s*now\b/, reason: "performance.now()" },
14276
+ {
14277
+ pattern: /\bcrypto\s*\.\s*(?:randomUUID|getRandomValues)\b/,
14278
+ reason: "crypto random"
14279
+ },
14280
+ // Sandbox escape / I/O.
14281
+ { pattern: /(?<!\.)\bfetch\s*\(/, reason: "fetch()" },
14282
+ { pattern: /(?<!\.)\bimport\s*\(/, reason: "dynamic import()" },
14283
+ { pattern: /(?<!\.)\brequire\s*\(/, reason: "require()" },
14284
+ { pattern: /(?<!\.)\beval\s*\(/, reason: "eval()" },
14285
+ {
14286
+ pattern: /(?<!\.)\bnew\s+Function\b|(?<!\.)\bFunction\s*\(/,
14287
+ reason: "the Function constructor"
14288
+ },
14289
+ { pattern: /(?<!\.)\bprocess\b/, reason: "process" },
14290
+ { pattern: /(?<!\.)\bglobalThis\b/, reason: "globalThis" },
14291
+ { pattern: /(?<!\.)\b(?:window|self)\b/, reason: "window/self" },
14292
+ { pattern: /(?<!\.)\bXMLHttpRequest\b/, reason: "XMLHttpRequest" },
14293
+ { pattern: /(?<!\.)\bWebAssembly\b/, reason: "WebAssembly" }
14294
+ ];
14295
+ function assertUserCodeIsSafe(code, label) {
14296
+ if (typeof code !== "string" || !code.trim()) return;
14297
+ for (const { pattern, reason } of FORBIDDEN) {
14298
+ if (pattern.test(code)) {
14299
+ throw new Error(
14300
+ `${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.`
14301
+ );
14302
+ }
14303
+ }
14304
+ }
14305
+
14306
+ // src/cli/enrich-play-compiler.ts
14017
14307
  function isWaterfall(command) {
14018
14308
  return "with_waterfall" in command;
14019
14309
  }
14020
- function safeIdentifier2(value, fallback) {
14021
- const cleaned = value.replace(/[^A-Za-z0-9_$]/g, "_");
14022
- const prefixed = /^[A-Za-z_$]/.test(cleaned) ? cleaned : `_${cleaned}`;
14023
- if (!prefixed || RESERVED_WORDS.has(prefixed)) {
14024
- return fallback;
14025
- }
14026
- return prefixed;
14310
+ function configHasRunJavascript(config) {
14311
+ const walk = (commands) => commands.some(
14312
+ (command) => isWaterfall(command) ? walk(command.commands) : command.tool === "run_javascript"
14313
+ );
14314
+ return walk(config.commands ?? []);
14027
14315
  }
14028
14316
  function stringLiteral(value) {
14029
14317
  return JSON.stringify(value);
@@ -14050,12 +14338,23 @@ function commandCallId(command) {
14050
14338
  function normalizeAlias(value) {
14051
14339
  return value.toLowerCase().replace(/[^a-z0-9]/g, "");
14052
14340
  }
14053
- function renderExecuteStep(command, options = { force: false }) {
14341
+ function renderExecuteStep(command, options = {
14342
+ force: false
14343
+ }) {
14344
+ if (command.play) {
14345
+ return renderPlayStep(command, options);
14346
+ }
14347
+ if (command.tool === "run_javascript" && options.inlineRunJavascript) {
14348
+ return renderInlineJavascriptStep(command, options);
14349
+ }
14350
+ if (options.idiomaticGetters) {
14351
+ return renderIdiomaticExecuteStep(command, options);
14352
+ }
14054
14353
  const alias = stringLiteral(command.alias);
14055
14354
  const callId = stringLiteral(commandCallId(command));
14056
14355
  const tool = stringLiteral(command.tool);
14057
14356
  const payload = stableJson(command.payload ?? {});
14058
- const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, target }) => { const input = row; const context = row;
14357
+ const extractJs = command.extract_js ? `({ row, result, data, raw, pick, extract, extractList, target, get }) => { const input = row; const context = row;
14059
14358
  ${indent(renderJavascriptBody(command.extract_js), 6)}
14060
14359
  }` : "null";
14061
14360
  const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
@@ -14065,6 +14364,8 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14065
14364
  description: ${stringLiteral(command.description)}` : "";
14066
14365
  const force = options.force ? `,
14067
14366
  force: true` : "";
14367
+ const legacyEnvelope = options.legacyEnvelope ? `,
14368
+ legacyEnvelope: true` : "";
14068
14369
  return [
14069
14370
  `async (row, stepCtx) => {`,
14070
14371
  ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
@@ -14076,253 +14377,842 @@ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14076
14377
  ` extract: ${extractJs},`,
14077
14378
  ` runIf: ${runIfJs},`,
14078
14379
  ` row,`,
14079
- ` stepCtx${description}${force}`,
14380
+ ` stepCtx${description}${force}${legacyEnvelope}`,
14381
+ ` });`,
14382
+ `}`
14383
+ ].join("\n");
14384
+ }
14385
+ function renderIdiomaticExecuteStep(command, options) {
14386
+ const callId = stringLiteral(commandCallId(command));
14387
+ const tool = stringLiteral(command.tool);
14388
+ const input2 = renderToolPayloadExpression(command.payload ?? {});
14389
+ const getter = getterFromLegacyExtractJs(command.extract_js, command.alias);
14390
+ const extraction = getter ? `${renderExtractedValueGetterExpression("result", getter)} ?? null` : "result";
14391
+ const runIfLines = command.run_if_js ? [
14392
+ ` if (`,
14393
+ ` !((row: Record<string, any>) => {`,
14394
+ ` const input = row;`,
14395
+ ` const context = row;`,
14396
+ indent(renderJavascriptBody(command.run_if_js), 6),
14397
+ ` })(row as Record<string, any>)`,
14398
+ ` ) return null;`
14399
+ ] : [];
14400
+ return [
14401
+ `async (row, ctx) => {`,
14402
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14403
+ ...runIfLines,
14404
+ ` const result: any = await ctx.tools.execute({`,
14405
+ ` id: ${callId},`,
14406
+ ` tool: ${tool},`,
14407
+ ` input: ${input2} as any,`,
14408
+ ` description: ${stringLiteral((command.description ?? "").trim() || `Run ${command.alias} via ${command.tool}.`)},`,
14409
+ ...options.force ? [` staleAfterSeconds: 1,`] : [],
14410
+ ` });`,
14411
+ ` return ${extraction};`,
14412
+ `}`
14413
+ ].join("\n");
14414
+ }
14415
+ function renderInlineJavascriptStep(command, options) {
14416
+ const code = typeof command.payload?.code === "string" ? command.payload.code : "return null;";
14417
+ const runIfLines = command.run_if_js ? [
14418
+ ` if (!((row: Record<string, any>) => { const input = row; const context = row;`,
14419
+ indent(renderJavascriptBody(command.run_if_js), 4),
14420
+ ` })(row as Record<string, any>)) return null;`
14421
+ ] : [];
14422
+ return [
14423
+ `async (row) => {`,
14424
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14425
+ ...runIfLines,
14426
+ ` return ((row: Record<string, any>, input: Record<string, any>, context: Record<string, any>) => {`,
14427
+ indent(renderJavascriptBody(code), 4),
14428
+ ` })(row as Record<string, any>, row as Record<string, any>, row as Record<string, any>);`,
14429
+ `}`
14430
+ ].join("\n");
14431
+ }
14432
+ function renderPlayStep(command, options) {
14433
+ const alias = stringLiteral(command.alias);
14434
+ const callId = stringLiteral(commandCallId(command));
14435
+ const playRef = stringLiteral(command.play?.ref ?? command.tool);
14436
+ const payload = stableJson(command.payload ?? {});
14437
+ const runIfJs = command.run_if_js ? `(row) => { const input = row; const context = row;
14438
+ ${indent(renderJavascriptBody(command.run_if_js), 6)}
14439
+ }` : "null";
14440
+ const runIfLines = command.run_if_js ? [` const __dlRunIf = ${runIfJs};`, ` if (!__dlRunIf(row)) return null;`] : [];
14441
+ const playOptions = [
14442
+ ` description: ${stringLiteral(command.description ?? command.alias)}`,
14443
+ ...options.force ? [` staleAfterSeconds: 1`] : []
14444
+ ].join(",\n");
14445
+ return [
14446
+ `async (row, stepCtx) => {`,
14447
+ ...options.precheck ? [` if (${options.precheck}) return null;`] : [],
14448
+ ...runIfLines,
14449
+ ` const payload = __dlTemplate(${payload}, row) as Record<string, unknown>;`,
14450
+ ` const result = await stepCtx.runPlay(${callId}, ${playRef}, payload, {`,
14451
+ playOptions,
14080
14452
  ` });`,
14453
+ ` return __dlPlayResultValue(${alias}, result);`,
14081
14454
  `}`
14082
14455
  ].join("\n");
14083
14456
  }
14084
14457
  function renderJavascriptBody(source) {
14458
+ assertUserCodeIsSafe(source, "play step code");
14085
14459
  const trimmed = source.trim();
14460
+ if (/^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed)) {
14461
+ return `return (${trimmed});`;
14462
+ }
14086
14463
  if (trimmed && !trimmed.includes("\n") && !trimmed.includes(";") && !/\breturn\b/.test(trimmed)) {
14087
14464
  return `return (${trimmed});`;
14088
14465
  }
14089
14466
  return source;
14090
14467
  }
14091
- function renderWaterfallProgram(command, index, forceAliases) {
14092
- const variableName = safeIdentifier2(
14093
- `${command.with_waterfall}_${index}_waterfall`,
14094
- `waterfall_${index}`
14468
+ function renderColumnStep(alias, resolverSource, force) {
14469
+ const resolver = indent(resolverSource, 8);
14470
+ return [
14471
+ ` .withColumn(${stringLiteral(alias)},`,
14472
+ force ? `${resolver},` : resolver,
14473
+ ...force ? [` { staleAfterSeconds: 1 }`] : [],
14474
+ ` )`
14475
+ ].join("\n");
14476
+ }
14477
+ function metadataMode(command) {
14478
+ return /\bextractList\s*\(/.test(command.extract_js ?? "") ? "list" : "scalar";
14479
+ }
14480
+ function metadataEntryForCommand(command, waterfallGroupId) {
14481
+ const entry = {
14482
+ tool_id: command.tool
14483
+ };
14484
+ if (command.play?.ref) {
14485
+ entry.play_ref = command.play.ref;
14486
+ entry.kind = "play_call";
14487
+ }
14488
+ if (command.operation) {
14489
+ entry.operation = command.operation;
14490
+ }
14491
+ if (command.extract_js?.trim()) {
14492
+ entry.extract_js = command.extract_js.trim();
14493
+ }
14494
+ if (waterfallGroupId) {
14495
+ entry.waterfall = {
14496
+ group_id: waterfallGroupId,
14497
+ mode: metadataMode(command)
14498
+ };
14499
+ }
14500
+ return entry;
14501
+ }
14502
+ function collectMetadataColumns(commands, waterfallGroupId) {
14503
+ const columns = {};
14504
+ for (const command of commands) {
14505
+ if (isWaterfall(command)) {
14506
+ Object.assign(
14507
+ columns,
14508
+ collectMetadataColumns(command.commands, command.with_waterfall)
14509
+ );
14510
+ continue;
14511
+ }
14512
+ if (command.disabled) {
14513
+ continue;
14514
+ }
14515
+ columns[normalizeAlias(command.alias)] = metadataEntryForCommand(
14516
+ command,
14517
+ waterfallGroupId
14518
+ );
14519
+ }
14520
+ return columns;
14521
+ }
14522
+ function renderMetadataColumnStep(config) {
14523
+ const columns = collectMetadataColumns(config.commands);
14524
+ if (Object.keys(columns).length === 0) {
14525
+ return "";
14526
+ }
14527
+ return [
14528
+ ` .withColumn('_metadata',`,
14529
+ ` (row) => __dlMergeMetadata(row._metadata, ${stableJson({ columns })}),`,
14530
+ ` { staleAfterSeconds: 1 }`,
14531
+ ` )`
14532
+ ].join("\n");
14533
+ }
14534
+ function renderWaterfallColumns(command, forceAliases, inlineRunJavascript, idiomaticGetters) {
14535
+ const activeChildren = command.commands.filter(
14536
+ (nested) => !isWaterfall(nested) && !nested.disabled
14095
14537
  );
14096
- const stepLines = command.commands.map((nested, stepIndex) => {
14538
+ const minResults = typeof command.min_results === "number" ? Math.max(1, Math.trunc(command.min_results)) : 1;
14539
+ const columnSteps = activeChildren.map((nested, stepIndex) => {
14097
14540
  if (isWaterfall(nested)) {
14098
14541
  throw new Error("Nested with_waterfall blocks are not supported.");
14099
14542
  }
14100
14543
  if (nested.disabled) {
14101
14544
  return null;
14102
14545
  }
14103
- const priorAliases = command.commands.slice(0, stepIndex).filter((prior) => !isWaterfall(prior)).filter((prior) => !prior.disabled).map((prior) => prior.alias);
14104
- const minResults = typeof command.min_results === "number" ? Math.max(1, Math.trunc(command.min_results)) : 1;
14105
- return [
14106
- ` .step(${stringLiteral(nested.alias)},`,
14107
- indent(
14108
- renderExecuteStep(nested, {
14109
- force: forceAliases.has(normalizeAlias(nested.alias)),
14110
- precheck: priorAliases.length > 0 ? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})` : void 0
14111
- }),
14112
- 4
14113
- ),
14114
- ` )`
14115
- ].join("\n");
14546
+ const priorAliases = activeChildren.slice(0, stepIndex).map((prior) => prior.alias);
14547
+ const force = forceAliases.has(normalizeAlias(nested.alias));
14548
+ return renderColumnStep(
14549
+ nested.alias,
14550
+ renderExecuteStep(nested, {
14551
+ force,
14552
+ precheck: priorAliases.length > 0 ? `__dlWaterfallSatisfied(row, ${stableJson(priorAliases)}, ${minResults})` : void 0,
14553
+ legacyEnvelope: Boolean(nested.extract_js),
14554
+ inlineRunJavascript,
14555
+ idiomaticGetters
14556
+ }),
14557
+ force
14558
+ );
14116
14559
  }).filter((line) => line !== null);
14117
- const aliases = command.commands.filter((nested) => !isWaterfall(nested)).filter((nested) => !nested.disabled).map((nested) => nested.alias);
14560
+ const aliases = activeChildren.map((nested) => nested.alias);
14561
+ const forceParent = forceAliases.has(normalizeAlias(command.with_waterfall)) || activeChildren.some(
14562
+ (nested) => forceAliases.has(normalizeAlias(nested.alias))
14563
+ );
14118
14564
  const returnExpr = typeof command.min_results === "number" ? `__dlFirstMinResults(row, ${stableJson(aliases)}, ${Math.max(
14119
14565
  1,
14120
14566
  Math.trunc(command.min_results)
14121
14567
  )})` : `__dlFirstMeaningful(row, ${stableJson(aliases)})`;
14122
- return {
14123
- variableName,
14124
- hasSteps: stepLines.length > 0,
14125
- source: [
14126
- `const ${variableName} = steps<Record<string, unknown>>()`,
14127
- ...stepLines,
14128
- ` .return((row) => ${returnExpr});`
14129
- ].join("\n")
14130
- };
14568
+ if (columnSteps.length === 0) {
14569
+ return [];
14570
+ }
14571
+ return [
14572
+ ...columnSteps,
14573
+ renderColumnStep(
14574
+ command.with_waterfall,
14575
+ `(row) => ${returnExpr}`,
14576
+ forceParent
14577
+ ),
14578
+ renderColumnStep(
14579
+ `${command.with_waterfall}_source`,
14580
+ typeof command.min_results === "number" ? `(row) => __dlContributingAliases(row, ${stableJson(aliases)})` : `(row) => __dlFirstMeaningfulAlias(row, ${stableJson(aliases)})`,
14581
+ forceParent
14582
+ )
14583
+ ];
14131
14584
  }
14132
14585
  function compileEnrichConfigToPlaySource(config, options = {}) {
14133
14586
  const playName = options.playName ?? "deepline-enrich-v1-compat";
14134
14587
  const mapName = options.mapName ?? "deepline_enrich_rows";
14588
+ const inlineRunJavascript = options.inlineRunJavascript ?? false;
14589
+ const idiomaticGetters = options.idiomaticGetters ?? false;
14135
14590
  const forceAliases = new Set(
14136
14591
  [...options.forceAliases ?? []].map((alias) => normalizeAlias(alias))
14137
14592
  );
14138
- const waterfalls = [];
14139
- const mapSteps = [];
14140
- config.commands.forEach((command, index) => {
14593
+ const columnSteps = [];
14594
+ config.commands.forEach((command) => {
14141
14595
  if (isWaterfall(command)) {
14142
- const rendered = renderWaterfallProgram(command, index, forceAliases);
14143
- if (!rendered.hasSteps) {
14144
- return;
14145
- }
14146
- waterfalls.push({
14147
- alias: command.with_waterfall,
14148
- variableName: rendered.variableName,
14149
- source: rendered.source
14150
- });
14151
- mapSteps.push(
14152
- ` .step(${stringLiteral(command.with_waterfall)}, ${rendered.variableName})`
14596
+ columnSteps.push(
14597
+ ...renderWaterfallColumns(
14598
+ command,
14599
+ forceAliases,
14600
+ inlineRunJavascript,
14601
+ idiomaticGetters
14602
+ )
14153
14603
  );
14154
14604
  return;
14155
14605
  }
14156
14606
  if (command.disabled) {
14157
14607
  return;
14158
14608
  }
14159
- mapSteps.push(
14160
- [
14161
- ` .step(${stringLiteral(command.alias)},`,
14162
- indent(
14163
- renderExecuteStep(command, {
14164
- force: forceAliases.has(normalizeAlias(command.alias))
14165
- }),
14166
- 8
14167
- ),
14168
- ` )`
14169
- ].join("\n")
14609
+ const force = forceAliases.has(normalizeAlias(command.alias));
14610
+ columnSteps.push(
14611
+ renderColumnStep(
14612
+ command.alias,
14613
+ renderExecuteStep(command, {
14614
+ force,
14615
+ inlineRunJavascript,
14616
+ idiomaticGetters
14617
+ }),
14618
+ force
14619
+ )
14170
14620
  );
14171
14621
  });
14172
- const waterfallSource = waterfalls.map((entry) => indent(entry.source, 4));
14173
- const mapStepSource = mapSteps.length > 0 ? mapSteps.join("\n") : ` .step('noop', (row) => row)`;
14622
+ const columnStepSource = columnSteps.length > 0 ? columnSteps.join("\n") : ` .withColumn('noop', () => null)`;
14623
+ const metadataColumnSource = renderMetadataColumnStep(config);
14624
+ const body = [
14625
+ `export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
14626
+ ` const sourceRows = await ctx.csv<Record<string, unknown>>(input.file);`,
14627
+ ` const rowStart = __dlNonNegativeInteger(input.rowStart, 0);`,
14628
+ ` const rowEndExclusive = Number.isFinite(input.rowEnd) ? Math.max(rowStart, __dlWholeNumber(input.rowEnd, rowStart) + 1) : undefined;`,
14629
+ ` const rows = sourceRows.slice(rowStart, rowEndExclusive, { key: 'deepline_enrich_selected_rows', sourceLabel: 'Selected enrich rows' });`,
14630
+ ` const enriched = await ctx`,
14631
+ ` .dataset(${stringLiteral(mapName)}, rows)`,
14632
+ columnStepSource,
14633
+ ...metadataColumnSource ? [metadataColumnSource] : [],
14634
+ ` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
14635
+ ` return { rows: enriched, count: await enriched.count() };`,
14636
+ `});`
14637
+ ];
14638
+ const helpers = idiomaticGetters ? selectUsedHelpers(helperSource(), body.join("\n")) : helperSource();
14174
14639
  return [
14175
- `import { definePlay, steps } from 'deepline';`,
14640
+ ...inlineRunJavascript && configHasRunJavascript(config) ? ["// @ts-nocheck", "/* eslint-disable */", ""] : [],
14641
+ `import { definePlay } from 'deepline';`,
14176
14642
  ``,
14177
14643
  `type EnrichInput = { file: string; rowStart?: number | null; rowEnd?: number | null };`,
14178
14644
  ``,
14179
- helperSource(),
14645
+ helpers,
14180
14646
  ``,
14181
- `export default definePlay(${stringLiteral(playName)}, async (ctx, input: EnrichInput) => {`,
14182
- ` const allRows = await ctx.csv<Record<string, unknown>>(input.file);`,
14183
- ` const rowStart = Number.isFinite(input.rowStart) ? Math.max(0, Math.trunc(Number(input.rowStart))) : 0;`,
14184
- ` const rowEnd = Number.isFinite(input.rowEnd) ? Math.max(rowStart, Math.trunc(Number(input.rowEnd))) : allRows.length;`,
14185
- ` const rows = allRows.slice(rowStart, rowEnd);`,
14186
- ...waterfallSource,
14187
- ` const enriched = await ctx`,
14188
- ` .map(${stringLiteral(mapName)}, rows)`,
14189
- mapStepSource,
14190
- ` .run({ key: (row, index) => __dlStableRowKey(row, index + rowStart) });`,
14191
- ` return { rows: enriched, count: await enriched.count() };`,
14192
- `});`,
14647
+ ...body,
14193
14648
  ``
14194
14649
  ].join("\n");
14195
14650
  }
14651
+ function selectUsedHelpers(helperBlock, referenceSource) {
14652
+ const referencesSymbol = (source, symbol) => new RegExp(`\\b${symbol}\\b`).test(source);
14653
+ const blocks = helperBlock.split("\n\n").map((source) => source.trim()).filter(Boolean).map((source) => ({
14654
+ name: source.match(/(?:function|type|const)\s+(__[A-Za-z]\w*)/)?.[1] ?? "",
14655
+ source
14656
+ }));
14657
+ const used = /* @__PURE__ */ new Set();
14658
+ for (const { name } of blocks) {
14659
+ if (name && referencesSymbol(referenceSource, name)) used.add(name);
14660
+ }
14661
+ let changed = true;
14662
+ while (changed) {
14663
+ changed = false;
14664
+ const usedSource = blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n");
14665
+ for (const { name } of blocks) {
14666
+ if (!name || used.has(name)) continue;
14667
+ if (referencesSymbol(usedSource, name)) {
14668
+ used.add(name);
14669
+ changed = true;
14670
+ }
14671
+ }
14672
+ }
14673
+ return blocks.filter((block) => used.has(block.name)).map((block) => block.source).join("\n\n");
14674
+ }
14196
14675
  function helperSource() {
14197
14676
  return [
14198
- `function __dlGetByPath(root: unknown, path: string): unknown {`,
14199
- ` return String(path || '')`,
14200
- ` .replace(/\\[(\\d+)\\]/g, '.$1')`,
14201
- ` .split('.')`,
14202
- ` .map((part) => part.trim())`,
14203
- ` .filter(Boolean)`,
14204
- ` .reduce((cursor: unknown, part: string) => {`,
14205
- ` if (!cursor || typeof cursor !== 'object') return undefined;`,
14206
- ` const record = cursor as Record<string, unknown>;`,
14207
- ` if (part in record) return record[part];`,
14208
- ` const data = record.data;`,
14209
- ` return data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
14210
- ` }, root);`,
14677
+ `function __dlWholeNumber(value: unknown, fallback: number): number {`,
14678
+ ` const numeric = Number(value);`,
14679
+ ` if (!Number.isFinite(numeric)) return fallback;`,
14680
+ ` return Math.floor(numeric);`,
14211
14681
  `}`,
14212
14682
  ``,
14213
- `function __dlMeaningful(value: unknown): boolean {`,
14214
- ` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
14683
+ `function __dlNonNegativeInteger(value: unknown, fallback: number): number {`,
14684
+ ` return Math.max(0, __dlWholeNumber(value, fallback));`,
14215
14685
  `}`,
14216
14686
  ``,
14217
- `function __dlRawToolOutput(result: unknown): unknown {`,
14218
- ` if (!result || typeof result !== 'object') return result;`,
14219
- ` const record = result as Record<string, unknown>;`,
14220
- ` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
14687
+ `function __dlAtLeastOneInteger(value: number): number {`,
14688
+ ` return Math.max(1, Math.floor(Number(value)));`,
14221
14689
  `}`,
14222
14690
  ``,
14223
- `function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
14224
- ` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
14225
- ` if (value && typeof value === 'object') {`,
14226
- ` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
14227
- ` }`,
14228
- ` if (typeof value !== 'string') return value;`,
14229
- ` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
14230
- ` if (exact) return __dlGetByPath(row, exact[1] || '');`,
14231
- ` return value.replace(/\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g, (_match, path) => {`,
14232
- ` const replacement = __dlGetByPath(row, String(path || ''));`,
14233
- ` return replacement === null || replacement === undefined ? '' : String(replacement);`,
14234
- ` });`,
14691
+ `function __dlRecord(value: unknown): value is Record<string, unknown> {`,
14692
+ ` return Boolean(value && typeof value === 'object' && !Array.isArray(value));`,
14235
14693
  `}`,
14236
14694
  ``,
14237
- `function __dlStableRowKey(row: Record<string, unknown>, index: number): string {`,
14238
- ` for (const key of ['id', 'ID', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN']) {`,
14239
- ` const value = row[key];`,
14240
- ` if (__dlMeaningful(value)) return String(value);`,
14695
+ `function __dlParseMetadata(value: unknown): Record<string, unknown> | null {`,
14696
+ ` if (__dlRecord(value)) return value;`,
14697
+ ` if (typeof value !== 'string') return null;`,
14698
+ ` const trimmed = value.trim();`,
14699
+ ` if (!trimmed) return null;`,
14700
+ ` const candidates = [trimmed];`,
14701
+ ` if (trimmed.includes('\\\\"')) candidates.push(trimmed.replace(/\\\\"/g, '"'));`,
14702
+ ` for (const candidate of candidates) {`,
14703
+ ` try {`,
14704
+ ` const parsed = JSON.parse(candidate);`,
14705
+ ` if (__dlRecord(parsed)) return parsed;`,
14706
+ ` if (typeof parsed === 'string') {`,
14707
+ ` const nested = JSON.parse(parsed);`,
14708
+ ` if (__dlRecord(nested)) return nested;`,
14709
+ ` }`,
14710
+ ` } catch {}`,
14711
+ ` }`,
14712
+ ` return null;`,
14713
+ `}`,
14714
+ ``,
14715
+ `function __dlMergeMetadata(existing: unknown, patch: Record<string, unknown>): Record<string, unknown> {`,
14716
+ ` const base = __dlParseMetadata(existing) ?? {};`,
14717
+ ` const baseColumns = __dlRecord(base.columns) ? base.columns : {};`,
14718
+ ` const patchColumns = __dlRecord(patch.columns) ? patch.columns : {};`,
14719
+ ` return {`,
14720
+ ` ...base,`,
14721
+ ` ...patch,`,
14722
+ ` columns: {`,
14723
+ ` ...baseColumns,`,
14724
+ ` ...patchColumns,`,
14725
+ ` },`,
14726
+ ` };`,
14727
+ `}`,
14728
+ ``,
14729
+ `function __dlPathParts(path: string): string[] {`,
14730
+ ` const source = String(path || '');`,
14731
+ ` const parts: string[] = [];`,
14732
+ ` let current = '';`,
14733
+ ` for (let index = 0; index < source.length; index += 1) {`,
14734
+ ` const char = source.slice(index, index + 1);`,
14735
+ ` if (char === '.' || char === '[' || char === ']') {`,
14736
+ ` const trimmed = current.trim();`,
14737
+ ` if (trimmed) parts.push(trimmed);`,
14738
+ ` current = '';`,
14739
+ ` continue;`,
14740
+ ` }`,
14741
+ ` current += char;`,
14742
+ ` }`,
14743
+ ` const trimmed = current.trim();`,
14744
+ ` if (trimmed) parts.push(trimmed);`,
14745
+ ` return parts;`,
14746
+ `}`,
14747
+ ``,
14748
+ `function __dlGetByPath(root: unknown, path: string): unknown {`,
14749
+ ` let cursor = root;`,
14750
+ ` for (const part of __dlPathParts(path)) {`,
14751
+ ` if (!cursor || typeof cursor !== 'object') return undefined;`,
14752
+ ` const record = cursor as Record<string, unknown>;`,
14753
+ ` if (part in record) {`,
14754
+ ` cursor = record[part];`,
14755
+ ` continue;`,
14756
+ ` }`,
14757
+ ` const data = record.data;`,
14758
+ ` cursor = data && typeof data === 'object' ? (data as Record<string, unknown>)[part] : undefined;`,
14759
+ ` }`,
14760
+ ` return cursor;`,
14761
+ `}`,
14762
+ ``,
14763
+ `function __dlMeaningful(value: unknown): boolean {`,
14764
+ ` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
14765
+ ` const record = value as Record<string, unknown>;`,
14766
+ ` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
14767
+ ` if (status === 'error' || status === 'failed') return false;`,
14768
+ ` if (typeof record.error === 'string' && record.error.trim()) return false;`,
14769
+ ` const result = record.result;`,
14770
+ ` if (result && typeof result === 'object' && !Array.isArray(result)) {`,
14771
+ ` const resultRecord = result as Record<string, unknown>;`,
14772
+ ` if (typeof resultRecord.error === 'string' && resultRecord.error.trim()) return false;`,
14773
+ ` if (typeof resultRecord.message === 'string' && resultRecord.message.trim()) return false;`,
14774
+ ` }`,
14775
+ ` if ('matched_result' in record) return __dlMeaningful(record.matched_result);`,
14776
+ ` }`,
14777
+ ` return value !== null && value !== undefined && !(typeof value === 'string' && value.trim() === '') && !(Array.isArray(value) && value.length === 0);`,
14778
+ `}`,
14779
+ ``,
14780
+ `function __dlErrorPayload(value: unknown): boolean {`,
14781
+ ` if (!value || typeof value !== 'object' || Array.isArray(value)) return false;`,
14782
+ ` const record = value as Record<string, unknown>;`,
14783
+ ` const status = typeof record.status === 'string' ? record.status.toLowerCase() : '';`,
14784
+ ` const result = record.result;`,
14785
+ ` const resultError = result && typeof result === 'object' && !Array.isArray(result) ? (result as Record<string, unknown>).error : null;`,
14786
+ ` return status === 'error' || status === 'failed' || (typeof record.error === 'string' && record.error.trim() !== '') || (typeof resultError === 'string' && resultError.trim() !== '');`,
14787
+ `}`,
14788
+ ``,
14789
+ `function __dlRawToolOutput(result: unknown): unknown {`,
14790
+ ` if (!result || typeof result !== 'object') return result;`,
14791
+ ` const record = result as Record<string, unknown>;`,
14792
+ ` return __dlGetByPath(record, 'toolOutput.raw') ?? __dlGetByPath(record, 'toolResponse.raw') ?? record.result ?? record.output ?? result;`,
14793
+ `}`,
14794
+ ``,
14795
+ `function __dlPushCandidate(candidates: unknown[], value: unknown): void {`,
14796
+ ` if (value === null || value === undefined) return;`,
14797
+ ` if (!candidates.includes(value)) candidates.push(value);`,
14798
+ `}`,
14799
+ ``,
14800
+ `function __dlRawToolCandidates(raw: unknown): unknown[] {`,
14801
+ ` const candidates: unknown[] = [raw];`,
14802
+ ` if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return candidates;`,
14803
+ ` const record = raw as Record<string, unknown>;`,
14804
+ ` __dlPushCandidate(candidates, record.data);`,
14805
+ ` __dlPushCandidate(candidates, record.result);`,
14806
+ ` __dlPushCandidate(candidates, record.output);`,
14807
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'result.data'));`,
14808
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'output.body'));`,
14809
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolResponse.raw'));`,
14810
+ ` __dlPushCandidate(candidates, __dlGetByPath(record, 'toolOutput.raw'));`,
14811
+ ` return candidates;`,
14812
+ `}`,
14813
+ ``,
14814
+ `function __dlLegacyResultData(value: unknown): unknown {`,
14815
+ ` if (!value || typeof value !== 'object' || Array.isArray(value)) return value;`,
14816
+ ` const record = value as Record<string, unknown>;`,
14817
+ ` return 'data' in record ? record.data : value;`,
14818
+ `}`,
14819
+ ``,
14820
+ `function __dlLegacyOutputData(result: unknown, raw: unknown): unknown {`,
14821
+ ` const rawRecord = raw && typeof raw === 'object' && !Array.isArray(raw) ? (raw as Record<string, unknown>) : null;`,
14822
+ ` const data = rawRecord && 'data' in rawRecord ? rawRecord.data : raw;`,
14823
+ ` const existingResult = rawRecord && rawRecord.result && typeof rawRecord.result === 'object' && !Array.isArray(rawRecord.result) ? (rawRecord.result as Record<string, unknown>) : null;`,
14824
+ ` const resultData = existingResult && 'data' in existingResult ? __dlLegacyResultData(existingResult.data) : __dlLegacyResultData(data);`,
14825
+ ` const resultObject = resultData && typeof resultData === 'object' && !Array.isArray(resultData) ? (resultData as Record<string, unknown>) : {};`,
14826
+ ` return {`,
14827
+ ` ...(rawRecord ?? {}),`,
14828
+ ` data,`,
14829
+ ` result: { ...resultObject, ...(existingResult ?? {}), data: resultData },`,
14830
+ ` raw,`,
14831
+ ` toolResponse: { raw },`,
14832
+ ` originalResult: result,`,
14833
+ ` };`,
14834
+ `}`,
14835
+ ``,
14836
+ `function __dlTemplate(value: unknown, row: Record<string, unknown>): unknown {`,
14837
+ ` if (Array.isArray(value)) return value.map((entry) => __dlTemplate(entry, row));`,
14838
+ ` if (value && typeof value === 'object') {`,
14839
+ ` return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, __dlTemplate(entry, row)]));`,
14840
+ ` }`,
14841
+ ` if (typeof value !== 'string') return value;`,
14842
+ ` const exact = value.match(/^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/);`,
14843
+ ` if (exact) return __dlGetByPath(row, exact[1] || '');`,
14844
+ ` let rendered = '';`,
14845
+ ` let cursor = 0;`,
14846
+ ` while (cursor < value.length) {`,
14847
+ ` const open = value.indexOf('{{', cursor);`,
14848
+ ` if (open < 0) {`,
14849
+ ` rendered += value.slice(cursor);`,
14850
+ ` break;`,
14851
+ ` }`,
14852
+ ` const close = value.indexOf('}}', open + 2);`,
14853
+ ` if (close < 0) {`,
14854
+ ` rendered += value.slice(cursor);`,
14855
+ ` break;`,
14856
+ ` }`,
14857
+ ` rendered += value.slice(cursor, open);`,
14858
+ ` const path = value.slice(open + 2, close).trim();`,
14859
+ ` const replacement = __dlGetByPath(row, path);`,
14860
+ ` rendered += replacement === null || replacement === undefined ? '' : String(replacement);`,
14861
+ ` cursor = close + 2;`,
14862
+ ` }`,
14863
+ ` return rendered;`,
14864
+ `}`,
14865
+ ``,
14866
+ `function __dlStableRowKey(row: Record<string, unknown>, index: number): string {`,
14867
+ ` for (const key of ['id', 'ID', 'row_key', 'ROW_KEY', 'email', 'Email', 'linkedin_url', 'LINKEDIN_URL', 'domain', 'DOMAIN', 'name', 'Name']) {`,
14868
+ ` const value = row[key];`,
14869
+ ` if (__dlMeaningful(value)) return String(value);`,
14241
14870
  ` }`,
14242
14871
  ` return String(index);`,
14243
14872
  `}`,
14244
14873
  ``,
14874
+ `function __dlScalarValue(value: unknown): unknown {`,
14875
+ ` if (value && typeof value === 'object' && !Array.isArray(value)) {`,
14876
+ ` const record = value as Record<string, unknown>;`,
14877
+ ` if ('matched_result' in record) return __dlScalarValue(record.matched_result);`,
14878
+ ` for (const key of ['value', 'email', 'output', 'result', 'data']) {`,
14879
+ ` const nested = record[key];`,
14880
+ ` if (__dlMeaningful(nested)) return __dlScalarValue(nested);`,
14881
+ ` }`,
14882
+ ` }`,
14883
+ ` return value;`,
14884
+ `}`,
14885
+ ``,
14245
14886
  `function __dlFirstMeaningful(row: Record<string, unknown>, aliases: string[]): unknown {`,
14246
14887
  ` for (const alias of aliases) {`,
14247
14888
  ` const value = row[alias];`,
14248
- ` if (__dlMeaningful(value)) return value;`,
14889
+ ` if (__dlMeaningful(value)) return __dlScalarValue(value);`,
14890
+ ` }`,
14891
+ ` return null;`,
14892
+ `}`,
14893
+ ``,
14894
+ `function __dlFirstMeaningfulAlias(row: Record<string, unknown>, aliases: string[]): string | null {`,
14895
+ ` for (const alias of aliases) {`,
14896
+ ` if (__dlMeaningful(row[alias])) return alias;`,
14249
14897
  ` }`,
14250
14898
  ` return null;`,
14251
14899
  `}`,
14252
14900
  ``,
14901
+ `function __dlListValue(value: unknown): unknown[] {`,
14902
+ ` if (Array.isArray(value)) return value.filter(__dlMeaningful);`,
14903
+ ` if (value && typeof value === 'object') {`,
14904
+ ` const record = value as Record<string, unknown>;`,
14905
+ ` if ('matched_result' in record) return __dlListValue(record.matched_result);`,
14906
+ ` for (const key of ['result', 'value', 'data']) {`,
14907
+ ` const nested = record[key];`,
14908
+ ` const nestedList = __dlListValue(nested);`,
14909
+ ` if (nestedList.length > 0) return nestedList;`,
14910
+ ` }`,
14911
+ ` return __dlMeaningful(value) ? [value] : [];`,
14912
+ ` }`,
14913
+ ` return __dlMeaningful(value) ? [value] : [];`,
14914
+ `}`,
14915
+ ``,
14253
14916
  `function __dlFirstMinResults(row: Record<string, unknown>, aliases: string[], minResults: number): unknown {`,
14254
14917
  ` const values: unknown[] = [];`,
14255
14918
  ` for (const alias of aliases) {`,
14256
- ` const value = row[alias];`,
14257
- ` if (Array.isArray(value)) values.push(...value.filter(__dlMeaningful));`,
14258
- ` else if (__dlMeaningful(value)) values.push(value);`,
14259
- ` if (values.length >= minResults) return values.slice(0, minResults);`,
14919
+ ` values.push(...__dlListValue(row[alias]));`,
14920
+ ` if (values.length >= minResults) return values;`,
14260
14921
  ` }`,
14261
14922
  ` return values.length > 0 ? values : null;`,
14262
14923
  `}`,
14263
14924
  ``,
14925
+ `function __dlContributingAliases(row: Record<string, unknown>, aliases: string[]): string[] | null {`,
14926
+ ` const sources: string[] = [];`,
14927
+ ` for (const alias of aliases) {`,
14928
+ ` if (__dlListValue(row[alias]).length > 0) sources.push(alias);`,
14929
+ ` }`,
14930
+ ` return sources.length > 0 ? sources : null;`,
14931
+ `}`,
14932
+ ``,
14264
14933
  `function __dlWaterfallSatisfied(row: Record<string, unknown>, aliases: string[], minResults: number): boolean {`,
14265
14934
  ` let count = 0;`,
14266
14935
  ` for (const alias of aliases) {`,
14267
- ` const value = row[alias];`,
14268
- ` if (Array.isArray(value)) count += value.filter(__dlMeaningful).length;`,
14269
- ` else if (__dlMeaningful(value)) count += 1;`,
14270
- ` if (count >= Math.max(1, Math.trunc(minResults))) return true;`,
14936
+ ` count += __dlListValue(row[alias]).length;`,
14937
+ ` if (count >= __dlAtLeastOneInteger(minResults)) return true;`,
14271
14938
  ` }`,
14272
14939
  ` return false;`,
14273
14940
  `}`,
14274
14941
  ``,
14275
- `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 {`,
14942
+ `function __dlKeyPaths(key: string): string[] {`,
14943
+ ` const normalized = String(key || '').trim();`,
14944
+ ` if (normalized === 'email') return ['email', 'email_address', 'person.email', 'contact.email', 'data.email', 'result.data.email'];`,
14945
+ ` if (normalized === 'personal_email') return ['personal_email', 'email', 'email_address', 'data.personal_email', 'data.email'];`,
14946
+ ` if (normalized === 'phone') return ['phone', 'phone_number', 'mobile_phone', 'mobile_phone_number', 'data.phone'];`,
14947
+ ` if (normalized === 'linkedin') return ['linkedin', 'linkedin_url', 'linkedin_profile', 'profile_url', 'person.linkedin', 'person.linkedin_url'];`,
14948
+ ` if (normalized === 'full_name') return ['full_name', 'name', 'person.full_name', 'person.name'];`,
14949
+ ` if (normalized === 'first_name') return ['first_name', 'person.first_name'];`,
14950
+ ` if (normalized === 'last_name') return ['last_name', 'person.last_name'];`,
14951
+ ` if (normalized === 'title') return ['title', 'job_title', 'current_title', 'headline', 'person.title'];`,
14952
+ ` if (normalized === 'company_name') return ['company_name', 'company.name', 'organization.name'];`,
14953
+ ` if (normalized === 'company_domain') return ['company_domain', 'domain', 'company.domain', 'organization.domain'];`,
14954
+ ` if (normalized === 'status') return ['status', 'verdict', 'state'];`,
14955
+ ` if (normalized === 'email_status') return ['email_status', 'status', 'verdict', 'data.email_status'];`,
14956
+ ` return [normalized];`,
14957
+ `}`,
14958
+ ``,
14959
+ `function __dlSelectorKeys(selector: unknown): string[] | null {`,
14960
+ ` if (!selector || typeof selector !== 'object' || Array.isArray(selector)) return null;`,
14961
+ ` const keys = (selector as { keys?: unknown }).keys;`,
14962
+ ` if (!Array.isArray(keys) || keys.length === 0) return null;`,
14963
+ ` const out: string[] = [];`,
14964
+ ` for (const key of keys) {`,
14965
+ ` if (typeof key === 'string' && key.trim()) out.push(key.trim());`,
14966
+ ` }`,
14967
+ ` return out.length > 0 ? out : null;`,
14968
+ `}`,
14969
+ ``,
14970
+ `function __dlFirstByPaths(payload: unknown, paths: string[] | string): unknown {`,
14971
+ ` const candidates = Array.isArray(paths) ? paths : [paths];`,
14972
+ ` for (const path of candidates) {`,
14973
+ ` for (const candidate of __dlRawToolCandidates(payload)) {`,
14974
+ ` const value = __dlGetByPath(candidate, String(path));`,
14975
+ ` if (__dlMeaningful(value)) return value;`,
14976
+ ` }`,
14977
+ ` }`,
14978
+ ` return null;`,
14979
+ `}`,
14980
+ ``,
14981
+ `function __dlExtractTarget(payload: unknown, key: string): unknown {`,
14982
+ ` return __dlFirstByPaths(payload, __dlKeyPaths(key));`,
14983
+ `}`,
14984
+ ``,
14985
+ `function __dlExtractedValue(result: unknown, key: string): unknown {`,
14986
+ ` if (!result || typeof result !== 'object' || Array.isArray(result)) return undefined;`,
14987
+ ` const extractedValues = (result as Record<string, unknown>).extractedValues;`,
14988
+ ` if (!extractedValues || typeof extractedValues !== 'object' || Array.isArray(extractedValues)) return undefined;`,
14989
+ ` const accessor = (extractedValues as Record<string, unknown>)[key];`,
14990
+ ` if (!accessor || typeof accessor !== 'object' || Array.isArray(accessor)) return undefined;`,
14991
+ ` const record = accessor as Record<string, unknown>;`,
14992
+ ` const get = record.get;`,
14993
+ ` if (typeof get === 'function') {`,
14994
+ ` try {`,
14995
+ ` return (get as () => unknown)();`,
14996
+ ` } catch {`,
14997
+ ` return undefined;`,
14998
+ ` }`,
14999
+ ` }`,
15000
+ ` return record.value;`,
15001
+ `}`,
15002
+ ``,
15003
+ `function __dlLegacyExtractorPayload(payload: unknown, raw: unknown): unknown {`,
15004
+ ` if (payload === undefined || payload === null) return raw;`,
15005
+ ` if (payload && typeof payload === 'object' && !Array.isArray(payload) && Object.keys(payload as Record<string, unknown>).length === 0) return raw;`,
15006
+ ` return payload;`,
15007
+ `}`,
15008
+ ``,
15009
+ `function __dlLegacyMatchedEnvelope(value: unknown, raw: unknown): unknown {`,
15010
+ ` if (value && typeof value === 'object' && !Array.isArray(value) && 'matched_result' in (value as Record<string, unknown>)) return value;`,
15011
+ ` const legacyOutput = __dlLegacyOutputData(undefined, raw) as Record<string, unknown>;`,
15012
+ ` const legacyResult = legacyOutput.result && typeof legacyOutput.result === 'object' && !Array.isArray(legacyOutput.result) ? legacyOutput.result : raw;`,
15013
+ ` return { matched_result: value, result: legacyResult };`,
15014
+ `}`,
15015
+ ``,
15016
+ `function __dlString(value: unknown): string | null {`,
15017
+ ` return typeof value === 'string' && value.trim() ? value.trim() : null;`,
15018
+ `}`,
15019
+ ``,
15020
+ `// NOTE: email_status is NOT normalized here. It is materialized once by the`,
15021
+ `// provider's emailStatus({...}) contract via buildEmailStatus and read back`,
15022
+ `// through __dlExtractedValue(result, 'email_status') below. There is no`,
15023
+ `// legacy string coarsening \u2014 see CONTEXT.md "Provider Email Status Contract".`,
15024
+ ``,
15025
+ `function __dlListPathCandidates(selector: unknown): string[] {`,
15026
+ ` const paths: string[] = [];`,
15027
+ ` const push = (path: string) => { if (path && !paths.includes(path)) paths.push(path); };`,
15028
+ ` if (Array.isArray(selector)) {`,
15029
+ ` for (const path of selector) push(String(path));`,
15030
+ ` } else if (typeof selector === 'string') {`,
15031
+ ` push(selector);`,
15032
+ ` }`,
15033
+ ` 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);`,
15034
+ ` return paths;`,
15035
+ `}`,
15036
+ ``,
15037
+ `function __dlFindList(payload: unknown, selector: unknown): unknown[] {`,
15038
+ ` if (Array.isArray(payload)) return payload;`,
15039
+ ` for (const path of __dlListPathCandidates(selector)) {`,
15040
+ ` for (const candidate of __dlRawToolCandidates(payload)) {`,
15041
+ ` const value = __dlGetByPath(candidate, path);`,
15042
+ ` if (Array.isArray(value)) return value;`,
15043
+ ` }`,
15044
+ ` }`,
15045
+ ` const queue = __dlRawToolCandidates(payload);`,
15046
+ ` const seen: unknown[] = [];`,
15047
+ ` for (let index = 0; index < queue.length; index += 1) {`,
15048
+ ` const current = queue[index];`,
15049
+ ` if (!current || typeof current !== 'object' || seen.includes(current)) continue;`,
15050
+ ` seen.push(current);`,
15051
+ ` for (const value of Object.values(current as Record<string, unknown>)) {`,
15052
+ ` if (Array.isArray(value)) return value;`,
15053
+ ` if (value && typeof value === 'object') queue.push(value);`,
15054
+ ` }`,
15055
+ ` }`,
15056
+ ` return [];`,
15057
+ `}`,
15058
+ ``,
15059
+ `function __dlDeriveFullName(row: Record<string, unknown>): string | null {`,
15060
+ ` const existing = __dlFirstByPaths(row, ['full_name', 'name']);`,
15061
+ ` if (typeof existing === 'string' && existing.trim()) return existing.trim();`,
15062
+ ` const first = __dlFirstByPaths(row, ['first_name', 'firstName']);`,
15063
+ ` const last = __dlFirstByPaths(row, ['last_name', 'lastName']);`,
15064
+ ` const parts = [first, last].filter((part): part is string => typeof part === 'string').map((part) => part.trim()).filter(Boolean);`,
15065
+ ` return parts.length > 0 ? parts.join(' ') : null;`,
15066
+ `}`,
15067
+ ``,
15068
+ `function __dlProjectListRows(rows: unknown[], keys: string[], payload: unknown): Array<Record<string, unknown>> {`,
15069
+ ` const projected: Array<Record<string, unknown>> = [];`,
15070
+ ` for (const row of rows) {`,
15071
+ ` const record = row && typeof row === 'object' && !Array.isArray(row) ? (row as Record<string, unknown>) : { value: row };`,
15072
+ ` const out: Record<string, unknown> = {};`,
15073
+ ` for (const key of keys) {`,
15074
+ ` let value = __dlExtractTarget(record, key);`,
15075
+ ` if (!__dlMeaningful(value) && key === 'full_name') value = __dlDeriveFullName(record);`,
15076
+ ` if (!__dlMeaningful(value)) value = __dlExtractTarget(payload, key);`,
15077
+ ` out[key] = value === undefined ? null : value;`,
15078
+ ` }`,
15079
+ ` if (Object.values(out).some(__dlMeaningful)) projected.push(out);`,
15080
+ ` }`,
15081
+ ` return projected;`,
15082
+ `}`,
15083
+ ``,
15084
+ `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 };`,
15085
+ ``,
15086
+ `function __dlErrorMessage(error: unknown): string {`,
15087
+ ` if (error instanceof Error && error.message) return error.message;`,
15088
+ ` if (typeof error === 'string' && error.trim()) return error.trim();`,
15089
+ ` return 'Extractor failed';`,
15090
+ `}`,
15091
+ ``,
15092
+ `function __dlExtractorFailure(error: unknown): unknown {`,
15093
+ ` const message = __dlErrorMessage(error);`,
15094
+ ` return { matched_result: null, result: { error: message, message } };`,
15095
+ `}`,
15096
+ ``,
15097
+ `function __dlExtract(alias: string, result: unknown, row: Record<string, unknown>, extractor: ((args: __DlExtractorHelpers) => unknown) | null, legacyEnvelope = false): unknown {`,
14276
15098
  ` const raw = __dlRawToolOutput(result);`,
14277
15099
  ` if (!extractor) return raw;`,
14278
15100
  ` const pick = (paths: string[] | string) => {`,
14279
15101
  ` const candidates = Array.isArray(paths) ? paths : [paths];`,
14280
15102
  ` for (const path of candidates) {`,
14281
- ` const value = __dlGetByPath(raw, String(path));`,
14282
- ` if (__dlMeaningful(value)) return value;`,
15103
+ ` if (typeof path === 'string') {`,
15104
+ ` const extractedValue = __dlExtractedValue(result, path);`,
15105
+ ` if (__dlMeaningful(extractedValue)) return extractedValue;`,
15106
+ ` }`,
15107
+ ` for (const candidate of __dlRawToolCandidates(raw)) {`,
15108
+ ` const value = __dlGetByPath(candidate, String(path));`,
15109
+ ` if (__dlMeaningful(value)) return value;`,
15110
+ ` }`,
14283
15111
  ` }`,
14284
15112
  ` return null;`,
14285
15113
  ` };`,
14286
- ` const extracted = extractor({ row, result, data: raw, raw, pick, extract: pick, target: pick });`,
14287
- ` if (extracted && typeof extracted === 'object' && !Array.isArray(extracted) && alias in (extracted as Record<string, unknown>)) {`,
14288
- ` return (extracted as Record<string, unknown>)[alias];`,
15114
+ ` const extract = (...args: unknown[]): unknown => {`,
15115
+ ` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
15116
+ ` const selector = args.length >= 3 ? args[2] : args[0];`,
15117
+ ` if (selector === undefined) return payload;`,
15118
+ ` const keys = __dlSelectorKeys(selector);`,
15119
+ ` if (keys) return Object.fromEntries(keys.map((key) => [key, __dlExtractTarget(payload, key) ?? null]));`,
15120
+ ` if (Array.isArray(selector)) return __dlFirstByPaths(payload, selector.map(String));`,
15121
+ ` if (typeof selector === 'string') {`,
15122
+ ` const extractedValue = __dlExtractedValue(result, selector);`,
15123
+ ` if (__dlMeaningful(extractedValue)) return __dlLegacyMatchedEnvelope(extractedValue, payload);`,
15124
+ ` return __dlExtractTarget(payload, selector) ?? __dlFirstByPaths(payload, selector);`,
15125
+ ` }`,
15126
+ ` return selector;`,
15127
+ ` };`,
15128
+ ` const extractList = (...args: unknown[]): unknown[] => {`,
15129
+ ` const payload = args.length >= 3 ? __dlLegacyExtractorPayload(args[1], raw) : raw;`,
15130
+ ` const selector = args.length >= 3 ? args[2] : args[0];`,
15131
+ ` const rows = __dlFindList(payload, selector);`,
15132
+ ` const keys = __dlSelectorKeys(selector);`,
15133
+ ` return keys ? __dlProjectListRows(rows, keys, payload) : rows;`,
15134
+ ` };`,
15135
+ ` const get = (path: string): unknown => __dlExtractedValue(result, path) ?? __dlFirstByPaths(raw, path);`,
15136
+ ` let resolved: unknown;`,
15137
+ ` try {`,
15138
+ ` const extracted = extractor({ row, result, data: raw, raw, pick, extract, extractList, target: pick, get });`,
15139
+ ` resolved = typeof extracted === 'function' ? (extracted as (outputData: unknown) => unknown)(__dlLegacyOutputData(result, raw)) : extracted;`,
15140
+ ` } catch (error) {`,
15141
+ ` return __dlExtractorFailure(error);`,
15142
+ ` }`,
15143
+ ` if (resolved && typeof resolved === 'object' && !Array.isArray(resolved) && alias in (resolved as Record<string, unknown>)) {`,
15144
+ ` const aliasValue = (resolved as Record<string, unknown>)[alias];`,
15145
+ ` return legacyEnvelope && __dlMeaningful(aliasValue) ? __dlLegacyMatchedEnvelope(aliasValue, raw) : aliasValue;`,
15146
+ ` }`,
15147
+ ` if (Array.isArray(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
15148
+ ` if ((resolved === null || resolved === undefined) && __dlErrorPayload(raw)) return raw;`,
15149
+ ` if (legacyEnvelope && __dlMeaningful(resolved)) return __dlLegacyMatchedEnvelope(resolved, raw);`,
15150
+ ` return resolved === undefined ? raw : resolved;`,
15151
+ `}`,
15152
+ ``,
15153
+ `function __dlMergeContextRecord(existing: unknown, defaults: Record<string, unknown>): Record<string, unknown> {`,
15154
+ ` if (!existing || typeof existing !== 'object' || Array.isArray(existing)) return { ...defaults };`,
15155
+ ` return { ...defaults, ...(existing as Record<string, unknown>) };`,
15156
+ `}`,
15157
+ ``,
15158
+ `function __dlRuntimePayload(tool: string, payload: Record<string, unknown>, row: Record<string, unknown>): Record<string, unknown> {`,
15159
+ ` if (tool !== 'run_javascript') return payload;`,
15160
+ ` return {`,
15161
+ ` ...payload,`,
15162
+ ` row: __dlMergeContextRecord(payload.row, row),`,
15163
+ ` input: __dlMergeContextRecord(payload.input, row),`,
15164
+ ` context: __dlMergeContextRecord(payload.context, row),`,
15165
+ ` };`,
15166
+ `}`,
15167
+ ``,
15168
+ `function __dlAliasCandidates(alias: string): string[] {`,
15169
+ ` const aliases: string[] = [];`,
15170
+ ` for (const candidate of [alias, alias.replace(/-/g, '_'), alias.replace(/_/g, '-')]) {`,
15171
+ ` if (candidate && !aliases.includes(candidate)) aliases.push(candidate);`,
15172
+ ` }`,
15173
+ ` return aliases;`,
15174
+ `}`,
15175
+ ``,
15176
+ `function __dlPlayResultValue(alias: string, result: unknown): unknown {`,
15177
+ ` if (!result || typeof result !== 'object' || Array.isArray(result)) return result;`,
15178
+ ` const record = result as Record<string, unknown>;`,
15179
+ ` const aliases = __dlAliasCandidates(alias);`,
15180
+ ` for (const key of aliases) {`,
15181
+ ` if (key in record && __dlMeaningful(record[key])) return __dlScalarValue(record[key]);`,
14289
15182
  ` }`,
14290
- ` return extracted === undefined ? raw : extracted;`,
15183
+ ` const values = Object.values(record).filter(__dlMeaningful);`,
15184
+ ` if (values.length === 1) return __dlScalarValue(values[0]);`,
15185
+ ` return result;`,
14291
15186
  `}`,
14292
15187
  ``,
14293
- `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> {`,
15188
+ `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> {`,
14294
15189
  ` if (input.runIf) {`,
14295
15190
  ` const shouldRun = input.runIf(input.row);`,
14296
15191
  ` if (!shouldRun) return null;`,
14297
15192
  ` }`,
15193
+ ` const payload = __dlRuntimePayload(input.tool, __dlTemplate(input.payload, input.row) as Record<string, unknown>, input.row);`,
14298
15194
  ` const result = await input.stepCtx.tools.execute({`,
14299
15195
  ` id: input.callId,`,
14300
15196
  ` tool: input.tool,`,
14301
- ` input: __dlTemplate(input.payload, input.row) as Record<string, unknown>,`,
15197
+ ` input: payload,`,
14302
15198
  ` ...(input.description ? { description: input.description } : {}),`,
14303
- ` ...(input.force ? { staleAfterSeconds: 0 } : {}),`,
15199
+ ` ...(input.force ? { staleAfterSeconds: 1 } : {}),`,
14304
15200
  ` });`,
14305
- ` return __dlExtract(input.alias, result, input.row, input.extract);`,
15201
+ ` return __dlExtract(input.alias, result, input.row, input.extract, Boolean(input.legacyEnvelope));`,
14306
15202
  `}`
14307
15203
  ].join("\n");
14308
15204
  }
14309
15205
 
14310
15206
  // src/cli/commands/enrich.ts
15207
+ var ENRICH_EXPORT_PAGE_SIZE = 5e3;
15208
+ var EXIT_SERVER2 = 5;
15209
+ var ENRICH_DEBUG_T0 = Date.now();
14311
15210
  var PLAN_SHAPING_OPTION_NAMES = [
14312
15211
  "with",
14313
15212
  "withWaterfall",
14314
15213
  "minResults",
14315
15214
  "endWaterfall"
14316
15215
  ];
14317
- var ENRICH_DEPRECATION_NOTICE = {
14318
- status: "deprecated",
14319
- message: "The enrich compatibility command is deprecated. This run generates a temporary .play.ts file and executes it through plays run.",
14320
- generatedPlayFile: "Temporary compatibility play file; deleted after the command exits.",
14321
- printGeneratedPlayCommand: "deepline enrich <same args> --dry-run > enrich.play.ts",
14322
- recommendedCommand: `deepline plays run enrich.play.ts --input '{"file":"<input.csv>"}'`
14323
- };
14324
- var ENRICH_DEPRECATION_TEXT = `${ENRICH_DEPRECATION_NOTICE.message} Print the generated play with: ${ENRICH_DEPRECATION_NOTICE.printGeneratedPlayCommand}. Then run: ${ENRICH_DEPRECATION_NOTICE.recommendedCommand}
14325
- `;
14326
15216
  function optionWasProvided(args, ...flags) {
14327
15217
  return args.some(
14328
15218
  (arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`))
@@ -14334,9 +15224,103 @@ function normalizeAlias2(value) {
14334
15224
  function hasPlanShapingArgs(args) {
14335
15225
  return optionWasProvided(args, "--with") || optionWasProvided(args, "--with-waterfall") || optionWasProvided(args, "--min-results") || optionWasProvided(args, "--end-waterfall");
14336
15226
  }
14337
- function printDeprecationNotice(options) {
14338
- if (!options.json) {
14339
- process.stderr.write(ENRICH_DEPRECATION_TEXT);
15227
+ function enrichDebugEnabled(env = process.env) {
15228
+ const raw = String(env.DEEPLINE_DEBUG_ENRICH ?? "").trim().toLowerCase();
15229
+ if (["1", "true", "yes", "on"].includes(raw)) {
15230
+ return true;
15231
+ }
15232
+ const nodeEnv = String(env.NODE_ENV ?? "").trim().toLowerCase();
15233
+ const deeplineEnv = String(env.DEEPLINE_ENV ?? "").trim().toLowerCase();
15234
+ return nodeEnv === "development" || ["dev", "development"].includes(deeplineEnv);
15235
+ }
15236
+ function decodeStringLiteral(raw) {
15237
+ try {
15238
+ const parsed = JSON.parse(raw);
15239
+ return typeof parsed === "string" ? parsed : null;
15240
+ } catch {
15241
+ const quote = raw[0];
15242
+ if ((quote === "'" || quote === '"') && raw[raw.length - 1] === quote) {
15243
+ return raw.slice(1, -1).replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
15244
+ }
15245
+ return null;
15246
+ }
15247
+ }
15248
+ function addStringArrayLiteralTargets(targets, arrayLiteral) {
15249
+ for (const match of arrayLiteral.matchAll(/(["'])(?:\\.|(?!\1).)*\1/g)) {
15250
+ const parsed = decodeStringLiteral(match[0] ?? "");
15251
+ if (parsed?.trim()) {
15252
+ targets.add(parsed.trim());
15253
+ }
15254
+ }
15255
+ }
15256
+ function extractExtractorTargetKeys(source) {
15257
+ const targets = /* @__PURE__ */ new Set();
15258
+ for (const match of source.matchAll(/\bkeys\s*:\s*(\[[^\]]*\])/g)) {
15259
+ addStringArrayLiteralTargets(targets, match[1] ?? "");
15260
+ }
15261
+ for (const match of source.matchAll(
15262
+ /\btarget\s*\(\s*(["'][^"']+["'])\s*\)/g
15263
+ )) {
15264
+ const parsed = decodeStringLiteral(match[1] ?? "");
15265
+ if (parsed?.trim()) {
15266
+ targets.add(parsed.trim());
15267
+ }
15268
+ }
15269
+ for (const match of source.matchAll(
15270
+ /\bextract\s*\(\s*["'][^"']+["']\s*,[^,]*,\s*(["'][^"']+["'])\s*\)/g
15271
+ )) {
15272
+ const parsed = decodeStringLiteral(match[1] ?? "");
15273
+ if (parsed?.trim()) {
15274
+ targets.add(parsed.trim());
15275
+ }
15276
+ }
15277
+ return [...targets].sort();
15278
+ }
15279
+ function flattenEnrichStepCommands(commands) {
15280
+ const steps = [];
15281
+ for (const command of commands) {
15282
+ if ("with_waterfall" in command) {
15283
+ steps.push(...flattenEnrichStepCommands(command.commands));
15284
+ continue;
15285
+ }
15286
+ if (!command.disabled) {
15287
+ steps.push(command);
15288
+ }
15289
+ }
15290
+ return steps;
15291
+ }
15292
+ function buildEnrichDebugValidationLines(config) {
15293
+ return flattenEnrichStepCommands(config.commands).filter(
15294
+ (command) => typeof command.extract_js === "string" && command.extract_js.trim()
15295
+ ).map((command) => {
15296
+ const source = command.extract_js ?? "";
15297
+ const targets = extractExtractorTargetKeys(source);
15298
+ const mode = /\bextractList\s*\(/.test(source) ? "list" : "scalar";
15299
+ return [
15300
+ "validate extractor",
15301
+ `column=${command.alias}`,
15302
+ `tool_id=${command.tool || "(none)"}`,
15303
+ `mode=${mode}`,
15304
+ "has_result_sample=false",
15305
+ "sample_keys=<none>",
15306
+ `targets=${JSON.stringify(targets)}`
15307
+ ].join(" ");
15308
+ });
15309
+ }
15310
+ function emitEnrichDebug(message) {
15311
+ if (!enrichDebugEnabled()) {
15312
+ return;
15313
+ }
15314
+ const now = /* @__PURE__ */ new Date();
15315
+ const elapsedMs = Date.now() - ENRICH_DEBUG_T0;
15316
+ process.stderr.write(
15317
+ `[deepline:enrich] ${now.toISOString()} +${elapsedMs}ms ${message}
15318
+ `
15319
+ );
15320
+ }
15321
+ function emitEnrichDebugValidationLines(config) {
15322
+ for (const line of buildEnrichDebugValidationLines(config)) {
15323
+ emitEnrichDebug(line);
14340
15324
  }
14341
15325
  }
14342
15326
  function expandAtFilePath(rawPath) {
@@ -14473,15 +15457,18 @@ async function buildPlanArgs(args) {
14473
15457
  "--csv",
14474
15458
  "--output",
14475
15459
  "--config",
15460
+ "--name",
14476
15461
  "--rows",
14477
- "--with-force"
15462
+ "--with-force",
15463
+ "--timeout"
14478
15464
  ]);
14479
15465
  const localBooleanOptions = /* @__PURE__ */ new Set([
14480
15466
  "--dry-run",
14481
15467
  "--json",
14482
15468
  "--force",
14483
15469
  "--all",
14484
- "--in-place"
15470
+ "--in-place",
15471
+ "--no-open"
14485
15472
  ]);
14486
15473
  const planArgs = [];
14487
15474
  for (let index = 0; index < args.length; index += 1) {
@@ -14547,7 +15534,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14547
15534
  const output2 = resolve11(outputPath);
14548
15535
  if (input2 === output2) {
14549
15536
  throw new Error(
14550
- "--output must be a different path from --input. --in-place is not supported by this V2 enrich runner yet."
15537
+ "--input and --output must be different files when not using --in-place."
14551
15538
  );
14552
15539
  }
14553
15540
  try {
@@ -14557,7 +15544,7 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14557
15544
  ]);
14558
15545
  if (inputInfo.dev === outputInfo.dev && inputInfo.ino === outputInfo.ino) {
14559
15546
  throw new Error(
14560
- "--output must be a different file from --input. --in-place is not supported by this V2 enrich runner yet."
15547
+ "--input and --output must be different files when not using --in-place."
14561
15548
  );
14562
15549
  }
14563
15550
  } catch (error) {
@@ -14571,6 +15558,18 @@ async function assertSafeOutputPath(inputCsv, outputPath) {
14571
15558
  throw error;
14572
15559
  }
14573
15560
  }
15561
+ async function regularFileExists(path) {
15562
+ try {
15563
+ const info = await stat3(resolve11(path));
15564
+ return info.isFile();
15565
+ } catch (error) {
15566
+ const code = error && typeof error === "object" ? error.code : void 0;
15567
+ if (code === "ENOENT") {
15568
+ return false;
15569
+ }
15570
+ throw error;
15571
+ }
15572
+ }
14574
15573
  async function readConfig(path) {
14575
15574
  const source = await readFile3(resolve11(path), "utf8");
14576
15575
  let parsed;
@@ -14593,27 +15592,30 @@ function parseRows(value, all) {
14593
15592
  const trimmed = value.trim();
14594
15593
  const range = trimmed.match(/^(\d*)\s*:\s*(\d*)$/);
14595
15594
  if (range) {
14596
- if (!range[1] && !range[2]) {
15595
+ if (!range[1]) {
14597
15596
  throw new Error(
14598
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15597
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14599
15598
  );
14600
15599
  }
14601
15600
  const start = range[1] ? Number.parseInt(range[1], 10) : 0;
14602
15601
  const end = range[2] ? Number.parseInt(range[2], 10) : null;
15602
+ if (end !== null && end < start) {
15603
+ throw new Error("Invalid --rows range: end must be >= start.");
15604
+ }
14603
15605
  return { rowStart: start, rowEnd: end };
14604
15606
  }
14605
15607
  if (!/^\d+$/.test(trimmed)) {
14606
15608
  throw new Error(
14607
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15609
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14608
15610
  );
14609
15611
  }
14610
15612
  const single = Number.parseInt(trimmed, 10);
14611
15613
  if (!Number.isFinite(single) || single < 0) {
14612
15614
  throw new Error(
14613
- "--rows must be a zero-based row number or end-exclusive range like 0:10."
15615
+ "--rows must be a zero-based row number or inclusive range like 0:10."
14614
15616
  );
14615
15617
  }
14616
- return { rowStart: single, rowEnd: single + 1 };
15618
+ return { rowStart: single, rowEnd: single };
14617
15619
  }
14618
15620
  function summarizePlan(config, playSource) {
14619
15621
  const steps = [];
@@ -14692,76 +15694,715 @@ function parseWithForceAliases(values) {
14692
15694
  }
14693
15695
  }
14694
15696
  }
14695
- return aliases;
15697
+ return aliases;
15698
+ }
15699
+ function resolveForceAliases(config, options) {
15700
+ const { allAliases, waterfallGroups } = collectCommandAliases(config);
15701
+ if (options.force) {
15702
+ return new Set(allAliases);
15703
+ }
15704
+ const requested = parseWithForceAliases(options.withForce);
15705
+ const unknown = [...requested].filter((alias) => !allAliases.has(alias));
15706
+ if (unknown.length > 0) {
15707
+ throw new Error(
15708
+ `--with-force references unknown --with column alias(es): ${unknown.sort().join(", ")}.`
15709
+ );
15710
+ }
15711
+ const resolved = /* @__PURE__ */ new Set();
15712
+ for (const alias of requested) {
15713
+ const children = waterfallGroups.get(alias);
15714
+ if (children) {
15715
+ resolved.add(alias);
15716
+ for (const child of children) {
15717
+ resolved.add(child);
15718
+ }
15719
+ } else {
15720
+ resolved.add(alias);
15721
+ }
15722
+ }
15723
+ return resolved;
15724
+ }
15725
+ function isPersistedFailureCell(value) {
15726
+ const raw = String(value ?? "").trim();
15727
+ if (!raw) {
15728
+ return false;
15729
+ }
15730
+ if (cellFailureError(value)) {
15731
+ return true;
15732
+ }
15733
+ return raw.includes("Error:") || raw.includes("Traceback");
15734
+ }
15735
+ function isPersistedFailureStatusCell(value) {
15736
+ const raw = String(value ?? "").trim().toLowerCase();
15737
+ return raw === "error" || raw === "failed" || raw === "failure";
15738
+ }
15739
+ function stripFailureCompanionSuffix(key) {
15740
+ const lowerKey = key.toLowerCase();
15741
+ for (const suffix of [".error", "__error"]) {
15742
+ if (lowerKey.endsWith(suffix)) {
15743
+ return { alias: key.slice(0, -suffix.length), kind: "error" };
15744
+ }
15745
+ }
15746
+ for (const suffix of [".status", "__status"]) {
15747
+ if (lowerKey.endsWith(suffix)) {
15748
+ return { alias: key.slice(0, -suffix.length), kind: "status" };
15749
+ }
15750
+ }
15751
+ return null;
15752
+ }
15753
+ function collectFailedInputAliases(config, sourceCsvPath, rows) {
15754
+ const { scalarAliases } = collectCommandAliases(config);
15755
+ if (scalarAliases.size === 0) {
15756
+ return /* @__PURE__ */ new Set();
15757
+ }
15758
+ const inputRows = readCsvRows(sourceCsvPath);
15759
+ if (inputRows.length === 0) {
15760
+ return /* @__PURE__ */ new Set();
15761
+ }
15762
+ const start = rows.rowStart ?? 0;
15763
+ const maxEnd = Math.max(start, inputRows.length - 1);
15764
+ const inclusiveEnd = rows.rowEnd === null || rows.rowEnd === void 0 ? maxEnd : Math.min(maxEnd, rows.rowEnd);
15765
+ const failedAliases = /* @__PURE__ */ new Set();
15766
+ for (let index = start; index <= inclusiveEnd; index += 1) {
15767
+ const row = inputRows[index];
15768
+ if (!row) {
15769
+ continue;
15770
+ }
15771
+ for (const [key, value] of Object.entries(row)) {
15772
+ const alias = normalizeAlias2(key);
15773
+ if (scalarAliases.has(alias) && isPersistedFailureCell(value)) {
15774
+ failedAliases.add(alias);
15775
+ continue;
15776
+ }
15777
+ const companion = stripFailureCompanionSuffix(key);
15778
+ if (!companion) {
15779
+ continue;
15780
+ }
15781
+ const companionAlias = normalizeAlias2(companion.alias);
15782
+ if (!scalarAliases.has(companionAlias)) {
15783
+ continue;
15784
+ }
15785
+ if (companion.kind === "error" ? isPersistedFailureCell(value) : isPersistedFailureStatusCell(value)) {
15786
+ failedAliases.add(companionAlias);
15787
+ }
15788
+ }
15789
+ }
15790
+ return failedAliases;
15791
+ }
15792
+ function parseJsonOutput(stdout) {
15793
+ const trimmed = stdout.trim();
15794
+ if (!trimmed) return null;
15795
+ try {
15796
+ return JSON.parse(trimmed);
15797
+ } catch {
15798
+ const start = trimmed.lastIndexOf("\n{");
15799
+ if (start >= 0) {
15800
+ return JSON.parse(trimmed.slice(start + 1));
15801
+ }
15802
+ throw new Error(
15803
+ "The generated play completed but did not emit parseable JSON."
15804
+ );
15805
+ }
15806
+ }
15807
+ function extractRunIdFromPlayOutput(stdout) {
15808
+ const runIdLine = stdout.match(/^\s*run id:\s*(play\/\S+\/run\/\S+)\s*$/im);
15809
+ if (runIdLine?.[1]) {
15810
+ return runIdLine[1];
15811
+ }
15812
+ return stdout.match(/\b(play\/\S+\/run\/\S+)/)?.[1] ?? null;
15813
+ }
15814
+ async function resolveWatchedGeneratedPlayStatus(input2) {
15815
+ const runId = extractRunIdFromPlayOutput(input2.stdout);
15816
+ if (!runId) {
15817
+ if (input2.exitCode === 0) {
15818
+ throw new Error(
15819
+ "The generated play completed but did not print a run id to inspect."
15820
+ );
15821
+ }
15822
+ return null;
15823
+ }
15824
+ return input2.client.runs.get(runId, { full: true });
15825
+ }
15826
+ function isRecord6(value) {
15827
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
15828
+ }
15829
+ async function captureStdout(run, options = {}) {
15830
+ const originalWrite = process.stdout.write.bind(process.stdout);
15831
+ let stdout = "";
15832
+ process.stdout.write = ((chunk, ..._args) => {
15833
+ stdout += typeof chunk === "string" ? chunk : String(chunk);
15834
+ if (options.passthrough) {
15835
+ return originalWrite(typeof chunk === "string" ? chunk : String(chunk));
15836
+ }
15837
+ return true;
15838
+ });
15839
+ try {
15840
+ const result = await run();
15841
+ return { result, stdout };
15842
+ } finally {
15843
+ process.stdout.write = originalWrite;
15844
+ }
15845
+ }
15846
+ function isPlayStartStreamEndedError(error) {
15847
+ return error instanceof DeeplineError && error.code === "PLAY_START_STREAM_ENDED" || error instanceof Error && error.message.includes(
15848
+ "Play start stream ended before a terminal status"
15849
+ );
15850
+ }
15851
+ async function runGeneratedEnrichPlay(runArgs, options = {}) {
15852
+ for (let attempt = 0; attempt < 2; attempt += 1) {
15853
+ try {
15854
+ return await captureStdout(() => handlePlayRun(runArgs), {
15855
+ passthrough: options.passthroughStdout
15856
+ });
15857
+ } catch (error) {
15858
+ if (attempt === 0 && isPlayStartStreamEndedError(error)) {
15859
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
15860
+ continue;
15861
+ }
15862
+ throw error;
15863
+ }
15864
+ }
15865
+ return captureStdout(() => handlePlayRun(runArgs), {
15866
+ passthrough: options.passthroughStdout
15867
+ });
15868
+ }
15869
+ async function writeOutputCsv(outputPath, status, options) {
15870
+ let rowsInfo = extractCanonicalRowsInfo(status);
15871
+ if (!rowsInfo) {
15872
+ throw new Error("The generated play did not return row-shaped output.");
15873
+ }
15874
+ if (!rowsInfo.complete && options?.client) {
15875
+ rowsInfo = await fetchBackingRowsForCsvExport({
15876
+ client: options.client,
15877
+ status,
15878
+ rowsInfo
15879
+ }) ?? rowsInfo;
15880
+ }
15881
+ assertCompleteRowsForCsvExport(rowsInfo, status, outputPath);
15882
+ const merged = mergeRowsForCsvExport(rowsInfo.rows, options);
15883
+ const columns = orderEnrichCsvColumns(
15884
+ dataExportColumns(merged.rows, [
15885
+ ...merged.preferredColumns,
15886
+ ...rowsInfo.columns
15887
+ ]),
15888
+ options?.config
15889
+ );
15890
+ await writeFile4(
15891
+ resolve11(outputPath),
15892
+ csvStringFromRows(merged.rows, columns),
15893
+ "utf8"
15894
+ );
15895
+ return {
15896
+ rows: merged.rows.length,
15897
+ path: resolve11(outputPath),
15898
+ enrichedRows: rowsInfo.rows
15899
+ };
15900
+ }
15901
+ function recordField(value, key) {
15902
+ return value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
15903
+ }
15904
+ function extractRunId(status) {
15905
+ const candidates = [
15906
+ recordField(status, "runId"),
15907
+ recordField(recordField(status, "run"), "id")
15908
+ ];
15909
+ for (const candidate of candidates) {
15910
+ if (typeof candidate === "string" && candidate.trim()) {
15911
+ return candidate.trim();
15912
+ }
15913
+ }
15914
+ return null;
15915
+ }
15916
+ function extractPlayName2(status) {
15917
+ const run = recordField(status, "run");
15918
+ const candidates = [
15919
+ recordField(status, "playName"),
15920
+ recordField(status, "name"),
15921
+ recordField(run, "playName"),
15922
+ recordField(run, "name")
15923
+ ];
15924
+ for (const candidate of candidates) {
15925
+ if (typeof candidate === "string" && candidate.trim()) {
15926
+ return candidate.trim();
15927
+ }
15928
+ }
15929
+ return null;
15930
+ }
15931
+ function exportableSheetRow2(row) {
15932
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
15933
+ return null;
15934
+ }
15935
+ const record = row;
15936
+ const data = record.data;
15937
+ if (data && typeof data === "object" && !Array.isArray(data)) {
15938
+ return data;
15939
+ }
15940
+ const fallback = { ...record };
15941
+ for (const key of [
15942
+ "key",
15943
+ "status",
15944
+ "cellMeta",
15945
+ "inputIndex",
15946
+ "runId",
15947
+ "error",
15948
+ "stage",
15949
+ "provider",
15950
+ "seq",
15951
+ "createdAt",
15952
+ "updatedAt"
15953
+ ]) {
15954
+ delete fallback[key];
15955
+ }
15956
+ return fallback;
15957
+ }
15958
+ function collectHardFailureAliases(config) {
15959
+ const aliases = [];
15960
+ const seen = /* @__PURE__ */ new Set();
15961
+ for (const command of config.commands) {
15962
+ if ("with_waterfall" in command) {
15963
+ continue;
15964
+ }
15965
+ if (command.disabled) {
15966
+ continue;
15967
+ }
15968
+ const alias = command.alias.trim();
15969
+ if (!alias || seen.has(alias)) {
15970
+ continue;
15971
+ }
15972
+ seen.add(alias);
15973
+ aliases.push(alias);
15974
+ }
15975
+ return aliases;
15976
+ }
15977
+ function cellFailureError(value) {
15978
+ const parsed = parseMaybeJsonObject(value);
15979
+ if (!isRecord6(parsed)) {
15980
+ return null;
15981
+ }
15982
+ const status = typeof parsed.status === "string" ? parsed.status.trim().toLowerCase() : "";
15983
+ const directError = typeof parsed.error === "string" ? parsed.error.trim() : typeof parsed.last_error === "string" ? parsed.last_error.trim() : "";
15984
+ const result = isRecord6(parsed.result) ? parsed.result : null;
15985
+ const resultError = typeof result?.error === "string" ? result.error.trim() : typeof result?.message === "string" ? result.message.trim() : "";
15986
+ if (!directError && !resultError && status !== "error" && status !== "failed") {
15987
+ return null;
15988
+ }
15989
+ const message = directError || resultError || `Column status: ${status}`;
15990
+ return {
15991
+ message,
15992
+ ...typeof parsed.operation === "string" && parsed.operation.trim() ? { operation: parsed.operation.trim() } : {},
15993
+ ...typeof parsed.provider === "string" && parsed.provider.trim() ? { provider: parsed.provider.trim() } : {}
15994
+ };
15995
+ }
15996
+ function buildEnrichWaterfallSummaryLines(config, rows) {
15997
+ if (rows.length === 0) {
15998
+ return [];
15999
+ }
16000
+ const lines = [];
16001
+ for (const command of config.commands) {
16002
+ if (!("with_waterfall" in command)) {
16003
+ continue;
16004
+ }
16005
+ for (const child of command.commands) {
16006
+ if ("with_waterfall" in child || child.disabled) {
16007
+ continue;
16008
+ }
16009
+ const failures = rows.filter(
16010
+ (row) => cellFailureError(row[child.alias])
16011
+ ).length;
16012
+ if (failures > 0) {
16013
+ lines.push(
16014
+ `Column '${child.alias}' had high failure rate (${failures}/${rows.length}); continuing waterfall group '${command.with_waterfall}'.`
16015
+ );
16016
+ }
16017
+ }
16018
+ }
16019
+ return lines;
16020
+ }
16021
+ function collectEnrichFailureJobs(input2) {
16022
+ const aliases = collectHardFailureAliases(input2.config);
16023
+ if (aliases.length === 0 || input2.rows.length === 0) {
16024
+ return [];
16025
+ }
16026
+ const rowOffset = input2.rowStart ?? 0;
16027
+ const jobs = [];
16028
+ input2.rows.forEach((row, rowIndex) => {
16029
+ aliases.forEach((alias, aliasIndex) => {
16030
+ const failure = cellFailureError(row[alias]);
16031
+ if (!failure) {
16032
+ return;
16033
+ }
16034
+ const rowId = rowOffset + rowIndex;
16035
+ jobs.push({
16036
+ job_id: `row-${rowId}-${alias}`,
16037
+ row_id: rowId,
16038
+ col_index: aliasIndex,
16039
+ column: alias,
16040
+ status: "failed",
16041
+ last_error: failure.message,
16042
+ error: failure.message,
16043
+ ...failure.operation ? { operation: failure.operation } : {},
16044
+ ...failure.provider ? { provider: failure.provider } : {}
16045
+ });
16046
+ });
16047
+ });
16048
+ return jobs;
16049
+ }
16050
+ function summarizeFailedJobError(value) {
16051
+ const text = String(value ?? "").trim();
16052
+ return text.length > 500 ? `${text.slice(0, 497)}...` : text;
16053
+ }
16054
+ function formatFailureReportCommand(reportPath) {
16055
+ const quoted = reportPath.replace(/'/g, `'\\''`);
16056
+ return `Inspect failed jobs: jq -r '.jobs[] | [.job_id,.row_id,.col_index,.column,.status,.last_error] | @tsv' '${quoted}'`;
16057
+ }
16058
+ async function persistEnrichFailureReport(input2) {
16059
+ if (input2.jobs.length === 0) {
16060
+ return null;
16061
+ }
16062
+ const stateDir = join8(homedir4(), ".local", "deepline", "runtime", "state");
16063
+ await mkdir4(stateDir, { recursive: true });
16064
+ const reportPath = join8(
16065
+ stateDir,
16066
+ `run-block-failures-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
16067
+ );
16068
+ const report = {
16069
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
16070
+ api_url: input2.apiUrl,
16071
+ output_csv: input2.outputPath ?? "",
16072
+ summary: {
16073
+ failed_count: input2.jobs.length,
16074
+ canceled_count: 0,
16075
+ pending_count: 0,
16076
+ missing_count: 0,
16077
+ total_jobs: input2.jobs.length
16078
+ },
16079
+ jobs: input2.jobs,
16080
+ failed_jobs: input2.jobs,
16081
+ canceled_jobs: [],
16082
+ pending_jobs: [],
16083
+ missing_job_ids: []
16084
+ };
16085
+ if (input2.rows.rowStart !== null && input2.rows.rowEnd !== null) {
16086
+ report.rows = { start: input2.rows.rowStart, end: input2.rows.rowEnd };
16087
+ }
16088
+ await writeFile4(reportPath, `${JSON.stringify(report, null, 2)}
16089
+ `, "utf8");
16090
+ return reportPath;
16091
+ }
16092
+ async function maybeEmitEnrichFailureReport(input2) {
16093
+ const jobs = collectEnrichFailureJobs({
16094
+ config: input2.config,
16095
+ rows: input2.rows,
16096
+ rowStart: input2.rowRange.rowStart
16097
+ });
16098
+ if (jobs.length === 0) {
16099
+ return null;
16100
+ }
16101
+ const reportPath = await persistEnrichFailureReport({
16102
+ jobs,
16103
+ apiUrl: input2.client.baseUrl,
16104
+ outputPath: input2.outputPath,
16105
+ rows: input2.rowRange
16106
+ });
16107
+ process.stderr.write("Execution failed.\n");
16108
+ process.stderr.write("Failure preview (up to 5 failed jobs):\n");
16109
+ process.stderr.write("job_id row_id col_index column status error\n");
16110
+ for (const job of jobs.slice(0, 5)) {
16111
+ process.stderr.write(
16112
+ `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
16113
+ `
16114
+ );
16115
+ }
16116
+ if (jobs.length > 5) {
16117
+ process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
16118
+ `);
16119
+ }
16120
+ if (reportPath) {
16121
+ process.stderr.write(`Full failure report: ${reportPath}
16122
+ `);
16123
+ process.stderr.write(`${formatFailureReportCommand(reportPath)}
16124
+ `);
16125
+ process.stderr.write("Enrichment failed. See failure report above.\n");
16126
+ return { path: reportPath, jobs };
16127
+ }
16128
+ process.stderr.write("Enrichment failed.\n");
16129
+ return { path: "", jobs };
16130
+ }
16131
+ function mergePreferredColumns(preferredColumns, rows) {
16132
+ const columns = [];
16133
+ const seen = /* @__PURE__ */ new Set();
16134
+ for (const column of preferredColumns) {
16135
+ if (!column || seen.has(column)) {
16136
+ continue;
16137
+ }
16138
+ seen.add(column);
16139
+ columns.push(column);
16140
+ }
16141
+ for (const row of rows) {
16142
+ for (const column of Object.keys(row)) {
16143
+ if (seen.has(column)) {
16144
+ continue;
16145
+ }
16146
+ seen.add(column);
16147
+ columns.push(column);
16148
+ }
16149
+ }
16150
+ return columns;
16151
+ }
16152
+ async function fetchBackingRowsForCsvExport(input2) {
16153
+ const runId = extractRunId(input2.status);
16154
+ const playName = extractPlayName2(input2.status);
16155
+ const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
16156
+ if (!runId || !playName || !tableNamespace) {
16157
+ return null;
16158
+ }
16159
+ const sheetRows = [];
16160
+ let offset = 0;
16161
+ let expectedTotal = input2.rowsInfo.totalRows;
16162
+ while (true) {
16163
+ const page = await input2.client.runs.exportDatasetRows({
16164
+ playName,
16165
+ tableNamespace,
16166
+ runId,
16167
+ limit: ENRICH_EXPORT_PAGE_SIZE,
16168
+ offset
16169
+ });
16170
+ sheetRows.push(...page.rows);
16171
+ const summaryTotal = page.summary?.stats?.total;
16172
+ if (typeof summaryTotal === "number" && Number.isFinite(summaryTotal)) {
16173
+ expectedTotal = Math.max(expectedTotal, Math.trunc(summaryTotal));
16174
+ }
16175
+ if (page.rows.length < ENRICH_EXPORT_PAGE_SIZE || sheetRows.length >= expectedTotal) {
16176
+ break;
16177
+ }
16178
+ offset += page.rows.length;
16179
+ }
16180
+ const rows = sheetRows.map(exportableSheetRow2).filter((row) => Boolean(row));
16181
+ if (rows.length < input2.rowsInfo.totalRows) {
16182
+ return null;
16183
+ }
16184
+ return {
16185
+ ...input2.rowsInfo,
16186
+ rows,
16187
+ columns: mergePreferredColumns(
16188
+ input2.rowsInfo.columnsExplicit ? input2.rowsInfo.columns : [],
16189
+ rows
16190
+ ),
16191
+ totalRows: rows.length,
16192
+ complete: true,
16193
+ source: `${input2.rowsInfo.source ?? "result.rows"} -> /api/v2/plays/${playName}/sheet?tableNamespace=${tableNamespace}`
16194
+ };
16195
+ }
16196
+ function mergeRowsForCsvExport(enrichedRows, options) {
16197
+ const rows = dataExportRows(normalizeEnrichRowsForCsvExport(enrichedRows));
16198
+ const range = options?.rows;
16199
+ if (range?.rowStart === null || range?.rowStart === void 0) {
16200
+ return { rows, preferredColumns: [] };
16201
+ }
16202
+ if (!options?.sourceCsvPath) {
16203
+ return { rows, preferredColumns: [] };
16204
+ }
16205
+ const parsedBaseRows = readCsvRows(options.sourceCsvPath);
16206
+ const preferredColumns = Object.keys(parsedBaseRows[0] ?? {});
16207
+ const baseRows = parsedBaseRows.map(
16208
+ (row) => ({ ...row })
16209
+ );
16210
+ const start = Math.max(0, range.rowStart);
16211
+ const maxEnd = Math.max(start, baseRows.length - 1);
16212
+ const inclusiveEnd = range.rowEnd === null ? maxEnd : Math.min(maxEnd, range.rowEnd);
16213
+ const merged = [...baseRows];
16214
+ let selectedIndex = 0;
16215
+ for (let rowIndex = start; rowIndex <= inclusiveEnd; rowIndex += 1) {
16216
+ const enriched = rows[selectedIndex++];
16217
+ if (!enriched) {
16218
+ break;
16219
+ }
16220
+ const base = merged[rowIndex] ?? {};
16221
+ merged[rowIndex] = {
16222
+ ...base,
16223
+ ...enriched
16224
+ };
16225
+ const metadata = mergeLegacyMetadataCell(
16226
+ base._metadata,
16227
+ enriched._metadata
16228
+ );
16229
+ if (metadata !== void 0) {
16230
+ merged[rowIndex]._metadata = metadata;
16231
+ }
16232
+ }
16233
+ return { rows: merged, preferredColumns };
16234
+ }
16235
+ function collectConfigScalarAliasOrder(config) {
16236
+ const aliases = [];
16237
+ const seen = /* @__PURE__ */ new Set();
16238
+ const addAlias = (alias) => {
16239
+ const normalized = normalizeAlias2(alias);
16240
+ if (!normalized || seen.has(normalized)) {
16241
+ return;
16242
+ }
16243
+ seen.add(normalized);
16244
+ aliases.push(alias);
16245
+ };
16246
+ for (const command of config.commands) {
16247
+ if ("with_waterfall" in command) {
16248
+ for (const child of command.commands) {
16249
+ if ("with_waterfall" in child || child.disabled) {
16250
+ continue;
16251
+ }
16252
+ addAlias(child.alias);
16253
+ }
16254
+ continue;
16255
+ }
16256
+ if (!command.disabled) {
16257
+ addAlias(command.alias);
16258
+ }
16259
+ }
16260
+ return aliases;
16261
+ }
16262
+ function orderEnrichCsvColumns(columns, config) {
16263
+ if (!config || columns.length < 2) {
16264
+ return columns;
16265
+ }
16266
+ const aliasOrder = collectConfigScalarAliasOrder(config);
16267
+ if (aliasOrder.length < 2) {
16268
+ return columns;
16269
+ }
16270
+ const normalizedAliasOrder = aliasOrder.map(normalizeAlias2);
16271
+ const requestedColumnByAlias = /* @__PURE__ */ new Map();
16272
+ for (const column of columns) {
16273
+ const normalizedColumn = normalizeAlias2(column);
16274
+ if (normalizedAliasOrder.includes(normalizedColumn) && !requestedColumnByAlias.has(normalizedColumn)) {
16275
+ requestedColumnByAlias.set(normalizedColumn, column);
16276
+ }
16277
+ }
16278
+ const requestedColumns = normalizedAliasOrder.map((alias) => requestedColumnByAlias.get(alias)).filter((column) => Boolean(column));
16279
+ if (requestedColumns.length < 2) {
16280
+ return columns;
16281
+ }
16282
+ const requestedSet = new Set(requestedColumns);
16283
+ const firstRequestedIndex = columns.findIndex(
16284
+ (column) => requestedSet.has(column)
16285
+ );
16286
+ if (firstRequestedIndex < 0) {
16287
+ return columns;
16288
+ }
16289
+ const withoutRequested = columns.filter(
16290
+ (column) => !requestedSet.has(column)
16291
+ );
16292
+ withoutRequested.splice(firstRequestedIndex, 0, ...requestedColumns);
16293
+ return withoutRequested;
16294
+ }
16295
+ function isNonEmptyCsvCell(value) {
16296
+ return value !== null && value !== void 0 && String(value).trim() !== "";
16297
+ }
16298
+ function normalizeEnrichRowsForCsvExport(rows) {
16299
+ return rows.map((row) => {
16300
+ const metadata = legacyMetadataFromRow(row);
16301
+ if (!metadata) {
16302
+ return row;
16303
+ }
16304
+ const normalized = { ...row, _metadata: metadata };
16305
+ delete normalized.metadata;
16306
+ delete normalized["metadata.columns"];
16307
+ delete normalized.metadata__columns;
16308
+ for (const key of Object.keys(normalized)) {
16309
+ if (key.startsWith("metadata.columns.") || key.startsWith("metadata__columns.")) {
16310
+ delete normalized[key];
16311
+ }
16312
+ }
16313
+ return normalized;
16314
+ });
16315
+ }
16316
+ function legacyMetadataFromRow(row) {
16317
+ const direct = parseLegacyMetadataCell(row._metadata);
16318
+ if (direct && isRecord6(direct.columns)) {
16319
+ return direct;
16320
+ }
16321
+ const relocated = parseLegacyMetadataCell(row.metadata);
16322
+ if (relocated && isRecord6(relocated.columns)) {
16323
+ return relocated;
16324
+ }
16325
+ const flattenedColumns = parseLegacyMetadataCell(row["metadata.columns"]);
16326
+ if (flattenedColumns) {
16327
+ return { columns: flattenedColumns };
16328
+ }
16329
+ const flattenedUnderscoreColumns = parseLegacyMetadataCell(
16330
+ row.metadata__columns
16331
+ );
16332
+ if (flattenedUnderscoreColumns) {
16333
+ return { columns: flattenedUnderscoreColumns };
16334
+ }
16335
+ return null;
16336
+ }
16337
+ function parseLegacyMetadataCell(value) {
16338
+ const parsed = parseMaybeJsonObject(value);
16339
+ if (isRecord6(parsed)) {
16340
+ return parsed;
16341
+ }
16342
+ if (typeof value !== "string") {
16343
+ return null;
16344
+ }
16345
+ const trimmed = value.trim();
16346
+ if (!trimmed) {
16347
+ return null;
16348
+ }
16349
+ const candidates = [trimmed];
16350
+ if (trimmed.includes('\\"')) {
16351
+ candidates.push(trimmed.replace(/\\"/g, '"'));
16352
+ }
16353
+ for (const candidate of candidates) {
16354
+ try {
16355
+ const decoded = JSON.parse(candidate);
16356
+ if (isRecord6(decoded)) {
16357
+ return decoded;
16358
+ }
16359
+ if (typeof decoded === "string") {
16360
+ const nested = JSON.parse(decoded);
16361
+ if (isRecord6(nested)) {
16362
+ return nested;
16363
+ }
16364
+ }
16365
+ } catch {
16366
+ }
16367
+ }
16368
+ return null;
14696
16369
  }
14697
- function resolveForceAliases(config, options) {
14698
- const { allAliases, scalarAliases, waterfallGroups } = collectCommandAliases(config);
14699
- if (options.force) {
14700
- return new Set(scalarAliases);
16370
+ function mergeLegacyMetadataRecords(base, enriched) {
16371
+ const baseColumns = isRecord6(base.columns) ? base.columns : null;
16372
+ const enrichedColumns = isRecord6(enriched.columns) ? enriched.columns : null;
16373
+ const merged = {
16374
+ ...base,
16375
+ ...enriched
16376
+ };
16377
+ if (baseColumns || enrichedColumns) {
16378
+ merged.columns = {
16379
+ ...baseColumns ?? {},
16380
+ ...enrichedColumns ?? {}
16381
+ };
14701
16382
  }
14702
- const requested = parseWithForceAliases(options.withForce);
14703
- const unknown = [...requested].filter((alias) => !allAliases.has(alias));
14704
- if (unknown.length > 0) {
14705
- throw new Error(
14706
- `--with-force references unknown --with column alias(es): ${unknown.sort().join(", ")}.`
16383
+ return merged;
16384
+ }
16385
+ function mergeLegacyMetadataCell(base, enriched) {
16386
+ const baseRecord = parseLegacyMetadataCell(base);
16387
+ const enrichedRecord = parseLegacyMetadataCell(enriched);
16388
+ if (baseRecord && enrichedRecord) {
16389
+ return JSON.stringify(
16390
+ mergeLegacyMetadataRecords(baseRecord, enrichedRecord)
14707
16391
  );
14708
16392
  }
14709
- const resolved = /* @__PURE__ */ new Set();
14710
- for (const alias of requested) {
14711
- const children = waterfallGroups.get(alias);
14712
- if (children) {
14713
- for (const child of children) {
14714
- resolved.add(child);
14715
- }
14716
- } else {
14717
- resolved.add(alias);
14718
- }
16393
+ if (enrichedRecord) {
16394
+ return JSON.stringify(enrichedRecord);
14719
16395
  }
14720
- return resolved;
14721
- }
14722
- function parseJsonOutput(stdout) {
14723
- const trimmed = stdout.trim();
14724
- if (!trimmed) return null;
14725
- try {
14726
- return JSON.parse(trimmed);
14727
- } catch {
14728
- const start = trimmed.lastIndexOf("\n{");
14729
- if (start >= 0) {
14730
- return JSON.parse(trimmed.slice(start + 1));
14731
- }
14732
- throw new Error(
14733
- "The generated play completed but did not emit parseable JSON."
14734
- );
16396
+ if (baseRecord) {
16397
+ return JSON.stringify(baseRecord);
14735
16398
  }
14736
- }
14737
- async function captureStdout(run) {
14738
- const originalWrite = process.stdout.write.bind(process.stdout);
14739
- let stdout = "";
14740
- process.stdout.write = ((chunk, ..._args) => {
14741
- stdout += typeof chunk === "string" ? chunk : String(chunk);
14742
- return true;
14743
- });
14744
- try {
14745
- const result = await run();
14746
- return { result, stdout };
14747
- } finally {
14748
- process.stdout.write = originalWrite;
16399
+ if (isNonEmptyCsvCell(enriched)) {
16400
+ return enriched;
14749
16401
  }
14750
- }
14751
- async function writeOutputCsv(outputPath, status) {
14752
- const rowsInfo = extractCanonicalRowsInfo(status);
14753
- if (!rowsInfo) {
14754
- throw new Error("The generated play did not return row-shaped output.");
16402
+ if (isNonEmptyCsvCell(base)) {
16403
+ return base;
14755
16404
  }
14756
- assertCompleteRowsForCsvExport(rowsInfo, status, outputPath);
14757
- const rows = dataExportRows(rowsInfo.rows);
14758
- const columns = dataExportColumns(rows, rowsInfo.columns);
14759
- await writeFile4(
14760
- resolve11(outputPath),
14761
- csvStringFromRows(rows, columns),
14762
- "utf8"
14763
- );
14764
- return { rows: rows.length, path: resolve11(outputPath) };
16405
+ return void 0;
14765
16406
  }
14766
16407
  function assertCompleteRowsForCsvExport(rowsInfo, status, outputPath) {
14767
16408
  if (rowsInfo.complete) {
@@ -14782,13 +16423,26 @@ async function compileConfig(input2) {
14782
16423
  );
14783
16424
  }
14784
16425
  const config = await readConfig(input2.options.config);
14785
- return (await input2.client.compileEnrichPlan({ config })).config;
16426
+ return (await input2.client.compileEnrichPlan({
16427
+ config,
16428
+ native_play_materialization: "inline_prebuilt"
16429
+ })).config;
14786
16430
  }
14787
16431
  const planArgs = await buildPlanArgs(input2.args);
14788
- return (await input2.client.compileEnrichPlan({ plan_args: planArgs })).config;
16432
+ return (await input2.client.compileEnrichPlan({
16433
+ plan_args: planArgs,
16434
+ native_play_materialization: "inline_prebuilt"
16435
+ })).config;
14789
16436
  }
14790
16437
  function registerEnrichCommand(program) {
14791
- 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(
16438
+ program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").configureHelp({
16439
+ optionTerm(option) {
16440
+ return option.long === "--timeout" ? "--timeout SECONDS" : option.flags;
16441
+ }
16442
+ }).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(
16443
+ "--name <name>",
16444
+ "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."
16445
+ ).option(
14792
16446
  "--with <json>",
14793
16447
  "Add a scalar enrich command.",
14794
16448
  (value, previous = []) => [...previous, value]
@@ -14801,70 +16455,77 @@ function registerEnrichCommand(program) {
14801
16455
  "Minimum list results for the current waterfall."
14802
16456
  ).option("--end-waterfall", "End the current waterfall group.").option(
14803
16457
  "--rows <range>",
14804
- "Zero-based row number or end-exclusive range, e.g. 0:10."
16458
+ "Zero-based row number or inclusive range, e.g. 0:10."
14805
16459
  ).option("--all", "Run all rows.").option(
14806
16460
  "--dry-run",
14807
16461
  "Compile and print the generated plan without starting a run."
14808
- ).option("--json", "Emit JSON.").option("--force", "Force rerun for all enrich aliases.").option(
16462
+ ).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(
14809
16463
  "--with-force <aliases>",
14810
16464
  "Force rerun for selected aliases.",
14811
16465
  (value, previous = []) => [...previous, value]
14812
- ).option(
14813
- "--in-place",
14814
- "Not supported by the V2 enrich compatibility runner yet."
16466
+ ).option("--in-place", "Write enriched output back to the input CSV.").option(
16467
+ "--timeout <SECONDS>",
16468
+ "API read timeout for enrich status/API requests."
14815
16469
  ).action(async (options, _command) => {
14816
- if (options.inPlace) {
14817
- throw new Error(
14818
- "--in-place is not supported by this V2 enrich runner yet. Use --output instead."
14819
- );
16470
+ if (options.inPlace && options.dryRun) {
16471
+ throw new Error("--in-place is not supported with --dry-run.");
14820
16472
  }
14821
16473
  const inputCsv = options.input ?? options.csv;
14822
16474
  if (!inputCsv) {
14823
16475
  throw new Error("Missing required --input <csv> (or --csv <csv>).");
14824
16476
  }
14825
16477
  await assertInputCsvExists(inputCsv);
14826
- if (options.output) {
16478
+ if (options.output && !options.inPlace) {
14827
16479
  await assertSafeOutputPath(inputCsv, options.output);
14828
16480
  }
14829
16481
  if (!options.config && !hasPlanShapingArgs(currentEnrichArgs())) {
14830
16482
  throw new Error("Pass --config or at least one --with enrich spec.");
14831
16483
  }
14832
- const client = new DeeplineClient();
16484
+ const client2 = new DeeplineClient();
14833
16485
  const args = currentEnrichArgs();
14834
- const config = await compileConfig({ client, args, options });
14835
- const forceAliases = resolveForceAliases(config, options);
14836
- const playSource = compileEnrichConfigToPlaySource(config, {
14837
- forceAliases
14838
- });
14839
- const summary = summarizePlan(config, playSource);
14840
- printDeprecationNotice(options);
16486
+ const config = await compileConfig({ client: client2, args, options });
16487
+ emitEnrichDebugValidationLines(config);
14841
16488
  if (options.dryRun) {
16489
+ const forceAliases2 = resolveForceAliases(config, options);
16490
+ const playSource2 = compileEnrichConfigToPlaySource(config, {
16491
+ forceAliases: forceAliases2,
16492
+ playName: options.name
16493
+ });
16494
+ const summary = summarizePlan(config, playSource2);
14842
16495
  if (options.json) {
14843
16496
  printJson({
14844
16497
  dryRun: true,
14845
- deprecation: ENRICH_DEPRECATION_NOTICE,
14846
16498
  input: resolve11(inputCsv),
14847
16499
  output: options.output ? resolve11(options.output) : null,
14848
16500
  plan: summary
14849
16501
  });
14850
16502
  return;
14851
16503
  }
14852
- process.stdout.write(`${playSource}
16504
+ process.stdout.write(`${playSource2}
14853
16505
  `);
14854
16506
  return;
14855
16507
  }
14856
16508
  const rows = parseRows(options.rows, options.all);
14857
- if (options.output && options.rows) {
14858
- throw new Error(
14859
- "CSV export with --rows is not supported yet because it would write only the selected rows. Run without --rows or omit --output."
14860
- );
16509
+ const outputPath = options.inPlace ? inputCsv : options.output;
16510
+ const sourceCsvPath = !options.inPlace && outputPath && await regularFileExists(outputPath) ? outputPath : inputCsv;
16511
+ const forceAliases = resolveForceAliases(config, options);
16512
+ for (const alias of collectFailedInputAliases(
16513
+ config,
16514
+ sourceCsvPath,
16515
+ rows
16516
+ )) {
16517
+ forceAliases.add(alias);
14861
16518
  }
16519
+ const playSource = compileEnrichConfigToPlaySource(config, {
16520
+ forceAliases,
16521
+ playName: options.name
16522
+ });
14862
16523
  const tempDir = await mkdtemp(join8(tmpdir3(), "deepline-enrich-play-"));
14863
16524
  const tempPlay = join8(tempDir, "deepline-enrich.play.ts");
14864
16525
  try {
14865
16526
  await writeFile4(tempPlay, playSource, "utf8");
14866
16527
  const runtimeInput = {
14867
- file: resolve11(inputCsv),
16528
+ file: resolve11(sourceCsvPath),
14868
16529
  ...rows.rowStart !== null ? { rowStart: rows.rowStart } : {},
14869
16530
  ...rows.rowEnd !== null ? { rowEnd: rows.rowEnd } : {}
14870
16531
  };
@@ -14873,40 +16534,91 @@ function registerEnrichCommand(program) {
14873
16534
  tempPlay,
14874
16535
  "--input",
14875
16536
  JSON.stringify(runtimeInput),
14876
- "--watch",
14877
- "--no-open",
14878
- "--json"
16537
+ "--watch"
14879
16538
  ];
14880
- const captured = await captureStdout(() => handlePlayRun(runArgs));
14881
- const status = parseJsonOutput(captured.stdout);
16539
+ if (options.noOpen) {
16540
+ runArgs.push("--no-open");
16541
+ }
16542
+ if (options.json) {
16543
+ runArgs.push("--json");
16544
+ } else {
16545
+ runArgs.push("--logs");
16546
+ }
16547
+ const timeoutSeconds = options.timeout ? Number.parseInt(options.timeout, 10) : null;
16548
+ if (timeoutSeconds !== null && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
16549
+ runArgs.push("--tail-timeout-ms", String(timeoutSeconds * 1e3));
16550
+ }
16551
+ const captured = await runGeneratedEnrichPlay(runArgs, {
16552
+ passthroughStdout: !options.json
16553
+ });
16554
+ const status = options.json ? parseJsonOutput(captured.stdout) : await resolveWatchedGeneratedPlayStatus({
16555
+ client: client2,
16556
+ stdout: captured.stdout,
16557
+ exitCode: captured.result
16558
+ });
14882
16559
  if (captured.result !== 0) {
14883
16560
  if (options.json) {
14884
16561
  printJson({
14885
- deprecation: ENRICH_DEPRECATION_NOTICE,
14886
16562
  result: status
14887
16563
  });
14888
- } else {
14889
- process.stdout.write(captured.stdout);
14890
16564
  }
14891
16565
  process.exitCode = captured.result;
14892
16566
  return;
14893
16567
  }
14894
- const exportResult = options.output ? await writeOutputCsv(options.output, status) : null;
16568
+ const exportResult = outputPath ? await writeOutputCsv(outputPath, status, {
16569
+ client: client2,
16570
+ config,
16571
+ sourceCsvPath,
16572
+ rows
16573
+ }) : null;
16574
+ const rowsForFailureReport = exportResult?.enrichedRows ?? extractCanonicalRowsInfo(status)?.rows ?? [];
14895
16575
  if (options.json) {
16576
+ const failureReport2 = await maybeEmitEnrichFailureReport({
16577
+ config,
16578
+ rows: rowsForFailureReport,
16579
+ rowRange: rows,
16580
+ client: client2,
16581
+ outputPath: exportResult?.path ?? outputPath ?? null
16582
+ });
14896
16583
  printJson({
14897
- ok: true,
14898
- deprecation: ENRICH_DEPRECATION_NOTICE,
16584
+ ok: !failureReport2,
14899
16585
  run: status,
14900
- output: exportResult
16586
+ output: exportResult ? { rows: exportResult.rows, path: exportResult.path } : null,
16587
+ ...failureReport2 ? {
16588
+ failure_report: {
16589
+ path: failureReport2.path,
16590
+ jobs: failureReport2.jobs.length
16591
+ }
16592
+ } : {}
14901
16593
  });
16594
+ if (failureReport2) {
16595
+ process.exitCode = EXIT_SERVER2;
16596
+ }
14902
16597
  return;
14903
16598
  }
14904
- process.stdout.write(captured.stdout);
14905
16599
  if (exportResult) {
14906
16600
  process.stderr.write(
14907
16601
  `Wrote ${exportResult.rows} row(s) to ${exportResult.path}
14908
16602
  `
14909
16603
  );
16604
+ const waterfallSummaryLines = buildEnrichWaterfallSummaryLines(
16605
+ config,
16606
+ exportResult.enrichedRows
16607
+ );
16608
+ if (waterfallSummaryLines.length > 0) {
16609
+ process.stdout.write(`${waterfallSummaryLines.join("\n")}
16610
+ `);
16611
+ }
16612
+ }
16613
+ const failureReport = await maybeEmitEnrichFailureReport({
16614
+ config,
16615
+ rows: rowsForFailureReport,
16616
+ rowRange: rows,
16617
+ client: client2,
16618
+ outputPath: exportResult?.path ?? outputPath ?? null
16619
+ });
16620
+ if (failureReport) {
16621
+ process.exitCode = EXIT_SERVER2;
14910
16622
  }
14911
16623
  } finally {
14912
16624
  await rm(tempDir, { recursive: true, force: true });
@@ -15146,7 +16858,7 @@ async function readHiddenLine(prompt) {
15146
16858
  if (typeof input.setRawMode === "function") input.setRawMode(true);
15147
16859
  let value = "";
15148
16860
  input.resume();
15149
- return await new Promise((resolve14, reject) => {
16861
+ return await new Promise((resolve15, reject) => {
15150
16862
  let settled = false;
15151
16863
  const cleanup = () => {
15152
16864
  input.off("data", onData);
@@ -15161,7 +16873,7 @@ async function readHiddenLine(prompt) {
15161
16873
  settled = true;
15162
16874
  output.write("\n");
15163
16875
  cleanup();
15164
- resolve14(line);
16876
+ resolve15(line);
15165
16877
  };
15166
16878
  const fail = (error) => {
15167
16879
  if (settled) return;
@@ -15225,8 +16937,8 @@ function preventShellHistoryLeak(forbidden) {
15225
16937
  }
15226
16938
  }
15227
16939
  async function handleList(options) {
15228
- const client = new DeeplineClient();
15229
- const secrets = await client.listSecrets();
16940
+ const client2 = new DeeplineClient();
16941
+ const secrets = await client2.listSecrets();
15230
16942
  printCommandEnvelope(
15231
16943
  {
15232
16944
  secrets,
@@ -15245,8 +16957,8 @@ async function handleList(options) {
15245
16957
  }
15246
16958
  async function handleCheck(nameInput, options) {
15247
16959
  const name = normalizeSecretName(nameInput);
15248
- const client = new DeeplineClient();
15249
- const secret = await client.checkSecret(name);
16960
+ const client2 = new DeeplineClient();
16961
+ const secret = await client2.checkSecret(name);
15250
16962
  printCommandEnvelope(
15251
16963
  {
15252
16964
  ok: Boolean(secret),
@@ -15560,12 +17272,12 @@ function matchesGrepQuery(value, query, mode) {
15560
17272
  return terms.every((term) => haystack.includes(term));
15561
17273
  }
15562
17274
  async function listTools(args) {
15563
- const client = new DeeplineClient();
17275
+ const client2 = new DeeplineClient();
15564
17276
  const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
15565
17277
  const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
15566
17278
  const compact = !args.includes("--full");
15567
17279
  const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
15568
- const items = (await client.listTools({
17280
+ const items = (await client2.listTools({
15569
17281
  ...categoryFilter ? { categories: categoryFilter } : {},
15570
17282
  compact
15571
17283
  })).map(toListedTool).filter(
@@ -15612,8 +17324,8 @@ async function searchTools(queryInput, options = {}) {
15612
17324
  console.error("Usage: deepline tools search <query> [--json]");
15613
17325
  return 1;
15614
17326
  }
15615
- const client = new DeeplineClient();
15616
- const result = await client.searchTools({
17327
+ const client2 = new DeeplineClient();
17328
+ const result = await client2.searchTools({
15617
17329
  query,
15618
17330
  categories: options.categories,
15619
17331
  searchTerms: options.searchTerms,
@@ -15636,10 +17348,10 @@ async function grepTools(queryInput, options = {}) {
15636
17348
  console.error("Usage: deepline tools grep <query> [--json]");
15637
17349
  return 1;
15638
17350
  }
15639
- const client = new DeeplineClient();
17351
+ const client2 = new DeeplineClient();
15640
17352
  const requestedCategories = options.categories ? options.categories.split(",").map((item) => item.trim()).filter(Boolean) : [];
15641
17353
  const mode = options.mode ?? "all";
15642
- const tools = (await client.listTools({
17354
+ const tools = (await client2.listTools({
15643
17355
  grep: query,
15644
17356
  grepMode: mode,
15645
17357
  ...options.categories ? { categories: options.categories } : {},
@@ -15709,12 +17421,12 @@ function compactTool(tool) {
15709
17421
  function playIdentifiers(play) {
15710
17422
  return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
15711
17423
  }
15712
- async function findPlayForToolId(client, toolId) {
17424
+ async function findPlayForToolId(client2, toolId) {
15713
17425
  const requested = toolId.trim();
15714
17426
  if (!requested) {
15715
17427
  return null;
15716
17428
  }
15717
- const plays = await client.searchPlays({ query: requested, compact: true });
17429
+ const plays = await client2.searchPlays({ query: requested, compact: true });
15718
17430
  return plays.find((play) => playIdentifiers(play).includes(requested)) ?? null;
15719
17431
  }
15720
17432
  function playAliasToolErrorMessage(toolId, play) {
@@ -15729,7 +17441,7 @@ function printPlayAliasToolError(toolId, play) {
15729
17441
  function isPlayLikeTool(tool) {
15730
17442
  const record = tool;
15731
17443
  if (record.isPlay === true || record.is_play === true) return true;
15732
- const playExpansion = recordField(record, "playExpansion", "play_expansion");
17444
+ const playExpansion = recordField2(record, "playExpansion", "play_expansion");
15733
17445
  if (Object.keys(playExpansion).length > 0) return true;
15734
17446
  const toolId = typeof record.toolId === "string" ? record.toolId : "";
15735
17447
  return toolId.endsWith("_waterfall");
@@ -15963,12 +17675,12 @@ async function getTool(toolId, options = {}) {
15963
17675
  console.error("Usage: deepline tools get <toolId> [--json]");
15964
17676
  return 1;
15965
17677
  }
15966
- const client = new DeeplineClient();
17678
+ const client2 = new DeeplineClient();
15967
17679
  let tool;
15968
17680
  try {
15969
- tool = await client.getTool(toolId);
17681
+ tool = await client2.getTool(toolId);
15970
17682
  } catch (error) {
15971
- const play = await findPlayForToolId(client, toolId);
17683
+ const play = await findPlayForToolId(client2, toolId);
15972
17684
  if (play) {
15973
17685
  printPlayAliasToolError(toolId, play);
15974
17686
  return 2;
@@ -16035,10 +17747,10 @@ async function getTool(toolId, options = {}) {
16035
17747
  function toolContractJsonForDescribe(tool, requestedToolId) {
16036
17748
  const toolId = String(tool.toolId || requestedToolId);
16037
17749
  const inputFields = toolInputFieldsForDisplay(
16038
- recordField(tool, "inputSchema", "input_schema")
17750
+ recordField2(tool, "inputSchema", "input_schema")
16039
17751
  );
16040
- const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
16041
- const toolExecutionResult = recordField(
17752
+ const usageGuidance = recordField2(tool, "usageGuidance", "usage_guidance");
17753
+ const toolExecutionResult = recordField2(
16042
17754
  usageGuidance,
16043
17755
  "toolExecutionResult",
16044
17756
  "tool_execution_result"
@@ -16049,7 +17761,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
16049
17761
  const extractedValues = extractionContractEntries(
16050
17762
  arrayField(toolExecutionResult, "extractedValues", "extracted_values")
16051
17763
  );
16052
- const cost = recordField(tool, "cost");
17764
+ const cost = recordField2(tool, "cost");
16053
17765
  const deeplineCredits = numberField(
16054
17766
  tool,
16055
17767
  "deeplineCreditsPerPricingUnit",
@@ -16103,7 +17815,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
16103
17815
  }
16104
17816
  function extractionContractEntries(entries) {
16105
17817
  return entries.flatMap((entry) => {
16106
- if (!isRecord6(entry)) return [];
17818
+ if (!isRecord7(entry)) return [];
16107
17819
  const name = stringField(entry, "name");
16108
17820
  const expression = stringField(entry, "expression");
16109
17821
  return name && expression ? [{ name, expression }] : [];
@@ -16111,8 +17823,8 @@ function extractionContractEntries(entries) {
16111
17823
  }
16112
17824
  function printCompactToolContract(tool, requestedToolId) {
16113
17825
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16114
- const cost = isRecord6(contract.cost) ? contract.cost : {};
16115
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17826
+ const cost = isRecord7(contract.cost) ? contract.cost : {};
17827
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16116
17828
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16117
17829
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16118
17830
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
@@ -16129,7 +17841,7 @@ function printCompactToolContract(tool, requestedToolId) {
16129
17841
  console.log("");
16130
17842
  console.log("Inputs:");
16131
17843
  for (const field of inputFields) {
16132
- if (!isRecord6(field)) continue;
17844
+ if (!isRecord7(field)) continue;
16133
17845
  const name = stringField(field, "name");
16134
17846
  if (!name) continue;
16135
17847
  const required = field.required ? "*" : "";
@@ -16142,7 +17854,7 @@ function printCompactToolContract(tool, requestedToolId) {
16142
17854
  }
16143
17855
  console.log("");
16144
17856
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
16145
- const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
17857
+ const starterScript = isRecord7(contract.starterScript) ? contract.starterScript : {};
16146
17858
  const starterPath = stringField(starterScript, "path");
16147
17859
  if (starterPath) {
16148
17860
  console.log("");
@@ -16156,14 +17868,14 @@ function printCompactToolContract(tool, requestedToolId) {
16156
17868
  console.log("Getters:");
16157
17869
  if (listGetters.length) console.log("Lists:");
16158
17870
  for (const entry of listGetters) {
16159
- if (isRecord6(entry))
17871
+ if (isRecord7(entry))
16160
17872
  console.log(
16161
17873
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16162
17874
  );
16163
17875
  }
16164
17876
  if (valueGetters.length) console.log("Values:");
16165
17877
  for (const entry of valueGetters) {
16166
- if (isRecord6(entry))
17878
+ if (isRecord7(entry))
16167
17879
  console.log(
16168
17880
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16169
17881
  );
@@ -16176,7 +17888,7 @@ function printCompactToolContract(tool, requestedToolId) {
16176
17888
  }
16177
17889
  function printToolPricingOnly(tool, requestedToolId, options = {}) {
16178
17890
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16179
- const cost = isRecord6(contract.cost) ? contract.cost : {};
17891
+ const cost = isRecord7(contract.cost) ? contract.cost : {};
16180
17892
  const pricingModel = stringField(cost, "pricingModel") || "unknown";
16181
17893
  const billingMode = stringField(cost, "billingMode") || "unknown";
16182
17894
  const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
@@ -16196,7 +17908,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
16196
17908
  }
16197
17909
  console.log("Inputs:");
16198
17910
  for (const field of inputFields) {
16199
- if (!isRecord6(field)) continue;
17911
+ if (!isRecord7(field)) continue;
16200
17912
  const name = stringField(field, "name");
16201
17913
  if (!name) continue;
16202
17914
  const required = field.required ? "*" : "";
@@ -16222,27 +17934,27 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
16222
17934
  ` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
16223
17935
  );
16224
17936
  console.log("});");
16225
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17937
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16226
17938
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16227
17939
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16228
- const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
17940
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord7);
16229
17941
  if (firstGetter) {
16230
17942
  const name = stringField(firstGetter, "name") || "value";
16231
17943
  const expression = stringField(firstGetter, "expression");
16232
17944
  if (expression)
16233
17945
  console.log(
16234
- `const ${safeIdentifier3(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
17946
+ `const ${safeIdentifier2(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
16235
17947
  );
16236
17948
  }
16237
17949
  console.log("```");
16238
17950
  if (options.includeSamples !== false) {
16239
- const samples = recordField(tool, "samples");
17951
+ const samples = recordField2(tool, "samples");
16240
17952
  printSamples(samples);
16241
17953
  }
16242
17954
  }
16243
17955
  function printToolGettersOnly(tool, requestedToolId) {
16244
17956
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
16245
- const getters = isRecord6(contract.getters) ? contract.getters : {};
17957
+ const getters = isRecord7(contract.getters) ? contract.getters : {};
16246
17958
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
16247
17959
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
16248
17960
  console.log(`Getters: ${contract.toolId}`);
@@ -16255,7 +17967,7 @@ function printToolGettersOnly(tool, requestedToolId) {
16255
17967
  if (listGetters.length) {
16256
17968
  console.log("Lists:");
16257
17969
  for (const entry of listGetters) {
16258
- if (isRecord6(entry))
17970
+ if (isRecord7(entry))
16259
17971
  console.log(
16260
17972
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16261
17973
  );
@@ -16264,7 +17976,7 @@ function printToolGettersOnly(tool, requestedToolId) {
16264
17976
  if (valueGetters.length) {
16265
17977
  console.log("Values:");
16266
17978
  for (const entry of valueGetters) {
16267
- if (isRecord6(entry))
17979
+ if (isRecord7(entry))
16268
17980
  console.log(
16269
17981
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
16270
17982
  );
@@ -16290,7 +18002,7 @@ function sampleValueForField(field) {
16290
18002
  function samplePayloadForInputFields(fields) {
16291
18003
  return Object.fromEntries(
16292
18004
  fields.slice(0, 4).flatMap((field) => {
16293
- if (!isRecord6(field)) return [];
18005
+ if (!isRecord7(field)) return [];
16294
18006
  const name = stringField(field, "name");
16295
18007
  if (!name) return [];
16296
18008
  return [[name, sampleValueForField(field)]];
@@ -16300,7 +18012,7 @@ function samplePayloadForInputFields(fields) {
16300
18012
  function stableStepIdForTool(toolId) {
16301
18013
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
16302
18014
  }
16303
- function safeIdentifier3(name) {
18015
+ function safeIdentifier2(name) {
16304
18016
  const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
16305
18017
  return cleaned || "value";
16306
18018
  }
@@ -16313,7 +18025,7 @@ function playResultExpression(entry) {
16313
18025
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
16314
18026
  const toolId = String(tool.toolId || requestedToolId);
16315
18027
  const inputFields = toolInputFieldsForDisplay(
16316
- recordField(tool, "inputSchema", "input_schema")
18028
+ recordField2(tool, "inputSchema", "input_schema")
16317
18029
  );
16318
18030
  const starterScript = seedToolListScript({
16319
18031
  toolId,
@@ -16340,7 +18052,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
16340
18052
  provider: tool.provider,
16341
18053
  displayName: tool.displayName,
16342
18054
  usageGuidance: usageGuidanceWithAccessDefaults(
16343
- recordField(tool, "usageGuidance", "usage_guidance")
18055
+ recordField2(tool, "usageGuidance", "usage_guidance")
16344
18056
  ),
16345
18057
  runtimeOutputHelp: {
16346
18058
  contract: "tools describe shows declared schema and Deepline getters; it is not an observed provider response.",
@@ -16365,7 +18077,7 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
16365
18077
  }
16366
18078
  function usageGuidanceWithAccessDefaults(usageGuidance) {
16367
18079
  if (Object.keys(usageGuidance).length === 0) return usageGuidance;
16368
- const existingAccess = recordField(usageGuidance, "access");
18080
+ const existingAccess = recordField2(usageGuidance, "access");
16369
18081
  return {
16370
18082
  ...usageGuidance,
16371
18083
  access: {
@@ -16398,18 +18110,18 @@ function listedToolDescription(tool) {
16398
18110
  }
16399
18111
  function formatListedToolCost(tool) {
16400
18112
  const record = tool;
16401
- const pricing = recordField(record, "pricing");
18113
+ const pricing = recordField2(record, "pricing");
16402
18114
  const displayText = stringField(pricing, "displayText", "display_text");
16403
18115
  return displayText ? `Cost: ${displayText}` : "";
16404
18116
  }
16405
18117
  function toolInputFieldsForDisplay(inputSchema) {
16406
18118
  if (Array.isArray(inputSchema.fields))
16407
- return inputSchema.fields.filter(isRecord6);
16408
- const jsonSchema = isRecord6(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
16409
- const properties = isRecord6(jsonSchema.properties) ? jsonSchema.properties : {};
18119
+ return inputSchema.fields.filter(isRecord7);
18120
+ const jsonSchema = isRecord7(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
18121
+ const properties = isRecord7(jsonSchema.properties) ? jsonSchema.properties : {};
16410
18122
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
16411
18123
  return Object.entries(properties).map(([name, value]) => {
16412
- const property = isRecord6(value) ? value : {};
18124
+ const property = isRecord7(value) ? value : {};
16413
18125
  return {
16414
18126
  name,
16415
18127
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -16438,15 +18150,15 @@ function printJsonPreview(label, payload) {
16438
18150
  }
16439
18151
  function samplePayload(samples, key) {
16440
18152
  const entry = samples[key];
16441
- if (!isRecord6(entry)) return void 0;
18153
+ if (!isRecord7(entry)) return void 0;
16442
18154
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
16443
18155
  }
16444
18156
  function commandEnvelopeFromRawResponse(rawResponse) {
16445
- return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
18157
+ return isRecord7(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
16446
18158
  }
16447
18159
  function listExtractorPathsFromUsageGuidance(tool) {
16448
18160
  const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
16449
- const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord6(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
18161
+ const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord7(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
16450
18162
  return extractedLists.flatMap((entry) => {
16451
18163
  const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
16452
18164
  if (!Array.isArray(paths)) return [];
@@ -16462,7 +18174,7 @@ function formatDecimal(value) {
16462
18174
  function formatUsd(value) {
16463
18175
  return `$${formatDecimal(value)}`;
16464
18176
  }
16465
- function isRecord6(value) {
18177
+ function isRecord7(value) {
16466
18178
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
16467
18179
  }
16468
18180
  function stringField(source, ...keys) {
@@ -16486,10 +18198,10 @@ function arrayField(source, ...keys) {
16486
18198
  }
16487
18199
  return [];
16488
18200
  }
16489
- function recordField(source, ...keys) {
18201
+ function recordField2(source, ...keys) {
16490
18202
  for (const key of keys) {
16491
18203
  const value = source[key];
16492
- if (isRecord6(value)) return value;
18204
+ if (isRecord7(value)) return value;
16493
18205
  }
16494
18206
  return {};
16495
18207
  }
@@ -16555,7 +18267,7 @@ function parseJsonObjectArgument(raw, flagName) {
16555
18267
  }
16556
18268
  throw invalidJsonError(flagName, message);
16557
18269
  }
16558
- if (!isRecord6(parsed)) {
18270
+ if (!isRecord7(parsed)) {
16559
18271
  throw invalidJsonError(flagName, "expected an object.");
16560
18272
  }
16561
18273
  return parsed;
@@ -16678,7 +18390,7 @@ function buildToolExecuteBaseEnvelope(input2) {
16678
18390
  kind: summaryEntries.length > 0 ? "object" : "raw",
16679
18391
  summary: input2.summary
16680
18392
  };
16681
- const envelopeHasCanonicalOutput = isRecord6(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
18393
+ const envelopeHasCanonicalOutput = isRecord7(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
16682
18394
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
16683
18395
  const actions = input2.listConversion ? [
16684
18396
  {
@@ -16733,12 +18445,12 @@ async function executeTool(args) {
16733
18445
  }
16734
18446
  return 1;
16735
18447
  }
16736
- const client = new DeeplineClient();
18448
+ const client2 = new DeeplineClient();
16737
18449
  let metadata;
16738
18450
  try {
16739
- metadata = await client.getTool(parsed.toolId);
18451
+ metadata = await client2.getTool(parsed.toolId);
16740
18452
  } catch (error) {
16741
- const play = await findPlayForToolId(client, parsed.toolId);
18453
+ const play = await findPlayForToolId(client2, parsed.toolId);
16742
18454
  if (play) {
16743
18455
  if (argsWantJson(args)) {
16744
18456
  printJsonError(
@@ -16762,7 +18474,7 @@ async function executeTool(args) {
16762
18474
  }
16763
18475
  return 2;
16764
18476
  }
16765
- const rawResponse = await client.executeTool(parsed.toolId, parsed.params);
18477
+ const rawResponse = await client2.executeTool(parsed.toolId, parsed.params);
16766
18478
  const listConversion = tryConvertToList(rawResponse, {
16767
18479
  listExtractorPaths: listExtractorPathsFromUsageGuidance(metadata)
16768
18480
  });
@@ -16787,7 +18499,7 @@ async function executeTool(args) {
16787
18499
  {
16788
18500
  ...baseEnvelope,
16789
18501
  local: {
16790
- ...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
18502
+ ...isRecord7(baseEnvelope.local) ? baseEnvelope.local : {},
16791
18503
  payload_file: jsonPath
16792
18504
  }
16793
18505
  },
@@ -16908,10 +18620,499 @@ async function executeTool(args) {
16908
18620
  return 0;
16909
18621
  }
16910
18622
 
18623
+ // src/cli/commands/workflow.ts
18624
+ import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
18625
+ import { dirname as dirname9, join as join11, resolve as resolve13 } from "path";
18626
+
18627
+ // src/cli/workflow-to-play.ts
18628
+ import { createHash as createHash4 } from "crypto";
18629
+ var HITL_WAIT_FOR_SIGNAL_TOOL = "deepline_workflow_wait_for_signal";
18630
+ var HITL_SLACK_TOOL = "slack_message_with_hitl";
18631
+ var SUB_WORKFLOW_TOOL_PREFIX = "deepline_workflow_";
18632
+ var UNSUPPORTED_REASON_HINT = {
18633
+ 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.",
18634
+ hitl: "human-in-the-loop / wait-for-signal steps have no play-runtime equivalent yet.",
18635
+ sub_workflow: "sub-workflow calls have no play-runtime equivalent yet (a future ctx.runPlay bridge).",
18636
+ nested_waterfall: "nested with_waterfall blocks are not supported by the play compiler.",
18637
+ 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.'
18638
+ };
18639
+ var UnsupportedWorkflowCommandError = class extends Error {
18640
+ status = 400;
18641
+ unsupported;
18642
+ constructor(unsupported) {
18643
+ super(
18644
+ `Workflow uses ${unsupported.length} command${unsupported.length === 1 ? "" : "s"} that cannot run as a play yet: ${unsupported.map((entry) => `${entry.alias} (${entry.reason})`).join(", ")}.`
18645
+ );
18646
+ this.name = "UnsupportedWorkflowCommandError";
18647
+ this.unsupported = unsupported;
18648
+ }
18649
+ };
18650
+ function isWaterfall2(command) {
18651
+ return "with_waterfall" in command;
18652
+ }
18653
+ function stepInlinedText(command) {
18654
+ return [
18655
+ JSON.stringify(command.payload ?? {}),
18656
+ command.extract_js ?? "",
18657
+ command.run_if_js ?? ""
18658
+ ].join("\n");
18659
+ }
18660
+ function stepUserCode(command) {
18661
+ const code = [];
18662
+ if (command.extract_js) code.push(command.extract_js);
18663
+ if (command.run_if_js) code.push(command.run_if_js);
18664
+ if (command.tool === "run_javascript" && typeof command.payload?.code === "string") {
18665
+ code.push(command.payload.code);
18666
+ }
18667
+ return code;
18668
+ }
18669
+ function hasUnsafeUserCode(command) {
18670
+ return stepUserCode(command).some((code) => {
18671
+ try {
18672
+ assertUserCodeIsSafe(code, "play step code");
18673
+ return false;
18674
+ } catch {
18675
+ return true;
18676
+ }
18677
+ });
18678
+ }
18679
+ function classifyUnsupportedCommand(command) {
18680
+ const { tool } = command;
18681
+ if (tool === HITL_SLACK_TOOL || tool === HITL_WAIT_FOR_SIGNAL_TOOL) {
18682
+ return "hitl";
18683
+ }
18684
+ if (tool.startsWith(SUB_WORKFLOW_TOOL_PREFIX)) return "sub_workflow";
18685
+ if (collectInlineSecretFindings(stepInlinedText(command)).length > 0) {
18686
+ return "inline_secret";
18687
+ }
18688
+ if (hasUnsafeUserCode(command)) return "unsafe_user_code";
18689
+ return null;
18690
+ }
18691
+ function collectStepOffenders(command, offenders) {
18692
+ if (command.disabled) return;
18693
+ const reason = classifyUnsupportedCommand(command);
18694
+ if (reason) {
18695
+ offenders.push({ alias: command.alias, tool: command.tool, reason });
18696
+ }
18697
+ }
18698
+ function findUnsupportedWorkflowCommands(config) {
18699
+ const offenders = [];
18700
+ for (const command of config.commands ?? []) {
18701
+ if (!isWaterfall2(command)) {
18702
+ collectStepOffenders(command, offenders);
18703
+ continue;
18704
+ }
18705
+ for (const child of command.commands ?? []) {
18706
+ if (isWaterfall2(child)) {
18707
+ offenders.push({
18708
+ alias: command.with_waterfall,
18709
+ tool: "with_waterfall",
18710
+ reason: "nested_waterfall"
18711
+ });
18712
+ continue;
18713
+ }
18714
+ collectStepOffenders(child, offenders);
18715
+ }
18716
+ }
18717
+ return offenders;
18718
+ }
18719
+ function validateWorkflowConfigForPlay(config) {
18720
+ const offenders = findUnsupportedWorkflowCommands(config);
18721
+ if (offenders.length > 0) {
18722
+ throw new UnsupportedWorkflowCommandError(offenders);
18723
+ }
18724
+ }
18725
+ function describeUnsupportedReason(reason) {
18726
+ return UNSUPPORTED_REASON_HINT[reason];
18727
+ }
18728
+ var MAX_PLAY_NAME_LENGTH = 39;
18729
+ function sanitizePlayNameSegment(value) {
18730
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
18731
+ }
18732
+ function deriveWorkflowPlayName(workflowName) {
18733
+ const base = sanitizePlayNameSegment(workflowName) || "workflow";
18734
+ const suffix = createHash4("sha256").update(workflowName).digest("hex").slice(0, 8);
18735
+ const reserved = suffix.length + 1;
18736
+ const allowedBase = Math.max(1, MAX_PLAY_NAME_LENGTH - reserved);
18737
+ let name = `${base.slice(0, allowedBase)}_${suffix}`;
18738
+ if (name.startsWith("prebuilt")) {
18739
+ name = `wf_${name}`.slice(0, MAX_PLAY_NAME_LENGTH);
18740
+ }
18741
+ return name;
18742
+ }
18743
+ function compileWorkflowConfigToPlay(config, options) {
18744
+ validateWorkflowConfigForPlay(config);
18745
+ const playName = deriveWorkflowPlayName(options.workflowName);
18746
+ const sourceCode = compileEnrichConfigToPlaySource(config, {
18747
+ playName,
18748
+ inlineRunJavascript: true,
18749
+ idiomaticGetters: true
18750
+ });
18751
+ const warnings = [];
18752
+ if (typeof config.cost_cap_usd_per_run === "number") {
18753
+ warnings.push(
18754
+ `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.`
18755
+ );
18756
+ }
18757
+ return { playName, sourceCode, warnings };
18758
+ }
18759
+
18760
+ // src/cli/commands/workflow.ts
18761
+ var PLAY_EQUIVALENT = {
18762
+ list: "deepline plays list",
18763
+ get: "deepline plays get <name>",
18764
+ call: "deepline plays run <name>",
18765
+ apply: "deepline plays publish <file>",
18766
+ lint: "deepline plays check <file>",
18767
+ schema: "deepline plays describe <name>",
18768
+ runs: "deepline runs list --play <name>",
18769
+ run: "deepline runs get <run-id>",
18770
+ tail: "deepline runs tail <run-id>",
18771
+ cancel: "deepline runs stop <run-id>",
18772
+ delete: "deepline plays delete <name>"
18773
+ };
18774
+ var MIGRATE_HINT = "deepline workflows transform <id>";
18775
+ var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
18776
+ function withJsonOption(command) {
18777
+ return command.option("--json", JSON_OPTION_DESCRIPTION);
18778
+ }
18779
+ var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
18780
+ "completed",
18781
+ "succeeded",
18782
+ "success",
18783
+ "failed",
18784
+ "error",
18785
+ "errored",
18786
+ "cancelled",
18787
+ "canceled",
18788
+ "done"
18789
+ ]);
18790
+ function migrationDeprecation(command) {
18791
+ const use = PLAY_EQUIVALENT[command];
18792
+ if (!use) return null;
18793
+ return { use, migrate: MIGRATE_HINT };
18794
+ }
18795
+ function noticeToStderr(command) {
18796
+ const deprecation = migrationDeprecation(command);
18797
+ if (!deprecation) return;
18798
+ process.stderr.write(
18799
+ `note: \`deepline workflows ${command}\` is a legacy surface \u2014 workflows are becoming plays.
18800
+ equivalent: \`${deprecation.use}\`
18801
+ migrate this workflow: \`${MIGRATE_HINT}\`
18802
+ `
18803
+ );
18804
+ }
18805
+ function emitPassThrough(command, payload, options) {
18806
+ const json = shouldEmitJson(options.json);
18807
+ const deprecation = migrationDeprecation(command);
18808
+ if (json) {
18809
+ const enriched = payload && typeof payload === "object" && !Array.isArray(payload) ? { ...payload, deprecation } : { result: payload, deprecation };
18810
+ printJson(enriched);
18811
+ return;
18812
+ }
18813
+ printJson(payload);
18814
+ noticeToStderr(command);
18815
+ }
18816
+ function client() {
18817
+ return new DeeplineClient();
18818
+ }
18819
+ function readStatus(payload) {
18820
+ if (!payload || typeof payload !== "object") return null;
18821
+ const record = payload;
18822
+ if (typeof record.status === "string") return record.status;
18823
+ const run = record.run;
18824
+ if (run && typeof run === "object") {
18825
+ const status = run.status;
18826
+ if (typeof status === "string") return status;
18827
+ }
18828
+ return null;
18829
+ }
18830
+ async function readJsonOption(payload, file) {
18831
+ if (file) {
18832
+ const raw = await readFile4(resolve13(file), "utf8");
18833
+ return JSON.parse(raw);
18834
+ }
18835
+ if (payload) {
18836
+ return JSON.parse(payload);
18837
+ }
18838
+ throw new Error("Provide --payload <json> or --file <path>.");
18839
+ }
18840
+ async function transformOne(api, workflowId, outDir, publish) {
18841
+ const { workflow } = await api.getWorkflow(workflowId);
18842
+ if (!workflow) {
18843
+ return {
18844
+ ok: false,
18845
+ workflowId,
18846
+ workflowName: workflowId,
18847
+ reason: "Workflow not found."
18848
+ };
18849
+ }
18850
+ const revision = workflow.current_published_revision;
18851
+ if (!revision) {
18852
+ return {
18853
+ ok: false,
18854
+ workflowId,
18855
+ workflowName: workflow.name,
18856
+ reason: "Workflow has no published revision to transform."
18857
+ };
18858
+ }
18859
+ try {
18860
+ const compiled = compileWorkflowConfigToPlay(
18861
+ // The API returns the config as `unknown`; it is structurally the
18862
+ // EnrichCompiledConfig the compiler expects. Cast at this single boundary.
18863
+ revision.config,
18864
+ { workflowName: workflow.name, version: revision.version }
18865
+ );
18866
+ const file = join11(resolve13(outDir), `${compiled.playName}.play.ts`);
18867
+ await mkdir5(dirname9(file), { recursive: true });
18868
+ await writeFile5(file, compiled.sourceCode, "utf8");
18869
+ let published = false;
18870
+ if (publish) {
18871
+ const code = await handlePlayPublish([file]);
18872
+ if (code !== 0) {
18873
+ return {
18874
+ ok: false,
18875
+ workflowId,
18876
+ workflowName: workflow.name,
18877
+ reason: `Wrote ${file} but publish failed (exit ${code}).`
18878
+ };
18879
+ }
18880
+ published = true;
18881
+ }
18882
+ return {
18883
+ ok: true,
18884
+ workflowId,
18885
+ workflowName: workflow.name,
18886
+ playName: compiled.playName,
18887
+ file,
18888
+ warnings: compiled.warnings,
18889
+ published
18890
+ };
18891
+ } catch (error) {
18892
+ if (error instanceof UnsupportedWorkflowCommandError) {
18893
+ return {
18894
+ ok: false,
18895
+ workflowId,
18896
+ workflowName: workflow.name,
18897
+ reason: error.unsupported.map((u) => `${u.alias} (${describeUnsupportedReason(u.reason)})`).join("; "),
18898
+ unsupported: error.unsupported
18899
+ };
18900
+ }
18901
+ throw error;
18902
+ }
18903
+ }
18904
+ function renderTransformReport(outcomes) {
18905
+ const lines = [];
18906
+ for (const outcome of outcomes) {
18907
+ if (outcome.ok) {
18908
+ lines.push(
18909
+ `\u2705 ${outcome.workflowName} \u2192 ${outcome.file}` + (outcome.published ? " (published)" : ` (next: deepline plays publish ${outcome.file})`)
18910
+ );
18911
+ for (const warning of outcome.warnings) {
18912
+ lines.push(` \u26A0\uFE0F ${warning}`);
18913
+ }
18914
+ } else {
18915
+ lines.push(`\u26A0\uFE0F ${outcome.workflowName} \u2014 skipped: ${outcome.reason}`);
18916
+ }
18917
+ }
18918
+ const ok = outcomes.filter((o) => o.ok).length;
18919
+ const total = outcomes.length;
18920
+ const manual = total - ok;
18921
+ lines.push("");
18922
+ lines.push(
18923
+ `${ok}/${total} transformed` + (manual > 0 ? `, ${manual} need manual attention` : "")
18924
+ );
18925
+ return `${lines.join("\n")}
18926
+ `;
18927
+ }
18928
+ async function handleTransform(id, options) {
18929
+ const api = client();
18930
+ const outDir = options.out ?? "./plays";
18931
+ const publish = options.publish === true;
18932
+ let ids;
18933
+ if (options.all) {
18934
+ const { workflows } = await api.listWorkflows();
18935
+ ids = workflows.map((w) => w.id);
18936
+ } else if (id) {
18937
+ ids = [id];
18938
+ } else {
18939
+ throw new Error("Provide a workflow <id> or --all.");
18940
+ }
18941
+ const outcomes = [];
18942
+ for (const workflowId of ids) {
18943
+ outcomes.push(await transformOne(api, workflowId, outDir, publish));
18944
+ }
18945
+ printCommandEnvelope(
18946
+ { outcomes, deprecation: null },
18947
+ {
18948
+ json: shouldEmitJson(options.json),
18949
+ text: renderTransformReport(outcomes)
18950
+ }
18951
+ );
18952
+ return outcomes.every((o) => o.ok) ? 0 : 1;
18953
+ }
18954
+ async function handleList2(options) {
18955
+ const limit = options.limit ? Number(options.limit) : void 0;
18956
+ const result = await client().listWorkflows({
18957
+ limit: Number.isFinite(limit) ? limit : void 0
18958
+ });
18959
+ emitPassThrough("list", result, options);
18960
+ }
18961
+ async function handleGet(id, options) {
18962
+ emitPassThrough("get", await client().getWorkflow(id), options);
18963
+ }
18964
+ async function handleDelete(id, options) {
18965
+ emitPassThrough("delete", await client().deleteWorkflow(id), options);
18966
+ }
18967
+ async function handleDisable(id, options) {
18968
+ const result = await client().disableWorkflow(id);
18969
+ printJson(result);
18970
+ }
18971
+ async function handleEnable(id, options) {
18972
+ const result = await client().enableWorkflow(id);
18973
+ printJson(result);
18974
+ }
18975
+ async function handleApply(options) {
18976
+ const body = await readJsonOption(options.payload, options.file);
18977
+ emitPassThrough("apply", await client().applyWorkflow(body), options);
18978
+ }
18979
+ async function handleLint(options) {
18980
+ const body = await readJsonOption(options.payload, options.file);
18981
+ emitPassThrough("lint", await client().lintWorkflow(body), options);
18982
+ }
18983
+ async function handleSchema(options) {
18984
+ emitPassThrough(
18985
+ "schema",
18986
+ await client().getWorkflowSchema(options.subject),
18987
+ options
18988
+ );
18989
+ }
18990
+ async function handleCall(options) {
18991
+ const body = {};
18992
+ if (options.workflowId) body.workflow_id = options.workflowId;
18993
+ if (options.workflowName) body.workflow_name = options.workflowName;
18994
+ const input2 = options.payload ? JSON.parse(options.payload) : {};
18995
+ if (options.mode) input2.__execution_mode = options.mode;
18996
+ body.input = input2;
18997
+ emitPassThrough("call", await client().callWorkflow(body), options);
18998
+ }
18999
+ async function handleRuns(id, options) {
19000
+ const limit = options.limit ? Number(options.limit) : void 0;
19001
+ emitPassThrough(
19002
+ "runs",
19003
+ await client().listWorkflowRuns(id, {
19004
+ limit: Number.isFinite(limit) ? limit : void 0
19005
+ }),
19006
+ options
19007
+ );
19008
+ }
19009
+ async function handleRun(id, runId, options) {
19010
+ emitPassThrough("run", await client().getWorkflowRun(id, runId), options);
19011
+ }
19012
+ async function handleCancel(id, runId, options) {
19013
+ emitPassThrough(
19014
+ "cancel",
19015
+ await client().cancelWorkflowRun(id, runId),
19016
+ options
19017
+ );
19018
+ }
19019
+ async function handleTail(id, runId, options) {
19020
+ const api = client();
19021
+ const intervalMs = Math.max(500, Number(options.intervalMs ?? 2e3) || 2e3);
19022
+ const deadline = Date.now() + 10 * 6e4;
19023
+ for (; ; ) {
19024
+ const run = await api.getWorkflowRun(id, runId);
19025
+ emitPassThrough("tail", run, options);
19026
+ const status = readStatus(run);
19027
+ if (status && TERMINAL_RUN_STATUSES.has(status) || Date.now() > deadline) {
19028
+ return;
19029
+ }
19030
+ await sleep3(intervalMs);
19031
+ }
19032
+ }
19033
+ function registerWorkflowCommands(program) {
19034
+ const workflows = program.command("workflows").description(
19035
+ "Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play."
19036
+ ).addHelpText(
19037
+ "after",
19038
+ `
19039
+ Workflows are a legacy surface. They still run in the cloud and these commands
19040
+ keep working, but new work belongs in plays. Migrate a workflow with:
19041
+
19042
+ deepline workflows transform <id> # write a reviewable .play.ts
19043
+ deepline workflows transform --all --publish # migrate + publish them all
19044
+
19045
+ Equivalents: list\u2192plays list \xB7 call\u2192plays run \xB7 apply\u2192plays publish \xB7
19046
+ lint\u2192plays check \xB7 runs\u2192runs list.
19047
+ `
19048
+ );
19049
+ withJsonOption(
19050
+ 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")
19051
+ ).addHelpText(
19052
+ "after",
19053
+ `
19054
+ Examples:
19055
+ deepline workflows transform wf_123 --out ./plays
19056
+ deepline workflows transform --all
19057
+ deepline workflows transform --all --publish --json
19058
+
19059
+ Notes:
19060
+ HITL, run_javascript steps, and sub-workflow calls cannot run as a play yet;
19061
+ those workflows are reported as needing manual attention and skipped.
19062
+ `
19063
+ ).action(async (id, options) => {
19064
+ process.exitCode = await handleTransform(id, options);
19065
+ });
19066
+ withJsonOption(
19067
+ workflows.command("list").description("List workspace workflows.").option("--limit <n>", "Max workflows to return")
19068
+ ).action(handleList2);
19069
+ withJsonOption(
19070
+ workflows.command("get <id>").description(
19071
+ "Fetch a workflow, including its published-revision config."
19072
+ )
19073
+ ).action(handleGet);
19074
+ withJsonOption(
19075
+ workflows.command("disable <id>").description("Turn a workflow off.")
19076
+ ).action(handleDisable);
19077
+ withJsonOption(
19078
+ workflows.command("enable <id>").description("Turn a workflow back on.")
19079
+ ).action(handleEnable);
19080
+ withJsonOption(
19081
+ workflows.command("delete <id>").description("Delete a workflow and its revisions/runs.")
19082
+ ).action(handleDelete);
19083
+ withJsonOption(
19084
+ 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")
19085
+ ).action(handleApply);
19086
+ withJsonOption(
19087
+ 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")
19088
+ ).action(handleLint);
19089
+ withJsonOption(
19090
+ workflows.command("schema").description("Fetch live workflow request schemas.").option(
19091
+ "--subject <subject>",
19092
+ "Schema subject (apply|call|trigger|all|\u2026)"
19093
+ )
19094
+ ).action(handleSchema);
19095
+ withJsonOption(
19096
+ 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")
19097
+ ).action(handleCall);
19098
+ withJsonOption(
19099
+ workflows.command("runs <id>").description("List a workflow\u2019s runs.").option("--limit <n>", "Max runs to return")
19100
+ ).action(handleRuns);
19101
+ withJsonOption(
19102
+ workflows.command("run <id> <runId>").description("Fetch a single workflow run.")
19103
+ ).action(handleRun);
19104
+ withJsonOption(
19105
+ 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)")
19106
+ ).action(handleTail);
19107
+ withJsonOption(
19108
+ workflows.command("cancel <id> <runId>").description("Cancel a running workflow run.")
19109
+ ).action(handleCancel);
19110
+ }
19111
+
16911
19112
  // src/cli/commands/update.ts
16912
19113
  import { spawn } from "child_process";
16913
19114
  import { existsSync as existsSync9 } from "fs";
16914
- import { dirname as dirname9, join as join11, resolve as resolve13 } from "path";
19115
+ import { dirname as dirname10, join as join12, resolve as resolve14 } from "path";
16915
19116
  function posixShellQuote(value) {
16916
19117
  return `'${value.replace(/'/g, `'\\''`)}'`;
16917
19118
  }
@@ -16930,19 +19131,19 @@ function buildSourceUpdateCommand(sourceRoot) {
16930
19131
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
16931
19132
  }
16932
19133
  function findRepoBackedSdkRoot(startPath) {
16933
- let current = resolve13(startPath);
19134
+ let current = resolve14(startPath);
16934
19135
  while (true) {
16935
- if (existsSync9(join11(current, "sdk", "package.json")) && existsSync9(join11(current, "sdk", "bin", "deepline-dev.ts"))) {
19136
+ if (existsSync9(join12(current, "sdk", "package.json")) && existsSync9(join12(current, "sdk", "bin", "deepline-dev.ts"))) {
16936
19137
  return current;
16937
19138
  }
16938
- const parent = dirname9(current);
19139
+ const parent = dirname10(current);
16939
19140
  if (parent === current) return null;
16940
19141
  current = parent;
16941
19142
  }
16942
19143
  }
16943
19144
  function resolveUpdatePlan() {
16944
- const entrypoint = process.argv[1] ? resolve13(process.argv[1]) : "";
16945
- const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname9(entrypoint)) : null;
19145
+ const entrypoint = process.argv[1] ? resolve14(process.argv[1]) : "";
19146
+ const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname10(entrypoint)) : null;
16946
19147
  if (sourceRoot) {
16947
19148
  return {
16948
19149
  kind: "source",
@@ -17214,7 +19415,7 @@ import {
17214
19415
  writeFileSync as writeFileSync10
17215
19416
  } from "fs";
17216
19417
  import { homedir as homedir6 } from "os";
17217
- import { dirname as dirname10, join as join12 } from "path";
19418
+ import { dirname as dirname11, join as join13 } from "path";
17218
19419
  var CHECK_TIMEOUT_MS2 = 3e3;
17219
19420
  var SDK_SKILL_NAME = "deepline-plays";
17220
19421
  var attemptedSync = false;
@@ -17234,14 +19435,14 @@ function readPluginSkillsVersion() {
17234
19435
  const dir = activePluginSkillsDir();
17235
19436
  if (!dir) return "";
17236
19437
  try {
17237
- return readFileSync8(join12(dir, ".version"), "utf-8").trim();
19438
+ return readFileSync8(join13(dir, ".version"), "utf-8").trim();
17238
19439
  } catch {
17239
19440
  return "";
17240
19441
  }
17241
19442
  }
17242
19443
  function sdkSkillsVersionPath(baseUrl) {
17243
19444
  const home = process.env.HOME?.trim() || homedir6();
17244
- return join12(
19445
+ return join13(
17245
19446
  home,
17246
19447
  ".local",
17247
19448
  "deepline",
@@ -17263,16 +19464,16 @@ function readLocalSkillsVersion(baseUrl) {
17263
19464
  }
17264
19465
  function writeLocalSkillsVersion(baseUrl, version) {
17265
19466
  const path = sdkSkillsVersionPath(baseUrl);
17266
- mkdirSync5(dirname10(path), { recursive: true });
19467
+ mkdirSync5(dirname11(path), { recursive: true });
17267
19468
  writeFileSync10(path, `${version}
17268
19469
  `, "utf-8");
17269
19470
  }
17270
19471
  function installedSdkSkillHasStalePositionalExecuteExamples() {
17271
19472
  const home = process.env.HOME?.trim() || homedir6();
17272
19473
  const pluginSkillsDir = activePluginSkillsDir();
17273
- const roots = pluginSkillsDir ? [join12(pluginSkillsDir, SDK_SKILL_NAME)] : [
17274
- join12(home, ".claude", "skills", SDK_SKILL_NAME),
17275
- join12(home, ".agents", "skills", SDK_SKILL_NAME)
19474
+ const roots = pluginSkillsDir ? [join13(pluginSkillsDir, SDK_SKILL_NAME)] : [
19475
+ join13(home, ".claude", "skills", SDK_SKILL_NAME),
19476
+ join13(home, ".agents", "skills", SDK_SKILL_NAME)
17276
19477
  ];
17277
19478
  const staleMarkers = [
17278
19479
  "ctx.tools.execute(key",
@@ -17283,7 +19484,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
17283
19484
  ];
17284
19485
  const scan = (dir) => {
17285
19486
  for (const entry of readdirSync2(dir)) {
17286
- const path = join12(dir, entry);
19487
+ const path = join13(dir, entry);
17287
19488
  const stat4 = statSync2(path);
17288
19489
  if (stat4.isDirectory()) {
17289
19490
  if (scan(path)) return true;
@@ -17369,7 +19570,7 @@ function resolveSkillsInstallCommands(baseUrl) {
17369
19570
  return [npxInstall];
17370
19571
  }
17371
19572
  function runOneSkillsInstall(install) {
17372
- return new Promise((resolve14) => {
19573
+ return new Promise((resolve15) => {
17373
19574
  const child = spawn2(install.command, install.args, {
17374
19575
  stdio: ["ignore", "ignore", "pipe"],
17375
19576
  env: process.env
@@ -17379,7 +19580,7 @@ function runOneSkillsInstall(install) {
17379
19580
  stderr += chunk.toString("utf-8");
17380
19581
  });
17381
19582
  child.on("error", (error) => {
17382
- resolve14({
19583
+ resolve15({
17383
19584
  ok: false,
17384
19585
  detail: `failed to start ${install.command}: ${error.message}`,
17385
19586
  manualCommand: install.manualCommand
@@ -17387,11 +19588,11 @@ function runOneSkillsInstall(install) {
17387
19588
  });
17388
19589
  child.on("close", (code) => {
17389
19590
  if (code === 0) {
17390
- resolve14({ ok: true, detail: "", manualCommand: install.manualCommand });
19591
+ resolve15({ ok: true, detail: "", manualCommand: install.manualCommand });
17391
19592
  return;
17392
19593
  }
17393
19594
  const detail = stderr.trim();
17394
- resolve14({
19595
+ resolve15({
17395
19596
  ok: false,
17396
19597
  detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
17397
19598
  manualCommand: install.manualCommand
@@ -17479,10 +19680,10 @@ function shouldDeferSkillsSyncForCommand() {
17479
19680
  return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
17480
19681
  }
17481
19682
  async function runPlayRunnerHealthCheck() {
17482
- const dir = await mkdtemp2(join13(tmpdir5(), "deepline-health-play-"));
17483
- const file = join13(dir, "health-check.play.ts");
19683
+ const dir = await mkdtemp2(join14(tmpdir5(), "deepline-health-play-"));
19684
+ const file = join14(dir, "health-check.play.ts");
17484
19685
  try {
17485
- await writeFile5(
19686
+ await writeFile6(
17486
19687
  file,
17487
19688
  [
17488
19689
  "import { definePlay } from 'deepline';",
@@ -17579,14 +19780,12 @@ async function runPreflightCheck() {
17579
19780
  connected: false,
17580
19781
  error: preflightErrorMessage(error)
17581
19782
  })),
17582
- http.get("/api/v2/billing/balance").catch(
17583
- (error) => ({
17584
- balance: null,
17585
- balance_display: "unavailable",
17586
- balance_status: "unknown",
17587
- error: preflightErrorMessage(error)
17588
- })
17589
- )
19783
+ http.get("/api/v2/billing/balance").catch((error) => ({
19784
+ balance: null,
19785
+ balance_display: "unavailable",
19786
+ balance_status: "unknown",
19787
+ error: preflightErrorMessage(error)
19788
+ }))
17590
19789
  ]) : [
17591
19790
  {
17592
19791
  status: "not_connected",
@@ -17722,6 +19921,7 @@ Exit codes:
17722
19921
  registerAuthCommands(program);
17723
19922
  registerToolsCommands(program);
17724
19923
  registerPlayCommands(program);
19924
+ registerWorkflowCommands(program);
17725
19925
  registerSecretsCommands(program);
17726
19926
  registerBillingCommands(program);
17727
19927
  registerOrgCommands(program);
@@ -17774,8 +19974,8 @@ Examples:
17774
19974
  `);
17775
19975
  return;
17776
19976
  }
17777
- const client = new DeeplineClient();
17778
- const data = await client.health();
19977
+ const client2 = new DeeplineClient();
19978
+ const data = await client2.health();
17779
19979
  process.stdout.write(`${JSON.stringify(data, null, 2)}
17780
19980
  `);
17781
19981
  } catch (error) {