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.
@@ -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.77",
209
+ version: "0.1.79",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.77",
212
+ latest: "0.1.79",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
@@ -379,8 +379,8 @@ var HttpClient = class {
379
379
  if (lastError instanceof DeeplineError) {
380
380
  throw lastError;
381
381
  }
382
- const errorMessage2 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
383
- throw new DeeplineError(errorMessage2);
382
+ const errorMessage3 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
383
+ throw new DeeplineError(errorMessage3);
384
384
  }
385
385
  /**
386
386
  * Send a GET request.
@@ -785,6 +785,7 @@ var DeeplineClient = class {
785
785
  aliases,
786
786
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
787
787
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
788
+ staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
788
789
  ...csvInput ? { csvInput } : {},
789
790
  ...rowOutputSchema ? { rowOutputSchema } : {},
790
791
  runCommand: runCommand2,
@@ -4016,6 +4017,51 @@ function customerDbRows(result) {
4016
4017
  function customerDbColumnNames(result) {
4017
4018
  return result.columns.map((column) => column.name).filter(Boolean);
4018
4019
  }
4020
+ function errorMessage(value) {
4021
+ return value instanceof Error ? value.message : String(value ?? "");
4022
+ }
4023
+ function collectErrorText(value) {
4024
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4025
+ return errorMessage(value);
4026
+ }
4027
+ const record = value;
4028
+ return [
4029
+ errorMessage(value),
4030
+ typeof record.error === "string" ? record.error : "",
4031
+ typeof record.message === "string" ? record.message : "",
4032
+ typeof record.detail === "string" ? record.detail : "",
4033
+ typeof record.hint === "string" ? record.hint : "",
4034
+ typeof record.code === "string" ? record.code : "",
4035
+ record.response ? collectErrorText(record.response) : "",
4036
+ record.details ? collectErrorText(record.details) : ""
4037
+ ].filter(Boolean).join("\n");
4038
+ }
4039
+ function formatDbQueryError(sql, error) {
4040
+ const text = collectErrorText(error);
4041
+ const lower = text.toLowerCase();
4042
+ const referencesStorage = /"storage"\.|storage\./i.test(sql);
4043
+ const runIdColumnMissing = /column\s+"?run_id"?\s+does not exist/i.test(text) || /run_id.*does not exist/i.test(lower);
4044
+ const relationMissing = /relation\s+["']?[^"']*storage[^"']*["']?\s+does not exist/i.test(text) || /42p01/i.test(text);
4045
+ if (referencesStorage && relationMissing) {
4046
+ return [
4047
+ "Customer DB query failed: the referenced storage table does not exist.",
4048
+ "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.",
4049
+ "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`.",
4050
+ `Original error: ${errorMessage(error)}`
4051
+ ].join("\n");
4052
+ }
4053
+ if (referencesStorage && runIdColumnMissing) {
4054
+ return [
4055
+ "Customer DB query failed: storage map tables use `_run_id`, not `run_id`.",
4056
+ "Prefer `deepline runs export <run-id> --dataset result.rows --out rows.csv` unless you are doing deep table debugging.",
4057
+ `Original error: ${errorMessage(error)}`
4058
+ ].join("\n");
4059
+ }
4060
+ if (error instanceof DeeplineError) {
4061
+ return error.message;
4062
+ }
4063
+ return errorMessage(error);
4064
+ }
4019
4065
  function writeCustomerDbCsv(result, outPath) {
4020
4066
  const resolved = resolve5(outPath);
4021
4067
  writeFileSync5(
@@ -4076,7 +4122,13 @@ async function handleDbQuery(args) {
4076
4122
  const jsonOutput = argsWantJson(args);
4077
4123
  const explicitJsonOutput = args.includes("--json");
4078
4124
  const client = new DeeplineClient();
4079
- const result = await client.queryCustomerDb({ sql, maxRows });
4125
+ let result;
4126
+ try {
4127
+ result = await client.queryCustomerDb({ sql, maxRows });
4128
+ } catch (error) {
4129
+ console.error(formatDbQueryError(sql, error));
4130
+ return 1;
4131
+ }
4080
4132
  const toolCommand = `deepline tools execute query_customer_db --payload ${JSON.stringify(
4081
4133
  {
4082
4134
  sql,
@@ -4237,7 +4289,7 @@ import {
4237
4289
  readFileSync as readFileSync6,
4238
4290
  readdirSync,
4239
4291
  realpathSync,
4240
- writeFileSync as writeFileSync6
4292
+ writeFileSync as writeFileSync7
4241
4293
  } from "fs";
4242
4294
  import { basename as basename3, dirname as dirname8, join as join7, resolve as resolve10 } from "path";
4243
4295
 
@@ -5910,7 +5962,7 @@ async function bundlePlayFile2(filePath, options = {}) {
5910
5962
  }
5911
5963
 
5912
5964
  // src/cli/commands/plays/bootstrap.ts
5913
- import { closeSync, openSync, readSync, statSync } from "fs";
5965
+ import { closeSync, openSync, readSync, statSync, writeFileSync as writeFileSync6 } from "fs";
5914
5966
  import { isAbsolute as isAbsolute3, relative as relative2, resolve as resolve9 } from "path";
5915
5967
  import { parse as parseCsvSync } from "csv-parse/sync";
5916
5968
 
@@ -6085,19 +6137,19 @@ function playBootstrapTemplateConfig(template) {
6085
6137
  function templateExample(template) {
6086
6138
  switch (template) {
6087
6139
  case "people-list":
6088
- return "deepline plays bootstrap people-list --from provider:dropleads_search_people > people.play.ts";
6140
+ return "deepline plays bootstrap people-list --from provider:dropleads_search_people --out people.play.ts";
6089
6141
  case "company-list":
6090
- return "deepline plays bootstrap company-list --from provider:apollo_company_search > companies.play.ts";
6142
+ return "deepline plays bootstrap company-list --from provider:apollo_company_search --out companies.play.ts";
6091
6143
  case "people-email":
6092
- return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts";
6144
+ 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";
6093
6145
  case "people-phone":
6094
- return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone > phone-flow.play.ts";
6146
+ return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone --out phone-flow.play.ts";
6095
6147
  case "company-people":
6096
- return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact > company-people.play.ts";
6148
+ return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact --out company-people.play.ts";
6097
6149
  case "company-people-email":
6098
- 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";
6150
+ 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";
6099
6151
  case "company-people-phone":
6100
- 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";
6152
+ 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";
6101
6153
  }
6102
6154
  }
6103
6155
  var PLAY_BOOTSTRAP_STAGE_NAMES = [
@@ -6106,19 +6158,22 @@ var PLAY_BOOTSTRAP_STAGE_NAMES = [
6106
6158
  "phone"
6107
6159
  ];
6108
6160
  function playBootstrapUsageLine() {
6109
- 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`;
6161
+ 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]`;
6110
6162
  }
6111
6163
  function requireBootstrapTemplate(rawTemplate) {
6112
6164
  if (!rawTemplate) {
6113
6165
  throw new PlayBootstrapUsageError(
6114
6166
  `plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}.
6115
- Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts`
6167
+ 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`
6116
6168
  );
6117
6169
  }
6118
6170
  if (!isPlayBootstrapTemplate(rawTemplate)) {
6171
+ const looksLikePlayReference = rawTemplate.includes("/") || rawTemplate.endsWith(".play.ts");
6119
6172
  throw new PlayBootstrapUsageError(
6120
6173
  `Unknown plays bootstrap template: ${rawTemplate}
6121
- Supported templates: ${formatPlayBootstrapTemplates()}`
6174
+ Supported templates: ${formatPlayBootstrapTemplates()}` + (looksLikePlayReference ? `
6175
+
6176
+ "${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.` : "")
6122
6177
  );
6123
6178
  }
6124
6179
  return rawTemplate;
@@ -6260,7 +6315,8 @@ function parsePlayBootstrapOptions(args) {
6260
6315
  people: null,
6261
6316
  email: null,
6262
6317
  phone: null,
6263
- limit: 5
6318
+ limit: 5,
6319
+ out: null
6264
6320
  };
6265
6321
  for (let index = 0; index < rest.length; index += 1) {
6266
6322
  const arg = rest[index];
@@ -6294,6 +6350,10 @@ function parsePlayBootstrapOptions(args) {
6294
6350
  options.limit = parsePositiveInteger2(value(), "--limit");
6295
6351
  index += 1;
6296
6352
  break;
6353
+ case "--out":
6354
+ options.out = value();
6355
+ index += 1;
6356
+ break;
6297
6357
  default:
6298
6358
  throw new PlayBootstrapUsageError(
6299
6359
  `Unknown plays bootstrap option: ${arg}
@@ -6411,7 +6471,7 @@ function packagedCsvPathForPlay(csvPath) {
6411
6471
  const relativePath = relative2(playDir, absoluteCsvPath);
6412
6472
  if (relativePath === "" || relativePath.startsWith("..") || isAbsolute3(relativePath)) {
6413
6473
  throw new PlayBootstrapUsageError(
6414
- `--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.`
6474
+ `--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.`
6415
6475
  );
6416
6476
  }
6417
6477
  const portablePath = relativePath.split("\\").join("/");
@@ -6818,8 +6878,18 @@ function validateBootstrapRoutes(input2) {
6818
6878
  "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."
6819
6879
  );
6820
6880
  }
6881
+ assertComposablePlayRoute({
6882
+ stageLabel: "--people",
6883
+ playRef: stagePlayRef(input2.options.people),
6884
+ play: input2.peoplePlay
6885
+ });
6821
6886
  for (const finder of ["email_finder", "phone_finder"]) {
6822
6887
  const requiredCategory = PLAY_BOOTSTRAP_PROVIDER_CATEGORY_BY_FINDER[finder];
6888
+ assertComposablePlayRoute({
6889
+ stageLabel: finder === "email_finder" ? "--email/--using" : "--phone/--using",
6890
+ playRef: stagePlayRef(finderStage(input2.options, finder)),
6891
+ play: input2.finderPlays[finder] ?? null
6892
+ });
6823
6893
  for (const tool of input2.finderTools[finder] ?? []) {
6824
6894
  if (!tool.categories.includes(requiredCategory)) {
6825
6895
  throw new PlayBootstrapValidationError(
@@ -6834,6 +6904,38 @@ function validateBootstrapRoutes(input2) {
6834
6904
  }
6835
6905
  }
6836
6906
  }
6907
+ function staticPipelineSubsteps(pipeline) {
6908
+ if (!isRecord3(pipeline)) return [];
6909
+ return [
6910
+ ...extractionEntries(pipeline.stages),
6911
+ ...extractionEntries(pipeline.substeps)
6912
+ ];
6913
+ }
6914
+ function playUsesMapBackedRuntime(play) {
6915
+ const pipeline = play?.staticPipeline;
6916
+ if (!isRecord3(pipeline)) return false;
6917
+ if (stringValue(pipeline.tableNamespace)) return true;
6918
+ return staticPipelineSubsteps(pipeline).some((substep) => {
6919
+ if (stringValue(substep.type) === "map") return true;
6920
+ return playUsesMapBackedRuntime({
6921
+ name: play?.name ?? "child",
6922
+ aliases: [],
6923
+ runCommand: "",
6924
+ examples: [],
6925
+ staticPipeline: isRecord3(substep.pipeline) ? substep.pipeline : null
6926
+ });
6927
+ });
6928
+ }
6929
+ function assertComposablePlayRoute(input2) {
6930
+ if (!input2.playRef || !playUsesMapBackedRuntime(input2.play)) return;
6931
+ const runCommand2 = input2.play?.runCommand?.trim() || `deepline plays run ${input2.playRef} --input '{...}' --watch`;
6932
+ throw new PlayBootstrapValidationError(
6933
+ `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}`
6934
+ );
6935
+ }
6936
+ function sourcePlayNeedsExportFirst(input2) {
6937
+ return input2.source.kind === "play" && playUsesMapBackedRuntime(input2.sourcePlay);
6938
+ }
6837
6939
  function sourceCollectionName(entity) {
6838
6940
  switch (entity) {
6839
6941
  case "company":
@@ -6863,6 +6965,22 @@ function generateCsvSourceRowsBlock(input2) {
6863
6965
  const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
6864
6966
  }
6865
6967
  function generatePlaySourceRowsBlock(input2) {
6968
+ if (sourcePlayNeedsExportFirst({
6969
+ source: input2.source,
6970
+ sourcePlay: input2.sourcePlay
6971
+ })) {
6972
+ const sourcePlay = input2.sourcePlay;
6973
+ const runCommand2 = sourcePlay?.runCommand?.trim() || `deepline plays run ${input2.source.value} --input '{...}' --watch`;
6974
+ return `// Source play ${input2.source.value} is map-backed/direct-run-only, so this generated play is stage 2.
6975
+ // Stage 1:
6976
+ // ${runCommand2}
6977
+ // Stage 2:
6978
+ // deepline runs export <stage-1-run-id> --dataset 'result.rows' --out source-export.csv
6979
+ // Stage 3:
6980
+ // deepline plays run <this-file.play.ts> --input '{"sourceCsv":"source-export.csv","limit":${input2.sourcePlay ? "5" : "5"}}' --watch
6981
+ const sourceDataset = await ctx.csv<${input2.collectionType}>(input.sourceCsv ?? './source-export.csv');
6982
+ const ${input2.collection}: ${input2.collectionType}[] = await sourceDataset.peek(limit);`;
6983
+ }
6866
6984
  const playInput = generatePlayInputObject({
6867
6985
  schema: input2.sourcePlay?.inputSchema,
6868
6986
  indent: " ",
@@ -7147,6 +7265,7 @@ ${typeDefinitions}
7147
7265
 
7148
7266
  type Input = {
7149
7267
  limit?: number;
7268
+ sourceCsv?: string;
7150
7269
  };
7151
7270
 
7152
7271
  export default definePlay(${jsString(input2.options.name)}, async (ctx, input: Input = {}) => {
@@ -7213,6 +7332,7 @@ async function loadBootstrapContracts(client, options) {
7213
7332
  validateBootstrapRoutes({
7214
7333
  options,
7215
7334
  sourceTools: contracts.sourceTools,
7335
+ sourcePlay: contracts.sourcePlay,
7216
7336
  peoplePlay: contracts.peoplePlay,
7217
7337
  finderTools: contracts.finderTools,
7218
7338
  finderPlays: contracts.finderPlays
@@ -7234,11 +7354,11 @@ function loadCsvContext(source) {
7234
7354
  };
7235
7355
  }
7236
7356
  }
7237
- function errorMessage(error) {
7357
+ function errorMessage2(error) {
7238
7358
  return error instanceof Error ? error.message : String(error);
7239
7359
  }
7240
7360
  function renderPlayBootstrapError(error) {
7241
- console.error(errorMessage(error));
7361
+ console.error(errorMessage2(error));
7242
7362
  return error instanceof PlayBootstrapError ? error.exitCode : 1;
7243
7363
  }
7244
7364
  async function runPlayBootstrap(args) {
@@ -7251,6 +7371,12 @@ async function runPlayBootstrap(args) {
7251
7371
  ...contracts,
7252
7372
  ...csvContext
7253
7373
  });
7374
+ if (options.out) {
7375
+ writeFileSync6(resolve9(options.out), source, "utf-8");
7376
+ process.stdout.write(`Wrote ${resolve9(options.out)}
7377
+ `);
7378
+ return 0;
7379
+ }
7254
7380
  process.stdout.write(source);
7255
7381
  return 0;
7256
7382
  }
@@ -7263,7 +7389,7 @@ function registerPlayBootstrapCommand(play) {
7263
7389
  `
7264
7390
  Notes:
7265
7391
  Cloud-validated play generator for agents. Pick the JTBD as the positional
7266
- template, bind resources with typed refs, redirect stdout to a .play.ts file,
7392
+ template, bind resources with typed refs, write the .play.ts file with --out,
7267
7393
  then edit the generated TODO mapping comments. Multiple finder providers are
7268
7394
  generated as a waterfall in the order you pass them.
7269
7395
 
@@ -7271,9 +7397,8 @@ Notes:
7271
7397
  Deepline. It prints TypeScript source only and does not run paid tools; the
7272
7398
  generated play may spend credits later when you run it.
7273
7399
 
7274
- stdout is the generated .play.ts source. Errors and diagnostics go to stderr.
7275
- There is no JSON mode and no --out; use shell redirection:
7276
- deepline plays bootstrap ... > scratchpad.play.ts
7400
+ By default stdout is the generated .play.ts source. Use --out to write a file
7401
+ directly. Errors and diagnostics go to stderr. There is no JSON mode.
7277
7402
 
7278
7403
  Templates:
7279
7404
  people-list start from people/contact rows
@@ -7297,7 +7422,7 @@ Notes:
7297
7422
  email/phone finder providers must match their category and expose value getters
7298
7423
  finder plays/providers must match the route; generated code leaves input mapping explicit
7299
7424
  business-specific provider inputs and company -> people persona fields are TODOs in code
7300
- csv: paths are resolved from the directory where you run bootstrap; redirect the play file there too
7425
+ csv: paths are resolved from the directory where you run bootstrap; write the play file there too
7301
7426
 
7302
7427
  Exit codes:
7303
7428
  0 success
@@ -7305,13 +7430,13 @@ Notes:
7305
7430
  7 route validation failed
7306
7431
 
7307
7432
  Examples:
7308
- 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
7433
+ 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
7309
7434
  deepline plays check email-flow.play.ts
7310
7435
  deepline plays run email-flow.play.ts --input '{"limit":5}' --watch
7311
7436
 
7312
- deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 > prospecting.play.ts
7313
- 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
7314
- deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 > companies.play.ts
7437
+ deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 --out prospecting.play.ts
7438
+ 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
7439
+ deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 --out companies.play.ts
7315
7440
  `
7316
7441
  ).option("--name <name>", "Generated play name").option(
7317
7442
  "--from <ref>",
@@ -7328,7 +7453,7 @@ Examples:
7328
7453
  ).option(
7329
7454
  "--phone <ref>",
7330
7455
  "Phone finder stage: play:REF, provider:ID, or providers:ID,ID"
7331
- ).option("--limit <n>", "Maximum rows to fan out in the generated play").action(async (template, options) => {
7456
+ ).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) => {
7332
7457
  process.exitCode = await handlePlayBootstrap([
7333
7458
  template,
7334
7459
  ...options.name ? ["--name", options.name] : [],
@@ -7337,7 +7462,8 @@ Examples:
7337
7462
  ...options.people ? ["--people", options.people] : [],
7338
7463
  ...options.email ? ["--email", options.email] : [],
7339
7464
  ...options.phone ? ["--phone", options.phone] : [],
7340
- ...options.limit ? ["--limit", options.limit] : []
7465
+ ...options.limit ? ["--limit", options.limit] : [],
7466
+ ...options.out ? ["--out", options.out] : []
7341
7467
  ]);
7342
7468
  });
7343
7469
  }
@@ -7487,204 +7613,6 @@ function createCliProgress(enabled) {
7487
7613
  return progress;
7488
7614
  }
7489
7615
 
7490
- // ../shared_libs/plays/row-identity.ts
7491
- var POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
7492
- var PLAY_NAME_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
7493
- var MAP_KEY_NAMESPACE_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
7494
- var SHA256_INITIAL_HASH = [
7495
- 1779033703,
7496
- 3144134277,
7497
- 1013904242,
7498
- 2773480762,
7499
- 1359893119,
7500
- 2600822924,
7501
- 528734635,
7502
- 1541459225
7503
- ];
7504
- var SHA256_ROUND_CONSTANTS = [
7505
- 1116352408,
7506
- 1899447441,
7507
- 3049323471,
7508
- 3921009573,
7509
- 961987163,
7510
- 1508970993,
7511
- 2453635748,
7512
- 2870763221,
7513
- 3624381080,
7514
- 310598401,
7515
- 607225278,
7516
- 1426881987,
7517
- 1925078388,
7518
- 2162078206,
7519
- 2614888103,
7520
- 3248222580,
7521
- 3835390401,
7522
- 4022224774,
7523
- 264347078,
7524
- 604807628,
7525
- 770255983,
7526
- 1249150122,
7527
- 1555081692,
7528
- 1996064986,
7529
- 2554220882,
7530
- 2821834349,
7531
- 2952996808,
7532
- 3210313671,
7533
- 3336571891,
7534
- 3584528711,
7535
- 113926993,
7536
- 338241895,
7537
- 666307205,
7538
- 773529912,
7539
- 1294757372,
7540
- 1396182291,
7541
- 1695183700,
7542
- 1986661051,
7543
- 2177026350,
7544
- 2456956037,
7545
- 2730485921,
7546
- 2820302411,
7547
- 3259730800,
7548
- 3345764771,
7549
- 3516065817,
7550
- 3600352804,
7551
- 4094571909,
7552
- 275423344,
7553
- 430227734,
7554
- 506948616,
7555
- 659060556,
7556
- 883997877,
7557
- 958139571,
7558
- 1322822218,
7559
- 1537002063,
7560
- 1747873779,
7561
- 1955562222,
7562
- 2024104815,
7563
- 2227730452,
7564
- 2361852424,
7565
- 2428436474,
7566
- 2756734187,
7567
- 3204031479,
7568
- 3329325298
7569
- ];
7570
- function rightRotate32(value, bits) {
7571
- return value >>> bits | value << 32 - bits;
7572
- }
7573
- function sha256Hex(input2) {
7574
- const bytes = Array.from(new TextEncoder().encode(input2));
7575
- const bitLength = bytes.length * 8;
7576
- bytes.push(128);
7577
- while (bytes.length % 64 !== 56) {
7578
- bytes.push(0);
7579
- }
7580
- const highBits = Math.floor(bitLength / 4294967296);
7581
- const lowBits = bitLength >>> 0;
7582
- bytes.push(
7583
- highBits >>> 24 & 255,
7584
- highBits >>> 16 & 255,
7585
- highBits >>> 8 & 255,
7586
- highBits & 255,
7587
- lowBits >>> 24 & 255,
7588
- lowBits >>> 16 & 255,
7589
- lowBits >>> 8 & 255,
7590
- lowBits & 255
7591
- );
7592
- const hash = [...SHA256_INITIAL_HASH];
7593
- const words = new Array(64).fill(0);
7594
- for (let offset = 0; offset < bytes.length; offset += 64) {
7595
- for (let index = 0; index < 16; index += 1) {
7596
- const wordOffset = offset + index * 4;
7597
- words[index] = (bytes[wordOffset] ?? 0) << 24 | (bytes[wordOffset + 1] ?? 0) << 16 | (bytes[wordOffset + 2] ?? 0) << 8 | (bytes[wordOffset + 3] ?? 0);
7598
- }
7599
- for (let index = 16; index < 64; index += 1) {
7600
- const s0 = rightRotate32(words[index - 15], 7) ^ rightRotate32(words[index - 15], 18) ^ words[index - 15] >>> 3;
7601
- const s1 = rightRotate32(words[index - 2], 17) ^ rightRotate32(words[index - 2], 19) ^ words[index - 2] >>> 10;
7602
- words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
7603
- }
7604
- let [a, b, c, d, e, f, g, h] = hash;
7605
- for (let index = 0; index < 64; index += 1) {
7606
- const s1 = rightRotate32(e, 6) ^ rightRotate32(e, 11) ^ rightRotate32(e, 25);
7607
- const ch = e & f ^ ~e & g;
7608
- const temp1 = h + s1 + ch + SHA256_ROUND_CONSTANTS[index] + words[index] >>> 0;
7609
- const s0 = rightRotate32(a, 2) ^ rightRotate32(a, 13) ^ rightRotate32(a, 22);
7610
- const maj = a & b ^ a & c ^ b & c;
7611
- const temp2 = s0 + maj >>> 0;
7612
- h = g;
7613
- g = f;
7614
- f = e;
7615
- e = d + temp1 >>> 0;
7616
- d = c;
7617
- c = b;
7618
- b = a;
7619
- a = temp1 + temp2 >>> 0;
7620
- }
7621
- hash[0] = hash[0] + a >>> 0;
7622
- hash[1] = hash[1] + b >>> 0;
7623
- hash[2] = hash[2] + c >>> 0;
7624
- hash[3] = hash[3] + d >>> 0;
7625
- hash[4] = hash[4] + e >>> 0;
7626
- hash[5] = hash[5] + f >>> 0;
7627
- hash[6] = hash[6] + g >>> 0;
7628
- hash[7] = hash[7] + h >>> 0;
7629
- }
7630
- return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
7631
- }
7632
- function sanitizeIdentifierPart(value) {
7633
- return value.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
7634
- }
7635
- function validateIdentifierPart(rawValue, label, maxLength) {
7636
- const sanitized = sanitizeIdentifierPart(rawValue);
7637
- if (!sanitized) {
7638
- throw new Error(
7639
- `${label} must contain at least one letter or number after normalization. Use only letters, numbers, underscores, or hyphens.`
7640
- );
7641
- }
7642
- if (sanitized.length > maxLength) {
7643
- throw new Error(
7644
- `${label} is too long after normalization (${sanitized.length}/${maxLength}). Shorten it to ${maxLength} characters or fewer. Normalized value: "${sanitized}".`
7645
- );
7646
- }
7647
- return sanitized;
7648
- }
7649
- function normalizePlayName(value) {
7650
- if (value.includes("/")) {
7651
- throw new Error(
7652
- 'Play name cannot contain "/". Slash is reserved for qualified play references like "prebuilt/example" or "self/example".'
7653
- );
7654
- }
7655
- return validateIdentifierPart(value, "Play name", PLAY_NAME_MAX_LENGTH);
7656
- }
7657
- function normalizePlayNameForSheet(value) {
7658
- if (!value.includes("/")) {
7659
- return normalizePlayName(value);
7660
- }
7661
- const digest = sha256Hex(value).slice(0, 12);
7662
- const normalizedReference = sanitizeIdentifierPart(
7663
- value.replace(/\//g, "__")
7664
- );
7665
- const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
7666
- const prefix = normalizedReference.slice(0, prefixLength).replace(/_+$/g, "") || "qualified_play";
7667
- return `${prefix}_${digest}`;
7668
- }
7669
- function normalizeTableNamespace(value) {
7670
- return validateIdentifierPart(
7671
- value,
7672
- "ctx.dataset() key",
7673
- MAP_KEY_NAMESPACE_MAX_LENGTH
7674
- );
7675
- }
7676
- function validatePlaySheetTableName(playName, tableNamespace) {
7677
- const playSegment = normalizePlayNameForSheet(playName);
7678
- const keySegment = normalizeTableNamespace(tableNamespace);
7679
- const resolved = `${playSegment}_${keySegment}`;
7680
- if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
7681
- throw new Error(
7682
- `Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.dataset() key. Resolved table name: "${resolved}".`
7683
- );
7684
- }
7685
- return resolved;
7686
- }
7687
-
7688
7616
  // src/cli/trace.ts
7689
7617
  var cliTraceStartedAt = Date.now();
7690
7618
  function isTruthyEnv(value) {
@@ -7735,6 +7663,11 @@ async function traceCliSpan(phase, fields, run) {
7735
7663
 
7736
7664
  // src/cli/play-check-hints.ts
7737
7665
  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.";
7666
+ 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.";
7667
+ 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.";
7668
+ 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.";
7669
+ 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.";
7670
+ 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.";
7738
7671
  function sourceLineForError(sourceCode, error) {
7739
7672
  const match = error.match(/:(\d+):(\d+)\s/);
7740
7673
  const lineNumber = match?.[1] ? Number(match[1]) : NaN;
@@ -7745,21 +7678,65 @@ function looksLikeInvalidExtractedGetter(error, sourceLine) {
7745
7678
  if (!/Property '[^']+' does not exist on type/.test(error)) return false;
7746
7679
  return /\bextracted(?:Values|Lists)\s*\./.test(sourceLine);
7747
7680
  }
7681
+ function looksLikeDatasetApiMisuse(error, sourceLine) {
7682
+ return /Property '(?:rows|toArray|forEach|map|filter|reduce)' does not exist on type '[^']*PlayDataset/.test(
7683
+ error
7684
+ ) || /\b(?:\.rows|\.toArray\(\)|\.forEach\(|\.map\(|\.filter\(|\.reduce\()/.test(
7685
+ sourceLine
7686
+ );
7687
+ }
7688
+ function looksLikeRowPropertyMismatch(error, sourceLine) {
7689
+ if (!/Property '[^']+' does not exist on type/.test(error)) return false;
7690
+ if (looksLikeInvalidExtractedGetter(error, sourceLine)) return false;
7691
+ return /\brow\.[A-Za-z_][A-Za-z0-9_]*/.test(sourceLine);
7692
+ }
7693
+ function looksLikeToolsExecuteSignature(error, sourceLine) {
7694
+ return /ctx\.tools\.execute requires a request object/i.test(error) || /Expected 1 arguments?, but got [2-9]/.test(error) && /\btools\.execute\(/.test(sourceLine);
7695
+ }
7696
+ function looksLikeRunPlaySignature(error, sourceLine) {
7697
+ return /ctx\.runPlay/i.test(error) || /(?:Expected|Argument of type|No overload matches)/.test(error) && /\brunPlay\(/.test(sourceLine);
7698
+ }
7699
+ function looksLikeMapBackedChild(error) {
7700
+ return /map-backed child play|direct-run-only|cannot call a map-backed|own durable table/i.test(
7701
+ error
7702
+ );
7703
+ }
7704
+ function hintForError(error, sourceLine) {
7705
+ if (looksLikeInvalidExtractedGetter(error, sourceLine)) {
7706
+ return EXTRACTED_GETTER_ERROR_HINT;
7707
+ }
7708
+ if (looksLikeDatasetApiMisuse(error, sourceLine)) {
7709
+ return DATASET_API_HINT;
7710
+ }
7711
+ if (looksLikeToolsExecuteSignature(error, sourceLine)) {
7712
+ return TOOLS_EXECUTE_SIGNATURE_HINT;
7713
+ }
7714
+ if (looksLikeMapBackedChild(error)) {
7715
+ return MAP_BACKED_CHILD_HINT;
7716
+ }
7717
+ if (looksLikeRunPlaySignature(error, sourceLine)) {
7718
+ return RUN_PLAY_SIGNATURE_HINT;
7719
+ }
7720
+ if (looksLikeRowPropertyMismatch(error, sourceLine)) {
7721
+ return ROW_PROPERTY_HINT;
7722
+ }
7723
+ return null;
7724
+ }
7748
7725
  function addPlayCheckRepairHints(input2) {
7749
- let addedHint = false;
7726
+ const addedHints = /* @__PURE__ */ new Set();
7750
7727
  return input2.errors.map((error) => {
7751
7728
  const line = sourceLineForError(input2.sourceCode, error);
7752
- if (addedHint || !looksLikeInvalidExtractedGetter(error, line) || error.includes(EXTRACTED_GETTER_ERROR_HINT)) {
7729
+ const hint = hintForError(error, line);
7730
+ if (!hint || addedHints.has(hint) || error.includes(hint)) {
7753
7731
  return error;
7754
7732
  }
7755
- addedHint = true;
7733
+ addedHints.add(hint);
7756
7734
  return `${error}
7757
- ${EXTRACTED_GETTER_ERROR_HINT}`;
7735
+ ${hint}`;
7758
7736
  });
7759
7737
  }
7760
7738
 
7761
7739
  // src/cli/commands/play.ts
7762
- var PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS = 2500;
7763
7740
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
7764
7741
  "--json",
7765
7742
  "--wait",
@@ -7869,10 +7846,10 @@ function materializeRemotePlaySource(input2) {
7869
7846
  if (existingSource === input2.sourceCode) {
7870
7847
  return { path: outputPath, status: "unchanged", created: false };
7871
7848
  }
7872
- writeFileSync6(outputPath, input2.sourceCode, "utf-8");
7849
+ writeFileSync7(outputPath, input2.sourceCode, "utf-8");
7873
7850
  return { path: outputPath, status: "updated", created: false };
7874
7851
  }
7875
- writeFileSync6(outputPath, input2.sourceCode, "utf-8");
7852
+ writeFileSync7(outputPath, input2.sourceCode, "utf-8");
7876
7853
  return { path: outputPath, status: "created", created: true };
7877
7854
  }
7878
7855
  function formatLoadedPlayMessage(materializedFile) {
@@ -8311,8 +8288,6 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
8311
8288
  "cancelled"
8312
8289
  ]);
8313
8290
  var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
8314
- var PLAY_CUSTOMER_STORAGE_SCHEMA_NAME = "storage";
8315
- var PLAY_INTERNAL_STEP_RECEIPT_TABLE = "_deepline_step_receipts";
8316
8291
  function getEventPayload(event) {
8317
8292
  return event.payload && typeof event.payload === "object" ? event.payload : {};
8318
8293
  }
@@ -8366,36 +8341,8 @@ function getLogLinesFromLiveEvent(event) {
8366
8341
  const lines = getEventPayload(event).lines;
8367
8342
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
8368
8343
  }
8369
- function quoteSqlIdentifier(identifier) {
8370
- return `"${identifier.replace(/"/g, '""')}"`;
8371
- }
8372
- function quoteSqlLiteral(value) {
8373
- return `'${value.replace(/'/g, "''")}'`;
8374
- }
8375
- function buildDebugDbQueryCommand(sql) {
8376
- return `deepline db query --sql ${shellSingleQuote(sql)} --max-rows 20 --json`;
8377
- }
8378
- function buildStepReceiptsDebugCommand(runId) {
8379
- const table = `${quoteSqlIdentifier(
8380
- PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
8381
- )}.${quoteSqlIdentifier(PLAY_INTERNAL_STEP_RECEIPT_TABLE)}`;
8382
- 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`;
8383
- return buildDebugDbQueryCommand(sql);
8384
- }
8385
- function buildMapTableDebugCommand(input2) {
8386
- try {
8387
- const tableName = validatePlaySheetTableName(
8388
- input2.playName,
8389
- input2.tableNamespace
8390
- );
8391
- const table = `${quoteSqlIdentifier(
8392
- PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
8393
- )}.${quoteSqlIdentifier(tableName)}`;
8394
- const sql = `select * from ${table} where _run_id = ${quoteSqlLiteral(input2.runId)} limit 20`;
8395
- return buildDebugDbQueryCommand(sql);
8396
- } catch {
8397
- return null;
8398
- }
8344
+ function buildRunInspectCommand(runId) {
8345
+ return `deepline runs get ${runId} --full --json`;
8399
8346
  }
8400
8347
  function extractTableNamespaceFromLiveEvent(event) {
8401
8348
  const payload = getEventPayload(event);
@@ -8420,7 +8367,7 @@ function emitLiveDebugTableHints(input2) {
8420
8367
  if (!input2.state.emittedDebugKeys.has(receiptsKey)) {
8421
8368
  input2.state.emittedDebugKeys.add(receiptsKey);
8422
8369
  input2.progress.writeLine(
8423
- `Debug top-level outputs: ${buildStepReceiptsDebugCommand(input2.runId)}`,
8370
+ `Inspect run output: ${buildRunInspectCommand(input2.runId)}`,
8424
8371
  process.stdout
8425
8372
  );
8426
8373
  }
@@ -8432,17 +8379,9 @@ function emitLiveDebugTableHints(input2) {
8432
8379
  if (input2.state.emittedDebugKeys.has(tableKey)) {
8433
8380
  return;
8434
8381
  }
8435
- const command = buildMapTableDebugCommand({
8436
- playName: input2.playName,
8437
- runId: input2.runId,
8438
- tableNamespace
8439
- });
8440
- if (!command) {
8441
- return;
8442
- }
8443
8382
  input2.state.emittedDebugKeys.add(tableKey);
8444
8383
  input2.progress.writeLine(
8445
- `Debug rows for ${tableNamespace}: ${command}`,
8384
+ `Possible map table ${tableNamespace}: created only after this ctx.map(...).run(...) executes. Inspect returned datasets with ${buildRunInspectCommand(input2.runId)}`,
8446
8385
  process.stdout
8447
8386
  );
8448
8387
  }
@@ -8646,7 +8585,7 @@ async function waitForPlayCompletionByStream(input2) {
8646
8585
  }
8647
8586
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8648
8587
  throw new DeeplineError(
8649
- `Play live stream ended before the run reached a terminal state runId=${input2.workflowId}${phaseSuffix}.`,
8588
+ `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}'.`,
8650
8589
  void 0,
8651
8590
  "PLAY_LIVE_STREAM_ENDED",
8652
8591
  {
@@ -8702,10 +8641,6 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8702
8641
  let eventCount = 0;
8703
8642
  let firstRunIdMs = null;
8704
8643
  let lastPhase = null;
8705
- const startRequest = {
8706
- ...input2.request,
8707
- waitForCompletionMs: typeof input2.request.waitForCompletionMs === "number" ? input2.request.waitForCompletionMs : PLAY_START_STREAM_FAST_COMPLETION_WAIT_MS
8708
- };
8709
8644
  const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
8710
8645
  () => {
8711
8646
  timedOut = true;
@@ -8714,7 +8649,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8714
8649
  Math.max(1, input2.waitTimeoutMs)
8715
8650
  );
8716
8651
  try {
8717
- for await (const event of input2.client.startPlayRunStream(startRequest, {
8652
+ for await (const event of input2.client.startPlayRunStream(input2.request, {
8718
8653
  signal: controller.signal
8719
8654
  })) {
8720
8655
  eventCount += 1;
@@ -8807,7 +8742,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8807
8742
  const reason = error instanceof Error ? error.message : String(error);
8808
8743
  if (!input2.jsonOutput) {
8809
8744
  process.stderr.write(
8810
- `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
8745
+ `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to canonical run stream (${reason})
8811
8746
  `
8812
8747
  );
8813
8748
  }
@@ -8844,7 +8779,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8844
8779
  if (lastKnownWorkflowId) {
8845
8780
  if (!input2.jsonOutput) {
8846
8781
  input2.progress.writeLine(
8847
- `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
8782
+ `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to canonical run stream`
8848
8783
  );
8849
8784
  }
8850
8785
  recordCliTrace({
@@ -8874,7 +8809,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8874
8809
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8875
8810
  const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
8876
8811
  throw new DeeplineError(
8877
- `Play start stream ended before the run reached a terminal state${idSuffix}${phaseSuffix}.`,
8812
+ `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>'.`,
8878
8813
  void 0,
8879
8814
  "PLAY_START_STREAM_ENDED",
8880
8815
  {
@@ -9222,6 +9157,32 @@ function formatPlayErrorForDisplay(status, error) {
9222
9157
  }
9223
9158
  return error;
9224
9159
  }
9160
+ function isGenericInternalServerError(error) {
9161
+ if (!error) return false;
9162
+ return /^(?:internalservererror|internal server error|http 500|500)$/i.test(
9163
+ error.trim()
9164
+ );
9165
+ }
9166
+ function selectRunErrorForDisplay(status) {
9167
+ const progressError = getStringField(status.progress, "error");
9168
+ if (!isGenericInternalServerError(progressError)) {
9169
+ return progressError;
9170
+ }
9171
+ const directErrors = getRecordField(status, "errors");
9172
+ if (Array.isArray(directErrors)) {
9173
+ for (const entry of directErrors) {
9174
+ const message = getStringField(entry, "message");
9175
+ if (message && !isGenericInternalServerError(message)) {
9176
+ return message;
9177
+ }
9178
+ }
9179
+ }
9180
+ const directError = getStringField(status, "error");
9181
+ if (directError && !isGenericInternalServerError(directError)) {
9182
+ return directError;
9183
+ }
9184
+ return progressError;
9185
+ }
9225
9186
  function normalizeRunStatusForEnvelope(status) {
9226
9187
  const run = status.run ?? null;
9227
9188
  return {
@@ -9359,8 +9320,7 @@ function compactPlayStatus(status) {
9359
9320
  const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
9360
9321
  status.billing
9361
9322
  ) : null;
9362
- const progressError = status.progress?.error;
9363
- const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
9323
+ const error = selectRunErrorForDisplay(status) ?? (typeof status.error === "string" ? String(status.error) : null);
9364
9324
  const displayError = formatPlayErrorForDisplay(status, error);
9365
9325
  return {
9366
9326
  runId: status.runId,
@@ -9615,8 +9575,8 @@ function writePlayResult(status, jsonOutput, options) {
9615
9575
  for (const warning of warnings) {
9616
9576
  lines.push(` warning: ${warning}`);
9617
9577
  }
9618
- const progressError = status.progress?.error;
9619
- if (progressError && typeof progressError === "string") {
9578
+ const progressError = selectRunErrorForDisplay(status);
9579
+ if (progressError) {
9620
9580
  const billing = extractBillingForStatus(status, progressError);
9621
9581
  if (isInsufficientCreditsBilling(billing)) {
9622
9582
  lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
@@ -9668,9 +9628,6 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
9668
9628
  function shellSingleQuote(value) {
9669
9629
  return `'${value.replace(/'/g, `'\\''`)}'`;
9670
9630
  }
9671
- function sqlStringLiteral(value) {
9672
- return `'${value.replace(/'/g, "''")}'`;
9673
- }
9674
9631
  function runExportRetryCommand(runId, outPath, datasetPath) {
9675
9632
  return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve10(outPath))}`;
9676
9633
  }
@@ -9689,26 +9646,6 @@ function extractRunPlayName(status) {
9689
9646
  }
9690
9647
  return null;
9691
9648
  }
9692
- function normalizeCustomerDbIdentifier(value) {
9693
- return value.split("/").pop().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
9694
- }
9695
- function buildCustomerDbQueryPlan(input2) {
9696
- const playName = extractRunPlayName(input2.status);
9697
- const tableNamespace = input2.rowsInfo.tableNamespace?.trim();
9698
- if (!playName || !tableNamespace || input2.rowsInfo.totalRows <= 0) {
9699
- return null;
9700
- }
9701
- const tableName = `${normalizeCustomerDbIdentifier(playName)}_${normalizeCustomerDbIdentifier(
9702
- tableNamespace
9703
- )}`;
9704
- const sql = `select * from "storage"."${tableName}" where _run_id = ${sqlStringLiteral(input2.status.runId)} limit ${input2.rowsInfo.totalRows}`;
9705
- const base = `deepline customer-db query --sql ${shellSingleQuote(sql)} --max-rows ${input2.rowsInfo.totalRows}`;
9706
- return {
9707
- sql,
9708
- json: `${base} --json`,
9709
- csv: `${base} --format csv --out ${shellSingleQuote(resolve10(input2.outPath))}`
9710
- };
9711
- }
9712
9649
  function exportableSheetRow(row) {
9713
9650
  if (!row || typeof row !== "object" || Array.isArray(row)) {
9714
9651
  return null;
@@ -10055,8 +9992,12 @@ function renderServerResultView(value) {
10055
9992
  lines.push(
10056
9993
  ` ${String(table.tableNamespace ?? "table")}${lineLabel}: ${rowLabel}${details.join(" ")}`
10057
9994
  );
10058
- if (typeof table.queryDatasetCommand === "string") {
10059
- lines.push(` inspect rows: ${table.queryDatasetCommand}`);
9995
+ if (typeof table.queryDatasetCommand === "string" && typeof table.rowCount === "number" && table.rowCount > 0) {
9996
+ lines.push(` debug backing table: ${table.queryDatasetCommand}`);
9997
+ } else if (typeof table.queryDatasetCommand === "string") {
9998
+ lines.push(
9999
+ " no rows observed for this run; backing table is created only if this map ran"
10000
+ );
10060
10001
  }
10061
10002
  if (table.debugHelp && typeof table.debugHelp === "object" && !Array.isArray(table.debugHelp)) {
10062
10003
  const debugHelp = table.debugHelp;
@@ -10402,9 +10343,51 @@ function printToolGetterHints(hints) {
10402
10343
  async function handlePlayCheck(args) {
10403
10344
  const options = parsePlayCheckOptions(args);
10404
10345
  if (!isFileTarget(options.target)) {
10405
- const resolved = resolve10(options.target);
10406
- console.error(`File not found: ${resolved}`);
10407
- return 1;
10346
+ const client2 = new DeeplineClient();
10347
+ try {
10348
+ await assertCanonicalNamedPlayReference(client2, options.target);
10349
+ const play = await client2.describePlay(
10350
+ parseReferencedPlayTarget2(options.target).playName,
10351
+ { compact: true }
10352
+ );
10353
+ const result2 = {
10354
+ valid: true,
10355
+ target: options.target,
10356
+ name: play.name,
10357
+ reference: play.reference ?? options.target,
10358
+ origin: play.origin ?? null,
10359
+ ownerType: play.ownerType ?? null,
10360
+ inputSchema: play.inputSchema ?? null,
10361
+ outputSchema: play.outputSchema ?? null,
10362
+ staticPipeline: play.staticPipeline ?? null,
10363
+ note: "Named/prebuilt play contract is available. No run was started."
10364
+ };
10365
+ if (options.jsonOutput) {
10366
+ process.stdout.write(`${JSON.stringify(result2)}
10367
+ `);
10368
+ } else {
10369
+ console.log(`\u2713 ${result2.reference} passed named play contract check`);
10370
+ console.log(" no run started; no Deepline credits spent");
10371
+ if (play.runCommand) console.log(` run: ${play.runCommand}`);
10372
+ }
10373
+ return 0;
10374
+ } catch (error) {
10375
+ const resolved = resolve10(options.target);
10376
+ const message = error instanceof Error && error.message ? error.message : `File not found: ${resolved}`;
10377
+ if (options.jsonOutput) {
10378
+ process.stdout.write(
10379
+ `${JSON.stringify({
10380
+ valid: false,
10381
+ target: options.target,
10382
+ errors: [message]
10383
+ })}
10384
+ `
10385
+ );
10386
+ } else {
10387
+ console.error(message);
10388
+ }
10389
+ return 1;
10390
+ }
10408
10391
  }
10409
10392
  const absolutePlayPath = resolve10(options.target);
10410
10393
  const sourceCode = readFileSync6(absolutePlayPath, "utf-8");
@@ -10941,7 +10924,7 @@ async function handleRunLogs(args) {
10941
10924
  const status = await client.runs.get(runId);
10942
10925
  const logs = status.progress?.logs ?? [];
10943
10926
  if (outPath) {
10944
- writeFileSync6(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
10927
+ writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
10945
10928
  printCommandEnvelope(
10946
10929
  {
10947
10930
  runId: status.runId,
@@ -11051,18 +11034,9 @@ async function handleRunExport(args) {
11051
11034
  datasetPath
11052
11035
  });
11053
11036
  const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
11054
- const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
11055
- status,
11056
- rowsInfo: exportResult.rowsInfo,
11057
- outPath
11058
- }) : null;
11059
11037
  const next = {
11060
11038
  ...buildRunNextCommands(status),
11061
- export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
11062
- ...queryPlan ? {
11063
- queryJson: queryPlan.json,
11064
- queryCsv: queryPlan.csv
11065
- } : {}
11039
+ export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source)
11066
11040
  };
11067
11041
  const payload = {
11068
11042
  runId: status.runId,
@@ -11071,13 +11045,6 @@ async function handleRunExport(args) {
11071
11045
  source,
11072
11046
  rowCount: exportResult?.rowsInfo.totalRows ?? null,
11073
11047
  columns: exportResult?.rowsInfo.columns ?? [],
11074
- ...queryPlan ? {
11075
- query: {
11076
- sql: queryPlan.sql,
11077
- json: queryPlan.json,
11078
- csv: queryPlan.csv
11079
- }
11080
- } : {},
11081
11048
  ...metadataOutPath ? { metadata_path: metadataOutPath } : {},
11082
11049
  local: { csv_path: exportResult?.path ?? null },
11083
11050
  next,
@@ -11087,15 +11054,14 @@ async function handleRunExport(args) {
11087
11054
  title: "run export",
11088
11055
  lines: [
11089
11056
  `Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
11090
- ...source ? [`source=${source}`] : [],
11091
- ...queryPlan ? [`query=${queryPlan.json}`] : []
11057
+ ...source ? [`source=${source}`] : []
11092
11058
  ]
11093
11059
  }
11094
11060
  ]
11095
11061
  }
11096
11062
  };
11097
11063
  if (metadataOutPath) {
11098
- writeFileSync6(
11064
+ writeFileSync7(
11099
11065
  metadataOutPath,
11100
11066
  `${JSON.stringify(payload, null, 2)}
11101
11067
  `,
@@ -11268,7 +11234,8 @@ function parsePlaySearchOptions(args) {
11268
11234
  return {
11269
11235
  query,
11270
11236
  jsonOutput: argsWantJson(args),
11271
- compact: args.includes("--compact")
11237
+ compact: args.includes("--compact"),
11238
+ prebuiltOnly: args.includes("--prebuilt")
11272
11239
  };
11273
11240
  }
11274
11241
  function printPlayDescription(play) {
@@ -11316,6 +11283,7 @@ function printPlayDescription(play) {
11316
11283
  console.log(` ${line}`);
11317
11284
  }
11318
11285
  }
11286
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11319
11287
  console.log(` Run: ${play.runCommand}`);
11320
11288
  const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
11321
11289
  if (cloneEditStarter) {
@@ -11323,6 +11291,31 @@ function printPlayDescription(play) {
11323
11291
  console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
11324
11292
  }
11325
11293
  }
11294
+ function inputFieldNames(schema) {
11295
+ const fields = Array.isArray(schema?.fields) ? schema.fields : [];
11296
+ return fields.map(
11297
+ (field) => field && typeof field === "object" ? String(field.name ?? "").trim() : ""
11298
+ ).filter(Boolean);
11299
+ }
11300
+ function printCompactPlaySearchResult(play) {
11301
+ const reference = formatPlayListReference(play);
11302
+ const aliases = play.aliases.slice(0, 6).join(", ");
11303
+ console.log(`Play: ${reference}`);
11304
+ if (play.displayName && play.displayName !== play.name) {
11305
+ console.log(` Display name: ${play.displayName}`);
11306
+ }
11307
+ if (aliases) {
11308
+ console.log(` Aliases: ${aliases}`);
11309
+ }
11310
+ const fields = inputFieldNames(play.inputSchema);
11311
+ if (fields.length > 0) {
11312
+ console.log(` Inputs: ${fields.join(", ")}`);
11313
+ } else if (play.inputSchema) {
11314
+ console.log(" Inputs: see describe");
11315
+ }
11316
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11317
+ console.log(` Run: ${play.runCommand}`);
11318
+ }
11326
11319
  function compactPlaySchema(schema) {
11327
11320
  if (!schema) return null;
11328
11321
  const fields = Array.isArray(schema.fields) ? schema.fields.map(
@@ -11386,22 +11379,44 @@ async function handlePlaySearch(args) {
11386
11379
  return 1;
11387
11380
  }
11388
11381
  const client = new DeeplineClient();
11389
- const plays = await client.searchPlays({
11382
+ const plays = (await client.searchPlays({
11390
11383
  query: options.query,
11391
11384
  compact: options.compact
11392
- });
11385
+ })).filter(
11386
+ (play) => options.prebuiltOnly ? play.origin === "prebuilt" || play.ownerType === "deepline" : true
11387
+ );
11393
11388
  if (options.jsonOutput) {
11394
- process.stdout.write(`${JSON.stringify({ plays })}
11395
- `);
11389
+ const jsonPlays = options.prebuiltOnly ? plays.map((play) => ({
11390
+ ...play,
11391
+ inputSchema: compactPlaySchema(play.inputSchema)
11392
+ })) : plays;
11393
+ process.stdout.write(
11394
+ `${JSON.stringify({
11395
+ plays: jsonPlays,
11396
+ total: jsonPlays.length,
11397
+ truncated: false
11398
+ })}
11399
+ `
11400
+ );
11396
11401
  return 0;
11397
11402
  }
11398
- process.stdout.write(`${plays.length} plays found:
11403
+ const displayPlays = options.prebuiltOnly ? plays.slice(0, 5) : plays;
11404
+ process.stdout.write(
11405
+ `${plays.length} plays found${options.prebuiltOnly && plays.length > displayPlays.length ? `; showing top ${displayPlays.length}` : ""}:
11399
11406
 
11400
- `);
11401
- for (const play of plays) {
11402
- printPlayDescription(play);
11407
+ `
11408
+ );
11409
+ for (const play of displayPlays) {
11410
+ if (options.prebuiltOnly) {
11411
+ printCompactPlaySearchResult(play);
11412
+ } else {
11413
+ printPlayDescription(play);
11414
+ }
11403
11415
  console.log("");
11404
11416
  }
11417
+ if (options.prebuiltOnly && plays.length > displayPlays.length) {
11418
+ console.log("Use --json for the full machine-readable result set.");
11419
+ }
11405
11420
  return 0;
11406
11421
  }
11407
11422
  function normalizePlayGrepText(value) {
@@ -11668,15 +11683,18 @@ Common commands:
11668
11683
  deepline plays get person-linkedin-to-email --json
11669
11684
  `
11670
11685
  );
11671
- play.command("check <target>").description("Bundle-check a local play file.").addHelpText(
11686
+ play.command("check <target>").description("Check a local play file or named/prebuilt play contract.").addHelpText(
11672
11687
  "after",
11673
11688
  `
11674
11689
  Notes:
11675
11690
  Validates a local play without storing it, promoting it, or starting a run.
11676
11691
  This uses the authoritative cloud preflight path.
11692
+ For named or prebuilt plays, validates that the contract is discoverable
11693
+ without starting a run or spending Deepline credits.
11677
11694
 
11678
11695
  Examples:
11679
11696
  deepline plays check my.play.ts
11697
+ deepline plays check prebuilt/name-and-domain-to-email-waterfall-batch
11680
11698
  deepline plays check my.play.ts --json
11681
11699
  `
11682
11700
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
@@ -11826,11 +11844,13 @@ Examples:
11826
11844
  ...options.json ? ["--json"] : []
11827
11845
  ]);
11828
11846
  });
11829
- const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
11847
+ const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").option("--prebuilt", "Only show Deepline-managed prebuilt plays").addHelpText(
11830
11848
  "after",
11831
11849
  `
11832
11850
  Notes:
11833
11851
  Ranked discovery for workflows. Use describe on a result before running it.
11852
+ Prefer --prebuilt for new GTM tasks so old workspace scratchpads do not
11853
+ outrank Deepline-managed routes unless the user names one explicitly.
11834
11854
  The grep alias is the same ranked retrieval surface with a more literal name
11835
11855
  for agents that are filtering the play registry.
11836
11856
 
@@ -11842,6 +11862,7 @@ Examples:
11842
11862
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
11843
11863
  process.exitCode = await handlePlaySearch([
11844
11864
  query,
11865
+ ...options.prebuilt ? ["--prebuilt"] : [],
11845
11866
  ...options.compact ? ["--compact"] : [],
11846
11867
  ...options.json ? ["--json"] : []
11847
11868
  ]);
@@ -12089,7 +12110,7 @@ Notes:
12089
12110
  Writes a returned dataset handle to the requested local CSV path. Use runs get
12090
12111
  first to inspect dataset paths like result.rows or result.nested.contacts.
12091
12112
  --metadata-out writes the same export metadata object returned by --json,
12092
- including source and follow-on customer-db query commands when available.
12113
+ including the source dataset path and row/column metadata.
12093
12114
 
12094
12115
  Examples:
12095
12116
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
@@ -13796,12 +13817,12 @@ Examples:
13796
13817
 
13797
13818
  // src/cli/commands/tools.ts
13798
13819
  import { Option } from "commander";
13799
- import { chmodSync, mkdtempSync, writeFileSync as writeFileSync8 } from "fs";
13820
+ import { chmodSync, mkdtempSync, writeFileSync as writeFileSync9 } from "fs";
13800
13821
  import { tmpdir as tmpdir4 } from "os";
13801
13822
  import { join as join10 } from "path";
13802
13823
 
13803
13824
  // src/tool-output.ts
13804
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
13825
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync8 } from "fs";
13805
13826
  import { homedir as homedir5 } from "os";
13806
13827
  import { join as join9 } from "path";
13807
13828
  function isPlainObject(value) {
@@ -13906,7 +13927,7 @@ function ensureOutputDir() {
13906
13927
  function writeJsonOutputFile(payload, stem) {
13907
13928
  const outputDir = ensureOutputDir();
13908
13929
  const outputPath = join9(outputDir, `${stem}_${Date.now()}.json`);
13909
- writeFileSync7(outputPath, JSON.stringify(payload, null, 2), "utf-8");
13930
+ writeFileSync8(outputPath, JSON.stringify(payload, null, 2), "utf-8");
13910
13931
  return outputPath;
13911
13932
  }
13912
13933
  function writeCsvOutputFile(rows, stem) {
@@ -13934,7 +13955,7 @@ function writeCsvOutputFile(rows, stem) {
13934
13955
  for (const row of rows) {
13935
13956
  lines.push(columns.map((column) => escapeCell(row[column])).join(","));
13936
13957
  }
13937
- writeFileSync7(outputPath, `${lines.join("\n")}
13958
+ writeFileSync8(outputPath, `${lines.join("\n")}
13938
13959
  `, "utf-8");
13939
13960
  const previewRows = rows.slice(0, 5);
13940
13961
  const previewColumns = columns.slice(0, 5);
@@ -14020,7 +14041,7 @@ async function listTools(args) {
14020
14041
  const client = new DeeplineClient();
14021
14042
  const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
14022
14043
  const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
14023
- const compact = args.includes("--compact");
14044
+ const compact = args.includes("--compact") || !args.includes("--json");
14024
14045
  const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
14025
14046
  const items = (await client.listTools({
14026
14047
  ...categoryFilter ? { categories: categoryFilter } : {}
@@ -14076,9 +14097,10 @@ async function searchTools(queryInput, options = {}) {
14076
14097
  searchMode: options.searchMode,
14077
14098
  includeSearchDebug: options.includeSearchDebug
14078
14099
  });
14079
- const payload = options.compact && Array.isArray(result.tools) ? {
14100
+ const shouldCompact = options.compact || !options.json;
14101
+ const payload = shouldCompact && Array.isArray(result.tools) ? {
14080
14102
  ...result,
14081
- tools: result.tools.map(compactTool)
14103
+ tools: result.tools.slice(0, 8).map(compactTool)
14082
14104
  } : result;
14083
14105
  printCommandEnvelope(payload, {
14084
14106
  json: options.json || shouldEmitJson()
@@ -14119,7 +14141,8 @@ async function grepTools(queryInput, options = {}) {
14119
14141
  mode
14120
14142
  )
14121
14143
  );
14122
- const outputTools = options.compact ? tools.map(compactTool) : tools;
14144
+ const shouldCompact = options.compact || !options.json;
14145
+ const outputTools = shouldCompact ? tools.slice(0, 8).map(compactTool) : tools;
14123
14146
  printCommandEnvelope(
14124
14147
  {
14125
14148
  tools: outputTools,
@@ -14138,11 +14161,19 @@ async function grepTools(queryInput, options = {}) {
14138
14161
  );
14139
14162
  return 0;
14140
14163
  }
14164
+ function numericToolField(tool, field) {
14165
+ const value = tool[field];
14166
+ return typeof value === "number" ? value : void 0;
14167
+ }
14141
14168
  function compactTool(tool) {
14142
14169
  const listed = toListedTool(tool);
14170
+ const searchScore = numericToolField(tool, "searchScore");
14171
+ const search_score = numericToolField(tool, "search_score");
14143
14172
  return {
14144
14173
  id: listed.id,
14145
14174
  toolId: listed.toolId,
14175
+ ...search_score !== void 0 ? { search_score } : {},
14176
+ ...searchScore !== void 0 ? { searchScore } : {},
14146
14177
  provider: listed.provider,
14147
14178
  displayName: listed.displayName,
14148
14179
  description: listed.description,
@@ -14463,7 +14494,7 @@ async function getTool(toolId, options = {}) {
14463
14494
  }
14464
14495
  if (shouldEmitJson()) {
14465
14496
  process.stdout.write(
14466
- `${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
14497
+ `${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
14467
14498
  `
14468
14499
  );
14469
14500
  return 0;
@@ -14788,8 +14819,8 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
14788
14819
  invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
14789
14820
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
14790
14821
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
14791
- forPlayGetterBugs: "Run the play, then inspect the emitted table commands from runs get. Use deepline db query against the run tables before editing getters.",
14792
- executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run tables."
14822
+ 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.",
14823
+ executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run output and returned dataset handles."
14793
14824
  },
14794
14825
  starterScript: {
14795
14826
  path: starterScript.path,
@@ -15040,7 +15071,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
15040
15071
  };
15041
15072
  });
15042
15073
  `;
15043
- writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
15074
+ writeFileSync9(scriptPath, script, { encoding: "utf-8", mode: 384 });
15044
15075
  return {
15045
15076
  path: scriptPath,
15046
15077
  sourceCode: script,
@@ -15596,7 +15627,7 @@ import {
15596
15627
  readdirSync as readdirSync2,
15597
15628
  readFileSync as readFileSync7,
15598
15629
  statSync as statSync2,
15599
- writeFileSync as writeFileSync9
15630
+ writeFileSync as writeFileSync10
15600
15631
  } from "fs";
15601
15632
  import { homedir as homedir6 } from "os";
15602
15633
  import { dirname as dirname10, join as join12 } from "path";
@@ -15630,7 +15661,7 @@ function readLocalSkillsVersion(baseUrl) {
15630
15661
  function writeLocalSkillsVersion(baseUrl, version) {
15631
15662
  const path = sdkSkillsVersionPath(baseUrl);
15632
15663
  mkdirSync5(dirname10(path), { recursive: true });
15633
- writeFileSync9(path, `${version}
15664
+ writeFileSync10(path, `${version}
15634
15665
  `, "utf-8");
15635
15666
  }
15636
15667
  function installedSdkSkillHasStalePositionalExecuteExamples() {
@@ -15813,6 +15844,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
15813
15844
  }
15814
15845
 
15815
15846
  // src/cli/index.ts
15847
+ var PREFLIGHT_TIMEOUT_MS = 3e3;
15816
15848
  function asCommanderError(error) {
15817
15849
  if (!(error instanceof Error) || !("code" in error)) {
15818
15850
  return null;
@@ -15893,6 +15925,124 @@ async function runPlayRunnerHealthCheck() {
15893
15925
  await rm2(dir, { recursive: true, force: true });
15894
15926
  }
15895
15927
  }
15928
+ function pickString(value, ...keys) {
15929
+ for (const key of keys) {
15930
+ const candidate = value[key];
15931
+ if (typeof candidate === "string" && candidate.trim()) {
15932
+ return candidate;
15933
+ }
15934
+ }
15935
+ return null;
15936
+ }
15937
+ function preflightErrorMessage(error) {
15938
+ return error instanceof Error ? error.message : String(error);
15939
+ }
15940
+ async function runPreflightCheck() {
15941
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
15942
+ const healthController = new AbortController();
15943
+ const healthTimeout = setTimeout(
15944
+ () => healthController.abort(),
15945
+ PREFLIGHT_TIMEOUT_MS
15946
+ );
15947
+ const health = await fetch(new URL("/api/v2/health", baseUrl), {
15948
+ signal: healthController.signal
15949
+ }).then(async (response) => {
15950
+ const payload = await response.json().catch(() => ({}));
15951
+ return {
15952
+ status: response.ok ? pickString(payload, "status") ?? "ok" : "unreachable",
15953
+ version: pickString(payload, "version")
15954
+ };
15955
+ }).catch(() => ({ status: "unreachable", version: null })).finally(() => clearTimeout(healthTimeout));
15956
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl);
15957
+ const http = apiKey ? new HttpClient(
15958
+ resolveConfig({
15959
+ baseUrl,
15960
+ apiKey,
15961
+ timeout: PREFLIGHT_TIMEOUT_MS,
15962
+ maxRetries: 0
15963
+ })
15964
+ ) : null;
15965
+ const [auth, billing] = http ? await Promise.all([
15966
+ http.post("/api/v2/auth/cli/status", {
15967
+ api_key: apiKey,
15968
+ reveal: false
15969
+ }).catch((error) => ({
15970
+ status: "not_connected",
15971
+ connected: false,
15972
+ error: preflightErrorMessage(error)
15973
+ })),
15974
+ http.get("/api/v2/billing/balance").catch(
15975
+ (error) => ({
15976
+ balance: null,
15977
+ balance_display: "unavailable",
15978
+ balance_status: "unknown",
15979
+ error: preflightErrorMessage(error)
15980
+ })
15981
+ )
15982
+ ]) : [
15983
+ {
15984
+ status: "not_connected",
15985
+ connected: false,
15986
+ error: "No API key found. Run: deepline auth register"
15987
+ },
15988
+ {
15989
+ balance: null,
15990
+ balance_display: "unavailable until authenticated",
15991
+ balance_status: "unknown"
15992
+ }
15993
+ ];
15994
+ const authStatus = pickString(auth, "status") ?? "unknown";
15995
+ const billingRecord = billing;
15996
+ const balanceDisplay = pickString(billing, "balance_display", "balanceDisplay") ?? `${String(billingRecord.balance ?? 0)} Deepline Credits`;
15997
+ const balanceStatus = pickString(billing, "balance_status", "balanceStatus") ?? "unknown";
15998
+ return {
15999
+ status: health.status === "ok" && (authStatus === "connected" || authStatus === "claimed") ? "ok" : "check",
16000
+ host: baseUrl,
16001
+ health: {
16002
+ status: health.status,
16003
+ version: health.version ?? null
16004
+ },
16005
+ auth: {
16006
+ status: authStatus,
16007
+ connected: authStatus === "connected" || authStatus === "claimed",
16008
+ org_id: pickString(auth, "org_id", "orgId"),
16009
+ org_name: pickString(auth, "org_name", "orgName"),
16010
+ rate_limit_tier: pickString(auth, "rate_limit_tier", "rateLimitTier"),
16011
+ error: pickString(auth, "error")
16012
+ },
16013
+ billing: {
16014
+ balance: billingRecord.balance ?? null,
16015
+ balance_display: balanceDisplay,
16016
+ rough_usd_balance: billingRecord.rough_usd_balance ?? billingRecord.roughUsdBalance ?? null,
16017
+ balance_status: balanceStatus,
16018
+ error: pickString(billing, "error")
16019
+ },
16020
+ render: {
16021
+ sections: [
16022
+ {
16023
+ title: "preflight",
16024
+ lines: [
16025
+ `host: ${baseUrl}`,
16026
+ `health: ${health.status}`,
16027
+ `auth: ${authStatus}`,
16028
+ `billing: ${balanceDisplay} (${balanceStatus})`
16029
+ ]
16030
+ }
16031
+ ]
16032
+ }
16033
+ };
16034
+ }
16035
+ function printPreflightHuman(data) {
16036
+ const render = data.render;
16037
+ const lines = render?.sections?.flatMap((section) => section.lines ?? []);
16038
+ if (lines?.length) {
16039
+ process.stdout.write(`${lines.join("\n")}
16040
+ `);
16041
+ return;
16042
+ }
16043
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16044
+ `);
16045
+ }
15896
16046
  async function main() {
15897
16047
  const mainStartedAt = Date.now();
15898
16048
  recordCliTrace({
@@ -15909,6 +16059,7 @@ async function main() {
15909
16059
  "after",
15910
16060
  `
15911
16061
  Common commands:
16062
+ deepline preflight
15912
16063
  deepline health
15913
16064
  deepline auth status --json
15914
16065
  deepline plays search email --json
@@ -15930,7 +16081,6 @@ Output:
15930
16081
 
15931
16082
  Safety:
15932
16083
  Commands that mutate state, open a browser, write files, stop work, or spend credits say so in their help.
15933
- Use --no-open where available for CI and agent runs.
15934
16084
 
15935
16085
  Exit codes:
15936
16086
  0 success; 2 usage/local input error; 3 auth/permission error; 4 not found;
@@ -15972,6 +16122,26 @@ Exit codes:
15972
16122
  registerDbCommands(program);
15973
16123
  registerFeedbackCommands(program);
15974
16124
  registerUpdateCommand(program);
16125
+ program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
16126
+ "after",
16127
+ `
16128
+ Notes:
16129
+ Read-only setup check for the configured Deepline host. Shows server health,
16130
+ auth connection, and customer-visible Deepline balance in one compact command.
16131
+
16132
+ Examples:
16133
+ deepline preflight
16134
+ deepline preflight --json
16135
+ `
16136
+ ).action(async (options) => {
16137
+ const data = await runPreflightCheck();
16138
+ if (shouldEmitJson(options.json)) {
16139
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16140
+ `);
16141
+ } else {
16142
+ printPreflightHuman(data);
16143
+ }
16144
+ });
15975
16145
  program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
15976
16146
  "--play-runner",
15977
16147
  "Run a tiny no-provider play to verify the full play execution plane."