deepline 0.1.48 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -24,7 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/index.ts
27
- var import_commander2 = require("commander");
27
+ var import_promises5 = require("fs/promises");
28
+ var import_node_path15 = require("path");
29
+ var import_node_os9 = require("os");
30
+ var import_commander3 = require("commander");
28
31
 
29
32
  // src/config.ts
30
33
  var import_node_fs = require("fs");
@@ -216,7 +219,7 @@ function resolveConfig(options) {
216
219
  }
217
220
 
218
221
  // src/version.ts
219
- var SDK_VERSION = "0.1.48";
222
+ var SDK_VERSION = "0.1.50";
220
223
  var SDK_API_CONTRACT = "2026-05-stripe-promo-checkout";
221
224
 
222
225
  // ../shared_libs/play-runtime/coordinator-headers.ts
@@ -736,9 +739,18 @@ var DeeplineClient = class {
736
739
  * console.log(`Found ${searchTools.length} search tools`);
737
740
  * ```
738
741
  */
739
- async listTools() {
742
+ async listTools(options) {
743
+ const params = new URLSearchParams();
744
+ if (options?.categories?.trim()) {
745
+ params.set("categories", options.categories.trim());
746
+ }
747
+ if (options?.grep?.trim()) {
748
+ params.set("grep", options.grep.trim());
749
+ params.set("grep_mode", options.grepMode ?? "all");
750
+ }
751
+ const suffix = params.toString() ? `?${params.toString()}` : "";
740
752
  const res = await this.http.get(
741
- "/api/v2/tools"
753
+ `/api/v2/tools${suffix}`
742
754
  );
743
755
  return res.tools;
744
756
  }
@@ -1373,9 +1385,17 @@ var DeeplineClient = class {
1373
1385
  options?.reason ? { reason: options.reason } : {}
1374
1386
  );
1375
1387
  }
1376
- async listPlays() {
1388
+ async listPlays(options) {
1389
+ const params = new URLSearchParams();
1390
+ if (options?.origin) params.set("origin", options.origin);
1391
+ if (options?.grep?.trim()) {
1392
+ params.set("grep", options.grep.trim());
1393
+ params.set("grep_mode", options.grepMode ?? "all");
1394
+ params.set("limit", "60");
1395
+ }
1396
+ const suffix = params.toString() ? `?${params.toString()}` : "";
1377
1397
  const response = await this.http.get(
1378
- "/api/v2/plays"
1398
+ `/api/v2/plays${suffix}`
1379
1399
  );
1380
1400
  return response.plays ?? [];
1381
1401
  }
@@ -5683,6 +5703,16 @@ function extractPlayName(code, filePath) {
5683
5703
  function isFileTarget(target) {
5684
5704
  return (0, import_node_fs8.existsSync)((0, import_node_path10.resolve)(target));
5685
5705
  }
5706
+ function looksLikeRunId(target) {
5707
+ return /^play\/[^/]+\/run\/[^/]+/.test(target.trim());
5708
+ }
5709
+ function formatPlayCommandReceivedRunIdError(input) {
5710
+ return `\`deepline plays ${input.command} <run-id>\` expects a play name, but this looks like a run id.
5711
+ Use:
5712
+ deepline runs get ${input.runId} --json
5713
+ deepline runs logs ${input.runId} --json
5714
+ deepline runs export ${input.runId} --out output.csv`;
5715
+ }
5686
5716
  function looksLikeFilePath(target) {
5687
5717
  if (target.trim().toLowerCase().startsWith("prebuilt/")) {
5688
5718
  return false;
@@ -6109,6 +6139,69 @@ function describeLiveEventPhase(event) {
6109
6139
  }
6110
6140
  return null;
6111
6141
  }
6142
+ function formatProgressLabel(raw) {
6143
+ const value = typeof raw === "string" && raw.trim() ? raw.trim() : "step";
6144
+ return value.replace(/^map:/, "").replace(/^tool:/, "");
6145
+ }
6146
+ function formatProgressCounts(input) {
6147
+ const completed = typeof input.completed === "number" && Number.isFinite(input.completed) ? input.completed : null;
6148
+ const total = typeof input.total === "number" && Number.isFinite(input.total) ? input.total : null;
6149
+ if (completed === null || total === null || total <= 0) {
6150
+ return null;
6151
+ }
6152
+ const percent = Math.max(0, Math.min(100, Math.round(completed / total * 100)));
6153
+ const failed = typeof input.failed === "number" && Number.isFinite(input.failed) && input.failed > 0 ? `, failed ${formatInteger(input.failed)}` : "";
6154
+ return `${formatInteger(completed)}/${formatInteger(total)} (${percent}%)${failed}`;
6155
+ }
6156
+ function getProgressLinesFromLiveEvent(event) {
6157
+ const payload = getEventPayload(event);
6158
+ if (event.type === "play.step.progress") {
6159
+ const counts = formatProgressCounts({
6160
+ completed: payload.completed,
6161
+ total: payload.total,
6162
+ failed: payload.failed
6163
+ });
6164
+ if (!counts) return [];
6165
+ return [`progress ${formatProgressLabel(payload.stepId)}: ${counts}`];
6166
+ }
6167
+ if (event.type !== "play.run.snapshot" && event.type !== "play.run.final_status") {
6168
+ return [];
6169
+ }
6170
+ const nodeStates = Array.isArray(payload.nodeStates) ? payload.nodeStates : Array.isArray(payload.steps) ? payload.steps : [];
6171
+ const lines = [];
6172
+ for (const state of nodeStates) {
6173
+ if (!state || typeof state !== "object" || Array.isArray(state)) {
6174
+ continue;
6175
+ }
6176
+ const record = state;
6177
+ const progress = record.progress && typeof record.progress === "object" && !Array.isArray(record.progress) ? record.progress : null;
6178
+ if (!progress) {
6179
+ continue;
6180
+ }
6181
+ const counts = formatProgressCounts({
6182
+ completed: progress.completed,
6183
+ total: progress.total,
6184
+ failed: progress.failed
6185
+ });
6186
+ if (!counts) {
6187
+ continue;
6188
+ }
6189
+ lines.push(
6190
+ `progress ${formatProgressLabel(record.nodeId ?? progress.artifactTableNamespace)}: ${counts}`
6191
+ );
6192
+ }
6193
+ return lines;
6194
+ }
6195
+ function printPlayProgressLines(input) {
6196
+ for (const line of input.lines) {
6197
+ const signature = line.trim();
6198
+ if (!signature || input.state.lastProgressSignature === signature) {
6199
+ continue;
6200
+ }
6201
+ input.state.lastProgressSignature = signature;
6202
+ input.progress.writeLine(line);
6203
+ }
6204
+ }
6112
6205
  function buildPlayDashboardUrl(baseUrl, playName) {
6113
6206
  const trimmedBase = baseUrl.replace(/\/$/, "");
6114
6207
  const encodedPlayName = encodeURIComponent(playName);
@@ -6182,6 +6275,13 @@ async function waitForPlayCompletionByStream(input) {
6182
6275
  state: input.state,
6183
6276
  progress: input.progress
6184
6277
  });
6278
+ if (!input.jsonOutput) {
6279
+ printPlayProgressLines({
6280
+ lines: getProgressLinesFromLiveEvent(event),
6281
+ state: input.state,
6282
+ progress: input.progress
6283
+ });
6284
+ }
6185
6285
  const status = getStatusFromLiveEvent(event);
6186
6286
  if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
6187
6287
  const finalStatus = await input.client.getPlayStatus(input.workflowId, {
@@ -6250,7 +6350,8 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6250
6350
  );
6251
6351
  const state = {
6252
6352
  lastLogIndex: 0,
6253
- emittedRunnerStarted: false
6353
+ emittedRunnerStarted: false,
6354
+ lastProgressSignature: null
6254
6355
  };
6255
6356
  const controller = new AbortController();
6256
6357
  let timedOut = false;
@@ -6278,11 +6379,6 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6278
6379
  }
6279
6380
  const workflowId = lastKnownWorkflowId || "pending";
6280
6381
  if (workflowId !== "pending" && !emittedDashboardUrl) {
6281
- openPlayDashboard({
6282
- dashboardUrl,
6283
- jsonOutput: input.jsonOutput,
6284
- noOpen: input.noOpen
6285
- });
6286
6382
  if (!input.jsonOutput) {
6287
6383
  writeStartedPlayRun({
6288
6384
  runId: workflowId,
@@ -6292,6 +6388,11 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6292
6388
  progress: input.progress
6293
6389
  });
6294
6390
  }
6391
+ openPlayDashboard({
6392
+ dashboardUrl,
6393
+ jsonOutput: input.jsonOutput,
6394
+ noOpen: input.noOpen
6395
+ });
6295
6396
  input.progress.phase(`loading play on ${dashboardUrl}`);
6296
6397
  emittedDashboardUrl = true;
6297
6398
  }
@@ -6314,6 +6415,13 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6314
6415
  state,
6315
6416
  progress: input.progress
6316
6417
  });
6418
+ if (!input.jsonOutput) {
6419
+ printPlayProgressLines({
6420
+ lines: getProgressLinesFromLiveEvent(event),
6421
+ state,
6422
+ progress: input.progress
6423
+ });
6424
+ }
6317
6425
  const finalStatus = getFinalStatusFromLiveEvent(event);
6318
6426
  if (finalStatus) {
6319
6427
  recordCliTrace({
@@ -6582,6 +6690,7 @@ function buildRunNextCommands(status) {
6582
6690
  const commands = {
6583
6691
  get: `deepline runs get ${runId} --json`,
6584
6692
  full: `deepline runs get ${runId} --full --json`,
6693
+ export: `deepline runs export ${runId} --out output.csv`,
6585
6694
  stop: `deepline runs stop ${runId} --reason "stale lock" --json`,
6586
6695
  logs: `deepline runs logs ${runId} --out run.log --json`
6587
6696
  };
@@ -6714,6 +6823,32 @@ function formatInsufficientCreditsMessage(input) {
6714
6823
  const addSuffix = billingUrl && recommended !== "-" ? ` Add >=${recommended} at ${billingUrl}.` : billingUrl ? ` Add credits at ${billingUrl}.` : "";
6715
6824
  return `Workspace balance ${balance} < required ${required} for ${operation}${workspaceSuffix}.${addSuffix}`;
6716
6825
  }
6826
+ function buildInsufficientCreditsSummaryLines(input) {
6827
+ const progress = input.status.progress;
6828
+ const rowsInfo = extractCanonicalRowsInfo(input.status);
6829
+ const completed = getNumericField(progress, "completed") ?? getNumericField(progress, "completedRows") ?? rowsInfo?.rows.length ?? null;
6830
+ const total = getNumericField(progress, "total") ?? getNumericField(progress, "totalRows") ?? rowsInfo?.totalRows ?? null;
6831
+ const lines = [
6832
+ " status: stopped_insufficient_credits",
6833
+ completed === null ? " completed rows: unknown" : ` completed rows: ${formatInteger(completed)}${total !== null ? ` of ${formatInteger(total)}` : ""}`,
6834
+ " reusable receipts: yes; rerun after adding credits to continue from completed provider work"
6835
+ ];
6836
+ const billingUrl = getStringField(input.billing, "billing_url");
6837
+ const recommended = formatCreditAmount(
6838
+ input.billing.recommended_add_credits ?? input.billing.needed_credits
6839
+ );
6840
+ if (billingUrl) {
6841
+ lines.push(
6842
+ recommended !== "-" ? ` add credits: add >=${recommended} at ${billingUrl}` : ` add credits: ${billingUrl}`
6843
+ );
6844
+ }
6845
+ const runId = input.status.runId?.trim();
6846
+ if (runId) {
6847
+ lines.push(` inspect: deepline runs get ${runId} --json`);
6848
+ lines.push(` export partial: deepline runs export ${runId} --out output.csv`);
6849
+ }
6850
+ return lines;
6851
+ }
6717
6852
  function formatPlayErrorForDisplay(status, error) {
6718
6853
  if (!error) {
6719
6854
  return null;
@@ -6904,6 +7039,19 @@ function formatDatasetStatsLines(datasetStats) {
6904
7039
  }
6905
7040
  return lines;
6906
7041
  }
7042
+ function buildJsonRunResultRenderLines(status) {
7043
+ const publicStatus = status.status ?? "running";
7044
+ const runId = status.runId ?? "unknown";
7045
+ const lines = [`${publicStatus} ${runId}`];
7046
+ const progressError = status.progress?.error;
7047
+ if (typeof progressError === "string") {
7048
+ const billing = extractBillingForStatus(status, progressError);
7049
+ if (isInsufficientCreditsBilling(billing)) {
7050
+ lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
7051
+ }
7052
+ }
7053
+ return lines;
7054
+ }
6907
7055
  function writePlayResult(status, jsonOutput, options) {
6908
7056
  if (jsonOutput) {
6909
7057
  const payload2 = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
@@ -6913,9 +7061,7 @@ function writePlayResult(status, jsonOutput, options) {
6913
7061
  sections: [
6914
7062
  {
6915
7063
  title: "run result",
6916
- lines: [
6917
- `${status.status ?? "running"} ${status.runId ?? "unknown"}`
6918
- ]
7064
+ lines: buildJsonRunResultRenderLines(status)
6919
7065
  }
6920
7066
  ]
6921
7067
  }
@@ -6942,6 +7088,10 @@ function writePlayResult(status, jsonOutput, options) {
6942
7088
  lines.push(...formatDatasetStatsLines(datasetStats));
6943
7089
  const progressError = status.progress?.error;
6944
7090
  if (progressError && typeof progressError === "string") {
7091
+ const billing = extractBillingForStatus(status, progressError);
7092
+ if (isInsufficientCreditsBilling(billing)) {
7093
+ lines.push(...buildInsufficientCreditsSummaryLines({ status, billing }));
7094
+ }
6945
7095
  const displayError = formatPlayErrorForDisplay(status, progressError) ?? progressError;
6946
7096
  lines.push(` error: ${displayError.slice(0, 200)}`);
6947
7097
  }
@@ -7321,24 +7471,31 @@ function writeStartedPlayRun(input) {
7321
7471
  workflowId: input.runId,
7322
7472
  name: input.playName,
7323
7473
  status: input.status ?? "started",
7324
- dashboardUrl: input.dashboardUrl
7474
+ dashboardUrl: input.dashboardUrl,
7475
+ next: {
7476
+ inspect: `deepline runs get ${input.runId} --json`,
7477
+ full: `deepline runs get ${input.runId} --full --json`,
7478
+ logs: `deepline runs logs ${input.runId} --json`,
7479
+ export: `deepline runs export ${input.runId} --out output.csv`,
7480
+ stop: `deepline runs stop ${input.runId} --reason "stale lock" --json`
7481
+ }
7325
7482
  };
7483
+ const lines = [
7484
+ `Started ${input.playName}`,
7485
+ ` run id: ${input.runId}`,
7486
+ ` inspect: deepline runs get ${input.runId} --json`,
7487
+ ` full debug: deepline runs get ${input.runId} --full --json`,
7488
+ ` logs: deepline runs logs ${input.runId} --json`,
7489
+ ` export after completion: deepline runs export ${input.runId} --out output.csv`,
7490
+ ` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`
7491
+ ];
7326
7492
  if (input.jsonOutput) {
7327
7493
  printCommandEnvelope({
7328
7494
  ...payload,
7329
- render: { sections: [{ title: "play run", lines: [`Started ${input.playName}`, `run id: ${input.runId}`] }] }
7495
+ render: { sections: [{ title: "play run", lines }] }
7330
7496
  }, { json: true });
7331
7497
  return;
7332
7498
  }
7333
- const lines = [
7334
- `Started ${input.playName}`,
7335
- ` run id: ${input.runId}`,
7336
- ` get status: deepline runs get ${input.runId} --json`,
7337
- ` logs: deepline runs logs ${input.runId} --json`,
7338
- ` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`,
7339
- ` result JSON: deepline runs get ${input.runId} --json`,
7340
- ` full debug JSON: deepline runs get ${input.runId} --full --json`
7341
- ];
7342
7499
  if (input.dashboardUrl) {
7343
7500
  lines.push(` play page: ${input.dashboardUrl}`);
7344
7501
  }
@@ -7354,13 +7511,13 @@ function writeStartedPlayRun(input) {
7354
7511
  ` });
7355
7512
  }
7356
7513
  function parsePlayRunOptions(args) {
7357
- const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--watch] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--watch] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
7514
+ const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
7358
7515
  let filePath = null;
7359
7516
  let playName = null;
7360
7517
  let input = null;
7361
7518
  let revisionId = null;
7362
7519
  let revisionSelector = null;
7363
- const watch = args.includes("--watch") || args.includes("--wait");
7520
+ const watch = !args.includes("--no-wait");
7364
7521
  let jsonOutput = watch ? args.includes("--json") : argsWantJson(args);
7365
7522
  const emitLogs = !jsonOutput || args.includes("--logs");
7366
7523
  const force = args.includes("--force");
@@ -7406,7 +7563,7 @@ function parsePlayRunOptions(args) {
7406
7563
  waitTimeoutMs = parsePositiveInteger2(args[++index], arg);
7407
7564
  continue;
7408
7565
  }
7409
- if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
7566
+ if (arg === "--json" || arg === "--wait" || arg === "--tail" || arg === "--watch" || arg === "--no-wait" || arg === "--logs" || arg === "--force" || arg === "--no-open") {
7410
7567
  if (arg === "--watch") {
7411
7568
  continue;
7412
7569
  }
@@ -8262,6 +8419,12 @@ async function handlePlayGet(args) {
8262
8419
  console.error("Usage: deepline play get <play-file.ts|play-name> [--json]");
8263
8420
  return 1;
8264
8421
  }
8422
+ if (looksLikeRunId(target)) {
8423
+ console.error(
8424
+ formatPlayCommandReceivedRunIdError({ command: "get", runId: target })
8425
+ );
8426
+ return 2;
8427
+ }
8265
8428
  const client = new DeeplineClient();
8266
8429
  const explicitJson = args.includes("--json");
8267
8430
  const sourceOutput = args.includes("--source");
@@ -8376,8 +8539,20 @@ async function handlePlayVersions(args) {
8376
8539
  }
8377
8540
  async function handlePlayList(args) {
8378
8541
  const jsonOutput = argsWantJson(args);
8542
+ const originArgIndex = args.findIndex((arg) => arg === "--origin");
8543
+ const rawOrigin = originArgIndex >= 0 ? args[originArgIndex + 1] : void 0;
8544
+ if (rawOrigin && rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8545
+ throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8546
+ }
8547
+ const origin = rawOrigin;
8379
8548
  const client = new DeeplineClient();
8380
- const plays = await client.listPlays();
8549
+ const plays = (await client.listPlays({
8550
+ ...origin ? { origin } : {}
8551
+ })).filter((play) => {
8552
+ if (!origin) return true;
8553
+ const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
8554
+ return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
8555
+ });
8381
8556
  if (jsonOutput) {
8382
8557
  process.stdout.write(`${JSON.stringify(plays)}
8383
8558
  `);
@@ -8480,6 +8655,54 @@ function printPlayDescription(play) {
8480
8655
  );
8481
8656
  }
8482
8657
  }
8658
+ function compactPlaySchema(schema) {
8659
+ if (!schema) return null;
8660
+ const fields = Array.isArray(schema.fields) ? schema.fields.map(
8661
+ (field) => field && typeof field === "object" ? {
8662
+ name: String(field.name ?? ""),
8663
+ type: field.type ?? void 0,
8664
+ required: field.required ?? void 0
8665
+ } : null
8666
+ ).filter((field) => Boolean(field?.name)) : [];
8667
+ return fields.length > 0 ? { fields } : schema;
8668
+ }
8669
+ function playSchemaMetadata(schema, key) {
8670
+ if (!schema || typeof schema !== "object" || Array.isArray(schema)) return null;
8671
+ const value = schema[key];
8672
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
8673
+ }
8674
+ function playRunCommand(play, options) {
8675
+ const target = play.reference || play.name;
8676
+ if (options?.csvInput) {
8677
+ const inputField = typeof options.csvInput.inputField === "string" && options.csvInput.inputField.trim() ? options.csvInput.inputField.trim() : "csv";
8678
+ return `deepline plays run ${target} --input '${JSON.stringify({ [inputField]: "leads.csv" })}' --watch`;
8679
+ }
8680
+ return `deepline plays run ${target} --input '{...}' --watch`;
8681
+ }
8682
+ function summarizePlayListItemForCli(play, options) {
8683
+ const aliases = play.aliases?.length ? play.aliases : [play.name];
8684
+ const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
8685
+ const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
8686
+ const runCommand2 = playRunCommand(play, { csvInput });
8687
+ return {
8688
+ name: play.name,
8689
+ ...play.reference ? { reference: play.reference } : {},
8690
+ ...play.displayName ? { displayName: play.displayName } : {},
8691
+ origin: play.origin,
8692
+ ownerType: play.ownerType,
8693
+ canEdit: play.canEdit,
8694
+ canClone: play.canClone,
8695
+ aliases,
8696
+ inputSchema: options?.compact ? compactPlaySchema(play.inputSchema) : play.inputSchema ?? null,
8697
+ outputSchema: options?.compact ? compactPlaySchema(play.outputSchema) : play.outputSchema ?? null,
8698
+ ...csvInput ? { csvInput } : {},
8699
+ ...rowOutputSchema ? { rowOutputSchema } : {},
8700
+ runCommand: runCommand2,
8701
+ examples: [runCommand2],
8702
+ currentPublishedVersion: play.currentPublishedVersion ?? null,
8703
+ isDraftDirty: play.isDraftDirty
8704
+ };
8705
+ }
8483
8706
  async function handlePlaySearch(args) {
8484
8707
  let options;
8485
8708
  try {
@@ -8501,6 +8724,100 @@ async function handlePlaySearch(args) {
8501
8724
  }
8502
8725
  process.stdout.write(`${plays.length} plays found:
8503
8726
 
8727
+ `);
8728
+ for (const play of plays) {
8729
+ printPlayDescription(play);
8730
+ console.log("");
8731
+ }
8732
+ return 0;
8733
+ }
8734
+ function normalizePlayGrepText(value) {
8735
+ if (value === null || value === void 0) return "";
8736
+ if (typeof value === "string") return value.toLowerCase();
8737
+ if (typeof value === "number" || typeof value === "boolean") {
8738
+ return String(value).toLowerCase();
8739
+ }
8740
+ if (Array.isArray(value)) return value.map(normalizePlayGrepText).join(" ");
8741
+ if (typeof value === "object") {
8742
+ return Object.values(value).map(normalizePlayGrepText).join(" ");
8743
+ }
8744
+ return "";
8745
+ }
8746
+ function parsePlayGrepTerms(query, mode) {
8747
+ const trimmed = query.trim().toLowerCase();
8748
+ if (!trimmed) return [];
8749
+ if (mode === "phrase") return [trimmed];
8750
+ const matches = trimmed.match(/"([^"]+)"|'([^']+)'|\S+/g) ?? [];
8751
+ return matches.map((term) => term.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
8752
+ }
8753
+ function matchesPlayGrepQuery(value, query, mode) {
8754
+ const terms = parsePlayGrepTerms(query, mode);
8755
+ if (terms.length === 0) return true;
8756
+ const haystack = normalizePlayGrepText(value);
8757
+ if (mode === "any") return terms.some((term) => haystack.includes(term));
8758
+ return terms.every((term) => haystack.includes(term));
8759
+ }
8760
+ async function handlePlayGrep(args) {
8761
+ const query = args[0]?.trim();
8762
+ if (!query) {
8763
+ console.error("Usage: deepline plays grep <query> [--origin prebuilt|owned] [--compact] [--json]");
8764
+ return 1;
8765
+ }
8766
+ let origin;
8767
+ let mode = "all";
8768
+ for (let index = 1; index < args.length; index += 1) {
8769
+ const arg = args[index];
8770
+ if (arg === "--origin" && args[index + 1]) {
8771
+ const rawOrigin = args[++index].trim().toLowerCase();
8772
+ if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8773
+ throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8774
+ }
8775
+ origin = rawOrigin;
8776
+ }
8777
+ if (arg === "--mode" && args[index + 1]) {
8778
+ const rawMode = args[++index].trim().toLowerCase();
8779
+ mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
8780
+ }
8781
+ }
8782
+ const compact = args.includes("--compact");
8783
+ const client = new DeeplineClient();
8784
+ const plays = (await client.listPlays({
8785
+ grep: query,
8786
+ grepMode: mode,
8787
+ ...origin ? { origin } : {}
8788
+ })).filter((play) => {
8789
+ if (!origin) return true;
8790
+ const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
8791
+ return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
8792
+ }).filter(
8793
+ (play) => matchesPlayGrepQuery(
8794
+ {
8795
+ name: play.name,
8796
+ reference: play.reference,
8797
+ displayName: play.displayName,
8798
+ origin: play.origin,
8799
+ ownerType: play.ownerType,
8800
+ aliases: play.aliases,
8801
+ inputSchema: play.inputSchema,
8802
+ outputSchema: play.outputSchema
8803
+ },
8804
+ query,
8805
+ mode
8806
+ )
8807
+ ).map((play) => summarizePlayListItemForCli(play, { compact }));
8808
+ if (argsWantJson(args)) {
8809
+ process.stdout.write(`${JSON.stringify({
8810
+ plays,
8811
+ count: plays.length,
8812
+ query,
8813
+ grep: { mode, terms: parsePlayGrepTerms(query, mode) },
8814
+ filters: { origin: origin ?? null }
8815
+ })}
8816
+ `);
8817
+ return 0;
8818
+ }
8819
+ process.stdout.write(`${plays.length} plays found:
8820
+
8504
8821
  `);
8505
8822
  for (const play of plays) {
8506
8823
  printPlayDescription(play);
@@ -8516,6 +8833,15 @@ async function handlePlayDescribe(args) {
8516
8833
  );
8517
8834
  return 1;
8518
8835
  }
8836
+ if (looksLikeRunId(playName)) {
8837
+ console.error(
8838
+ formatPlayCommandReceivedRunIdError({
8839
+ command: "describe",
8840
+ runId: playName
8841
+ })
8842
+ );
8843
+ return 2;
8844
+ }
8519
8845
  const client = new DeeplineClient();
8520
8846
  await assertCanonicalNamedPlayReference(client, playName);
8521
8847
  const play = await client.describePlay(
@@ -8672,7 +8998,7 @@ Common commands:
8672
8998
  deepline plays search email --json
8673
8999
  deepline plays describe person-linkedin-to-email --json
8674
9000
  deepline plays check my.play.ts
8675
- deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
9001
+ deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
8676
9002
  deepline plays set-live my.play.ts --json
8677
9003
  deepline plays get person-linkedin-to-email --json
8678
9004
  `
@@ -8703,8 +9029,10 @@ Notes:
8703
9029
  Unknown --foo value and --foo.bar value flags are passed into play input.
8704
9030
  Example: --limit 5 becomes input.limit = 5.
8705
9031
  File args accept local paths; the CLI stages files before submit.
8706
- --watch prints logs, previews, stats, and next commands.
8707
- --wait is accepted as a compatibility alias for --watch.
9032
+ By default, run waits for completion and prints logs, previews, stats, and
9033
+ next commands. --watch and --wait are accepted compatibility aliases for the
9034
+ default behavior. Use --no-wait only when you intentionally want
9035
+ a fire-and-forget run id.
8708
9036
  The play page opens in your browser as soon as the run starts; use --no-open
8709
9037
  to only print the URL.
8710
9038
  --force supersedes active runs; it does not bypass completed reuse.
@@ -8732,17 +9060,18 @@ Idempotent execution:
8732
9060
  .run({ key: 'domain', staleAfterSeconds: 86400 })
8733
9061
 
8734
9062
  Examples:
8735
- deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
8736
- deepline plays run my.play.ts --input @input.json --wait --json
8737
- deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}' --watch
8738
- deepline plays run cto-search.play.ts --limit 5 --watch
9063
+ deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
9064
+ deepline plays run my.play.ts --input @input.json --json
9065
+ deepline plays run person-linkedin-to-email --input '{"linkedin_url":"..."}'
9066
+ deepline plays run cto-search.play.ts --limit 5
9067
+ deepline plays run long-background-play --no-wait
8739
9068
  deepline runs export <run-id> --out output.csv
8740
9069
  deepline runs get <run-id>
8741
9070
  `
8742
9071
  ).option("--file <path>", "Local play file to run").option("--name <name>", "Saved play name to run").option("-i, --input <json>", "Input JSON object or @file path").option("--live", "Run the current live revision explicitly").option("--latest", "Run the newest saved revision, even if it is not live").option(
8743
9072
  "--revision-id <id>",
8744
9073
  "Run a specific saved revision instead of the live revision"
8745
- ).option("--watch", "Stream logs until completion").option("--wait", "Alias for --watch; stream logs until completion").option(
9074
+ ).option("--watch", "Compatibility alias; run waits by default").option("--wait", "Compatibility alias; run waits by default").option("--no-wait", "Start the run and return immediately").option(
8746
9075
  "--logs",
8747
9076
  "When output is non-interactive, stream play logs to stderr while waiting"
8748
9077
  ).option("--tail-timeout-ms <ms>", "Timeout while watching the run stream").option("--force", "Supersede any active runs for this play").option("--no-open", "Print the play page URL without opening a browser").option("--json", "Emit JSON output").addHelpText(
@@ -8775,6 +9104,7 @@ Pass-through input flags:
8775
9104
  ...options.live ? ["--live"] : [],
8776
9105
  ...options.latest ? ["--latest"] : [],
8777
9106
  ...options.revisionId ? ["--revision-id", options.revisionId] : [],
9107
+ ...options.wait === false ? ["--no-wait"] : [],
8778
9108
  ...options.watch || options.wait ? ["--watch"] : [],
8779
9109
  ...options.logs ? ["--logs"] : [],
8780
9110
  ...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
@@ -8815,24 +9145,27 @@ Notes:
8815
9145
 
8816
9146
  Examples:
8817
9147
  deepline plays list
8818
- deepline plays list --json
9148
+ deepline plays list --origin prebuilt --json
8819
9149
  deepline plays search email --origin prebuilt --json
8820
9150
  `
8821
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9151
+ ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
8822
9152
  process.exitCode = await handlePlayList([
9153
+ ...options.origin ? ["--origin", options.origin] : [],
8823
9154
  ...options.json ? ["--json"] : []
8824
9155
  ]);
8825
9156
  });
8826
- play.command("search <query>").description("Search saved and prebuilt plays.").addHelpText(
9157
+ const addPlaySearchCommand = (command) => command.description("Search saved and prebuilt plays.").addHelpText(
8827
9158
  "after",
8828
9159
  `
8829
9160
  Notes:
8830
9161
  Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
8831
9162
  you need to narrow results. Use describe on a result before running it.
9163
+ The grep alias is the same ranked retrieval surface with a more literal name
9164
+ for agents that are filtering the play registry.
8832
9165
 
8833
9166
  Examples:
8834
9167
  deepline plays search email
8835
- deepline plays search "linkedin to email" --origin prebuilt --compact --json
9168
+ deepline plays grep "linkedin to email" --origin prebuilt --compact --json
8836
9169
  deepline plays describe person-linkedin-to-email --json
8837
9170
  `
8838
9171
  ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
@@ -8843,6 +9176,30 @@ Examples:
8843
9176
  ...options.json ? ["--json"] : []
8844
9177
  ]);
8845
9178
  });
9179
+ addPlaySearchCommand(play.command("search <query>"));
9180
+ play.command("grep <query>").description("Literal grep over play names, aliases, schemas, and descriptions.").addHelpText(
9181
+ "after",
9182
+ `
9183
+ Notes:
9184
+ Literal registry filtering. Terms are matched case-insensitively against play
9185
+ names, references, display names, aliases, ownership, and schemas. Use
9186
+ --mode phrase for exact phrase matching, --mode any for OR, and the default
9187
+ --mode all for AND.
9188
+
9189
+ Examples:
9190
+ deepline plays grep email --origin prebuilt --json
9191
+ deepline plays grep "company contact" --origin prebuilt --mode all --json
9192
+ deepline plays describe prebuilt/company-to-contact --json
9193
+ `
9194
+ ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9195
+ process.exitCode = await handlePlayGrep([
9196
+ query,
9197
+ ...options.origin ? ["--origin", options.origin] : [],
9198
+ ...options.compact ? ["--compact"] : [],
9199
+ ...options.mode ? ["--mode", options.mode] : [],
9200
+ ...options.json ? ["--json"] : []
9201
+ ]);
9202
+ });
8846
9203
  play.command("describe <target>").description("Describe a play contract and how to run it.").addHelpText(
8847
9204
  "after",
8848
9205
  `
@@ -8871,7 +9228,7 @@ Notes:
8871
9228
  Examples:
8872
9229
  deepline plays versions --name my-play
8873
9230
  deepline plays versions --name my-play --json
8874
- deepline plays run my-play --revision-id <revision-id> --watch
9231
+ deepline plays run my-play --revision-id <revision-id>
8875
9232
  `
8876
9233
  ).option("--name <name>", "Saved play name").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
8877
9234
  process.exitCode = await handlePlayVersions([
@@ -9073,6 +9430,7 @@ Examples:
9073
9430
  }
9074
9431
 
9075
9432
  // src/cli/commands/tools.ts
9433
+ var import_commander2 = require("commander");
9076
9434
  var import_node_fs10 = require("fs");
9077
9435
  var import_node_os7 = require("os");
9078
9436
  var import_node_path12 = require("path");
@@ -9257,9 +9615,42 @@ function toListedTool(tool) {
9257
9615
  executeCommand: `deepline tools execute ${tool.toolId}`
9258
9616
  };
9259
9617
  }
9618
+ function normalizeGrepText(value) {
9619
+ if (value === null || value === void 0) return "";
9620
+ if (typeof value === "string") return value.toLowerCase();
9621
+ if (typeof value === "number" || typeof value === "boolean") {
9622
+ return String(value).toLowerCase();
9623
+ }
9624
+ if (Array.isArray(value)) return value.map(normalizeGrepText).join(" ");
9625
+ if (typeof value === "object") {
9626
+ return Object.values(value).map(normalizeGrepText).join(" ");
9627
+ }
9628
+ return "";
9629
+ }
9630
+ function parseGrepTerms(query, mode) {
9631
+ const trimmed = query.trim().toLowerCase();
9632
+ if (!trimmed) return [];
9633
+ if (mode === "phrase") return [trimmed];
9634
+ const matches = trimmed.match(/"([^"]+)"|'([^']+)'|\S+/g) ?? [];
9635
+ return matches.map((term) => term.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
9636
+ }
9637
+ function matchesGrepQuery(value, query, mode) {
9638
+ const terms = parseGrepTerms(query, mode);
9639
+ if (terms.length === 0) return true;
9640
+ const haystack = normalizeGrepText(value);
9641
+ if (mode === "any") return terms.some((term) => haystack.includes(term));
9642
+ return terms.every((term) => haystack.includes(term));
9643
+ }
9260
9644
  async function listTools(args) {
9261
9645
  const client = new DeeplineClient();
9262
- const items = (await client.listTools()).map(toListedTool);
9646
+ const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
9647
+ const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
9648
+ const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
9649
+ const items = (await client.listTools({
9650
+ ...categoryFilter ? { categories: categoryFilter } : {}
9651
+ })).map(toListedTool).filter(
9652
+ (item) => requestedCategories.length === 0 || requestedCategories.some((category) => item.categories.includes(category))
9653
+ );
9263
9654
  const render = {
9264
9655
  sections: [
9265
9656
  {
@@ -9282,6 +9673,9 @@ async function listTools(args) {
9282
9673
  {
9283
9674
  tools: items,
9284
9675
  count: items.length,
9676
+ filters: {
9677
+ categories: requestedCategories
9678
+ },
9285
9679
  commandTemplates: TOOL_COMMAND_TEMPLATES,
9286
9680
  render
9287
9681
  },
@@ -9308,6 +9702,56 @@ async function searchTools(queryInput, options = {}) {
9308
9702
  });
9309
9703
  return 0;
9310
9704
  }
9705
+ async function grepTools(queryInput, options = {}) {
9706
+ const query = queryInput.trim();
9707
+ if (!query) {
9708
+ console.error("Usage: deepline tools grep <query> [--json]");
9709
+ return 1;
9710
+ }
9711
+ const client = new DeeplineClient();
9712
+ const requestedCategories = options.categories ? options.categories.split(",").map((item) => item.trim()).filter(Boolean) : [];
9713
+ const mode = options.mode ?? "all";
9714
+ const tools = (await client.listTools({
9715
+ grep: query,
9716
+ grepMode: mode,
9717
+ ...options.categories ? { categories: options.categories } : {}
9718
+ })).map(toListedTool).filter(
9719
+ (item) => requestedCategories.length === 0 || requestedCategories.some((category) => item.categories.includes(category))
9720
+ ).filter(
9721
+ (item) => matchesGrepQuery(
9722
+ {
9723
+ id: item.toolId,
9724
+ toolId: item.toolId,
9725
+ provider: item.provider,
9726
+ displayName: item.displayName,
9727
+ description: item.description,
9728
+ categories: item.categories,
9729
+ operation: item.operation,
9730
+ operationAliases: item.operationAliases,
9731
+ inputFields: item.inputFields
9732
+ },
9733
+ query,
9734
+ mode
9735
+ )
9736
+ );
9737
+ printCommandEnvelope(
9738
+ {
9739
+ tools,
9740
+ count: tools.length,
9741
+ query,
9742
+ grep: {
9743
+ mode,
9744
+ terms: parseGrepTerms(query, mode)
9745
+ },
9746
+ filters: {
9747
+ categories: requestedCategories
9748
+ },
9749
+ commandTemplates: TOOL_COMMAND_TEMPLATES
9750
+ },
9751
+ { json: options.json || shouldEmitJson() }
9752
+ );
9753
+ return 0;
9754
+ }
9311
9755
  function playIdentifiers(play) {
9312
9756
  return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
9313
9757
  }
@@ -9377,24 +9821,27 @@ Notes:
9377
9821
 
9378
9822
  Examples:
9379
9823
  deepline tools list
9380
- deepline tools list --json
9824
+ deepline tools list --categories email_finder --json
9381
9825
  deepline tools search email --json
9382
9826
  `
9383
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9827
+ ).option("--categories <categories>", "Comma-separated categories to filter inventory").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9384
9828
  process.exitCode = await listTools([
9829
+ ...options.categories ? ["--categories", options.categories] : [],
9385
9830
  ...options.json ? ["--json"] : []
9386
9831
  ]);
9387
9832
  });
9388
- tools.command("search <query>").description("Search available tools.").addHelpText(
9833
+ const addToolSearchCommand = (command) => command.description("Search available tools.").addHelpText(
9389
9834
  "after",
9390
9835
  `
9391
9836
  Notes:
9392
9837
  Ranked discovery for atomic provider/API operations. Results include tool ids
9393
9838
  that can be passed to deepline tools describe or deepline tools execute.
9839
+ The grep alias is the same ranked retrieval surface with a more literal name
9840
+ for agents that are filtering a registry rather than choosing a workflow.
9394
9841
 
9395
9842
  Examples:
9396
9843
  deepline tools search email
9397
- deepline tools search "company enrichment" --categories enrichment --json
9844
+ deepline tools grep "company enrichment" --categories enrichment --json
9398
9845
  deepline tools search verifier --search-mode v2 --json
9399
9846
  `
9400
9847
  ).option("--categories <categories>", "Comma-separated categories to filter ranked search").option("--search_terms <terms>", "Structured search terms for ranked search").option("--search-terms <terms>", "Structured search terms for ranked search").option("--search-mode <mode>", "Ranked search mode: v1 or v2").option("--include-search-debug", "Include ranked search debug metadata").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
@@ -9406,24 +9853,55 @@ Examples:
9406
9853
  includeSearchDebug: Boolean(options.includeSearchDebug)
9407
9854
  });
9408
9855
  });
9856
+ addToolSearchCommand(tools.command("search <query>"));
9857
+ tools.command("grep <query>").description("Literal grep over tool ids, descriptions, categories, and input fields.").addHelpText(
9858
+ "after",
9859
+ `
9860
+ Notes:
9861
+ Literal registry filtering. Terms are matched case-insensitively against tool
9862
+ ids, provider, display name, description, categories, aliases, and input
9863
+ fields. Use --mode phrase for exact phrase matching, --mode any for OR, and
9864
+ the default --mode all for AND.
9865
+
9866
+ Examples:
9867
+ deepline tools grep email --categories email_finder --json
9868
+ deepline tools grep "phone validate" --mode all --json
9869
+ deepline tools grep hunter --mode phrase --json
9870
+ `
9871
+ ).option("--categories <categories>", "Comma-separated categories to filter inventory").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9872
+ const mode = options.mode === "any" || options.mode === "phrase" ? options.mode : "all";
9873
+ process.exitCode = await grepTools(query, {
9874
+ json: options.json,
9875
+ categories: options.categories,
9876
+ mode
9877
+ });
9878
+ });
9409
9879
  const addToolMetadataCommand = (command) => command.description("Show metadata for a tool.").addHelpText(
9410
9880
  "after",
9411
9881
  `
9412
9882
  Notes:
9413
- Shows the tool contract, input schema, output schema, Deepline cost, aliases,
9414
- and metadata. describe is the supported discovery verb. get is removed in
9415
- the V2 SDK CLI; use describe for the same metadata surface.
9883
+ Shows the compact agent contract by default: what the tool does, cost,
9884
+ required inputs, play getters, and a runnable ctx.tools.execute snippet.
9885
+ Use --json for the full metadata/debug payload.
9416
9886
 
9417
9887
  Examples:
9418
9888
  deepline tools describe hunter_email_verifier
9419
- deepline tools describe hunter_email_verifier --json | jq '.inputSchema'
9889
+ deepline tools describe hunter_email_verifier --pricing-only
9890
+ deepline tools describe hunter_email_verifier --schema-only
9891
+ deepline tools describe hunter_email_verifier --examples-only
9892
+ deepline tools describe hunter_email_verifier --json
9420
9893
  deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
9421
9894
  `
9422
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (toolId, options) => {
9423
- process.exitCode = await getTool([
9424
- toolId,
9425
- ...options.json ? ["--json"] : []
9426
- ]);
9895
+ ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--pricing-only", "Only print pricing and billing semantics").option("--schema-only", "Only print input schema fields").option("--examples-only", "Only print runnable examples and sample payloads").option("--getters-only", "Only print extracted list/value getters").addOption(new import_commander2.Option("--compact", "Compatibility alias for the default compact view").hideHelp()).addOption(new import_commander2.Option("--contract-json", "Compatibility alias for compact contract JSON").hideHelp()).action(async (toolId, options) => {
9896
+ process.exitCode = await getTool(toolId, {
9897
+ json: Boolean(options.json),
9898
+ compact: Boolean(options.compact),
9899
+ contractJson: Boolean(options.contractJson),
9900
+ pricingOnly: Boolean(options.pricingOnly),
9901
+ schemaOnly: Boolean(options.schemaOnly),
9902
+ examplesOnly: Boolean(options.examplesOnly),
9903
+ gettersOnly: Boolean(options.gettersOnly)
9904
+ });
9427
9905
  });
9428
9906
  addToolMetadataCommand(tools.command("describe <toolId>"));
9429
9907
  tools.command("get <toolId>").description("Deprecated. Use tools describe.").addHelpText(
@@ -9476,8 +9954,7 @@ Examples:
9476
9954
  process.exitCode = await executeTool(args);
9477
9955
  });
9478
9956
  }
9479
- async function getTool(args) {
9480
- const toolId = args[0];
9957
+ async function getTool(toolId, options = {}) {
9481
9958
  if (!toolId) {
9482
9959
  console.error("Usage: deepline tools get <toolId> [--json]");
9483
9960
  return 1;
@@ -9494,14 +9971,264 @@ async function getTool(args) {
9494
9971
  }
9495
9972
  throw error;
9496
9973
  }
9497
- if (argsWantJson(args)) {
9974
+ if (options.contractJson) {
9975
+ process.stdout.write(`${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
9976
+ `);
9977
+ return 0;
9978
+ }
9979
+ const emitJson = options.json === true;
9980
+ if (emitJson) {
9498
9981
  process.stdout.write(`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
9499
9982
  `);
9500
9983
  return 0;
9501
9984
  }
9502
- printToolDetails(tool, toolId);
9985
+ const onlyModes = [
9986
+ options.pricingOnly,
9987
+ options.schemaOnly,
9988
+ options.examplesOnly,
9989
+ options.gettersOnly
9990
+ ].filter(Boolean).length;
9991
+ if (onlyModes > 1) {
9992
+ console.error("Use only one of --pricing-only, --schema-only, --examples-only, or --getters-only.");
9993
+ return 2;
9994
+ }
9995
+ if (options.pricingOnly) {
9996
+ printToolPricingOnly(tool, toolId);
9997
+ return 0;
9998
+ }
9999
+ if (options.schemaOnly) {
10000
+ printToolSchemaOnly(tool, toolId);
10001
+ return 0;
10002
+ }
10003
+ if (options.examplesOnly) {
10004
+ printToolExamplesOnly(tool, toolId);
10005
+ return 0;
10006
+ }
10007
+ if (options.gettersOnly) {
10008
+ printToolGettersOnly(tool, toolId);
10009
+ return 0;
10010
+ }
10011
+ if (options.compact) {
10012
+ printCompactToolContract(tool, toolId);
10013
+ return 0;
10014
+ }
10015
+ if (shouldEmitJson()) {
10016
+ process.stdout.write(`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
10017
+ `);
10018
+ return 0;
10019
+ }
10020
+ printCompactToolContract(tool, toolId);
9503
10021
  return 0;
9504
10022
  }
10023
+ function toolContractJsonForDescribe(tool, requestedToolId) {
10024
+ const toolId = String(tool.toolId || requestedToolId);
10025
+ const inputFields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
10026
+ const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
10027
+ const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
10028
+ const extractedLists = extractionContractEntries(
10029
+ arrayField(toolExecutionResult, "extractedLists", "extracted_lists")
10030
+ );
10031
+ const extractedValues = extractionContractEntries(
10032
+ arrayField(toolExecutionResult, "extractedValues", "extracted_values")
10033
+ );
10034
+ const cost = recordField(tool, "cost");
10035
+ const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
10036
+ const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
10037
+ return {
10038
+ schemaVersion: 1,
10039
+ toolId,
10040
+ provider: tool.provider,
10041
+ displayName: tool.displayName,
10042
+ description: tool.description,
10043
+ categories: tool.categories,
10044
+ inputFields: inputFields.map((field) => ({
10045
+ name: field.name,
10046
+ type: field.type ?? "unknown",
10047
+ required: Boolean(field.required),
10048
+ ...field.description ? { description: field.description } : {},
10049
+ ...Object.prototype.hasOwnProperty.call(field, "default") ? { default: field.default } : {}
10050
+ })),
10051
+ cost: {
10052
+ pricingModel: stringField(cost, "pricingModel", "pricing_model") || null,
10053
+ billingMode: stringField(cost, "billingMode", "billing_mode") || null,
10054
+ deeplineCreditsPerPricingUnit: deeplineCredits,
10055
+ deeplineUsdPerPricingUnit
10056
+ },
10057
+ getters: {
10058
+ extractedLists,
10059
+ extractedValues
10060
+ },
10061
+ executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`
10062
+ };
10063
+ }
10064
+ function extractionContractEntries(entries) {
10065
+ return entries.flatMap((entry) => {
10066
+ if (!isRecord4(entry)) return [];
10067
+ const name = stringField(entry, "name");
10068
+ const expression = stringField(entry, "expression");
10069
+ return name && expression ? [{ name, expression }] : [];
10070
+ });
10071
+ }
10072
+ function printCompactToolContract(tool, requestedToolId) {
10073
+ const contract = toolContractJsonForDescribe(tool, requestedToolId);
10074
+ const cost = isRecord4(contract.cost) ? contract.cost : {};
10075
+ const getters = isRecord4(contract.getters) ? contract.getters : {};
10076
+ const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
10077
+ const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
10078
+ const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10079
+ console.log(String(contract.toolId));
10080
+ if (contract.displayName) console.log(`Best for: ${contract.displayName}`);
10081
+ if (typeof contract.description === "string" && contract.description.trim()) {
10082
+ console.log(`Description: ${contract.description.trim()}`);
10083
+ }
10084
+ if (Array.isArray(contract.categories) && contract.categories.length) {
10085
+ console.log(`Tags: ${contract.categories.join(", ")}`);
10086
+ }
10087
+ printToolPricingOnly(tool, requestedToolId, { heading: "Cost" });
10088
+ if (inputFields.length) {
10089
+ console.log("");
10090
+ console.log("Inputs:");
10091
+ for (const field of inputFields) {
10092
+ if (!isRecord4(field)) continue;
10093
+ const name = stringField(field, "name");
10094
+ if (!name) continue;
10095
+ const required = field.required ? "*" : "";
10096
+ const type = stringField(field, "type") || "unknown";
10097
+ const description = stringField(field, "description");
10098
+ console.log(`- ${name}${required}: ${type}${description ? ` - ${description}` : ""}`);
10099
+ }
10100
+ }
10101
+ console.log("");
10102
+ printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
10103
+ if (listGetters.length || valueGetters.length) {
10104
+ console.log("");
10105
+ console.log("Getters:");
10106
+ if (listGetters.length) console.log("Lists:");
10107
+ for (const entry of listGetters) {
10108
+ if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
10109
+ }
10110
+ if (valueGetters.length) console.log("Values:");
10111
+ for (const entry of valueGetters) {
10112
+ if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
10113
+ }
10114
+ }
10115
+ console.log("");
10116
+ console.log(`More: deepline tools describe ${contract.toolId} --pricing-only | --schema-only | --examples-only | --getters-only | --json`);
10117
+ }
10118
+ function printToolPricingOnly(tool, requestedToolId, options = {}) {
10119
+ const contract = toolContractJsonForDescribe(tool, requestedToolId);
10120
+ const cost = isRecord4(contract.cost) ? contract.cost : {};
10121
+ const pricingModel = stringField(cost, "pricingModel") || "unknown";
10122
+ const billingMode = stringField(cost, "billingMode") || "unknown";
10123
+ const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
10124
+ const credits = numberField(cost, "deeplineCreditsPerPricingUnit");
10125
+ const usd = numberField(cost, "deeplineUsdPerPricingUnit");
10126
+ const price = credits !== null ? `${formatDecimal(credits)} Deepline credits${usd !== null ? ` / ${formatUsd(usd)}` : ""} per ${unit}` : "pricing unavailable";
10127
+ console.log(`${options.heading ?? `Pricing: ${contract.toolId}`}: ${price}`);
10128
+ console.log(`Billing: ${billingMode}`);
10129
+ }
10130
+ function printToolSchemaOnly(tool, requestedToolId) {
10131
+ const contract = toolContractJsonForDescribe(tool, requestedToolId);
10132
+ const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10133
+ console.log(`Schema: ${contract.toolId}`);
10134
+ if (!inputFields.length) {
10135
+ console.log("Inputs: none");
10136
+ return;
10137
+ }
10138
+ console.log("Inputs:");
10139
+ for (const field of inputFields) {
10140
+ if (!isRecord4(field)) continue;
10141
+ const name = stringField(field, "name");
10142
+ if (!name) continue;
10143
+ const required = field.required ? "*" : "";
10144
+ const type = stringField(field, "type") || "unknown";
10145
+ const description = stringField(field, "description");
10146
+ const defaultSuffix = Object.prototype.hasOwnProperty.call(field, "default") ? ` default=${JSON.stringify(field.default)}` : "";
10147
+ console.log(`- ${name}${required}: ${type}${defaultSuffix}${description ? ` - ${description}` : ""}`);
10148
+ }
10149
+ }
10150
+ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
10151
+ const contract = toolContractJsonForDescribe(tool, requestedToolId);
10152
+ const toolId = String(contract.toolId);
10153
+ const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10154
+ const sampleInput = Object.fromEntries(
10155
+ inputFields.slice(0, 4).flatMap((field) => {
10156
+ if (!isRecord4(field)) return [];
10157
+ const name = stringField(field, "name");
10158
+ if (!name) return [];
10159
+ return [[name, sampleValueForField(field)]];
10160
+ })
10161
+ );
10162
+ console.log(`Use in a play:`);
10163
+ console.log("```ts");
10164
+ console.log("const result = await ctx.tools.execute({");
10165
+ console.log(` id: '${stableStepIdForTool(toolId)}',`);
10166
+ console.log(` tool: '${toolId}',`);
10167
+ console.log(` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`);
10168
+ console.log("});");
10169
+ const getters = isRecord4(contract.getters) ? contract.getters : {};
10170
+ const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
10171
+ const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
10172
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord4);
10173
+ if (firstGetter) {
10174
+ const name = stringField(firstGetter, "name") || "value";
10175
+ const expression = stringField(firstGetter, "expression");
10176
+ if (expression) console.log(`const ${safeIdentifier(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`);
10177
+ }
10178
+ console.log("```");
10179
+ if (options.includeSamples !== false) {
10180
+ const samples = recordField(tool, "samples");
10181
+ printSamples(samples);
10182
+ }
10183
+ }
10184
+ function printToolGettersOnly(tool, requestedToolId) {
10185
+ const contract = toolContractJsonForDescribe(tool, requestedToolId);
10186
+ const getters = isRecord4(contract.getters) ? contract.getters : {};
10187
+ const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
10188
+ const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
10189
+ console.log(`Getters: ${contract.toolId}`);
10190
+ if (!listGetters.length && !valueGetters.length) {
10191
+ console.log("No generated getters declared. Use --json only if you need raw metadata.");
10192
+ return;
10193
+ }
10194
+ if (listGetters.length) {
10195
+ console.log("Lists:");
10196
+ for (const entry of listGetters) {
10197
+ if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
10198
+ }
10199
+ }
10200
+ if (valueGetters.length) {
10201
+ console.log("Values:");
10202
+ for (const entry of valueGetters) {
10203
+ if (isRecord4(entry)) console.log(`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`);
10204
+ }
10205
+ }
10206
+ }
10207
+ function sampleValueForField(field) {
10208
+ const name = stringField(field, "name").toLowerCase();
10209
+ const type = stringField(field, "type").toLowerCase();
10210
+ if (Object.prototype.hasOwnProperty.call(field, "default")) return field.default;
10211
+ if (name.includes("email")) return "ada@example.com";
10212
+ if (name.includes("domain") || name.includes("website")) return "example.com";
10213
+ if (name.includes("first")) return "Ada";
10214
+ if (name.includes("last")) return "Lovelace";
10215
+ if (name.includes("name")) return "Ada Lovelace";
10216
+ if (type === "integer" || type === "number") return 1;
10217
+ if (type === "boolean") return true;
10218
+ if (type === "array") return [];
10219
+ if (type === "object") return {};
10220
+ return "...";
10221
+ }
10222
+ function stableStepIdForTool(toolId) {
10223
+ return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
10224
+ }
10225
+ function safeIdentifier(name) {
10226
+ const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
10227
+ return cleaned || "value";
10228
+ }
10229
+ function playResultExpression(entry) {
10230
+ return stringField(entry, "expression").replace(/^toolExecutionResult\./, "result.");
10231
+ }
9505
10232
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
9506
10233
  const toolId = String(tool.toolId || requestedToolId);
9507
10234
  const {
@@ -9532,152 +10259,6 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
9532
10259
  }
9533
10260
  };
9534
10261
  }
9535
- function printToolDetails(tool, requestedToolId) {
9536
- const toolId = String(tool.toolId || requestedToolId);
9537
- const operation = typeof tool.operation === "string" ? tool.operation : "";
9538
- const displayBase = operation && operation.startsWith(`${tool.provider}_`) ? operation.slice(String(tool.provider).length + 1) : operation ? `${tool.provider} ${operation}`.trim() : toolId;
9539
- const displayName = titleCase(displayBase || String(tool.displayName || toolId));
9540
- const cost = isRecord4(tool.cost) ? tool.cost : null;
9541
- const pricing = recordField(tool, "pricing");
9542
- const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
9543
- const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
9544
- const billingSource = stringField(tool, "billingSource", "billing_source");
9545
- const billingSourceLabel = stringField(tool, "billingSourceLabel", "billing_source_label");
9546
- const estimatedCreditsRange = stringField(tool, "estimatedCreditsRange", "estimated_credits_range");
9547
- const estimatedUsdRange = stringField(tool, "estimatedUsdRange", "estimated_usd_range");
9548
- const estimateModelVersion = stringField(tool, "estimateModelVersion", "estimate_model_version");
9549
- const estimateBasedOnTools = arrayField(tool, "estimateBasedOnTools", "estimate_based_on_tools").map((item) => String(item).trim()).filter(Boolean);
9550
- const stepContributions = arrayField(tool, "stepContributions", "step_contributions");
9551
- const playExpansion = recordField(tool, "playExpansion", "play_expansion");
9552
- const samples = recordField(tool, "samples");
9553
- const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
9554
- console.log(`Tool: ${toolId}`);
9555
- console.log(" Runtime output help:");
9556
- console.log(" describe shows declared schema/getters, not an observed provider response");
9557
- console.log(` observe actual shape: deepline tools execute ${toolId} --input '{...}' --json`);
9558
- console.log(" for play getter bugs: run the play, then use the db query commands printed by runs get");
9559
- if (displayName) {
9560
- console.log(" Display name:");
9561
- console.log(` ${displayName}`);
9562
- }
9563
- if (tool.categories.length > 0) {
9564
- console.log(" Categories:");
9565
- console.log(` ${tool.categories.join(", ")}`);
9566
- }
9567
- const printedCost = printToolCost({
9568
- pricing,
9569
- cost,
9570
- billingSource,
9571
- deeplineCredits,
9572
- deeplineUsdPerPricingUnit
9573
- });
9574
- if (!printedCost && ["run_javascript", "call_local_codex", "call_local_claude_code"].includes(toolId)) {
9575
- console.log(" Cost: free");
9576
- }
9577
- if (billingSourceLabel) {
9578
- console.log(` Billing source: ${billingSourceLabel}`);
9579
- }
9580
- if (estimatedCreditsRange) {
9581
- const usdSuffix = estimatedUsdRange ? ` (~${estimatedUsdRange})` : "";
9582
- console.log(` Estimated play spend: ${estimatedCreditsRange}${usdSuffix}`);
9583
- if (estimateModelVersion) console.log(` model: ${estimateModelVersion}`);
9584
- if (estimateBasedOnTools.length) console.log(` based on: ${estimateBasedOnTools.join(", ")}`);
9585
- if (stepContributions.length) {
9586
- console.log(" step contributions:");
9587
- for (const item of stepContributions) {
9588
- if (!isRecord4(item)) continue;
9589
- const stepTool = typeof item.tool === "string" ? item.tool.trim() : "";
9590
- const low = typeof item.lowCredits === "number" ? item.lowCredits : null;
9591
- const high = typeof item.highCredits === "number" ? item.highCredits : null;
9592
- const lowUsd = typeof item.lowUsd === "number" ? item.lowUsd : null;
9593
- const highUsd = typeof item.highUsd === "number" ? item.highUsd : null;
9594
- if (!stepTool || low === null || high === null) continue;
9595
- const stepUsdSuffix = lowUsd !== null && highUsd !== null ? ` (~${formatUsd(lowUsd)}-${formatUsd(highUsd)})` : "";
9596
- console.log(` - ${stepTool}: ${formatDecimal(low)}-${formatDecimal(high)} credits${stepUsdSuffix}`);
9597
- }
9598
- }
9599
- }
9600
- if (playExpansion && Object.keys(playExpansion).length > 0) {
9601
- const group = typeof playExpansion.group === "string" ? playExpansion.group.trim() : "";
9602
- console.log(" Play expansion:");
9603
- if (group) console.log(` group: ${group}`);
9604
- }
9605
- const fields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
9606
- if (fields.length) {
9607
- console.log(" Inputs (operation-specific):");
9608
- for (const field of fields) {
9609
- const name = String(field.name || "");
9610
- const typeName = String(field.type || "unknown");
9611
- const requiredLabel = field.required ? "required" : "optional";
9612
- const defaultSuffix = Object.prototype.hasOwnProperty.call(field, "default") ? `, default: ${JSON.stringify(field.default)}` : "";
9613
- const desc = typeof field.description === "string" && field.description.trim() ? ` - ${field.description.trim()}` : "";
9614
- console.log(` - ${name} (${typeName}, ${requiredLabel}${defaultSuffix})${desc}`);
9615
- }
9616
- console.log(" Tip: pass --payload with a JSON object.");
9617
- }
9618
- printSamples(samples);
9619
- printUsageGuidance(usageGuidance);
9620
- if (isPlayTool(tool)) {
9621
- console.log(" Play contract:");
9622
- console.log(" - This is a deepline-native waterfall; the returned rows are extracted by target getters, not by hand-authored payload shape.");
9623
- if (playExpansion && typeof playExpansion.group === "string" && playExpansion.group.trim()) {
9624
- console.log(` - Output alias/runtime group is: ${playExpansion.group.trim()}`);
9625
- }
9626
- const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult");
9627
- const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
9628
- const targets = extractedValues.map((entry) => isRecord4(entry) && typeof entry.name === "string" ? entry.name : "").filter(Boolean).sort();
9629
- if (targets.length) {
9630
- console.log(` - Built-in extract targets: ${targets.join(", ")}`);
9631
- }
9632
- }
9633
- console.log("");
9634
- console.log("Usage:");
9635
- const requestPayload = samplePayload(samples, "request");
9636
- if (isPlayTool(tool)) {
9637
- if (requestPayload !== void 0) {
9638
- console.log(` deepline enrich --with '${JSON.stringify({ alias: "result", tool: toolId, payload: requestPayload })}'`);
9639
- } else {
9640
- console.log(` deepline enrich --with '{"alias":"result","tool":"${toolId}","payload":{...}}'`);
9641
- }
9642
- } else if (requestPayload !== void 0) {
9643
- console.log(` deepline tools execute ${toolId} --payload '${JSON.stringify(requestPayload)}'`);
9644
- } else {
9645
- console.log(` deepline tools execute ${toolId} --payload '{...}'`);
9646
- }
9647
- console.log(" deepline tools describe <tool_id> --json");
9648
- }
9649
- function printUsageGuidance(usageGuidance) {
9650
- if (Object.keys(usageGuidance).length === 0) return;
9651
- const execute = stringField(usageGuidance, "execute");
9652
- const toolExecutionResult = recordField(usageGuidance, "toolExecutionResult", "tool_execution_result");
9653
- const toolResponse = recordField(toolExecutionResult, "toolResponse", "tool_response");
9654
- const extractedLists = arrayField(toolExecutionResult, "extractedLists", "extracted_lists");
9655
- const extractedValues = arrayField(toolExecutionResult, "extractedValues", "extracted_values");
9656
- console.log(" Usage guidance:");
9657
- if (execute) console.log(` ${execute}`);
9658
- const raw = stringField(toolResponse, "raw");
9659
- const meta = stringField(toolResponse, "meta");
9660
- if (raw) console.log(` Raw tool response: ${raw}`);
9661
- if (meta) console.log(` Tool response metadata: ${meta}`);
9662
- printExtractions("Extracted lists", extractedLists);
9663
- printExtractions("Extracted values", extractedValues);
9664
- }
9665
- function printExtractions(label, entries) {
9666
- if (!entries.length) return;
9667
- console.log(` ${label}:`);
9668
- for (const entry of entries) {
9669
- if (!isRecord4(entry)) continue;
9670
- const name = stringField(entry, "name");
9671
- const expression = stringField(entry, "expression");
9672
- const details = recordField(entry, "details");
9673
- const rawToolOutputPaths = arrayField(details, "rawToolOutputPaths", "raw_tool_output_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
9674
- const candidatePaths = arrayField(details, "candidatePaths", "candidate_paths").map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
9675
- if (!name || !expression) continue;
9676
- const paths = candidatePaths.length ? candidatePaths : rawToolOutputPaths;
9677
- const pathSuffix = paths.length ? ` from ${paths.join(", ")}` : "";
9678
- console.log(` - ${name}: ${expression}${pathSuffix}`);
9679
- }
9680
- }
9681
10262
  function singleLineText(value, maxLength = 260) {
9682
10263
  if (typeof value !== "string") return "";
9683
10264
  const text = value.replace(/\s+/g, " ").trim();
@@ -9694,42 +10275,6 @@ function formatListedToolCost(tool) {
9694
10275
  const displayText = stringField(pricing, "displayText", "display_text");
9695
10276
  return displayText ? `Cost: ${displayText}` : "";
9696
10277
  }
9697
- function printToolCost(input) {
9698
- const { pricing, cost, billingSource, deeplineCredits, deeplineUsdPerPricingUnit } = input;
9699
- if (billingSource === "own_provider_credentials") {
9700
- console.log(" Cost: free through Deepline");
9701
- return true;
9702
- }
9703
- const displayText = stringField(pricing, "displayText", "display_text");
9704
- if (displayText) {
9705
- console.log(` Cost: ${displayText}`);
9706
- const details = arrayField(pricing, "details").map((item) => String(item).trim()).filter(Boolean);
9707
- if (details.length) {
9708
- console.log(" notes:");
9709
- for (const detail of details) console.log(` - ${detail}`);
9710
- }
9711
- return true;
9712
- }
9713
- const pricingModel = cost ? typeof cost.pricingModel === "string" ? cost.pricingModel : typeof cost.pricing_model === "string" ? cost.pricing_model : "" : "";
9714
- const billingMode = cost ? typeof cost.billingMode === "string" ? cost.billingMode : typeof cost.billing_mode === "string" ? cost.billing_mode : "" : "";
9715
- if (deeplineCredits === 0) {
9716
- console.log(" Cost: Free");
9717
- return true;
9718
- }
9719
- if (pricingModel && deeplineCredits !== null) {
9720
- const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : "call";
9721
- const usdText = deeplineUsdPerPricingUnit !== null ? ` / ${formatUsd(deeplineUsdPerPricingUnit)}` : "";
9722
- const billingSuffix = billingMode ? ` (${billingMode})` : "";
9723
- console.log(` Cost: ${formatDecimal(deeplineCredits)} Deepline credits${usdText} per ${unit}${billingSuffix}`);
9724
- return true;
9725
- }
9726
- const summary = stringField(pricing, "summary");
9727
- if (summary) {
9728
- console.log(` Cost: ${summary}`);
9729
- return true;
9730
- }
9731
- return false;
9732
- }
9733
10278
  function toolInputFieldsForDisplay(inputSchema) {
9734
10279
  if (Array.isArray(inputSchema.fields)) return inputSchema.fields.filter(isRecord4);
9735
10280
  const jsonSchema = isRecord4(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
@@ -9780,17 +10325,6 @@ function listExtractorPathsFromUsageGuidance(tool) {
9780
10325
  ).filter(Boolean);
9781
10326
  });
9782
10327
  }
9783
- function isPlayTool(tool) {
9784
- const provider = typeof tool.provider === "string" ? tool.provider : "";
9785
- return provider === "deepline_native" && Boolean(recordField(tool, "playExpansion", "play_expansion"));
9786
- }
9787
- function titleCase(value) {
9788
- return value.replace(/[_-]+/g, " ").split(" ").filter(Boolean).map((part) => {
9789
- const lower = part.toLowerCase();
9790
- const special = { linkedin: "LinkedIn", crm: "CRM", api: "API" };
9791
- return special[lower] ?? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`;
9792
- }).join(" ");
9793
- }
9794
10328
  function formatDecimal(value) {
9795
10329
  const text = value.toFixed(12).replace(/0+$/, "").replace(/\.$/, "");
9796
10330
  return text || "0";
@@ -10550,6 +11084,61 @@ function shouldDeferSkillsSyncForCommand() {
10550
11084
  const subcommand = args[1];
10551
11085
  return (command === "play" || command === "plays") && subcommand === "run" && args.includes("--json");
10552
11086
  }
11087
+ async function runPlayRunnerHealthCheck() {
11088
+ const dir = await (0, import_promises5.mkdtemp)((0, import_node_path15.join)((0, import_node_os9.tmpdir)(), "deepline-health-play-"));
11089
+ const file = (0, import_node_path15.join)(dir, "health-check.play.ts");
11090
+ try {
11091
+ await (0, import_promises5.writeFile)(
11092
+ file,
11093
+ [
11094
+ "import { definePlay } from 'deepline';",
11095
+ "",
11096
+ "export default definePlay('health-check', async (ctx) => {",
11097
+ " const rows = await ctx",
11098
+ " .map('health_rows', [{ id: 'a' }, { id: 'b' }])",
11099
+ " .step('echo', (row) => ({ ok: true, id: row.id }))",
11100
+ " .run({ key: 'id' });",
11101
+ " return { ok: true, rows, source: 'deepline health --play-runner' };",
11102
+ "});",
11103
+ ""
11104
+ ].join("\n"),
11105
+ "utf8"
11106
+ );
11107
+ let capturedOutput = "";
11108
+ const originalWrite = process.stdout.write.bind(process.stdout);
11109
+ process.stdout.write = ((chunk, ...args) => {
11110
+ capturedOutput += typeof chunk === "string" ? chunk : String(chunk);
11111
+ return true;
11112
+ });
11113
+ let exitCode = 1;
11114
+ try {
11115
+ exitCode = await handlePlayRun([
11116
+ file,
11117
+ "--input",
11118
+ "{}",
11119
+ "--watch",
11120
+ "--no-open",
11121
+ "--json"
11122
+ ]);
11123
+ } finally {
11124
+ process.stdout.write = originalWrite;
11125
+ }
11126
+ if (exitCode !== 0) {
11127
+ throw new Error(
11128
+ `play runner canary exited ${exitCode}: ${capturedOutput.slice(0, 1e3)}`
11129
+ );
11130
+ }
11131
+ return {
11132
+ status: "ok",
11133
+ playRunner: {
11134
+ status: "ok",
11135
+ check: "no-provider local play run completed"
11136
+ }
11137
+ };
11138
+ } finally {
11139
+ await (0, import_promises5.rm)(dir, { recursive: true, force: true });
11140
+ }
11141
+ }
10553
11142
  async function main() {
10554
11143
  const mainStartedAt = Date.now();
10555
11144
  recordCliTrace({
@@ -10561,7 +11150,7 @@ async function main() {
10561
11150
  if (printStartupPhase) {
10562
11151
  progress?.phase("loading deepline cli");
10563
11152
  }
10564
- const program = new import_commander2.Command();
11153
+ const program = new import_commander3.Command();
10565
11154
  program.name("deepline").description("Deepline CLI (TypeScript SDK)").version(SDK_VERSION, "-v, --version", "Show version").showHelpAfterError().showSuggestionAfterError(true).addHelpText(
10566
11155
  "after",
10567
11156
  `
@@ -10570,7 +11159,7 @@ Common commands:
10570
11159
  deepline auth status --json
10571
11160
  deepline plays search email --json
10572
11161
  deepline plays describe person-linkedin-to-email --json
10573
- deepline plays run my.play.ts --input '{"domain":"stripe.com"}' --watch
11162
+ deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
10574
11163
  deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
10575
11164
  deepline update
10576
11165
 
@@ -10626,18 +11215,30 @@ Exit codes:
10626
11215
  registerDbCommands(program);
10627
11216
  registerFeedbackCommands(program);
10628
11217
  registerUpdateCommand(program);
10629
- program.command("health").description("Check server health.").option("--json", "Force JSON output.").addHelpText(
11218
+ program.command("health").description("Check server health.").option("--json", "Force JSON output.").option(
11219
+ "--play-runner",
11220
+ "Run a tiny no-provider play to verify the full play execution plane."
11221
+ ).addHelpText(
10630
11222
  "after",
10631
11223
  `
10632
11224
  Notes:
10633
11225
  Read-only connectivity check for the configured Deepline host. Prints the raw
10634
11226
  server health payload as JSON.
11227
+ Add --play-runner to verify bundling, coordinator dispatch, runtime callbacks,
11228
+ and run streaming with a tiny no-provider play.
10635
11229
 
10636
11230
  Examples:
10637
11231
  deepline health
11232
+ deepline health --play-runner
10638
11233
  `
10639
- ).action(async () => {
11234
+ ).action(async (options) => {
10640
11235
  try {
11236
+ if (options.playRunner) {
11237
+ const data2 = await runPlayRunnerHealthCheck();
11238
+ process.stdout.write(`${JSON.stringify(data2, null, 2)}
11239
+ `);
11240
+ return;
11241
+ }
10641
11242
  const client = new DeeplineClient();
10642
11243
  const data = await client.health();
10643
11244
  process.stdout.write(`${JSON.stringify(data, null, 2)}