deepline 0.1.77 → 0.1.78

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.78",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.77",
212
+ latest: "0.1.78",
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,16 +7678,61 @@ 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
 
@@ -7869,10 +7847,10 @@ function materializeRemotePlaySource(input2) {
7869
7847
  if (existingSource === input2.sourceCode) {
7870
7848
  return { path: outputPath, status: "unchanged", created: false };
7871
7849
  }
7872
- writeFileSync6(outputPath, input2.sourceCode, "utf-8");
7850
+ writeFileSync7(outputPath, input2.sourceCode, "utf-8");
7873
7851
  return { path: outputPath, status: "updated", created: false };
7874
7852
  }
7875
- writeFileSync6(outputPath, input2.sourceCode, "utf-8");
7853
+ writeFileSync7(outputPath, input2.sourceCode, "utf-8");
7876
7854
  return { path: outputPath, status: "created", created: true };
7877
7855
  }
7878
7856
  function formatLoadedPlayMessage(materializedFile) {
@@ -8311,8 +8289,6 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
8311
8289
  "cancelled"
8312
8290
  ]);
8313
8291
  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
8292
  function getEventPayload(event) {
8317
8293
  return event.payload && typeof event.payload === "object" ? event.payload : {};
8318
8294
  }
@@ -8366,36 +8342,8 @@ function getLogLinesFromLiveEvent(event) {
8366
8342
  const lines = getEventPayload(event).lines;
8367
8343
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
8368
8344
  }
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
- }
8345
+ function buildRunInspectCommand(runId) {
8346
+ return `deepline runs get ${runId} --full --json`;
8399
8347
  }
8400
8348
  function extractTableNamespaceFromLiveEvent(event) {
8401
8349
  const payload = getEventPayload(event);
@@ -8420,7 +8368,7 @@ function emitLiveDebugTableHints(input2) {
8420
8368
  if (!input2.state.emittedDebugKeys.has(receiptsKey)) {
8421
8369
  input2.state.emittedDebugKeys.add(receiptsKey);
8422
8370
  input2.progress.writeLine(
8423
- `Debug top-level outputs: ${buildStepReceiptsDebugCommand(input2.runId)}`,
8371
+ `Inspect run output: ${buildRunInspectCommand(input2.runId)}`,
8424
8372
  process.stdout
8425
8373
  );
8426
8374
  }
@@ -8432,17 +8380,9 @@ function emitLiveDebugTableHints(input2) {
8432
8380
  if (input2.state.emittedDebugKeys.has(tableKey)) {
8433
8381
  return;
8434
8382
  }
8435
- const command = buildMapTableDebugCommand({
8436
- playName: input2.playName,
8437
- runId: input2.runId,
8438
- tableNamespace
8439
- });
8440
- if (!command) {
8441
- return;
8442
- }
8443
8383
  input2.state.emittedDebugKeys.add(tableKey);
8444
8384
  input2.progress.writeLine(
8445
- `Debug rows for ${tableNamespace}: ${command}`,
8385
+ `Possible map table ${tableNamespace}: created only after this ctx.map(...).run(...) executes. Inspect returned datasets with ${buildRunInspectCommand(input2.runId)}`,
8446
8386
  process.stdout
8447
8387
  );
8448
8388
  }
@@ -8646,7 +8586,7 @@ async function waitForPlayCompletionByStream(input2) {
8646
8586
  }
8647
8587
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8648
8588
  throw new DeeplineError(
8649
- `Play live stream ended before the run reached a terminal state runId=${input2.workflowId}${phaseSuffix}.`,
8589
+ `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
8590
  void 0,
8651
8591
  "PLAY_LIVE_STREAM_ENDED",
8652
8592
  {
@@ -8807,7 +8747,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8807
8747
  const reason = error instanceof Error ? error.message : String(error);
8808
8748
  if (!input2.jsonOutput) {
8809
8749
  process.stderr.write(
8810
- `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to run stream (${reason})
8750
+ `[play watch] start stream failed after run ${lastKnownWorkflowId}; reconnecting to canonical run stream (${reason})
8811
8751
  `
8812
8752
  );
8813
8753
  }
@@ -8844,7 +8784,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8844
8784
  if (lastKnownWorkflowId) {
8845
8785
  if (!input2.jsonOutput) {
8846
8786
  input2.progress.writeLine(
8847
- `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to run stream`
8787
+ `[play watch] start stream ended after run ${lastKnownWorkflowId}; reconnecting to canonical run stream`
8848
8788
  );
8849
8789
  }
8850
8790
  recordCliTrace({
@@ -8874,7 +8814,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
8874
8814
  const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8875
8815
  const idSuffix = lastKnownWorkflowId ? ` runId=${lastKnownWorkflowId}` : "";
8876
8816
  throw new DeeplineError(
8877
- `Play start stream ended before the run reached a terminal state${idSuffix}${phaseSuffix}.`,
8817
+ `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
8818
  void 0,
8879
8819
  "PLAY_START_STREAM_ENDED",
8880
8820
  {
@@ -9222,6 +9162,32 @@ function formatPlayErrorForDisplay(status, error) {
9222
9162
  }
9223
9163
  return error;
9224
9164
  }
9165
+ function isGenericInternalServerError(error) {
9166
+ if (!error) return false;
9167
+ return /^(?:internalservererror|internal server error|http 500|500)$/i.test(
9168
+ error.trim()
9169
+ );
9170
+ }
9171
+ function selectRunErrorForDisplay(status) {
9172
+ const progressError = getStringField(status.progress, "error");
9173
+ if (!isGenericInternalServerError(progressError)) {
9174
+ return progressError;
9175
+ }
9176
+ const directErrors = getRecordField(status, "errors");
9177
+ if (Array.isArray(directErrors)) {
9178
+ for (const entry of directErrors) {
9179
+ const message = getStringField(entry, "message");
9180
+ if (message && !isGenericInternalServerError(message)) {
9181
+ return message;
9182
+ }
9183
+ }
9184
+ }
9185
+ const directError = getStringField(status, "error");
9186
+ if (directError && !isGenericInternalServerError(directError)) {
9187
+ return directError;
9188
+ }
9189
+ return progressError;
9190
+ }
9225
9191
  function normalizeRunStatusForEnvelope(status) {
9226
9192
  const run = status.run ?? null;
9227
9193
  return {
@@ -9359,8 +9325,7 @@ function compactPlayStatus(status) {
9359
9325
  const billing = status && typeof status === "object" ? stripProviderSpendFromBilling(
9360
9326
  status.billing
9361
9327
  ) : null;
9362
- const progressError = status.progress?.error;
9363
- const error = typeof progressError === "string" ? progressError : typeof status.error === "string" ? String(status.error) : null;
9328
+ const error = selectRunErrorForDisplay(status) ?? (typeof status.error === "string" ? String(status.error) : null);
9364
9329
  const displayError = formatPlayErrorForDisplay(status, error);
9365
9330
  return {
9366
9331
  runId: status.runId,
@@ -9615,8 +9580,8 @@ function writePlayResult(status, jsonOutput, options) {
9615
9580
  for (const warning of warnings) {
9616
9581
  lines.push(` warning: ${warning}`);
9617
9582
  }
9618
- const progressError = status.progress?.error;
9619
- if (progressError && typeof progressError === "string") {
9583
+ const progressError = selectRunErrorForDisplay(status);
9584
+ if (progressError) {
9620
9585
  const billing = extractBillingForStatus(status, progressError);
9621
9586
  if (isInsufficientCreditsBilling(billing)) {
9622
9587
  lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
@@ -9668,9 +9633,6 @@ var RUN_EXPORT_PAGE_SIZE = 5e3;
9668
9633
  function shellSingleQuote(value) {
9669
9634
  return `'${value.replace(/'/g, `'\\''`)}'`;
9670
9635
  }
9671
- function sqlStringLiteral(value) {
9672
- return `'${value.replace(/'/g, "''")}'`;
9673
- }
9674
9636
  function runExportRetryCommand(runId, outPath, datasetPath) {
9675
9637
  return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve10(outPath))}`;
9676
9638
  }
@@ -9689,26 +9651,6 @@ function extractRunPlayName(status) {
9689
9651
  }
9690
9652
  return null;
9691
9653
  }
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
9654
  function exportableSheetRow(row) {
9713
9655
  if (!row || typeof row !== "object" || Array.isArray(row)) {
9714
9656
  return null;
@@ -10055,8 +9997,12 @@ function renderServerResultView(value) {
10055
9997
  lines.push(
10056
9998
  ` ${String(table.tableNamespace ?? "table")}${lineLabel}: ${rowLabel}${details.join(" ")}`
10057
9999
  );
10058
- if (typeof table.queryDatasetCommand === "string") {
10059
- lines.push(` inspect rows: ${table.queryDatasetCommand}`);
10000
+ if (typeof table.queryDatasetCommand === "string" && typeof table.rowCount === "number" && table.rowCount > 0) {
10001
+ lines.push(` debug backing table: ${table.queryDatasetCommand}`);
10002
+ } else if (typeof table.queryDatasetCommand === "string") {
10003
+ lines.push(
10004
+ " no rows observed for this run; backing table is created only if this map ran"
10005
+ );
10060
10006
  }
10061
10007
  if (table.debugHelp && typeof table.debugHelp === "object" && !Array.isArray(table.debugHelp)) {
10062
10008
  const debugHelp = table.debugHelp;
@@ -10402,9 +10348,51 @@ function printToolGetterHints(hints) {
10402
10348
  async function handlePlayCheck(args) {
10403
10349
  const options = parsePlayCheckOptions(args);
10404
10350
  if (!isFileTarget(options.target)) {
10405
- const resolved = resolve10(options.target);
10406
- console.error(`File not found: ${resolved}`);
10407
- return 1;
10351
+ const client2 = new DeeplineClient();
10352
+ try {
10353
+ await assertCanonicalNamedPlayReference(client2, options.target);
10354
+ const play = await client2.describePlay(
10355
+ parseReferencedPlayTarget2(options.target).playName,
10356
+ { compact: true }
10357
+ );
10358
+ const result2 = {
10359
+ valid: true,
10360
+ target: options.target,
10361
+ name: play.name,
10362
+ reference: play.reference ?? options.target,
10363
+ origin: play.origin ?? null,
10364
+ ownerType: play.ownerType ?? null,
10365
+ inputSchema: play.inputSchema ?? null,
10366
+ outputSchema: play.outputSchema ?? null,
10367
+ staticPipeline: play.staticPipeline ?? null,
10368
+ note: "Named/prebuilt play contract is available. No run was started."
10369
+ };
10370
+ if (options.jsonOutput) {
10371
+ process.stdout.write(`${JSON.stringify(result2)}
10372
+ `);
10373
+ } else {
10374
+ console.log(`\u2713 ${result2.reference} passed named play contract check`);
10375
+ console.log(" no run started; no Deepline credits spent");
10376
+ if (play.runCommand) console.log(` run: ${play.runCommand}`);
10377
+ }
10378
+ return 0;
10379
+ } catch (error) {
10380
+ const resolved = resolve10(options.target);
10381
+ const message = error instanceof Error && error.message ? error.message : `File not found: ${resolved}`;
10382
+ if (options.jsonOutput) {
10383
+ process.stdout.write(
10384
+ `${JSON.stringify({
10385
+ valid: false,
10386
+ target: options.target,
10387
+ errors: [message]
10388
+ })}
10389
+ `
10390
+ );
10391
+ } else {
10392
+ console.error(message);
10393
+ }
10394
+ return 1;
10395
+ }
10408
10396
  }
10409
10397
  const absolutePlayPath = resolve10(options.target);
10410
10398
  const sourceCode = readFileSync6(absolutePlayPath, "utf-8");
@@ -10941,7 +10929,7 @@ async function handleRunLogs(args) {
10941
10929
  const status = await client.runs.get(runId);
10942
10930
  const logs = status.progress?.logs ?? [];
10943
10931
  if (outPath) {
10944
- writeFileSync6(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
10932
+ writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
10945
10933
  printCommandEnvelope(
10946
10934
  {
10947
10935
  runId: status.runId,
@@ -11051,18 +11039,9 @@ async function handleRunExport(args) {
11051
11039
  datasetPath
11052
11040
  });
11053
11041
  const source = exportResult?.rowsInfo.source ?? datasetPath ?? null;
11054
- const queryPlan = exportResult && outPath ? buildCustomerDbQueryPlan({
11055
- status,
11056
- rowsInfo: exportResult.rowsInfo,
11057
- outPath
11058
- }) : null;
11059
11042
  const next = {
11060
11043
  ...buildRunNextCommands(status),
11061
- export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source),
11062
- ...queryPlan ? {
11063
- queryJson: queryPlan.json,
11064
- queryCsv: queryPlan.csv
11065
- } : {}
11044
+ export: runExportRetryCommand(status.runId, outPath, datasetPath ?? source)
11066
11045
  };
11067
11046
  const payload = {
11068
11047
  runId: status.runId,
@@ -11071,13 +11050,6 @@ async function handleRunExport(args) {
11071
11050
  source,
11072
11051
  rowCount: exportResult?.rowsInfo.totalRows ?? null,
11073
11052
  columns: exportResult?.rowsInfo.columns ?? [],
11074
- ...queryPlan ? {
11075
- query: {
11076
- sql: queryPlan.sql,
11077
- json: queryPlan.json,
11078
- csv: queryPlan.csv
11079
- }
11080
- } : {},
11081
11053
  ...metadataOutPath ? { metadata_path: metadataOutPath } : {},
11082
11054
  local: { csv_path: exportResult?.path ?? null },
11083
11055
  next,
@@ -11087,15 +11059,14 @@ async function handleRunExport(args) {
11087
11059
  title: "run export",
11088
11060
  lines: [
11089
11061
  `Exported ${status.runId} to ${exportResult?.path ?? outPath}`,
11090
- ...source ? [`source=${source}`] : [],
11091
- ...queryPlan ? [`query=${queryPlan.json}`] : []
11062
+ ...source ? [`source=${source}`] : []
11092
11063
  ]
11093
11064
  }
11094
11065
  ]
11095
11066
  }
11096
11067
  };
11097
11068
  if (metadataOutPath) {
11098
- writeFileSync6(
11069
+ writeFileSync7(
11099
11070
  metadataOutPath,
11100
11071
  `${JSON.stringify(payload, null, 2)}
11101
11072
  `,
@@ -11268,7 +11239,8 @@ function parsePlaySearchOptions(args) {
11268
11239
  return {
11269
11240
  query,
11270
11241
  jsonOutput: argsWantJson(args),
11271
- compact: args.includes("--compact")
11242
+ compact: args.includes("--compact"),
11243
+ prebuiltOnly: args.includes("--prebuilt")
11272
11244
  };
11273
11245
  }
11274
11246
  function printPlayDescription(play) {
@@ -11316,6 +11288,7 @@ function printPlayDescription(play) {
11316
11288
  console.log(` ${line}`);
11317
11289
  }
11318
11290
  }
11291
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11319
11292
  console.log(` Run: ${play.runCommand}`);
11320
11293
  const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
11321
11294
  if (cloneEditStarter) {
@@ -11323,6 +11296,31 @@ function printPlayDescription(play) {
11323
11296
  console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
11324
11297
  }
11325
11298
  }
11299
+ function inputFieldNames(schema) {
11300
+ const fields = Array.isArray(schema?.fields) ? schema.fields : [];
11301
+ return fields.map(
11302
+ (field) => field && typeof field === "object" ? String(field.name ?? "").trim() : ""
11303
+ ).filter(Boolean);
11304
+ }
11305
+ function printCompactPlaySearchResult(play) {
11306
+ const reference = formatPlayListReference(play);
11307
+ const aliases = play.aliases.slice(0, 6).join(", ");
11308
+ console.log(`Play: ${reference}`);
11309
+ if (play.displayName && play.displayName !== play.name) {
11310
+ console.log(` Display name: ${play.displayName}`);
11311
+ }
11312
+ if (aliases) {
11313
+ console.log(` Aliases: ${aliases}`);
11314
+ }
11315
+ const fields = inputFieldNames(play.inputSchema);
11316
+ if (fields.length > 0) {
11317
+ console.log(` Inputs: ${fields.join(", ")}`);
11318
+ } else if (play.inputSchema) {
11319
+ console.log(" Inputs: see describe");
11320
+ }
11321
+ console.log(` Describe: deepline plays describe ${reference} --json`);
11322
+ console.log(` Run: ${play.runCommand}`);
11323
+ }
11326
11324
  function compactPlaySchema(schema) {
11327
11325
  if (!schema) return null;
11328
11326
  const fields = Array.isArray(schema.fields) ? schema.fields.map(
@@ -11386,22 +11384,44 @@ async function handlePlaySearch(args) {
11386
11384
  return 1;
11387
11385
  }
11388
11386
  const client = new DeeplineClient();
11389
- const plays = await client.searchPlays({
11387
+ const plays = (await client.searchPlays({
11390
11388
  query: options.query,
11391
11389
  compact: options.compact
11392
- });
11390
+ })).filter(
11391
+ (play) => options.prebuiltOnly ? play.origin === "prebuilt" || play.ownerType === "deepline" : true
11392
+ );
11393
11393
  if (options.jsonOutput) {
11394
- process.stdout.write(`${JSON.stringify({ plays })}
11395
- `);
11394
+ const jsonPlays = options.prebuiltOnly ? plays.map((play) => ({
11395
+ ...play,
11396
+ inputSchema: compactPlaySchema(play.inputSchema)
11397
+ })) : plays;
11398
+ process.stdout.write(
11399
+ `${JSON.stringify({
11400
+ plays: jsonPlays,
11401
+ total: jsonPlays.length,
11402
+ truncated: false
11403
+ })}
11404
+ `
11405
+ );
11396
11406
  return 0;
11397
11407
  }
11398
- process.stdout.write(`${plays.length} plays found:
11408
+ const displayPlays = options.prebuiltOnly ? plays.slice(0, 5) : plays;
11409
+ process.stdout.write(
11410
+ `${plays.length} plays found${options.prebuiltOnly && plays.length > displayPlays.length ? `; showing top ${displayPlays.length}` : ""}:
11399
11411
 
11400
- `);
11401
- for (const play of plays) {
11402
- printPlayDescription(play);
11412
+ `
11413
+ );
11414
+ for (const play of displayPlays) {
11415
+ if (options.prebuiltOnly) {
11416
+ printCompactPlaySearchResult(play);
11417
+ } else {
11418
+ printPlayDescription(play);
11419
+ }
11403
11420
  console.log("");
11404
11421
  }
11422
+ if (options.prebuiltOnly && plays.length > displayPlays.length) {
11423
+ console.log("Use --json for the full machine-readable result set.");
11424
+ }
11405
11425
  return 0;
11406
11426
  }
11407
11427
  function normalizePlayGrepText(value) {
@@ -11668,15 +11688,18 @@ Common commands:
11668
11688
  deepline plays get person-linkedin-to-email --json
11669
11689
  `
11670
11690
  );
11671
- play.command("check <target>").description("Bundle-check a local play file.").addHelpText(
11691
+ play.command("check <target>").description("Check a local play file or named/prebuilt play contract.").addHelpText(
11672
11692
  "after",
11673
11693
  `
11674
11694
  Notes:
11675
11695
  Validates a local play without storing it, promoting it, or starting a run.
11676
11696
  This uses the authoritative cloud preflight path.
11697
+ For named or prebuilt plays, validates that the contract is discoverable
11698
+ without starting a run or spending Deepline credits.
11677
11699
 
11678
11700
  Examples:
11679
11701
  deepline plays check my.play.ts
11702
+ deepline plays check prebuilt/name-and-domain-to-email-waterfall-batch
11680
11703
  deepline plays check my.play.ts --json
11681
11704
  `
11682
11705
  ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
@@ -11826,11 +11849,13 @@ Examples:
11826
11849
  ...options.json ? ["--json"] : []
11827
11850
  ]);
11828
11851
  });
11829
- const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
11852
+ const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").option("--prebuilt", "Only show Deepline-managed prebuilt plays").addHelpText(
11830
11853
  "after",
11831
11854
  `
11832
11855
  Notes:
11833
11856
  Ranked discovery for workflows. Use describe on a result before running it.
11857
+ Prefer --prebuilt for new GTM tasks so old workspace scratchpads do not
11858
+ outrank Deepline-managed routes unless the user names one explicitly.
11834
11859
  The grep alias is the same ranked retrieval surface with a more literal name
11835
11860
  for agents that are filtering the play registry.
11836
11861
 
@@ -11842,6 +11867,7 @@ Examples:
11842
11867
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
11843
11868
  process.exitCode = await handlePlaySearch([
11844
11869
  query,
11870
+ ...options.prebuilt ? ["--prebuilt"] : [],
11845
11871
  ...options.compact ? ["--compact"] : [],
11846
11872
  ...options.json ? ["--json"] : []
11847
11873
  ]);
@@ -12089,7 +12115,7 @@ Notes:
12089
12115
  Writes a returned dataset handle to the requested local CSV path. Use runs get
12090
12116
  first to inspect dataset paths like result.rows or result.nested.contacts.
12091
12117
  --metadata-out writes the same export metadata object returned by --json,
12092
- including source and follow-on customer-db query commands when available.
12118
+ including the source dataset path and row/column metadata.
12093
12119
 
12094
12120
  Examples:
12095
12121
  deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
@@ -13796,12 +13822,12 @@ Examples:
13796
13822
 
13797
13823
  // src/cli/commands/tools.ts
13798
13824
  import { Option } from "commander";
13799
- import { chmodSync, mkdtempSync, writeFileSync as writeFileSync8 } from "fs";
13825
+ import { chmodSync, mkdtempSync, writeFileSync as writeFileSync9 } from "fs";
13800
13826
  import { tmpdir as tmpdir4 } from "os";
13801
13827
  import { join as join10 } from "path";
13802
13828
 
13803
13829
  // src/tool-output.ts
13804
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
13830
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync8 } from "fs";
13805
13831
  import { homedir as homedir5 } from "os";
13806
13832
  import { join as join9 } from "path";
13807
13833
  function isPlainObject(value) {
@@ -13906,7 +13932,7 @@ function ensureOutputDir() {
13906
13932
  function writeJsonOutputFile(payload, stem) {
13907
13933
  const outputDir = ensureOutputDir();
13908
13934
  const outputPath = join9(outputDir, `${stem}_${Date.now()}.json`);
13909
- writeFileSync7(outputPath, JSON.stringify(payload, null, 2), "utf-8");
13935
+ writeFileSync8(outputPath, JSON.stringify(payload, null, 2), "utf-8");
13910
13936
  return outputPath;
13911
13937
  }
13912
13938
  function writeCsvOutputFile(rows, stem) {
@@ -13934,7 +13960,7 @@ function writeCsvOutputFile(rows, stem) {
13934
13960
  for (const row of rows) {
13935
13961
  lines.push(columns.map((column) => escapeCell(row[column])).join(","));
13936
13962
  }
13937
- writeFileSync7(outputPath, `${lines.join("\n")}
13963
+ writeFileSync8(outputPath, `${lines.join("\n")}
13938
13964
  `, "utf-8");
13939
13965
  const previewRows = rows.slice(0, 5);
13940
13966
  const previewColumns = columns.slice(0, 5);
@@ -14020,7 +14046,7 @@ async function listTools(args) {
14020
14046
  const client = new DeeplineClient();
14021
14047
  const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
14022
14048
  const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
14023
- const compact = args.includes("--compact");
14049
+ const compact = args.includes("--compact") || !args.includes("--json");
14024
14050
  const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
14025
14051
  const items = (await client.listTools({
14026
14052
  ...categoryFilter ? { categories: categoryFilter } : {}
@@ -14076,9 +14102,10 @@ async function searchTools(queryInput, options = {}) {
14076
14102
  searchMode: options.searchMode,
14077
14103
  includeSearchDebug: options.includeSearchDebug
14078
14104
  });
14079
- const payload = options.compact && Array.isArray(result.tools) ? {
14105
+ const shouldCompact = options.compact || !options.json;
14106
+ const payload = shouldCompact && Array.isArray(result.tools) ? {
14080
14107
  ...result,
14081
- tools: result.tools.map(compactTool)
14108
+ tools: result.tools.slice(0, 8).map(compactTool)
14082
14109
  } : result;
14083
14110
  printCommandEnvelope(payload, {
14084
14111
  json: options.json || shouldEmitJson()
@@ -14119,7 +14146,8 @@ async function grepTools(queryInput, options = {}) {
14119
14146
  mode
14120
14147
  )
14121
14148
  );
14122
- const outputTools = options.compact ? tools.map(compactTool) : tools;
14149
+ const shouldCompact = options.compact || !options.json;
14150
+ const outputTools = shouldCompact ? tools.slice(0, 8).map(compactTool) : tools;
14123
14151
  printCommandEnvelope(
14124
14152
  {
14125
14153
  tools: outputTools,
@@ -14138,11 +14166,19 @@ async function grepTools(queryInput, options = {}) {
14138
14166
  );
14139
14167
  return 0;
14140
14168
  }
14169
+ function numericToolField(tool, field) {
14170
+ const value = tool[field];
14171
+ return typeof value === "number" ? value : void 0;
14172
+ }
14141
14173
  function compactTool(tool) {
14142
14174
  const listed = toListedTool(tool);
14175
+ const searchScore = numericToolField(tool, "searchScore");
14176
+ const search_score = numericToolField(tool, "search_score");
14143
14177
  return {
14144
14178
  id: listed.id,
14145
14179
  toolId: listed.toolId,
14180
+ ...search_score !== void 0 ? { search_score } : {},
14181
+ ...searchScore !== void 0 ? { searchScore } : {},
14146
14182
  provider: listed.provider,
14147
14183
  displayName: listed.displayName,
14148
14184
  description: listed.description,
@@ -14463,7 +14499,7 @@ async function getTool(toolId, options = {}) {
14463
14499
  }
14464
14500
  if (shouldEmitJson()) {
14465
14501
  process.stdout.write(
14466
- `${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
14502
+ `${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
14467
14503
  `
14468
14504
  );
14469
14505
  return 0;
@@ -14788,8 +14824,8 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
14788
14824
  invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
14789
14825
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
14790
14826
  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."
14827
+ 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.",
14828
+ executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run output and returned dataset handles."
14793
14829
  },
14794
14830
  starterScript: {
14795
14831
  path: starterScript.path,
@@ -15040,7 +15076,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
15040
15076
  };
15041
15077
  });
15042
15078
  `;
15043
- writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
15079
+ writeFileSync9(scriptPath, script, { encoding: "utf-8", mode: 384 });
15044
15080
  return {
15045
15081
  path: scriptPath,
15046
15082
  sourceCode: script,
@@ -15596,7 +15632,7 @@ import {
15596
15632
  readdirSync as readdirSync2,
15597
15633
  readFileSync as readFileSync7,
15598
15634
  statSync as statSync2,
15599
- writeFileSync as writeFileSync9
15635
+ writeFileSync as writeFileSync10
15600
15636
  } from "fs";
15601
15637
  import { homedir as homedir6 } from "os";
15602
15638
  import { dirname as dirname10, join as join12 } from "path";
@@ -15630,7 +15666,7 @@ function readLocalSkillsVersion(baseUrl) {
15630
15666
  function writeLocalSkillsVersion(baseUrl, version) {
15631
15667
  const path = sdkSkillsVersionPath(baseUrl);
15632
15668
  mkdirSync5(dirname10(path), { recursive: true });
15633
- writeFileSync9(path, `${version}
15669
+ writeFileSync10(path, `${version}
15634
15670
  `, "utf-8");
15635
15671
  }
15636
15672
  function installedSdkSkillHasStalePositionalExecuteExamples() {
@@ -15813,6 +15849,7 @@ async function syncSdkSkillsIfNeeded(baseUrl) {
15813
15849
  }
15814
15850
 
15815
15851
  // src/cli/index.ts
15852
+ var PREFLIGHT_TIMEOUT_MS = 3e3;
15816
15853
  function asCommanderError(error) {
15817
15854
  if (!(error instanceof Error) || !("code" in error)) {
15818
15855
  return null;
@@ -15893,6 +15930,124 @@ async function runPlayRunnerHealthCheck() {
15893
15930
  await rm2(dir, { recursive: true, force: true });
15894
15931
  }
15895
15932
  }
15933
+ function pickString(value, ...keys) {
15934
+ for (const key of keys) {
15935
+ const candidate = value[key];
15936
+ if (typeof candidate === "string" && candidate.trim()) {
15937
+ return candidate;
15938
+ }
15939
+ }
15940
+ return null;
15941
+ }
15942
+ function preflightErrorMessage(error) {
15943
+ return error instanceof Error ? error.message : String(error);
15944
+ }
15945
+ async function runPreflightCheck() {
15946
+ const baseUrl = autoDetectBaseUrl().replace(/\/$/, "");
15947
+ const healthController = new AbortController();
15948
+ const healthTimeout = setTimeout(
15949
+ () => healthController.abort(),
15950
+ PREFLIGHT_TIMEOUT_MS
15951
+ );
15952
+ const health = await fetch(new URL("/api/v2/health", baseUrl), {
15953
+ signal: healthController.signal
15954
+ }).then(async (response) => {
15955
+ const payload = await response.json().catch(() => ({}));
15956
+ return {
15957
+ status: response.ok ? pickString(payload, "status") ?? "ok" : "unreachable",
15958
+ version: pickString(payload, "version")
15959
+ };
15960
+ }).catch(() => ({ status: "unreachable", version: null })).finally(() => clearTimeout(healthTimeout));
15961
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl);
15962
+ const http = apiKey ? new HttpClient(
15963
+ resolveConfig({
15964
+ baseUrl,
15965
+ apiKey,
15966
+ timeout: PREFLIGHT_TIMEOUT_MS,
15967
+ maxRetries: 0
15968
+ })
15969
+ ) : null;
15970
+ const [auth, billing] = http ? await Promise.all([
15971
+ http.post("/api/v2/auth/cli/status", {
15972
+ api_key: apiKey,
15973
+ reveal: false
15974
+ }).catch((error) => ({
15975
+ status: "not_connected",
15976
+ connected: false,
15977
+ error: preflightErrorMessage(error)
15978
+ })),
15979
+ http.get("/api/v2/billing/balance").catch(
15980
+ (error) => ({
15981
+ balance: null,
15982
+ balance_display: "unavailable",
15983
+ balance_status: "unknown",
15984
+ error: preflightErrorMessage(error)
15985
+ })
15986
+ )
15987
+ ]) : [
15988
+ {
15989
+ status: "not_connected",
15990
+ connected: false,
15991
+ error: "No API key found. Run: deepline auth register"
15992
+ },
15993
+ {
15994
+ balance: null,
15995
+ balance_display: "unavailable until authenticated",
15996
+ balance_status: "unknown"
15997
+ }
15998
+ ];
15999
+ const authStatus = pickString(auth, "status") ?? "unknown";
16000
+ const billingRecord = billing;
16001
+ const balanceDisplay = pickString(billing, "balance_display", "balanceDisplay") ?? `${String(billingRecord.balance ?? 0)} Deepline Credits`;
16002
+ const balanceStatus = pickString(billing, "balance_status", "balanceStatus") ?? "unknown";
16003
+ return {
16004
+ status: health.status === "ok" && (authStatus === "connected" || authStatus === "claimed") ? "ok" : "check",
16005
+ host: baseUrl,
16006
+ health: {
16007
+ status: health.status,
16008
+ version: health.version ?? null
16009
+ },
16010
+ auth: {
16011
+ status: authStatus,
16012
+ connected: authStatus === "connected" || authStatus === "claimed",
16013
+ org_id: pickString(auth, "org_id", "orgId"),
16014
+ org_name: pickString(auth, "org_name", "orgName"),
16015
+ rate_limit_tier: pickString(auth, "rate_limit_tier", "rateLimitTier"),
16016
+ error: pickString(auth, "error")
16017
+ },
16018
+ billing: {
16019
+ balance: billingRecord.balance ?? null,
16020
+ balance_display: balanceDisplay,
16021
+ rough_usd_balance: billingRecord.rough_usd_balance ?? billingRecord.roughUsdBalance ?? null,
16022
+ balance_status: balanceStatus,
16023
+ error: pickString(billing, "error")
16024
+ },
16025
+ render: {
16026
+ sections: [
16027
+ {
16028
+ title: "preflight",
16029
+ lines: [
16030
+ `host: ${baseUrl}`,
16031
+ `health: ${health.status}`,
16032
+ `auth: ${authStatus}`,
16033
+ `billing: ${balanceDisplay} (${balanceStatus})`
16034
+ ]
16035
+ }
16036
+ ]
16037
+ }
16038
+ };
16039
+ }
16040
+ function printPreflightHuman(data) {
16041
+ const render = data.render;
16042
+ const lines = render?.sections?.flatMap((section) => section.lines ?? []);
16043
+ if (lines?.length) {
16044
+ process.stdout.write(`${lines.join("\n")}
16045
+ `);
16046
+ return;
16047
+ }
16048
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16049
+ `);
16050
+ }
15896
16051
  async function main() {
15897
16052
  const mainStartedAt = Date.now();
15898
16053
  recordCliTrace({
@@ -15909,6 +16064,7 @@ async function main() {
15909
16064
  "after",
15910
16065
  `
15911
16066
  Common commands:
16067
+ deepline preflight
15912
16068
  deepline health
15913
16069
  deepline auth status --json
15914
16070
  deepline plays search email --json
@@ -15930,7 +16086,6 @@ Output:
15930
16086
 
15931
16087
  Safety:
15932
16088
  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
16089
 
15935
16090
  Exit codes:
15936
16091
  0 success; 2 usage/local input error; 3 auth/permission error; 4 not found;
@@ -15972,6 +16127,26 @@ Exit codes:
15972
16127
  registerDbCommands(program);
15973
16128
  registerFeedbackCommands(program);
15974
16129
  registerUpdateCommand(program);
16130
+ program.command("preflight").description("Run compact health, auth, and Deepline billing checks.").option("--json", "Force JSON output.").addHelpText(
16131
+ "after",
16132
+ `
16133
+ Notes:
16134
+ Read-only setup check for the configured Deepline host. Shows server health,
16135
+ auth connection, and customer-visible Deepline balance in one compact command.
16136
+
16137
+ Examples:
16138
+ deepline preflight
16139
+ deepline preflight --json
16140
+ `
16141
+ ).action(async (options) => {
16142
+ const data = await runPreflightCheck();
16143
+ if (shouldEmitJson(options.json)) {
16144
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
16145
+ `);
16146
+ } else {
16147
+ printPreflightHuman(data);
16148
+ }
16149
+ });
15975
16150
  program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
15976
16151
  "--play-runner",
15977
16152
  "Run a tiny no-provider play to verify the full play execution plane."