deepline 0.1.77 → 0.1.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.77",
232
+ version: "0.1.79",
233
233
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.77",
235
+ latest: "0.1.79",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -402,8 +402,8 @@ var HttpClient = class {
402
402
  if (lastError instanceof DeeplineError) {
403
403
  throw lastError;
404
404
  }
405
- const errorMessage2 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
406
- throw new DeeplineError(errorMessage2);
405
+ const errorMessage3 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
406
+ throw new DeeplineError(errorMessage3);
407
407
  }
408
408
  /**
409
409
  * Send a GET request.
@@ -808,6 +808,7 @@ var DeeplineClient = class {
808
808
  aliases,
809
809
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
810
810
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
811
+ staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
811
812
  ...csvInput ? { csvInput } : {},
812
813
  ...rowOutputSchema ? { rowOutputSchema } : {},
813
814
  runCommand: runCommand2,
@@ -4033,6 +4034,51 @@ function customerDbRows(result) {
4033
4034
  function customerDbColumnNames(result) {
4034
4035
  return result.columns.map((column) => column.name).filter(Boolean);
4035
4036
  }
4037
+ function errorMessage(value) {
4038
+ return value instanceof Error ? value.message : String(value ?? "");
4039
+ }
4040
+ function collectErrorText(value) {
4041
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4042
+ return errorMessage(value);
4043
+ }
4044
+ const record = value;
4045
+ return [
4046
+ errorMessage(value),
4047
+ typeof record.error === "string" ? record.error : "",
4048
+ typeof record.message === "string" ? record.message : "",
4049
+ typeof record.detail === "string" ? record.detail : "",
4050
+ typeof record.hint === "string" ? record.hint : "",
4051
+ typeof record.code === "string" ? record.code : "",
4052
+ record.response ? collectErrorText(record.response) : "",
4053
+ record.details ? collectErrorText(record.details) : ""
4054
+ ].filter(Boolean).join("\n");
4055
+ }
4056
+ function formatDbQueryError(sql, error) {
4057
+ const text = collectErrorText(error);
4058
+ const lower = text.toLowerCase();
4059
+ const referencesStorage = /"storage"\.|storage\./i.test(sql);
4060
+ const runIdColumnMissing = /column\s+"?run_id"?\s+does not exist/i.test(text) || /run_id.*does not exist/i.test(lower);
4061
+ const relationMissing = /relation\s+["']?[^"']*storage[^"']*["']?\s+does not exist/i.test(text) || /42p01/i.test(text);
4062
+ if (referencesStorage && relationMissing) {
4063
+ return [
4064
+ "Customer DB query failed: the referenced storage table does not exist.",
4065
+ "Play map tables are created only when the corresponding ctx.map(...).run(...) call executes. Pilot branches, early returns, and runs that fail before the map do not create that table.",
4066
+ "Use `deepline runs get <run-id> --full --json` to inspect returned dataset handles, then export them with `deepline runs export <run-id> --dataset result.rows --out rows.csv`.",
4067
+ `Original error: ${errorMessage(error)}`
4068
+ ].join("\n");
4069
+ }
4070
+ if (referencesStorage && runIdColumnMissing) {
4071
+ return [
4072
+ "Customer DB query failed: storage map tables use `_run_id`, not `run_id`.",
4073
+ "Prefer `deepline runs export <run-id> --dataset result.rows --out rows.csv` unless you are doing deep table debugging.",
4074
+ `Original error: ${errorMessage(error)}`
4075
+ ].join("\n");
4076
+ }
4077
+ if (error instanceof DeeplineError) {
4078
+ return error.message;
4079
+ }
4080
+ return errorMessage(error);
4081
+ }
4036
4082
  function writeCustomerDbCsv(result, outPath) {
4037
4083
  const resolved = (0, import_node_path7.resolve)(outPath);
4038
4084
  (0, import_node_fs6.writeFileSync)(
@@ -4093,7 +4139,13 @@ async function handleDbQuery(args) {
4093
4139
  const jsonOutput = argsWantJson(args);
4094
4140
  const explicitJsonOutput = args.includes("--json");
4095
4141
  const client = new DeeplineClient();
4096
- const result = await client.queryCustomerDb({ sql, maxRows });
4142
+ let result;
4143
+ try {
4144
+ result = await client.queryCustomerDb({ sql, maxRows });
4145
+ } catch (error) {
4146
+ console.error(formatDbQueryError(sql, error));
4147
+ return 1;
4148
+ }
4097
4149
  const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify(
4098
4150
  {
4099
4151
  sql,
@@ -6082,19 +6134,19 @@ function playBootstrapTemplateConfig(template) {
6082
6134
  function templateExample(template) {
6083
6135
  switch (template) {
6084
6136
  case "people-list":
6085
- return "deepline plays bootstrap people-list --from provider:dropleads_search_people > people.play.ts";
6137
+ return "deepline plays bootstrap people-list --from provider:dropleads_search_people --out people.play.ts";
6086
6138
  case "company-list":
6087
- return "deepline plays bootstrap company-list --from provider:apollo_company_search > companies.play.ts";
6139
+ return "deepline plays bootstrap company-list --from provider:apollo_company_search --out companies.play.ts";
6088
6140
  case "people-email":
6089
- return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts";
6141
+ return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --out email-flow.play.ts";
6090
6142
  case "people-phone":
6091
- return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone > phone-flow.play.ts";
6143
+ return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone --out phone-flow.play.ts";
6092
6144
  case "company-people":
6093
- return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact > company-people.play.ts";
6145
+ return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact --out company-people.play.ts";
6094
6146
  case "company-people-email":
6095
- return "deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder > account-emails.play.ts";
6147
+ return "deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder --out account-emails.play.ts";
6096
6148
  case "company-people-phone":
6097
- return "deepline plays bootstrap company-people-phone --from provider:apollo_company_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder > account-phones.play.ts";
6149
+ return "deepline plays bootstrap company-people-phone --from provider:apollo_company_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder --out account-phones.play.ts";
6098
6150
  }
6099
6151
  }
6100
6152
  var PLAY_BOOTSTRAP_STAGE_NAMES = [
@@ -6103,19 +6155,22 @@ var PLAY_BOOTSTRAP_STAGE_NAMES = [
6103
6155
  "phone"
6104
6156
  ];
6105
6157
  function playBootstrapUsageLine() {
6106
- return `Usage: deepline plays bootstrap <${formatPlayBootstrapTemplates()}> --from <csv:PATH|play:REF|provider:ID|providers:ID,ID> [--using <play:REF|providers:ID,ID>] [--people play:REF] [--email <play:REF|providers:ID,ID>] [--phone <play:REF|providers:ID,ID>] [--limit 5] > flow.play.ts`;
6158
+ return `Usage: deepline plays bootstrap <${formatPlayBootstrapTemplates()}> --from <csv:PATH|play:REF|provider:ID|providers:ID,ID> [--using <play:REF|providers:ID,ID>] [--people play:REF] [--email <play:REF|providers:ID,ID>] [--phone <play:REF|providers:ID,ID>] [--limit 5] [--out flow.play.ts]`;
6107
6159
  }
6108
6160
  function requireBootstrapTemplate(rawTemplate) {
6109
6161
  if (!rawTemplate) {
6110
6162
  throw new PlayBootstrapUsageError(
6111
6163
  `plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}.
6112
- Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts`
6164
+ Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --out email-flow.play.ts`
6113
6165
  );
6114
6166
  }
6115
6167
  if (!isPlayBootstrapTemplate(rawTemplate)) {
6168
+ const looksLikePlayReference = rawTemplate.includes("/") || rawTemplate.endsWith(".play.ts");
6116
6169
  throw new PlayBootstrapUsageError(
6117
6170
  `Unknown plays bootstrap template: ${rawTemplate}
6118
- Supported templates: ${formatPlayBootstrapTemplates()}`
6171
+ Supported templates: ${formatPlayBootstrapTemplates()}` + (looksLikePlayReference ? `
6172
+
6173
+ "${rawTemplate}" looks like an existing play reference or file, not a bootstrap template. Do not run plays bootstrap on prebuilt/<play-name>. Use "deepline plays run ${rawTemplate} --input '{...}' --watch" to run it directly, "deepline plays describe ${rawTemplate} --json" to inspect its contract, or "deepline plays get ${rawTemplate} --source --out scratchpad.play.ts" to clone/edit it.` : "")
6119
6174
  );
6120
6175
  }
6121
6176
  return rawTemplate;
@@ -6257,7 +6312,8 @@ function parsePlayBootstrapOptions(args) {
6257
6312
  people: null,
6258
6313
  email: null,
6259
6314
  phone: null,
6260
- limit: 5
6315
+ limit: 5,
6316
+ out: null
6261
6317
  };
6262
6318
  for (let index = 0; index < rest.length; index += 1) {
6263
6319
  const arg = rest[index];
@@ -6291,6 +6347,10 @@ function parsePlayBootstrapOptions(args) {
6291
6347
  options.limit = parsePositiveInteger2(value(), "--limit");
6292
6348
  index += 1;
6293
6349
  break;
6350
+ case "--out":
6351
+ options.out = value();
6352
+ index += 1;
6353
+ break;
6294
6354
  default:
6295
6355
  throw new PlayBootstrapUsageError(
6296
6356
  `Unknown plays bootstrap option: ${arg}
@@ -6408,7 +6468,7 @@ function packagedCsvPathForPlay(csvPath) {
6408
6468
  const relativePath = (0, import_node_path11.relative)(playDir, absoluteCsvPath);
6409
6469
  if (relativePath === "" || relativePath.startsWith("..") || (0, import_node_path11.isAbsolute)(relativePath)) {
6410
6470
  throw new PlayBootstrapUsageError(
6411
- `--from csv:${csvPath} must point to a file inside the directory where you run plays bootstrap. Run bootstrap from the intended play directory, then redirect stdout to a .play.ts file there.`
6471
+ `--from csv:${csvPath} must point to a file inside the directory where you run plays bootstrap. Run bootstrap from the intended play directory and write the play with --out there.`
6412
6472
  );
6413
6473
  }
6414
6474
  const portablePath = relativePath.split("\\").join("/");
@@ -6815,8 +6875,18 @@ function validateBootstrapRoutes(input2) {
6815
6875
  "Company-to-people bootstrap only accepts --people play:<play-ref> for now. Providers are too task-specific; choose or create a people play so the generated file can show the mapping contract."
6816
6876
  );
6817
6877
  }
6878
+ assertComposablePlayRoute({
6879
+ stageLabel: "--people",
6880
+ playRef: stagePlayRef(input2.options.people),
6881
+ play: input2.peoplePlay
6882
+ });
6818
6883
  for (const finder of ["email_finder", "phone_finder"]) {
6819
6884
  const requiredCategory = PLAY_BOOTSTRAP_PROVIDER_CATEGORY_BY_FINDER[finder];
6885
+ assertComposablePlayRoute({
6886
+ stageLabel: finder === "email_finder" ? "--email/--using" : "--phone/--using",
6887
+ playRef: stagePlayRef(finderStage(input2.options, finder)),
6888
+ play: input2.finderPlays[finder] ?? null
6889
+ });
6820
6890
  for (const tool of input2.finderTools[finder] ?? []) {
6821
6891
  if (!tool.categories.includes(requiredCategory)) {
6822
6892
  throw new PlayBootstrapValidationError(
@@ -6831,6 +6901,38 @@ function validateBootstrapRoutes(input2) {
6831
6901
  }
6832
6902
  }
6833
6903
  }
6904
+ function staticPipelineSubsteps(pipeline) {
6905
+ if (!isRecord3(pipeline)) return [];
6906
+ return [
6907
+ ...extractionEntries(pipeline.stages),
6908
+ ...extractionEntries(pipeline.substeps)
6909
+ ];
6910
+ }
6911
+ function playUsesMapBackedRuntime(play) {
6912
+ const pipeline = play?.staticPipeline;
6913
+ if (!isRecord3(pipeline)) return false;
6914
+ if (stringValue(pipeline.tableNamespace)) return true;
6915
+ return staticPipelineSubsteps(pipeline).some((substep) => {
6916
+ if (stringValue(substep.type) === "map") return true;
6917
+ return playUsesMapBackedRuntime({
6918
+ name: play?.name ?? "child",
6919
+ aliases: [],
6920
+ runCommand: "",
6921
+ examples: [],
6922
+ staticPipeline: isRecord3(substep.pipeline) ? substep.pipeline : null
6923
+ });
6924
+ });
6925
+ }
6926
+ function assertComposablePlayRoute(input2) {
6927
+ if (!input2.playRef || !playUsesMapBackedRuntime(input2.play)) return;
6928
+ const runCommand2 = input2.play?.runCommand?.trim() || `deepline plays run ${input2.playRef} --input '{...}' --watch`;
6929
+ throw new PlayBootstrapValidationError(
6930
+ `Cannot use ${input2.stageLabel} play:${input2.playRef} in plays bootstrap composition: the selected play is map-backed/direct-run-only. Child plays that use ctx.map() own durable table state and must be run directly, exported, or validated as their own play instead of wrapped with ctx.runPlay. Run it directly first: ${runCommand2}`
6931
+ );
6932
+ }
6933
+ function sourcePlayNeedsExportFirst(input2) {
6934
+ return input2.source.kind === "play" && playUsesMapBackedRuntime(input2.sourcePlay);
6935
+ }
6834
6936
  function sourceCollectionName(entity) {
6835
6937
  switch (entity) {
6836
6938
  case "company":
@@ -6860,6 +6962,22 @@ function generateCsvSourceRowsBlock(input2) {
6860
6962
  const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
6861
6963
  }
6862
6964
  function generatePlaySourceRowsBlock(input2) {
6965
+ if (sourcePlayNeedsExportFirst({
6966
+ source: input2.source,
6967
+ sourcePlay: input2.sourcePlay
6968
+ })) {
6969
+ const sourcePlay = input2.sourcePlay;
6970
+ const runCommand2 = sourcePlay?.runCommand?.trim() || `deepline plays run ${input2.source.value} --input '{...}' --watch`;
6971
+ return `// Source play ${input2.source.value} is map-backed/direct-run-only, so this generated play is stage 2.
6972
+ // Stage 1:
6973
+ // ${runCommand2}
6974
+ // Stage 2:
6975
+ // deepline runs export <stage-1-run-id> --dataset 'result.rows' --out source-export.csv
6976
+ // Stage 3:
6977
+ // deepline plays run <this-file.play.ts> --input '{"sourceCsv":"source-export.csv","limit":${input2.sourcePlay ? "5" : "5"}}' --watch
6978
+ const sourceDataset = await ctx.csv<${input2.collectionType}>(input.sourceCsv ?? './source-export.csv');
6979
+ const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
6980
+ }
6863
6981
  const playInput = generatePlayInputObject({
6864
6982
  schema: input2.sourcePlay?.inputSchema,
6865
6983
  indent: " ",
@@ -7144,6 +7262,7 @@ ${typeDefinitions}
7144
7262
 
7145
7263
  type Input = {
7146
7264
  limit?: number;
7265
+ sourceCsv?: string;
7147
7266
  };
7148
7267
 
7149
7268
  export default definePlay(${jsString(input2.options.name)}, async (ctx, input: Input = {}) => {
@@ -7210,6 +7329,7 @@ async function loadBootstrapContracts(client, options) {
7210
7329
  validateBootstrapRoutes({
7211
7330
  options,
7212
7331
  sourceTools: contracts.sourceTools,
7332
+ sourcePlay: contracts.sourcePlay,
7213
7333
  peoplePlay: contracts.peoplePlay,
7214
7334
  finderTools: contracts.finderTools,
7215
7335
  finderPlays: contracts.finderPlays
@@ -7231,11 +7351,11 @@ function loadCsvContext(source) {
7231
7351
  };
7232
7352
  }
7233
7353
  }
7234
- function errorMessage(error) {
7354
+ function errorMessage2(error) {
7235
7355
  return error instanceof Error ? error.message : String(error);
7236
7356
  }
7237
7357
  function renderPlayBootstrapError(error) {
7238
- console.error(errorMessage(error));
7358
+ console.error(errorMessage2(error));
7239
7359
  return error instanceof PlayBootstrapError ? error.exitCode : 1;
7240
7360
  }
7241
7361
  async function runPlayBootstrap(args) {
@@ -7248,6 +7368,12 @@ async function runPlayBootstrap(args) {
7248
7368
  ...contracts,
7249
7369
  ...csvContext
7250
7370
  });
7371
+ if (options.out) {
7372
+ (0, import_node_fs9.writeFileSync)((0, import_node_path11.resolve)(options.out), source, "utf-8");
7373
+ process.stdout.write(`Wrote ${(0, import_node_path11.resolve)(options.out)}
7374
+ `);
7375
+ return 0;
7376
+ }
7251
7377
  process.stdout.write(source);
7252
7378
  return 0;
7253
7379
  }
@@ -7260,7 +7386,7 @@ function registerPlayBootstrapCommand(play) {
7260
7386
  `
7261
7387
  Notes:
7262
7388
  Cloud-validated play generator for agents. Pick the JTBD as the positional
7263
- template, bind resources with typed refs, redirect stdout to a .play.ts file,
7389
+ template, bind resources with typed refs, write the .play.ts file with --out,
7264
7390
  then edit the generated TODO mapping comments. Multiple finder providers are
7265
7391
  generated as a waterfall in the order you pass them.
7266
7392
 
@@ -7268,9 +7394,8 @@ Notes:
7268
7394
  Deepline. It prints TypeScript source only and does not run paid tools; the
7269
7395
  generated play may spend credits later when you run it.
7270
7396
 
7271
- stdout is the generated .play.ts source. Errors and diagnostics go to stderr.
7272
- There is no JSON mode and no --out; use shell redirection:
7273
- deepline plays bootstrap ... > scratchpad.play.ts
7397
+ By default stdout is the generated .play.ts source. Use --out to write a file
7398
+ directly. Errors and diagnostics go to stderr. There is no JSON mode.
7274
7399
 
7275
7400
  Templates:
7276
7401
  people-list start from people/contact rows
@@ -7294,7 +7419,7 @@ Notes:
7294
7419
  email/phone finder providers must match their category and expose value getters
7295
7420
  finder plays/providers must match the route; generated code leaves input mapping explicit
7296
7421
  business-specific provider inputs and company -> people persona fields are TODOs in code
7297
- csv: paths are resolved from the directory where you run bootstrap; redirect the play file there too
7422
+ csv: paths are resolved from the directory where you run bootstrap; write the play file there too
7298
7423
 
7299
7424
  Exit codes:
7300
7425
  0 success
@@ -7302,13 +7427,13 @@ Notes:
7302
7427
  7 route validation failed
7303
7428
 
7304
7429
  Examples:
7305
- deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --limit 5 > email-flow.play.ts
7430
+ deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out email-flow.play.ts
7306
7431
  deepline plays check email-flow.play.ts
7307
7432
  deepline plays run email-flow.play.ts --input '{"limit":5}' --watch
7308
7433
 
7309
- deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 > prospecting.play.ts
7310
- deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 > account-contacts.play.ts
7311
- deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 > companies.play.ts
7434
+ deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 --out prospecting.play.ts
7435
+ deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out account-contacts.play.ts
7436
+ deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 --out companies.play.ts
7312
7437
  `
7313
7438
  ).option("--name <name>", "Generated play name").option(
7314
7439
  "--from <ref>",
@@ -7325,7 +7450,7 @@ Examples:
7325
7450
  ).option(
7326
7451
  "--phone <ref>",
7327
7452
  "Phone finder stage: play:REF, provider:ID, or providers:ID,ID"
7328
- ).option("--limit <n>", "Maximum rows to fan out in the generated play").action(async (template, options) => {
7453
+ ).option("--limit <n>", "Maximum rows to fan out in the generated play").option("--out <path>", "Write generated play source to a file").action(async (template, options) => {
7329
7454
  process.exitCode = await handlePlayBootstrap([
7330
7455
  template,
7331
7456
  ...options.name ? ["--name", options.name] : [],
@@ -7334,7 +7459,8 @@ Examples:
7334
7459
  ...options.people ? ["--people", options.people] : [],
7335
7460
  ...options.email ? ["--email", options.email] : [],
7336
7461
  ...options.phone ? ["--phone", options.phone] : [],
7337
- ...options.limit ? ["--limit", options.limit] : []
7462
+ ...options.limit ? ["--limit", options.limit] : [],
7463
+ ...options.out ? ["--out", options.out] : []
7338
7464
  ]);
7339
7465
  });
7340
7466
  }
@@ -7484,204 +7610,6 @@ function createCliProgress(enabled) {
7484
7610
  return progress;
7485
7611
  }
7486
7612
 
7487
- // ../shared_libs/plays/row-identity.ts
7488
- var POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
7489
- var PLAY_NAME_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
7490
- var MAP_KEY_NAMESPACE_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
7491
- var SHA256_INITIAL_HASH = [
7492
- 1779033703,
7493
- 3144134277,
7494
- 1013904242,
7495
- 2773480762,
7496
- 1359893119,
7497
- 2600822924,
7498
- 528734635,
7499
- 1541459225
7500
- ];
7501
- var SHA256_ROUND_CONSTANTS = [
7502
- 1116352408,
7503
- 1899447441,
7504
- 3049323471,
7505
- 3921009573,
7506
- 961987163,
7507
- 1508970993,
7508
- 2453635748,
7509
- 2870763221,
7510
- 3624381080,
7511
- 310598401,
7512
- 607225278,
7513
- 1426881987,
7514
- 1925078388,
7515
- 2162078206,
7516
- 2614888103,
7517
- 3248222580,
7518
- 3835390401,
7519
- 4022224774,
7520
- 264347078,
7521
- 604807628,
7522
- 770255983,
7523
- 1249150122,
7524
- 1555081692,
7525
- 1996064986,
7526
- 2554220882,
7527
- 2821834349,
7528
- 2952996808,
7529
- 3210313671,
7530
- 3336571891,
7531
- 3584528711,
7532
- 113926993,
7533
- 338241895,
7534
- 666307205,
7535
- 773529912,
7536
- 1294757372,
7537
- 1396182291,
7538
- 1695183700,
7539
- 1986661051,
7540
- 2177026350,
7541
- 2456956037,
7542
- 2730485921,
7543
- 2820302411,
7544
- 3259730800,
7545
- 3345764771,
7546
- 3516065817,
7547
- 3600352804,
7548
- 4094571909,
7549
- 275423344,
7550
- 430227734,
7551
- 506948616,
7552
- 659060556,
7553
- 883997877,
7554
- 958139571,
7555
- 1322822218,
7556
- 1537002063,
7557
- 1747873779,
7558
- 1955562222,
7559
- 2024104815,
7560
- 2227730452,
7561
- 2361852424,
7562
- 2428436474,
7563
- 2756734187,
7564
- 3204031479,
7565
- 3329325298
7566
- ];
7567
- function rightRotate32(value, bits) {
7568
- return value >>> bits | value << 32 - bits;
7569
- }
7570
- function sha256Hex(input2) {
7571
- const bytes = Array.from(new TextEncoder().encode(input2));
7572
- const bitLength = bytes.length * 8;
7573
- bytes.push(128);
7574
- while (bytes.length % 64 !== 56) {
7575
- bytes.push(0);
7576
- }
7577
- const highBits = Math.floor(bitLength / 4294967296);
7578
- const lowBits = bitLength >>> 0;
7579
- bytes.push(
7580
- highBits >>> 24 & 255,
7581
- highBits >>> 16 & 255,
7582
- highBits >>> 8 & 255,
7583
- highBits & 255,
7584
- lowBits >>> 24 & 255,
7585
- lowBits >>> 16 & 255,
7586
- lowBits >>> 8 & 255,
7587
- lowBits & 255
7588
- );
7589
- const hash = [...SHA256_INITIAL_HASH];
7590
- const words = new Array(64).fill(0);
7591
- for (let offset = 0; offset < bytes.length; offset += 64) {
7592
- for (let index = 0; index < 16; index += 1) {
7593
- const wordOffset = offset + index * 4;
7594
- words[index] = (bytes[wordOffset] ?? 0) << 24 | (bytes[wordOffset + 1] ?? 0) << 16 | (bytes[wordOffset + 2] ?? 0) << 8 | (bytes[wordOffset + 3] ?? 0);
7595
- }
7596
- for (let index = 16; index < 64; index += 1) {
7597
- const s0 = rightRotate32(words[index - 15], 7) ^ rightRotate32(words[index - 15], 18) ^ words[index - 15] >>> 3;
7598
- const s1 = rightRotate32(words[index - 2], 17) ^ rightRotate32(words[index - 2], 19) ^ words[index - 2] >>> 10;
7599
- words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
7600
- }
7601
- let [a, b, c, d, e, f, g, h] = hash;
7602
- for (let index = 0; index < 64; index += 1) {
7603
- const s1 = rightRotate32(e, 6) ^ rightRotate32(e, 11) ^ rightRotate32(e, 25);
7604
- const ch = e & f ^ ~e & g;
7605
- const temp1 = h + s1 + ch + SHA256_ROUND_CONSTANTS[index] + words[index] >>> 0;
7606
- const s0 = rightRotate32(a, 2) ^ rightRotate32(a, 13) ^ rightRotate32(a, 22);
7607
- const maj = a & b ^ a & c ^ b & c;
7608
- const temp2 = s0 + maj >>> 0;
7609
- h = g;
7610
- g = f;
7611
- f = e;
7612
- e = d + temp1 >>> 0;
7613
- d = c;
7614
- c = b;
7615
- b = a;
7616
- a = temp1 + temp2 >>> 0;
7617
- }
7618
- hash[0] = hash[0] + a >>> 0;
7619
- hash[1] = hash[1] + b >>> 0;
7620
- hash[2] = hash[2] + c >>> 0;
7621
- hash[3] = hash[3] + d >>> 0;
7622
- hash[4] = hash[4] + e >>> 0;
7623
- hash[5] = hash[5] + f >>> 0;
7624
- hash[6] = hash[6] + g >>> 0;
7625
- hash[7] = hash[7] + h >>> 0;
7626
- }
7627
- return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
7628
- }
7629
- function sanitizeIdentifierPart(value) {
7630
- return value.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
7631
- }
7632
- function validateIdentifierPart(rawValue, label, maxLength) {
7633
- const sanitized = sanitizeIdentifierPart(rawValue);
7634
- if (!sanitized) {
7635
- throw new Error(
7636
- `${label} must contain at least one letter or number after normalization. Use only letters, numbers, underscores, or hyphens.`
7637
- );
7638
- }
7639
- if (sanitized.length > maxLength) {
7640
- throw new Error(
7641
- `${label} is too long after normalization (${sanitized.length}/${maxLength}). Shorten it to ${maxLength} characters or fewer. Normalized value: "${sanitized}".`
7642
- );
7643
- }
7644
- return sanitized;
7645
- }
7646
- function normalizePlayName(value) {
7647
- if (value.includes("/")) {
7648
- throw new Error(
7649
- 'Play name cannot contain "/". Slash is reserved for qualified play references like "prebuilt/example" or "self/example".'
7650
- );
7651
- }
7652
- return validateIdentifierPart(value, "Play name", PLAY_NAME_MAX_LENGTH);
7653
- }
7654
- function normalizePlayNameForSheet(value) {
7655
- if (!value.includes("/")) {
7656
- return normalizePlayName(value);
7657
- }
7658
- const digest = sha256Hex(value).slice(0, 12);
7659
- const normalizedReference = sanitizeIdentifierPart(
7660
- value.replace(/\//g, "__")
7661
- );
7662
- const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
7663
- const prefix = normalizedReference.slice(0, prefixLength).replace(/_+$/g, "") || "qualified_play";
7664
- return `${prefix}_${digest}`;
7665
- }
7666
- function normalizeTableNamespace(value) {
7667
- return validateIdentifierPart(
7668
- value,
7669
- "ctx.dataset() key",
7670
- MAP_KEY_NAMESPACE_MAX_LENGTH
7671
- );
7672
- }
7673
- function validatePlaySheetTableName(playName, tableNamespace) {
7674
- const playSegment = normalizePlayNameForSheet(playName);
7675
- const keySegment = normalizeTableNamespace(tableNamespace);
7676
- const resolved = `${playSegment}_${keySegment}`;
7677
- if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
7678
- throw new Error(
7679
- `Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.dataset() key. Resolved table name: "${resolved}".`
7680
- );
7681
- }
7682
- return resolved;
7683
- }
7684
-
7685
7613
  // src/cli/trace.ts
7686
7614
  var cliTraceStartedAt = Date.now();
7687
7615
  function isTruthyEnv(value) {
@@ -7732,6 +7660,11 @@ async function traceCliSpan(phase, fields, run) {
7732
7660
 
7733
7661
  // src/cli/play-check-hints.ts
7734
7662
  var EXTRACTED_GETTER_ERROR_HINT = "Deepline hint: extractedValues/extractedLists .get() only works for declared Deepline getters listed by `deepline tools describe <tool> --json`. Use `toolExecutionResult.toolResponse.raw` for provider/tool-specific fields.";
7663
+ var DATASET_API_HINT = "Deepline hint: PlayDataset is lazy and durable. Use `.peek(n)` for a small preview or `.materialize()` when you intentionally need rows in memory; do not use `.rows`, `.toArray()`, or array methods directly on the dataset handle.";
7664
+ var ROW_PROPERTY_HINT = "Deepline hint: this row type only contains fields produced by the CSV/schema and previous map steps. Check source column casing and the exact output field names from earlier steps before scaling.";
7665
+ var TOOLS_EXECUTE_SIGNATURE_HINT = "Deepline hint: ctx.tools.execute requires a request object: `ctx.tools.execute({ id, tool, input, description })`. The stable `id` is required for replay-safe receipts.";
7666
+ var RUN_PLAY_SIGNATURE_HINT = "Deepline hint: ctx.runPlay uses a stable key plus a composable child play reference. Direct-run-only or map-backed batch plays must be run directly, exported, then consumed by a separate play.";
7667
+ var MAP_BACKED_CHILD_HINT = "Deepline hint: map-backed child plays own durable table state and cannot be called from another play. Run that play directly, export its dataset, then pass the CSV to the next play.";
7735
7668
  function sourceLineForError(sourceCode, error) {
7736
7669
  const match = error.match(/:(\d+):(\d+)\s/);
7737
7670
  const lineNumber = match?.[1] ? Number(match[1]) : NaN;
@@ -7742,21 +7675,65 @@ function looksLikeInvalidExtractedGetter(error, sourceLine) {
7742
7675
  if (!/Property '[^']+' does not exist on type/.test(error)) return false;
7743
7676
  return /\bextracted(?:Values|Lists)\s*\./.test(sourceLine);
7744
7677
  }
7678
+ function looksLikeDatasetApiMisuse(error, sourceLine) {
7679
+ return /Property '(?:rows|toArray|forEach|map|filter|reduce)' does not exist on type '[^']*PlayDataset/.test(
7680
+ error
7681
+ ) || /\b(?:\.rows|\.toArray\(\)|\.forEach\(|\.map\(|\.filter\(|\.reduce\()/.test(
7682
+ sourceLine
7683
+ );
7684
+ }
7685
+ function looksLikeRowPropertyMismatch(error, sourceLine) {
7686
+ if (!/Property '[^']+' does not exist on type/.test(error)) return false;
7687
+ if (looksLikeInvalidExtractedGetter(error, sourceLine)) return false;
7688
+ return /\brow\.[A-Za-z_][A-Za-z0-9_]*/.test(sourceLine);
7689
+ }
7690
+ function looksLikeToolsExecuteSignature(error, sourceLine) {
7691
+ return /ctx\.tools\.execute requires a request object/i.test(error) || /Expected 1 arguments?, but got [2-9]/.test(error) && /\btools\.execute\(/.test(sourceLine);
7692
+ }
7693
+ function looksLikeRunPlaySignature(error, sourceLine) {
7694
+ return /ctx\.runPlay/i.test(error) || /(?:Expected|Argument of type|No overload matches)/.test(error) && /\brunPlay\(/.test(sourceLine);
7695
+ }
7696
+ function looksLikeMapBackedChild(error) {
7697
+ return /map-backed child play|direct-run-only|cannot call a map-backed|own durable table/i.test(
7698
+ error
7699
+ );
7700
+ }
7701
+ function hintForError(error, sourceLine) {
7702
+ if (looksLikeInvalidExtractedGetter(error, sourceLine)) {
7703
+ return EXTRACTED_GETTER_ERROR_HINT;
7704
+ }
7705
+ if (looksLikeDatasetApiMisuse(error, sourceLine)) {
7706
+ return DATASET_API_HINT;
7707
+ }
7708
+ if (looksLikeToolsExecuteSignature(error, sourceLine)) {
7709
+ return TOOLS_EXECUTE_SIGNATURE_HINT;
7710
+ }
7711
+ if (looksLikeMapBackedChild(error)) {
7712
+ return MAP_BACKED_CHILD_HINT;
7713
+ }
7714
+ if (looksLikeRunPlaySignature(error, sourceLine)) {
7715
+ return RUN_PLAY_SIGNATURE_HINT;
7716
+ }
7717
+ if (looksLikeRowPropertyMismatch(error, sourceLine)) {
7718
+ return ROW_PROPERTY_HINT;
7719
+ }
7720
+ return null;
7721
+ }
7745
7722
  function addPlayCheckRepairHints(input2) {
7746
- let addedHint = false;
7723
+ const addedHints = /* @__PURE__ */ new Set();
7747
7724
  return input2.errors.map((error) => {
7748
7725
  const line = sourceLineForError(input2.sourceCode, error);
7749
- if (addedHint || !looksLikeInvalidExtractedGetter(error, line) || error.includes(EXTRACTED_GETTER_ERROR_HINT)) {
7726
+ const hint = hintForError(error, line);
7727
+ if (!hint || addedHints.has(hint) || error.includes(hint)) {
7750
7728
  return error;
7751
7729
  }
7752
- addedHint = true;
7730
+ addedHints.add(hint);
7753
7731
  return `${error}
7754
- ${EXTRACTED_GETTER_ERROR_HINT}`;
7732
+ ${hint}`;
7755
7733
  });
7756
7734
  }
7757
7735
 
7758
7736
  // src/cli/commands/play.ts
7759
- var PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS = 2500;
7760
7737
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
7761
7738
  "--json",
7762
7739
  "--wait",
@@ -8308,8 +8285,6 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
8308
8285
  "cancelled"
8309
8286
  ]);
8310
8287
  var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
8311
- var PLAY_CUSTOMER_STORAGE_SCHEMA_NAME = "storage";
8312
- var PLAY_INTERNAL_STEP_RECEIPT_TABLE = "_deepline_step_receipts";
8313
8288
  function getEventPayload(event) {
8314
8289
  return event.payload && typeof event.payload === "object" ? event.payload : {};
8315
8290
  }
@@ -8363,36 +8338,8 @@ function getLogLinesFromLiveEvent(event) {
8363
8338
  const lines = getEventPayload(event).lines;
8364
8339
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
8365
8340
  }
8366
- function quoteSqlIdentifier(identifier) {
8367
- return `"${identifier.replace(/"/g, '""')}"`;
8368
- }
8369
- function quoteSqlLiteral(value) {
8370
- return `'${value.replace(/'/g, "''")}'`;
8371
- }
8372
- function buildDebugDbQueryCommand(sql) {
8373
- return `deepline db query --sql ${shellSingleQuote(sql)} --max-rows 20 --json`;
8374
- }
8375
- function buildStepReceiptsDebugCommand(runId) {
8376
- const table = `${quoteSqlIdentifier(
8377
- PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
8378
- )}.${quoteSqlIdentifier(PLAY_INTERNAL_STEP_RECEIPT_TABLE)}`;
8379
- const sql = `select convert_from(k, 'UTF8') as receipt_key, case status when 0 then 'pending' when 1 then 'running' when 2 then 'completed' when 3 then 'failed' when 4 then 'skipped' else status::text end as status, output, error, updated_at from ${table} where run_id = ${quoteSqlLiteral(runId)} order by updated_at asc, receipt_key asc limit 20`;
8380
- return buildDebugDbQueryCommand(sql);
8381
- }
8382
- function buildMapTableDebugCommand(input2) {
8383
- try {
8384
- const tableName = validatePlaySheetTableName(
8385
- input2.playName,
8386
- input2.tableNamespace
8387
- );
8388
- const table = `${quoteSqlIdentifier(
8389
- PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
8390
- )}.${quoteSqlIdentifier(tableName)}`;
8391
- const sql = `select * from ${table} where _run_id = ${quoteSqlLiteral(input2.runId)} limit 20`;
8392
- return buildDebugDbQueryCommand(sql);
8393
- } catch {
8394
- return null;
8395
- }
8341
+ function buildRunInspectCommand(runId) {
8342
+ return `deepline runs get ${runId} --full --json`;
8396
8343
  }
8397
8344
  function extractTableNamespaceFromLiveEvent(event) {
8398
8345
  const payload = getEventPayload(event);
@@ -8417,7 +8364,7 @@ function emitLiveDebugTableHints(input2) {
8417
8364
  if (!input2.state.emittedDebugKeys.has(receiptsKey)) {
8418
8365
  input2.state.emittedDebugKeys.add(receiptsKey);
8419
8366
  input2.progress.writeLine(
8420
- `Debug top-level outputs: ${buildStepReceiptsDebugCommand(input2.runId)}`,
8367
+ `Inspect run output: ${buildRunInspectCommand(input2.runId)}`,
8421
8368
  process.stdout
8422
8369
  );
8423
8370
  }
@@ -8429,17 +8376,9 @@ function emitLiveDebugTableHints(input2) {
8429
8376
  if (input2.state.emittedDebugKeys.has(tableKey)) {
8430
8377
  return;
8431
8378
  }
8432
- const command = buildMapTableDebugCommand({
8433
- playName: input2.playName,
8434
- runId: input2.runId,
8435
- tableNamespace
8436
- });
8437
- if (!command) {
8438
- return;
8439
- }
8440
8379
  input2.state.emittedDebugKeys.add(tableKey);
8441
8380
  input2.progress.writeLine(
8442
- `Debug rows for ${tableNamespace}: ${command}`,
8381
+ `Possible map table ${tableNamespace}: created only after this ctx.map(...).run(...) executes. Inspect returned datasets with ${buildRunInspectCommand(input2.runId)}`,
8443
8382
  process.stdout
8444
8383
  );
8445
8384
  }
@@ -8643,7 +8582,7 @@ async function waitForPlayCompletionByStream(input2) {
8643
8582
  }
8644
8583
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8645
8584
  throw new DeeplineError(
8646
- `Play live stream ended before the run reached a terminal state runId=${input2.workflowId}${phaseSuffix}.`,
8585
+ `Play watch stream ended before the run reached a terminal status. runId=${input2.workflowId}${phaseSuffix}. Inspect the current run with 'deepline runs get ${input2.workflowId} --full --json' or continue watching with 'deepline runs tail ${input2.workflowId}'.`,
8647
8586
  void 0,
8648
8587
  "PLAY_LIVE_STREAM_ENDED",
8649
8588
  {
@@ -8699,10 +8638,6 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8699
8638
  let eventCount = 0;
8700
8639
  let firstRunIdMs = null;
8701
8640
  let lastPhase = null;
8702
- const startRequest = {
8703
- ...input2.request,
8704
- waitForCompletionMs: typeof input2.request.waitForCompletionMs === "number" ? input2.request.waitForCompletionMs : PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS
8705
- };
8706
8641
  const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
8707
8642
  () => {
8708
8643
  timedOut = true;
@@ -8711,7 +8646,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8711
8646
  Math.max(1, input2.waitTimeoutMs)
8712
8647
  );
8713
8648
  try {
8714
- for await (const event of input2.client.startPlayRunStream(startRequest, {
8649
+ for await (const event of input2.client.startPlayRunStream(input2.request, {
8715
8650
  signal: controller.signal
8716
8651
  })) {
8717
8652
  eventCount += 1;
@@ -8804,7 +8739,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8804
8739
  const reason = error instanceof Error ? error.message : String(error);
8805
8740
  if (!input2.jsonOutput) {
8806
8741
  process.stderr.write(
8807
- `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
8742
+ `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to canonical run stream (${reason})
8808
8743
  `
8809
8744
  );
8810
8745
  }
@@ -8841,7 +8776,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8841
8776
  if (lastKnownWorkflowId) {
8842
8777
  if (!input2.jsonOutput) {
8843
8778
  input2.progress.writeLine(
8844
- `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
8779
+ `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to canonical run stream`
8845
8780
  );
8846
8781
  }
8847
8782
  recordCliTrace({
@@ -8871,7 +8806,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8871
8806
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8872
8807
  const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
8873
8808
  throw new DeeplineError(
8874
- `Play start stream ended before the run reached a terminal state${idSuffix}${phaseSuffix}.`,
8809
+ `Play start stream ended before a terminal status was observed${idSuffix}${phaseSuffix}. If a run id was printed, inspect it with 'deepline runs get <run-id> --full --json' or continue watching with 'deepline runs tail <run-id>'.`,
8875
8810
  void 0,
8876
8811
  "PLAY_START_STREAM_ENDED",
8877
8812
  {
@@ -9219,6 +9154,32 @@ function formatPlayErrorForDisplay(status, error) {
9219
9154
  }
9220
9155
  return error;
9221
9156
  }
9157
+ function isGenericInternalServerError(error) {
9158
+ if (!error) return false;
9159
+ return /^(?:internalservererror|internal server error|http 500|500)$/i.test(
9160
+ error.trim()
9161
+ );
9162
+ }
9163
+ function selectRunErrorForDisplay(status) {
9164
+ const progressError = getStringField(status.progress, "error");
9165
+ if (!isGenericInternalServerError(progressError)) {
9166
+ return progressError;
9167
+ }
9168
+ const directErrors = getRecordField(status, "errors");
9169
+ if (Array.isArray(directErrors)) {
9170
+ for (const entry of directErrors) {
9171
+ const message = getStringField(entry, "message");
9172
+ if (message && !isGenericInternalServerError(message)) {
9173
+ return message;
9174
+ }
9175
+ }
9176
+ }
9177
+ const directError = getStringField(status, "error");
9178
+ if (directError && !isGenericInternalServerError(directError)) {
9179
+ return directError;
9180
+ }
9181
+ return progressError;
9182
+ }
9222
9183
  function normalizeRunStatusForEnvelope(status) {
9223
9184
  const run = status.run ?? null;
9224
9185
  return {
@@ -9356,8 +9317,7 @@ function compactPlayStatus(status) {
9356
9317
  const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
9357
9318
  status.billing
9358
9319
  ) : null;
9359
- const progressError = status.progress?.error;
9360
- const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
9320
+ const error = selectRunErrorForDisplay(status) ?? (typeof status.error === "string" ? String(status.error) : null);
9361
9321
  const displayError = formatPlayErrorForDisplay(status, error);
9362
9322
  return {
9363
9323
  runId: status.runId,
@@ -9612,8 +9572,8 @@ function writePlayResult(status, jsonOutput, options) {
9612
9572
  for (const warning of warnings) {
9613
9573
  lines.push(` warning: ${warning}`);
9614
9574
  }
9615
- const progressError = status.progress?.error;
9616
- if (progressError && typeof progressError === "string") {
9575
+ const progressError = selectRunErrorForDisplay(status);
9576
+ if (progressError) {
9617
9577
  const billing = extractBillingForStatus(status, progressError);
9618
9578
  if (isInsufficientCreditsBilling(billing)) {
9619
9579
  lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
@@ -9665,9 +9625,6 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
9665
9625
  function shellSingleQuote(value) {
9666
9626
  return `'${value.replace(/'/g, `'\\''`)}'`;
9667
9627
  }
9668
- function sqlStringLiteral(value) {
9669
- return `'${value.replace(/'/g, "''")}'`;
9670
- }
9671
9628
  function runExportRetryCommand(runId, outPath, datasetPath) {
9672
9629
  return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote((0, import_node_path12.resolve)(outPath))}`;
9673
9630
  }
@@ -9686,26 +9643,6 @@ function extractRunPlayName(status) {
9686
9643
  }
9687
9644
  return null;
9688
9645
  }
9689
- function normalizeCustomerDbIdentifier(value) {
9690
- return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
9691
- }
9692
- function buildCustomerDbQueryPlan(input2) {
9693
- const playName = extractRunPlayName(input2.status);
9694
- const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
9695
- if (!playName || !tableNamespace || input2.rowsInfo.totalRows <= 0) {
9696
- return null;
9697
- }
9698
- const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
9699
- tableNamespace
9700
- )}`;
9701
- const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input2.status.runId)} limit ${input2.rowsInfo.totalRows}`;
9702
- const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input2.rowsInfo.totalRows}`;
9703
- return {
9704
- sql,
9705
- json: `${base} --json`,
9706
- csv: `${base} --format csv --out ${shellSingleQuote((0, import_node_path12.resolve)(input2.outPath))}`
9707
- };
9708
- }
9709
9646
  function exportableSheetRow(row) {
9710
9647
  if (!row || typeof row !== "object" || Array.isArray(row)) {
9711
9648
  return null;
@@ -10052,8 +9989,12 @@ function renderServerResultView(value) {
10052
9989
  lines.push(
10053
9990
  ` ${String(table.tableNamespace ?? "table")}${lineLabel}: ${rowLabel}${details.join(" ")}`
10054
9991
  );
10055
- if (typeof table.queryDatasetCommand === "string") {
10056
- lines.push(` inspect rows: ${table.queryDatasetCommand}`);
9992
+ if (typeof table.queryDatasetCommand === "string" && typeof table.rowCount === "number" && table.rowCount > 0) {
9993
+ lines.push(` debug backing table: ${table.queryDatasetCommand}`);
9994
+ } else if (typeof table.queryDatasetCommand === "string") {
9995
+ lines.push(
9996
+ " no rows observed for this run; backing table is created only if this map ran"
9997
+ );
10057
9998
  }
10058
9999
  if (table.debugHelp && typeof table.debugHelp === "object" && !Array.isArray(table.debugHelp)) {
10059
10000
  const debugHelp = table.debugHelp;
@@ -10399,9 +10340,51 @@ function printToolGetterHints(hints) {
10399
10340
  async function handlePlayCheck(args) {
10400
10341
  const options = parsePlayCheckOptions(args);
10401
10342
  if (!isFileTarget(options.target)) {
10402
- const resolved = (0, import_node_path12.resolve)(options.target);
10403
- console.error(`File not found: ${resolved}`);
10404
- return 1;
10343
+ const client2 = new DeeplineClient();
10344
+ try {
10345
+ await assertCanonicalNamedPlayReference(client2, options.target);
10346
+ const play = await client2.describePlay(
10347
+ parseReferencedPlayTarget2(options.target).playName,
10348
+ { compact: true }
10349
+ );
10350
+ const result2 = {
10351
+ valid: true,
10352
+ target: options.target,
10353
+ name: play.name,
10354
+ reference: play.reference ?? options.target,
10355
+ origin: play.origin ?? null,
10356
+ ownerType: play.ownerType ?? null,
10357
+ inputSchema: play.inputSchema ?? null,
10358
+ outputSchema: play.outputSchema ?? null,
10359
+ staticPipeline: play.staticPipeline ?? null,
10360
+ note: "Named/prebuilt play contract is available. No run was started."
10361
+ };
10362
+ if (options.jsonOutput) {
10363
+ process.stdout.write(`${JSON.stringify(result2)}
10364
+ `);
10365
+ } else {
10366
+ console.log(`\u2713 ${result2.reference} passed named play contract check`);
10367
+ console.log(" no run started; no Deepline credits spent");
10368
+ if (play.runCommand) console.log(` run: ${play.runCommand}`);
10369
+ }
10370
+ return 0;
10371
+ } catch (error) {
10372
+ const resolved = (0, import_node_path12.resolve)(options.target);
10373
+ const message = error instanceof Error && error.message ? error.message : `File not found: ${resolved}`;
10374
+ if (options.jsonOutput) {
10375
+ process.stdout.write(
10376
+ `${JSON.stringify({
10377
+ valid: false,
10378
+ target: options.target,
10379
+ errors: [message]
10380
+ })}
10381
+ `
10382
+ );
10383
+ } else {
10384
+ console.error(message);
10385
+ }
10386
+ return 1;
10387
+ }
10405
10388
  }
10406
10389
  const absolutePlayPath = (0, import_node_path12.resolve)(options.target);
10407
10390
  const sourceCode = (0, import_node_fs10.readFileSync)(absolutePlayPath, "utf-8");
@@ -11048,18 +11031,9 @@ async function handleRunExport(args) {
11048
11031
  datasetPath
11049
11032
  });
11050
11033
  const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
11051
- const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
11052
- status,
11053
- rowsInfo: exportResult.rowsInfo,
11054
- outPath
11055
- }) : null;
11056
11034
  const next = {
11057
11035
  ...buildRunNextCommands(status),
11058
- export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
11059
- ...queryPlan ? {
11060
- queryJson: queryPlan.json,
11061
- queryCsv: queryPlan.csv
11062
- } : {}
11036
+ export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source)
11063
11037
  };
11064
11038
  const payload = {
11065
11039
  runId: status.runId,
@@ -11068,13 +11042,6 @@ async function handleRunExport(args) {
11068
11042
  source,
11069
11043
  rowCount: exportResult?.rowsInfo.totalRows ?? null,
11070
11044
  columns: exportResult?.rowsInfo.columns ?? [],
11071
- ...queryPlan ? {
11072
- query: {
11073
- sql: queryPlan.sql,
11074
- json: queryPlan.json,
11075
- csv: queryPlan.csv
11076
- }
11077
- } : {},
11078
11045
  ...metadataOutPath ? { metadata_path: metadataOutPath } : {},
11079
11046
  local: { csv_path: exportResult?.path ?? null },
11080
11047
  next,
@@ -11084,8 +11051,7 @@ async function handleRunExport(args) {
11084
11051
  title: "run export",
11085
11052
  lines: [
11086
11053
  `Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
11087
- ...source ? [`source=${source}`] : [],
11088
- ...queryPlan ? [`query=${queryPlan.json}`] : []
11054
+ ...source ? [`source=${source}`] : []
11089
11055
  ]
11090
11056
  }
11091
11057
  ]
@@ -11265,7 +11231,8 @@ function parsePlaySearchOptions(args) {
11265
11231
  return {
11266
11232
  query,
11267
11233
  jsonOutput: argsWantJson(args),
11268
- compact: args.includes("--compact")
11234
+ compact: args.includes("--compact"),
11235
+ prebuiltOnly: args.includes("--prebuilt")
11269
11236
  };
11270
11237
  }
11271
11238
  function printPlayDescription(play) {
@@ -11313,6 +11280,7 @@ function printPlayDescription(play) {
11313
11280
  console.log(` ${line}`);
11314
11281
  }
11315
11282
  }
11283
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11316
11284
  console.log(` Run: ${play.runCommand}`);
11317
11285
  const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
11318
11286
  if (cloneEditStarter) {
@@ -11320,6 +11288,31 @@ function printPlayDescription(play) {
11320
11288
  console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
11321
11289
  }
11322
11290
  }
11291
+ function inputFieldNames(schema) {
11292
+ const fields = Array.isArray(schema?.fields) ? schema.fields : [];
11293
+ return fields.map(
11294
+ (field) => field && typeof field === "object" ? String(field.name ?? "").trim() : ""
11295
+ ).filter(Boolean);
11296
+ }
11297
+ function printCompactPlaySearchResult(play) {
11298
+ const reference = formatPlayListReference(play);
11299
+ const aliases = play.aliases.slice(0, 6).join(", ");
11300
+ console.log(`Play: ${reference}`);
11301
+ if (play.displayName && play.displayName !== play.name) {
11302
+ console.log(` Display name: ${play.displayName}`);
11303
+ }
11304
+ if (aliases) {
11305
+ console.log(` Aliases: ${aliases}`);
11306
+ }
11307
+ const fields = inputFieldNames(play.inputSchema);
11308
+ if (fields.length > 0) {
11309
+ console.log(` Inputs: ${fields.join(", ")}`);
11310
+ } else if (play.inputSchema) {
11311
+ console.log(" Inputs: see describe");
11312
+ }
11313
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11314
+ console.log(` Run: ${play.runCommand}`);
11315
+ }
11323
11316
  function compactPlaySchema(schema) {
11324
11317
  if (!schema) return null;
11325
11318
  const fields = Array.isArray(schema.fields) ? schema.fields.map(
@@ -11383,22 +11376,44 @@ async function handlePlaySearch(args) {
11383
11376
  return 1;
11384
11377
  }
11385
11378
  const client = new DeeplineClient();
11386
- const plays = await client.searchPlays({
11379
+ const plays = (await client.searchPlays({
11387
11380
  query: options.query,
11388
11381
  compact: options.compact
11389
- });
11382
+ })).filter(
11383
+ (play) => options.prebuiltOnly ? play.origin === "prebuilt" || play.ownerType === "deepline" : true
11384
+ );
11390
11385
  if (options.jsonOutput) {
11391
- process.stdout.write(`${JSON.stringify({ plays })}
11392
- `);
11386
+ const jsonPlays = options.prebuiltOnly ? plays.map((play) => ({
11387
+ ...play,
11388
+ inputSchema: compactPlaySchema(play.inputSchema)
11389
+ })) : plays;
11390
+ process.stdout.write(
11391
+ `${JSON.stringify({
11392
+ plays: jsonPlays,
11393
+ total: jsonPlays.length,
11394
+ truncated: false
11395
+ })}
11396
+ `
11397
+ );
11393
11398
  return 0;
11394
11399
  }
11395
- process.stdout.write(`${plays.length} plays found:
11400
+ const displayPlays = options.prebuiltOnly ? plays.slice(0, 5) : plays;
11401
+ process.stdout.write(
11402
+ `${plays.length} plays found${options.prebuiltOnly && plays.length > displayPlays.length ? `; showing top ${displayPlays.length}` : ""}:
11396
11403
 
11397
- `);
11398
- for (const play of plays) {
11399
- printPlayDescription(play);
11404
+ `
11405
+ );
11406
+ for (const play of displayPlays) {
11407
+ if (options.prebuiltOnly) {
11408
+ printCompactPlaySearchResult(play);
11409
+ } else {
11410
+ printPlayDescription(play);
11411
+ }
11400
11412
  console.log("");
11401
11413
  }
11414
+ if (options.prebuiltOnly && plays.length > displayPlays.length) {
11415
+ console.log("Use --json for the full machine-readable result set.");
11416
+ }
11402
11417
  return 0;
11403
11418
  }
11404
11419
  function normalizePlayGrepText(value) {
@@ -11665,15 +11680,18 @@ Common commands:
11665
11680
  deepline plays get person-linkedin-to-email --json
11666
11681
  `
11667
11682
  );
11668
- play.command("check <target>").description("Bundle-check a local play file.").addHelpText(
11683
+ play.command("check <target>").description("Check a local play file or named/prebuilt play contract.").addHelpText(
11669
11684
  "after",
11670
11685
  `
11671
11686
  Notes:
11672
11687
  Validates a local play without storing it, promoting it, or starting a run.
11673
11688
  This uses the authoritative cloud preflight path.
11689
+ For named or prebuilt plays, validates that the contract is discoverable
11690
+ without starting a run or spending Deepline credits.
11674
11691
 
11675
11692
  Examples:
11676
11693
  deepline plays check my.play.ts
11694
+ deepline plays check prebuilt/name-and-domain-to-email-waterfall-batch
11677
11695
  deepline plays check my.play.ts --json
11678
11696
  `
11679
11697
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
@@ -11823,11 +11841,13 @@ Examples:
11823
11841
  ...options.json ? ["--json"] : []
11824
11842
  ]);
11825
11843
  });
11826
- const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
11844
+ const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").option("--prebuilt", "Only show Deepline-managed prebuilt plays").addHelpText(
11827
11845
  "after",
11828
11846
  `
11829
11847
  Notes:
11830
11848
  Ranked discovery for workflows. Use describe on a result before running it.
11849
+ Prefer --prebuilt for new GTM tasks so old workspace scratchpads do not
11850
+ outrank Deepline-managed routes unless the user names one explicitly.
11831
11851
  The grep alias is the same ranked retrieval surface with a more literal name
11832
11852
  for agents that are filtering the play registry.
11833
11853
 
@@ -11839,6 +11859,7 @@ Examples:
11839
11859
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
11840
11860
  process.exitCode = await handlePlaySearch([
11841
11861
  query,
11862
+ ...options.prebuilt ? ["--prebuilt"] : [],
11842
11863
  ...options.compact ? ["--compact"] : [],
11843
11864
  ...options.json ? ["--json"] : []
11844
11865
  ]);
@@ -12086,7 +12107,7 @@ Notes:
12086
12107
  Writes a returned dataset handle to the requested local CSV path. Use runs get
12087
12108
  first to inspect dataset paths like result.rows or result.nested.contacts.
12088
12109
  --metadata-out writes the same export metadata object returned by --json,
12089
- including source and follow-on customer-db query commands when available.
12110
+ including the source dataset path and row/column metadata.
12090
12111
 
12091
12112
  Examples:
12092
12113
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
@@ -14017,7 +14038,7 @@ async function listTools(args) {
14017
14038
  const client = new DeeplineClient();
14018
14039
  const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
14019
14040
  const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
14020
- const compact = args.includes("--compact");
14041
+ const compact = args.includes("--compact") || !args.includes("--json");
14021
14042
  const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
14022
14043
  const items = (await client.listTools({
14023
14044
  ...categoryFilter ? { categories: categoryFilter } : {}
@@ -14073,9 +14094,10 @@ async function searchTools(queryInput, options = {}) {
14073
14094
  searchMode: options.searchMode,
14074
14095
  includeSearchDebug: options.includeSearchDebug
14075
14096
  });
14076
- const payload = options.compact && Array.isArray(result.tools) ? {
14097
+ const shouldCompact = options.compact || !options.json;
14098
+ const payload = shouldCompact && Array.isArray(result.tools) ? {
14077
14099
  ...result,
14078
- tools: result.tools.map(compactTool)
14100
+ tools: result.tools.slice(0, 8).map(compactTool)
14079
14101
  } : result;
14080
14102
  printCommandEnvelope(payload, {
14081
14103
  json: options.json || shouldEmitJson()
@@ -14116,7 +14138,8 @@ async function grepTools(queryInput, options = {}) {
14116
14138
  mode
14117
14139
  )
14118
14140
  );
14119
- const outputTools = options.compact ? tools.map(compactTool) : tools;
14141
+ const shouldCompact = options.compact || !options.json;
14142
+ const outputTools = shouldCompact ? tools.slice(0, 8).map(compactTool) : tools;
14120
14143
  printCommandEnvelope(
14121
14144
  {
14122
14145
  tools: outputTools,
@@ -14135,11 +14158,19 @@ async function grepTools(queryInput, options = {}) {
14135
14158
  );
14136
14159
  return 0;
14137
14160
  }
14161
+ function numericToolField(tool, field) {
14162
+ const value = tool[field];
14163
+ return typeof value === "number" ? value : void 0;
14164
+ }
14138
14165
  function compactTool(tool) {
14139
14166
  const listed = toListedTool(tool);
14167
+ const searchScore = numericToolField(tool, "searchScore");
14168
+ const search_score = numericToolField(tool, "search_score");
14140
14169
  return {
14141
14170
  id: listed.id,
14142
14171
  toolId: listed.toolId,
14172
+ ...search_score !== void 0 ? { search_score } : {},
14173
+ ...searchScore !== void 0 ? { searchScore } : {},
14143
14174
  provider: listed.provider,
14144
14175
  displayName: listed.displayName,
14145
14176
  description: listed.description,
@@ -14460,7 +14491,7 @@ async function getTool(toolId, options = {}) {
14460
14491
  }
14461
14492
  if (shouldEmitJson()) {
14462
14493
  process.stdout.write(
14463
- `${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
14494
+ `${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
14464
14495
  `
14465
14496
  );
14466
14497
  return 0;
@@ -14785,8 +14816,8 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
14785
14816
  invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
14786
14817
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
14787
14818
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
14788
- forPlayGetterBugs: "Run the play, then inspect the emitted table commands from runs get. Use deepline db query against the run tables before editing getters.",
14789
- executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run tables."
14819
+ forPlayGetterBugs: "Run a tiny play, inspect `deepline runs get <run-id> --full --json`, and export returned dataset handles with `deepline runs export`. Backing tables exist only for ctx.map(...).run(...) stages that actually executed.",
14820
+ executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run output and returned dataset handles."
14790
14821
  },
14791
14822
  starterScript: {
14792
14823
  path: starterScript.path,
@@ -15803,6 +15834,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
15803
15834
  }
15804
15835
 
15805
15836
  // src/cli/index.ts
15837
+ var PREFLIGHT_TIMEOUT_MS = 3e3;
15806
15838
  function asCommanderError(error) {
15807
15839
  if (!(error instanceof Error) || !("code" in error)) {
15808
15840
  return null;
@@ -15883,6 +15915,124 @@ async function runPlayRunnerHealthCheck() {
15883
15915
  await (0, import_promises6.rm)(dir, { recursive: true, force: true });
15884
15916
  }
15885
15917
  }
15918
+ function pickString(value, ...keys) {
15919
+ for (const key of keys) {
15920
+ const candidate = value[key];
15921
+ if (typeof candidate === "string" && candidate.trim()) {
15922
+ return candidate;
15923
+ }
15924
+ }
15925
+ return null;
15926
+ }
15927
+ function preflightErrorMessage(error) {
15928
+ return error instanceof Error ? error.message : String(error);
15929
+ }
15930
+ async function runPreflightCheck() {
15931
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
15932
+ const healthController = new AbortController();
15933
+ const healthTimeout = setTimeout(
15934
+ () => healthController.abort(),
15935
+ PREFLIGHT_TIMEOUT_MS
15936
+ );
15937
+ const health = await fetch(new URL("/api/v2/health", baseUrl), {
15938
+ signal: healthController.signal
15939
+ }).then(async (response) => {
15940
+ const payload = await response.json().catch(() => ({}));
15941
+ return {
15942
+ status: response.ok ? pickString(payload, "status") ?? "ok" : "unreachable",
15943
+ version: pickString(payload, "version")
15944
+ };
15945
+ }).catch(() => ({ status: "unreachable", version: null })).finally(() => clearTimeout(healthTimeout));
15946
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl);
15947
+ const http = apiKey ? new HttpClient(
15948
+ resolveConfig({
15949
+ baseUrl,
15950
+ apiKey,
15951
+ timeout: PREFLIGHT_TIMEOUT_MS,
15952
+ maxRetries: 0
15953
+ })
15954
+ ) : null;
15955
+ const [auth, billing] = http ? await Promise.all([
15956
+ http.post("/api/v2/auth/cli/status", {
15957
+ api_key: apiKey,
15958
+ reveal: false
15959
+ }).catch((error) => ({
15960
+ status: "not_connected",
15961
+ connected: false,
15962
+ error: preflightErrorMessage(error)
15963
+ })),
15964
+ http.get("/api/v2/billing/balance").catch(
15965
+ (error) => ({
15966
+ balance: null,
15967
+ balance_display: "unavailable",
15968
+ balance_status: "unknown",
15969
+ error: preflightErrorMessage(error)
15970
+ })
15971
+ )
15972
+ ]) : [
15973
+ {
15974
+ status: "not_connected",
15975
+ connected: false,
15976
+ error: "No API key found. Run: deepline auth register"
15977
+ },
15978
+ {
15979
+ balance: null,
15980
+ balance_display: "unavailable until authenticated",
15981
+ balance_status: "unknown"
15982
+ }
15983
+ ];
15984
+ const authStatus = pickString(auth, "status") ?? "unknown";
15985
+ const billingRecord = billing;
15986
+ const balanceDisplay = pickString(billing, "balance_display", "balanceDisplay") ?? `${String(billingRecord.balance ?? 0)} Deepline Credits`;
15987
+ const balanceStatus = pickString(billing, "balance_status", "balanceStatus") ?? "unknown";
15988
+ return {
15989
+ status: health.status === "ok" && (authStatus === "connected" || authStatus === "claimed") ? "ok" : "check",
15990
+ host: baseUrl,
15991
+ health: {
15992
+ status: health.status,
15993
+ version: health.version ?? null
15994
+ },
15995
+ auth: {
15996
+ status: authStatus,
15997
+ connected: authStatus === "connected" || authStatus === "claimed",
15998
+ org_id: pickString(auth, "org_id", "orgId"),
15999
+ org_name: pickString(auth, "org_name", "orgName"),
16000
+ rate_limit_tier: pickString(auth, "rate_limit_tier", "rateLimitTier"),
16001
+ error: pickString(auth, "error")
16002
+ },
16003
+ billing: {
16004
+ balance: billingRecord.balance ?? null,
16005
+ balance_display: balanceDisplay,
16006
+ rough_usd_balance: billingRecord.rough_usd_balance ?? billingRecord.roughUsdBalance ?? null,
16007
+ balance_status: balanceStatus,
16008
+ error: pickString(billing, "error")
16009
+ },
16010
+ render: {
16011
+ sections: [
16012
+ {
16013
+ title: "preflight",
16014
+ lines: [
16015
+ `host: ${baseUrl}`,
16016
+ `health: ${health.status}`,
16017
+ `auth: ${authStatus}`,
16018
+ `billing: ${balanceDisplay} (${balanceStatus})`
16019
+ ]
16020
+ }
16021
+ ]
16022
+ }
16023
+ };
16024
+ }
16025
+ function printPreflightHuman(data) {
16026
+ const render = data.render;
16027
+ const lines = render?.sections?.flatMap((section) => section.lines ?? []);
16028
+ if (lines?.length) {
16029
+ process.stdout.write(`${lines.join("\n")}
16030
+ `);
16031
+ return;
16032
+ }
16033
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16034
+ `);
16035
+ }
15886
16036
  async function main() {
15887
16037
  const mainStartedAt = Date.now();
15888
16038
  recordCliTrace({
@@ -15899,6 +16049,7 @@ async function main() {
15899
16049
  "after",
15900
16050
  `
15901
16051
  Common commands:
16052
+ deepline preflight
15902
16053
  deepline health
15903
16054
  deepline auth status --json
15904
16055
  deepline plays search email --json
@@ -15920,7 +16071,6 @@ Output:
15920
16071
 
15921
16072
  Safety:
15922
16073
  Commands that mutate state, open a browser, write files, stop work, or spend credits say so in their help.
15923
- Use --no-open where available for CI and agent runs.
15924
16074
 
15925
16075
  Exit codes:
15926
16076
  0 success; 2 usage/local input error; 3 auth/permission error; 4 not found;
@@ -15962,6 +16112,26 @@ Exit codes:
15962
16112
  registerDbCommands(program);
15963
16113
  registerFeedbackCommands(program);
15964
16114
  registerUpdateCommand(program);
16115
+ program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
16116
+ "after",
16117
+ `
16118
+ Notes:
16119
+ Read-only setup check for the configured Deepline host. Shows server health,
16120
+ auth connection, and customer-visible Deepline balance in one compact command.
16121
+
16122
+ Examples:
16123
+ deepline preflight
16124
+ deepline preflight --json
16125
+ `
16126
+ ).action(async (options) => {
16127
+ const data = await runPreflightCheck();
16128
+ if (shouldEmitJson(options.json)) {
16129
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16130
+ `);
16131
+ } else {
16132
+ printPreflightHuman(data);
16133
+ }
16134
+ });
15965
16135
  program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
15966
16136
  "--play-runner",
15967
16137
  "Run a tiny no-provider play to verify the full play execution plane."