deepline 0.1.186 → 0.1.187

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.
@@ -608,10 +608,10 @@ var SDK_RELEASE = {
608
608
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
609
609
  // fields shipped in 0.1.153.
610
610
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
- version: "0.1.186",
611
+ version: "0.1.187",
612
612
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
613
613
  supportPolicy: {
614
- latest: "0.1.186",
614
+ latest: "0.1.187",
615
615
  minimumSupported: "0.1.53",
616
616
  deprecatedBelow: "0.1.53",
617
617
  commandMinimumSupported: [
@@ -8305,13 +8305,155 @@ var PLAY_BOOTSTRAP_STAGE_NAMES = [
8305
8305
  "email",
8306
8306
  "phone"
8307
8307
  ];
8308
+ var MONITOR_BOOTSTRAP_TEMPLATE = "monitor-triggered";
8309
+ var MONITOR_BOOTSTRAP_DEFAULT_TOOL = "instantly.campaign_events";
8310
+ var MONITOR_BOOTSTRAP_DEFAULT_STREAM = "webhook_events";
8311
+ function isMonitorBootstrapTemplate(value) {
8312
+ return value === MONITOR_BOOTSTRAP_TEMPLATE;
8313
+ }
8314
+ function hasMonitorBootstrapForbiddenChar(value) {
8315
+ for (const char of value) {
8316
+ const code = char.codePointAt(0) ?? 0;
8317
+ if (code < 32 || code === 127) return true;
8318
+ if (char === "'" || char === '"' || char === "`" || char === "\\") {
8319
+ return true;
8320
+ }
8321
+ }
8322
+ return false;
8323
+ }
8324
+ function validateMonitorBootstrapFlagValue(flag, value) {
8325
+ if (hasMonitorBootstrapForbiddenChar(value)) {
8326
+ throw new PlayBootstrapUsageError(
8327
+ `${flag} may not contain line breaks, control characters, quotes, or backticks. It is interpolated into the generated play source (including comments); use a plain monitor tool id / stream key / name.`
8328
+ );
8329
+ }
8330
+ return value;
8331
+ }
8332
+ function parseMonitorBootstrapOptions(args) {
8333
+ const [, ...rest] = args;
8334
+ const options = {
8335
+ name: MONITOR_BOOTSTRAP_TEMPLATE,
8336
+ tool: MONITOR_BOOTSTRAP_DEFAULT_TOOL,
8337
+ stream: MONITOR_BOOTSTRAP_DEFAULT_STREAM,
8338
+ out: null
8339
+ };
8340
+ for (let index = 0; index < rest.length; index += 1) {
8341
+ const arg = rest[index];
8342
+ const value = () => nextFlagValue(rest, index, arg);
8343
+ switch (arg) {
8344
+ case "--name":
8345
+ options.name = validateMonitorBootstrapFlagValue("--name", value());
8346
+ index += 1;
8347
+ break;
8348
+ case "--tool":
8349
+ options.tool = validateMonitorBootstrapFlagValue("--tool", value());
8350
+ index += 1;
8351
+ break;
8352
+ case "--stream":
8353
+ options.stream = validateMonitorBootstrapFlagValue("--stream", value());
8354
+ index += 1;
8355
+ break;
8356
+ case "--out":
8357
+ options.out = value();
8358
+ index += 1;
8359
+ break;
8360
+ default:
8361
+ throw new PlayBootstrapUsageError(
8362
+ `Unknown plays bootstrap option for ${MONITOR_BOOTSTRAP_TEMPLATE}: ${arg}
8363
+ Usage: deepline plays bootstrap ${MONITOR_BOOTSTRAP_TEMPLATE} [--tool <monitor-tool-id>] [--stream <stream-key>] [--name NAME] [--out monitor.play.ts]`
8364
+ );
8365
+ }
8366
+ }
8367
+ if (!options.tool) {
8368
+ throw new PlayBootstrapUsageError(
8369
+ `${MONITOR_BOOTSTRAP_TEMPLATE} needs a non-empty --tool.`
8370
+ );
8371
+ }
8372
+ if (!options.stream) {
8373
+ throw new PlayBootstrapUsageError(
8374
+ `${MONITOR_BOOTSTRAP_TEMPLATE} needs a non-empty --stream.`
8375
+ );
8376
+ }
8377
+ return options;
8378
+ }
8379
+ function generateMonitorTriggeredPlaySource(options) {
8380
+ return `import { definePlay } from 'deepline';
8381
+ import type { SqlListenerEvent } from 'deepline';
8382
+
8383
+ // Row shape delivered by ${options.tool} / ${options.stream}. This is a starting
8384
+ // point only: inspect the real columns with
8385
+ // deepline monitors available ${options.tool}
8386
+ // and tighten this type to the fields you read below.
8387
+ //
8388
+ // Before deploying a NEW monitor, run \`deepline monitors list\` \u2014 if one
8389
+ // already feeds this stream for your scope, this play will already react to its
8390
+ // rows (a play binds to the shared stream), so you may not need to deploy at all.
8391
+ type MonitorRow = Record<string, unknown>;
8392
+
8393
+ export default definePlay(
8394
+ ${jsString(options.name)},
8395
+ async (ctx, event: SqlListenerEvent<MonitorRow>) => {
8396
+ // The monitor delivers the changed row as event.after. It is null for
8397
+ // DELETE operations, so guard before reading fields.
8398
+ const row = event.after;
8399
+ if (!row) {
8400
+ ctx.log(\`Monitor \${event.tool}/\${event.stream} \${event.operation} with no row; nothing to do.\`);
8401
+ return { handled: false, operation: event.operation };
8402
+ }
8403
+
8404
+ ctx.log(
8405
+ \`Monitor \${event.tool}/\${event.stream} \${event.operation}: \${JSON.stringify(row).slice(0, 200)}\`,
8406
+ );
8407
+
8408
+ // Capture the changed row into a durable dataset so the run has inspectable,
8409
+ // exportable output. ctx.dataset is a CALLABLE: ctx.dataset(key, rows) where
8410
+ // key is a compile-time string LITERAL and rows is an array of row objects.
8411
+ // It returns a builder; add per-row columns with .withColumn(name, resolver)
8412
+ // and finish with .run(). There is no .push/.add \u2014 pass all rows up front.
8413
+ // TODO: replace this plain persist with your real per-row work \u2014 enrich each
8414
+ // row via .withColumn('...', (row, rowCtx) => rowCtx.tools.execute(...)),
8415
+ // notify, write to another table, or fan out with rowCtx.runPlay.
8416
+ const captured = await ctx.dataset('monitor_events', [row]).run({
8417
+ description: \`Captured \${event.tool}/\${event.stream} \${event.operation} rows.\`,
8418
+ });
8419
+
8420
+ return {
8421
+ handled: true,
8422
+ tool: event.tool,
8423
+ stream: event.stream,
8424
+ operation: event.operation,
8425
+ changedAt: event.changedAt,
8426
+ capturedRows: await captured.count(),
8427
+ };
8428
+ },
8429
+ {
8430
+ description: ${jsString(`Run whenever ${options.tool} writes a new ${options.stream} row.`)},
8431
+ // A monitor trigger: this play wakes on row changes written by the bound
8432
+ // monitor tool + stream. Discover a tool's streams and columns with:
8433
+ // deepline monitors available ${options.tool}
8434
+ // To bind a different monitor, swap tool/stream (and re-check operations)
8435
+ // for one of its (tool, stream) pairs.
8436
+ sqlListeners: [
8437
+ {
8438
+ id: 'events',
8439
+ tool: ${jsString(options.tool)},
8440
+ stream: ${jsString(options.stream)},
8441
+ operations: ['INSERT'],
8442
+ // Optional: only wake on some rows \u2014 add a where filter to the binding above:
8443
+ // where: { after: { <column>: { eq: 'value' } } } // operators: eq, neq, in, notIn, isNull, isNotNull, ilike
8444
+ },
8445
+ ],
8446
+ },
8447
+ );
8448
+ `;
8449
+ }
8308
8450
  function playBootstrapUsageLine() {
8309
8451
  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]`;
8310
8452
  }
8311
8453
  function requireBootstrapTemplate(rawTemplate) {
8312
8454
  if (!rawTemplate) {
8313
8455
  throw new PlayBootstrapUsageError(
8314
- `plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}.
8456
+ `plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}|${MONITOR_BOOTSTRAP_TEMPLATE}.
8315
8457
  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`
8316
8458
  );
8317
8459
  }
@@ -8319,7 +8461,7 @@ Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using
8319
8461
  const looksLikePlayReference = rawTemplate.includes("/") || rawTemplate.endsWith(".play.ts");
8320
8462
  throw new PlayBootstrapUsageError(
8321
8463
  `Unknown plays bootstrap template: ${rawTemplate}
8322
- Supported templates: ${formatPlayBootstrapTemplates()}` + (looksLikePlayReference ? `
8464
+ Supported templates: ${formatPlayBootstrapTemplates()}|${MONITOR_BOOTSTRAP_TEMPLATE}` + (looksLikePlayReference ? `
8323
8465
 
8324
8466
  "${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.` : "")
8325
8467
  );
@@ -9528,7 +9670,25 @@ function renderPlayBootstrapError(error) {
9528
9670
  console.error(errorMessage2(error));
9529
9671
  return error instanceof PlayBootstrapError ? error.exitCode : 1;
9530
9672
  }
9673
+ function writeBootstrapSource(source, out) {
9674
+ if (out) {
9675
+ writeFileSync8(resolve7(out), source, "utf-8");
9676
+ process.stdout.write(`Wrote ${resolve7(out)}
9677
+ `);
9678
+ return 0;
9679
+ }
9680
+ process.stdout.write(source);
9681
+ return 0;
9682
+ }
9683
+ function runMonitorBootstrap(args) {
9684
+ const options = parseMonitorBootstrapOptions(args);
9685
+ const source = generateMonitorTriggeredPlaySource(options);
9686
+ return writeBootstrapSource(source, options.out);
9687
+ }
9531
9688
  async function runPlayBootstrap(args) {
9689
+ if (isMonitorBootstrapTemplate(args[0])) {
9690
+ return runMonitorBootstrap(args);
9691
+ }
9532
9692
  const options = parsePlayBootstrapOptions(args);
9533
9693
  const client2 = new DeeplineClient();
9534
9694
  const contracts = await loadBootstrapContracts(client2, options);
@@ -9538,20 +9698,15 @@ async function runPlayBootstrap(args) {
9538
9698
  ...contracts,
9539
9699
  ...csvContext
9540
9700
  });
9541
- if (options.out) {
9542
- writeFileSync8(resolve7(options.out), source, "utf-8");
9543
- process.stdout.write(`Wrote ${resolve7(options.out)}
9544
- `);
9545
- return 0;
9546
- }
9547
- process.stdout.write(source);
9548
- return 0;
9701
+ return writeBootstrapSource(source, options.out);
9549
9702
  }
9550
9703
  async function handlePlayBootstrap(args) {
9551
9704
  return runPlayBootstrap(args).catch(renderPlayBootstrapError);
9552
9705
  }
9553
9706
  function registerPlayBootstrapCommand(play) {
9554
- play.command("bootstrap <template>").description("Print a scratchpad play for a GTM route template.").addHelpText(
9707
+ play.command("bootstrap <template>").description(
9708
+ "Print a scratchpad play for a GTM route or monitor-triggered template."
9709
+ ).addHelpText(
9555
9710
  "after",
9556
9711
  `
9557
9712
  Notes:
@@ -9575,6 +9730,13 @@ Notes:
9575
9730
  company-people company/account rows -> people play
9576
9731
  company-people-email company/account rows -> people play -> email finder
9577
9732
  company-people-phone company/account rows -> people play -> phone finder
9733
+ monitor-triggered run a play whenever a monitor writes a new row
9734
+ (definePlay with sqlListeners; no --from/stage flags)
9735
+
9736
+ Monitor-triggered options (only for the monitor-triggered template):
9737
+ --tool <monitor-tool-id> monitor tool to bind (default instantly.campaign_events)
9738
+ --stream <stream-key> output stream to listen on (default webhook_events)
9739
+ Discover a tool's streams/columns with: deepline monitors available <id>
9578
9740
 
9579
9741
  Resource refs:
9580
9742
  csv:./contacts.csv
@@ -9604,6 +9766,9 @@ Examples:
9604
9766
  deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 --out prospecting.play.ts
9605
9767
  deepline plays bootstrap company-people-email --from provider:crustdata_companydb_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 --out account-contacts.play.ts
9606
9768
  deepline plays bootstrap company-list --from provider:crustdata_companydb_search --limit 5 --out companies.play.ts
9769
+
9770
+ deepline plays bootstrap monitor-triggered --out on-new-row.play.ts
9771
+ deepline plays bootstrap monitor-triggered --tool tamradar.company_radar --stream company_job_openings --out on-new-job.play.ts
9607
9772
  `
9608
9773
  ).option("--name <name>", "Generated play name").option(
9609
9774
  "--from <ref>",
@@ -9620,7 +9785,13 @@ Examples:
9620
9785
  ).option(
9621
9786
  "--phone <ref>",
9622
9787
  "Phone finder stage: play:REF, provider:ID, or providers:ID,ID"
9623
- ).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) => {
9788
+ ).option("--limit <n>", "Maximum rows to fan out in the generated play").option(
9789
+ "--tool <id>",
9790
+ "monitor-triggered only: monitor tool id to bind (e.g. instantly.campaign_events)"
9791
+ ).option(
9792
+ "--stream <key>",
9793
+ "monitor-triggered only: monitor output stream to listen on (e.g. webhook_events)"
9794
+ ).option("--out <path>", "Write generated play source to a file").action(async (template, options) => {
9624
9795
  process.exitCode = await handlePlayBootstrap([
9625
9796
  template,
9626
9797
  ...options.name ? ["--name", options.name] : [],
@@ -9630,6 +9801,8 @@ Examples:
9630
9801
  ...options.email ? ["--email", options.email] : [],
9631
9802
  ...options.phone ? ["--phone", options.phone] : [],
9632
9803
  ...options.limit ? ["--limit", options.limit] : [],
9804
+ ...options.tool ? ["--tool", options.tool] : [],
9805
+ ...options.stream ? ["--stream", options.stream] : [],
9633
9806
  ...options.out ? ["--out", options.out] : []
9634
9807
  ]);
9635
9808
  });
@@ -13554,6 +13727,89 @@ function printToolGetterHints(hints) {
13554
13727
  if (hint.unavailable) console.log(` - warning: ${hint.unavailable}`);
13555
13728
  }
13556
13729
  }
13730
+ function printPlayTriggers(triggers) {
13731
+ if (!triggers) return;
13732
+ const hasAny = (triggers.sqlListeners?.length ?? 0) > 0 || Boolean(triggers.cron) || triggers.webhook === true;
13733
+ if (!hasAny) return;
13734
+ console.log(" triggers:");
13735
+ for (const listener of triggers.sqlListeners ?? []) {
13736
+ const target = listener.tool && listener.stream ? `${listener.tool}/${listener.stream}` : listener.tool ?? listener.stream ?? listener.id;
13737
+ const operations = listener.operations.length ? listener.operations.join(", ") : "INSERT, UPDATE";
13738
+ const whereNote = listener.where ? " (with where filter)" : "";
13739
+ console.log(` sqlListeners \u2192 ${target} on ${operations}${whereNote}`);
13740
+ }
13741
+ if (triggers.cron) {
13742
+ const timezone = triggers.cron.timezone ? ` (${triggers.cron.timezone})` : "";
13743
+ console.log(` cron \u2192 ${triggers.cron.schedule}${timezone}`);
13744
+ }
13745
+ if (triggers.webhook === true) {
13746
+ console.log(" webhook \u2192 enabled");
13747
+ }
13748
+ }
13749
+ function printRecognizedSummary(recognized) {
13750
+ if (!recognized) return;
13751
+ if (recognized.tools?.length) {
13752
+ console.log(` tools: ${recognized.tools.join(", ")}`);
13753
+ }
13754
+ for (const dataset of recognized.datasets ?? []) {
13755
+ const columns = dataset.columns?.length ? ` (${dataset.columns.length} col${dataset.columns.length === 1 ? "" : "s"}: ${dataset.columns.join(", ")})` : "";
13756
+ console.log(` dataset ${dataset.name}${columns}`);
13757
+ }
13758
+ if (recognized.inputs?.length) {
13759
+ console.log(` inputs: ${recognized.inputs.join(", ")}`);
13760
+ }
13761
+ if (recognized.outputs?.length) {
13762
+ console.log(` outputs: ${recognized.outputs.join(", ")}`);
13763
+ }
13764
+ }
13765
+ function formatPlayCheckIssueLines(issue) {
13766
+ const lines = [` - ${issue.message}`];
13767
+ if (issue.path) {
13768
+ lines.push(` at ${issue.path}`);
13769
+ }
13770
+ if (issue.validOptions?.length) {
13771
+ lines.push(` \u2192 valid: [${issue.validOptions.join(", ")}]`);
13772
+ }
13773
+ if (issue.hint?.trim()) {
13774
+ lines.push(` hint: ${issue.hint.trim()}`);
13775
+ }
13776
+ if (issue.docsHint?.trim()) {
13777
+ lines.push(` next: ${issue.docsHint.trim()}`);
13778
+ }
13779
+ return lines;
13780
+ }
13781
+ function printPlayCheckIssues(issues, writer) {
13782
+ if (!issues?.length) return;
13783
+ const errorIssues = issues.filter((issue) => issue.severity === "error");
13784
+ const warningIssues = issues.filter((issue) => issue.severity === "warning");
13785
+ if (errorIssues.length) {
13786
+ writer(" issues:");
13787
+ for (const issue of errorIssues) {
13788
+ for (const line of formatPlayCheckIssueLines(issue)) writer(line);
13789
+ }
13790
+ }
13791
+ if (warningIssues.length) {
13792
+ writer(" warnings:");
13793
+ for (const issue of warningIssues) {
13794
+ for (const line of formatPlayCheckIssueLines(issue)) writer(line);
13795
+ }
13796
+ }
13797
+ }
13798
+ function partitionMirroredErrors(errors, issues) {
13799
+ const errorMessages = (issues ?? []).filter((issue) => issue.severity === "error").map((issue) => issue.message.trim()).filter((message) => message.length > 0);
13800
+ if (errorMessages.length === 0) {
13801
+ return { unstructuredErrors: errors ?? [] };
13802
+ }
13803
+ const isMirror = (error) => {
13804
+ const trimmed = error.trim();
13805
+ return errorMessages.some(
13806
+ (message) => trimmed === message || trimmed.startsWith(message)
13807
+ );
13808
+ };
13809
+ return {
13810
+ unstructuredErrors: (errors ?? []).filter((error) => !isMirror(error))
13811
+ };
13812
+ }
13557
13813
  async function handlePlayCheck(args) {
13558
13814
  const options = parsePlayCheckOptions(args);
13559
13815
  if (!isFileTarget(options.target)) {
@@ -13686,16 +13942,27 @@ async function handlePlayCheck(args) {
13686
13942
  `
13687
13943
  );
13688
13944
  } else if (enrichedResult.valid) {
13689
- console.log(`\u2713 ${playName} passed cloud play check`);
13945
+ const summary = enrichedResult.summary?.trim();
13946
+ console.log(
13947
+ summary ? `\u2713 ${playName} valid \u2014 ${summary}` : `\u2713 ${playName} passed cloud play check`
13948
+ );
13690
13949
  if (enrichedResult.artifactHash) {
13691
13950
  console.log(` artifact: ${enrichedResult.artifactHash.slice(0, 12)}`);
13692
13951
  }
13952
+ printPlayTriggers(enrichedResult.triggers);
13953
+ printRecognizedSummary(enrichedResult.recognized);
13954
+ printPlayCheckIssues(enrichedResult.issues, (line) => console.log(line));
13693
13955
  printToolGetterHints(enrichedResult.toolGetterHints);
13694
13956
  } else {
13695
13957
  console.error(`\u2717 ${playName} failed cloud play check`);
13696
- for (const error of enrichedResult.errors) {
13958
+ const { unstructuredErrors } = partitionMirroredErrors(
13959
+ enrichedResult.errors,
13960
+ enrichedResult.issues
13961
+ );
13962
+ for (const error of unstructuredErrors) {
13697
13963
  console.error(` ${error}`);
13698
13964
  }
13965
+ printPlayCheckIssues(enrichedResult.issues, (line) => console.error(line));
13699
13966
  printToolGetterHints(enrichedResult.toolGetterHints);
13700
13967
  }
13701
13968
  return enrichedResult.valid ? 0 : 1;
@@ -21797,44 +22064,455 @@ Examples:
21797
22064
  }
21798
22065
 
21799
22066
  // src/cli/commands/monitors.ts
22067
+ import { readFileSync as readFileSync9 } from "fs";
22068
+ import { createInterface } from "readline/promises";
21800
22069
  var FORBIDDEN_AS_API_ERROR = { forbiddenAsApiError: true };
22070
+ var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
22071
+ function withJsonOption(command) {
22072
+ return command.option("--json", JSON_OPTION_DESCRIPTION);
22073
+ }
22074
+ var COMPACT_OPTION_DESCRIPTION = "Ask the server for high-signal fields only (smaller output for agent loops)";
21801
22075
  function buildHttpClient() {
21802
22076
  return new HttpClient(resolveConfig());
21803
22077
  }
21804
- function parseJsonObjectArg(raw, flag) {
22078
+ var MonitorsUsageError = class extends Error {
22079
+ code = "MONITORS_USAGE_ERROR";
22080
+ constructor(message) {
22081
+ super(message);
22082
+ this.name = "MonitorsUsageError";
22083
+ }
22084
+ };
22085
+ var MonitorDryRunUnsupportedError = class extends Error {
22086
+ code = "MONITOR_DRY_RUN_UNSUPPORTED";
22087
+ constructor(message) {
22088
+ super(message);
22089
+ this.name = "MonitorDryRunUnsupportedError";
22090
+ }
22091
+ };
22092
+ function monitorsErrorExitCode(error) {
22093
+ if (error instanceof MonitorsUsageError) return 2;
22094
+ if (error instanceof AuthError) return 3;
22095
+ if (error instanceof DeeplineError) {
22096
+ const status = error.statusCode;
22097
+ if (status === 401 || status === 403) return 3;
22098
+ if (status === 404) return 4;
22099
+ if (status === 400 || status === 422) return 7;
22100
+ return 5;
22101
+ }
22102
+ return 5;
22103
+ }
22104
+ function monitorsFailureNextCommand(error, exitCode) {
22105
+ if (error instanceof DeeplineError && error.code === "monitor_access_required") {
22106
+ return "deepline monitors status";
22107
+ }
22108
+ if (exitCode === 3) return "deepline auth status --json";
22109
+ if (exitCode === 4) return "deepline monitors list --status all --json";
22110
+ return void 0;
22111
+ }
22112
+ function readValidationIssues(error) {
22113
+ if (!(error instanceof DeeplineError)) return [];
22114
+ const response = asRecord(asRecord(error.details)?.response);
22115
+ const issues = Array.isArray(response?.issues) ? response.issues : [];
22116
+ return issues.flatMap((raw) => {
22117
+ const issue = asRecord(raw);
22118
+ if (!issue) return [];
22119
+ return [
22120
+ {
22121
+ ...asString(issue.path) ? { path: asString(issue.path) } : {},
22122
+ ...asString(issue.message) ? { message: asString(issue.message) } : {}
22123
+ }
22124
+ ];
22125
+ });
22126
+ }
22127
+ function reportMonitorsFailure(error) {
22128
+ const exitCode = monitorsErrorExitCode(error);
22129
+ const next = monitorsFailureNextCommand(error, exitCode);
22130
+ const wantsJson = shouldEmitJson(process.argv.includes("--json"));
22131
+ if (wantsJson) {
22132
+ const payload = errorToJsonPayload(error);
22133
+ printJson({
22134
+ ok: false,
22135
+ exitCode,
22136
+ ...next ? { next } : {},
22137
+ error: payload.error
22138
+ });
22139
+ } else {
22140
+ const message = error instanceof Error ? error.message : String(error);
22141
+ process.stderr.write(`Error: ${message}
22142
+ `);
22143
+ for (const issue of readValidationIssues(error)) {
22144
+ process.stderr.write(
22145
+ ` - ${issue.path ? `${issue.path}: ` : ""}${issue.message ?? ""}
22146
+ `
22147
+ );
22148
+ }
22149
+ if (next) process.stderr.write(`Next: ${next}
22150
+ `);
22151
+ }
22152
+ process.exitCode = exitCode;
22153
+ }
22154
+ function monitorsAction(handler) {
22155
+ return async (...args) => {
22156
+ try {
22157
+ await handler(...args);
22158
+ } catch (error) {
22159
+ reportMonitorsFailure(error);
22160
+ }
22161
+ };
22162
+ }
22163
+ function parseJsonObjectArg(raw, argLabel) {
21805
22164
  let parsed;
21806
22165
  try {
21807
22166
  parsed = JSON.parse(raw);
21808
22167
  } catch (error) {
21809
- throw new Error(
21810
- `${flag} must be a JSON object. ${error instanceof Error ? error.message : String(error)}`
22168
+ throw new MonitorsUsageError(
22169
+ `${argLabel} must be a JSON object. ${error instanceof Error ? error.message : String(error)}`
21811
22170
  );
21812
22171
  }
21813
22172
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
21814
- throw new Error(`${flag} must be a JSON object.`);
22173
+ throw new MonitorsUsageError(`${argLabel} must be a JSON object.`);
21815
22174
  }
21816
22175
  return parsed;
21817
22176
  }
22177
+ function resolveMonitorJsonBody(input2) {
22178
+ const readFile3 = input2.readFile ?? ((path) => readFileSync9(path, "utf-8"));
22179
+ const readStdin = input2.readStdin ?? (() => readFileSync9(0, "utf-8"));
22180
+ if (input2.positional !== void 0 && input2.file !== void 0) {
22181
+ throw new MonitorsUsageError(
22182
+ `Pass exactly one source for ${input2.argLabel}: the positional JSON or --file, not both.`
22183
+ );
22184
+ }
22185
+ if (input2.positional !== void 0) {
22186
+ return parseJsonObjectArg(input2.positional, input2.argLabel);
22187
+ }
22188
+ if (input2.file !== void 0) {
22189
+ if (input2.file === "-") {
22190
+ return parseJsonObjectArg(readStdin(), `${input2.argLabel} (stdin)`);
22191
+ }
22192
+ let raw;
22193
+ try {
22194
+ raw = readFile3(input2.file);
22195
+ } catch (error) {
22196
+ throw new MonitorsUsageError(
22197
+ `Could not read --file ${input2.file}: ${error instanceof Error ? error.message : String(error)}`
22198
+ );
22199
+ }
22200
+ return parseJsonObjectArg(raw, `${input2.argLabel} (--file ${input2.file})`);
22201
+ }
22202
+ throw new MonitorsUsageError(
22203
+ `${input2.command} needs ${input2.argLabel} (a JSON object). Pass it positionally:
22204
+ ${input2.command} '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
22205
+ or from a file / stdin:
22206
+ ${input2.command} --file monitor.json
22207
+ cat monitor.json | ${input2.command} --file -`
22208
+ );
22209
+ }
21818
22210
  function encodeKey(key) {
21819
22211
  return encodeURIComponent(key);
21820
22212
  }
21821
- async function handleMonitorsTools(options) {
22213
+ function asRecord(value) {
22214
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
22215
+ }
22216
+ function asString(value) {
22217
+ return typeof value === "string" && value.trim() ? value : void 0;
22218
+ }
22219
+ function asFiniteNumber(value) {
22220
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
22221
+ }
22222
+ function yesNo(value) {
22223
+ if (value === true) return "yes";
22224
+ if (value === false) return "no";
22225
+ return "unknown";
22226
+ }
22227
+ function readDeployOutputContract(payload) {
22228
+ const contract = asRecord(payload.output_contract);
22229
+ if (!contract) return { streams: [] };
22230
+ const rawOutputs = Array.isArray(contract.outputs) ? contract.outputs : [];
22231
+ const streams = rawOutputs.flatMap((raw) => {
22232
+ const output2 = asRecord(raw);
22233
+ const stream = output2 ? asString(output2.stream) : void 0;
22234
+ const table = output2 ? asString(output2.table) : void 0;
22235
+ if (!output2 || !stream || !table) return [];
22236
+ const columns = Array.isArray(output2.columns) ? output2.columns.flatMap((rawColumn) => {
22237
+ const column = asRecord(rawColumn);
22238
+ const name = column ? asString(column.name) : void 0;
22239
+ return name ? [name] : [];
22240
+ }) : [];
22241
+ return [
22242
+ {
22243
+ stream,
22244
+ table,
22245
+ columns,
22246
+ isEvent: output2.is_event_stream === true,
22247
+ matchesPayload: output2.matches_payload === true
22248
+ }
22249
+ ];
22250
+ });
22251
+ return { tool: asString(contract.tool), streams };
22252
+ }
22253
+ function renderMonitorDeployCompletion(payload) {
22254
+ const monitor = asRecord(payload.monitor);
22255
+ const key = monitor ? asString(monitor.key) : void 0;
22256
+ const { tool, streams } = readDeployOutputContract(payload);
22257
+ if (!key || streams.length === 0) return void 0;
22258
+ const toolId = tool ?? (monitor ? asString(monitor.tool) : void 0) ?? "<tool>";
22259
+ const name = monitor ? asString(monitor.name) : void 0;
22260
+ const status = monitor ? asString(monitor.status) : void 0;
22261
+ const eventStreams = streams.filter((stream) => stream.isEvent);
22262
+ const matched = eventStreams.filter((stream) => stream.matchesPayload);
22263
+ const shown = matched.length ? matched : eventStreams.length ? eventStreams : streams;
22264
+ const primary = shown[0];
22265
+ const lines = [
22266
+ `\u2713 Deployed monitor "${key}"${name ? ` (${name})` : ""} \u2014 tool ${toolId}${status ? `, status ${status}` : ""}`,
22267
+ "",
22268
+ "Output \u2192 Customer DB:"
22269
+ ];
22270
+ for (const stream of shown) {
22271
+ lines.push(` ${stream.table} (stream: ${stream.stream})`);
22272
+ lines.push(
22273
+ ` columns: ${stream.columns.length ? stream.columns.join(", ") : "(promoted dynamically as events arrive)"}`
22274
+ );
22275
+ }
22276
+ lines.push(
22277
+ "",
22278
+ "This monitor streams new rows into your Customer DB \u2014 there is no manual run.",
22279
+ "Next:",
22280
+ " \u2022 Query the table above directly, or",
22281
+ " \u2022 Run a play on each new row (third arg to definePlay in your .play.ts):",
22282
+ ` sqlListeners: [{ id: '${primary.stream}', tool: '${toolId}', stream: '${primary.stream}', operations: ['INSERT'] }]`,
22283
+ " The changed row arrives to your handler as the sqlListener event's `after`.",
22284
+ "",
22285
+ "Reuse: many plays can bind to this one stream \u2014 you do NOT need a separate",
22286
+ "monitor per play. Before deploying another monitor on this tool, run",
22287
+ "`deepline monitors list` and reuse an existing one that covers your scope."
22288
+ );
22289
+ return `${lines.join("\n")}
22290
+ `;
22291
+ }
22292
+ function renderMonitorDeployPlan(payload) {
22293
+ const valid = payload.valid !== false;
22294
+ const lines = [
22295
+ "DRY RUN \u2014 nothing was deployed.",
22296
+ valid ? "Definition: valid" : "Definition: INVALID"
22297
+ ];
22298
+ if (!valid) {
22299
+ const issues = Array.isArray(payload.issues) ? payload.issues : [];
22300
+ for (const raw of issues) {
22301
+ const issue = asRecord(raw);
22302
+ const path = issue ? asString(issue.path) : void 0;
22303
+ const message = issue ? asString(issue.message) : void 0;
22304
+ if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
22305
+ }
22306
+ }
22307
+ const estimate = asRecord(payload.deploy_cost_estimate);
22308
+ const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
22309
+ if (credits !== void 0) {
22310
+ const note = estimate ? asString(estimate.note) : void 0;
22311
+ lines.push(
22312
+ `Deploy cost: would cost ${credits} Deepline credits${note ? ` \u2014 ${note}` : ""}.`
22313
+ );
22314
+ } else {
22315
+ lines.push(
22316
+ "Deploy cost: estimate unavailable from this server (validation only)."
22317
+ );
22318
+ }
22319
+ const candidates = Array.isArray(payload.reuse_candidates) ? payload.reuse_candidates : [];
22320
+ const candidateLines = candidates.flatMap((raw) => {
22321
+ const candidate = asRecord(raw);
22322
+ const key = candidate ? asString(candidate.key) : void 0;
22323
+ if (!candidate || !key) return [];
22324
+ const name = asString(candidate.name);
22325
+ const status = asString(candidate.status);
22326
+ const covers = asString(candidate.covers);
22327
+ return [
22328
+ ` ${key}${name ? ` (${name})` : ""}${status ? ` \u2014 ${status}` : ""}${covers ? `; covers ${covers}` : ""}`
22329
+ ];
22330
+ });
22331
+ if (candidateLines.length > 0) {
22332
+ lines.push(
22333
+ "",
22334
+ "You may already have a monitor covering this \u2014 check before deploying:",
22335
+ ...candidateLines,
22336
+ " Inspect: deepline monitors get <key> --json"
22337
+ );
22338
+ }
22339
+ lines.push(
22340
+ "",
22341
+ valid ? "Deploy for real: re-run this command without --dry-run." : "Fix the definition, then re-run this command."
22342
+ );
22343
+ return `${lines.join("\n")}
22344
+ `;
22345
+ }
22346
+ function renderMonitorDeletePlan(payload, fallbackKey) {
22347
+ const plan = asRecord(payload.plan);
22348
+ const key = (plan ? asString(plan.monitor_key) : void 0) ?? fallbackKey;
22349
+ const lines = [
22350
+ "DRY RUN \u2014 nothing was deleted.",
22351
+ `Delete plan for monitor "${key}":`,
22352
+ ` delete Deepline record: ${yesNo(plan?.would_delete_record)}`,
22353
+ ` deprovision upstream provider resource: ${yesNo(plan?.would_deprovision_upstream)}`,
22354
+ "",
22355
+ `Delete for real: deepline monitors delete ${key} --yes`
22356
+ ];
22357
+ return `${lines.join("\n")}
22358
+ `;
22359
+ }
22360
+ function renderMonitorReactivatePlan(payload, key) {
22361
+ const estimate = asRecord(payload.cost_estimate);
22362
+ const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
22363
+ const lines = [
22364
+ "DRY RUN \u2014 nothing was reactivated.",
22365
+ credits !== void 0 ? `Reactivate cost: would cost ${credits} Deepline credits.` : "Reactivate cost: estimate unavailable from this server.",
22366
+ "",
22367
+ `Reactivate for real: deepline monitors reactivate ${key}`
22368
+ ];
22369
+ return `${lines.join("\n")}
22370
+ `;
22371
+ }
22372
+ function assertMonitorDryRunAcknowledged(payload, context) {
22373
+ if (payload.dry_run === true) return;
22374
+ throw new MonitorDryRunUnsupportedError(
22375
+ `${context.command} --dry-run: the server response did not acknowledge dry-run mode (missing "dry_run": true). This Deepline server may not support --dry-run for ${context.mutation}, so the response cannot be trusted as a plan. Nothing was rendered as a plan. Verify current state with \`deepline monitors get <key> --json\`, and re-run without --dry-run only when you intend the real ${context.mutation}.`
22376
+ );
22377
+ }
22378
+ function monitorDeleteRequiresYesError(key, options = {}) {
22379
+ const flags = options.localOnly ? " --local-only" : "";
22380
+ return new MonitorsUsageError(
22381
+ `monitors delete is destructive: it deletes monitor "${key}"${options.localOnly ? " (Deepline record only)" : " and deprovisions its upstream provider resource"}. Non-interactive runs must confirm with --yes:
22382
+ deepline monitors delete ${key}${flags} --yes
22383
+ Preview the plan first with:
22384
+ deepline monitors delete ${key}${flags} --dry-run`
22385
+ );
22386
+ }
22387
+ async function handleMonitorsStatus(options) {
22388
+ const http = buildHttpClient();
22389
+ const payload = await http.request(
22390
+ "/api/v2/monitors/access",
22391
+ { method: "GET" }
22392
+ );
22393
+ const hasAccess = payload.has_access === true;
22394
+ const reason = typeof payload.reason === "string" && payload.reason.trim() ? payload.reason.trim() : void 0;
22395
+ const text = hasAccess ? `\u2713 You have access to Deepline Monitors${reason ? `
22396
+ ${reason}` : ""}
22397
+ ` : `\u2717 No access to Deepline Monitors${reason ? ` \u2014 ${reason}` : ""}
22398
+ `;
22399
+ printCommandEnvelope(
22400
+ { has_access: hasAccess, ...reason ? { reason } : {} },
22401
+ { json: options.json, text }
22402
+ );
22403
+ if (!hasAccess) {
22404
+ process.exitCode = 1;
22405
+ }
22406
+ }
22407
+ function renderAvailableToolsText(payload) {
22408
+ const tools = Array.isArray(payload.tools) ? payload.tools : null;
22409
+ if (!tools) return void 0;
22410
+ const returned = asFiniteNumber(payload.returned) ?? tools.length;
22411
+ const total = asFiniteNumber(payload.total);
22412
+ const lines = [
22413
+ total !== void 0 ? `Monitor tools you can deploy (${returned} of ${total}):` : "Monitor tools you can deploy:"
22414
+ ];
22415
+ for (const raw of tools) {
22416
+ const entry = asRecord(raw);
22417
+ const id = entry ? asString(entry.tool) : void 0;
22418
+ if (!entry || !id) continue;
22419
+ const name = asString(entry.name) ?? asString(entry.display_name);
22420
+ const deployedCount = asFiniteNumber(entry.deployed_count);
22421
+ lines.push(
22422
+ ` ${id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (deployed: ${deployedCount})` : ""}`
22423
+ );
22424
+ }
22425
+ if (payload.is_truncated === true) {
22426
+ lines.push("List truncated; raise --limit to see more.");
22427
+ }
22428
+ lines.push(
22429
+ "",
22430
+ "Describe one (payload schema + output streams): deepline monitors available <tool-id> --json"
22431
+ );
22432
+ return `${lines.join("\n")}
22433
+ `;
22434
+ }
22435
+ async function handleMonitorsAvailable(toolId, options) {
22436
+ if (toolId !== void 0 && options.tool !== void 0) {
22437
+ if (toolId !== options.tool) {
22438
+ throw new MonitorsUsageError(
22439
+ `Pass the tool id once: got positional "${toolId}" and --tool "${options.tool}". Use: deepline monitors available ${toolId}`
22440
+ );
22441
+ }
22442
+ }
22443
+ const tool = toolId ?? options.tool;
21822
22444
  const http = buildHttpClient();
21823
22445
  const params = new URLSearchParams();
21824
22446
  if (options.provider) params.set("provider", options.provider);
21825
- if (options.tool) params.set("tool", options.tool);
22447
+ if (tool) params.set("tool", tool);
21826
22448
  if (options.search) params.set("search", options.search);
21827
22449
  if (options.limit) params.set("limit", options.limit);
22450
+ const compactList = !tool && !options.full;
22451
+ if (compactList || options.compact) params.set("compact", "true");
21828
22452
  const query = params.toString();
21829
22453
  const payload = await http.request(
21830
22454
  `/api/v2/monitors/tools${query ? `?${query}` : ""}`,
21831
22455
  { method: "GET", ...FORBIDDEN_AS_API_ERROR }
21832
22456
  );
21833
- printCommandEnvelope(payload, { json: options.json });
22457
+ printCommandEnvelope(payload, {
22458
+ json: options.json,
22459
+ // Human list view shows id + name + deployed_count ("deployed: N") so
22460
+ // can-deploy vs already-deployed is visible in one view. Describing a
22461
+ // single tool keeps the full JSON contract (payload schema, streams).
22462
+ text: tool ? void 0 : renderAvailableToolsText(payload)
22463
+ });
22464
+ }
22465
+ function renderDeployedListText(payload, requestedStatus) {
22466
+ const monitors = Array.isArray(payload.monitors) ? payload.monitors : null;
22467
+ if (!monitors) return void 0;
22468
+ const lines = [
22469
+ monitors.length === 0 ? "No deployed monitors matched." : `Deployed monitors (${monitors.length}):`
22470
+ ];
22471
+ for (const raw of monitors) {
22472
+ const entry = asRecord(raw);
22473
+ const key = entry ? asString(entry.key) ?? asString(entry.monitor_key) : void 0;
22474
+ if (!entry || !key) continue;
22475
+ const status = asString(entry.status);
22476
+ const tool = asString(entry.tool);
22477
+ const name = asString(entry.name);
22478
+ lines.push(
22479
+ ` ${key}${status ? ` ${status}` : ""}${tool ? ` ${tool}` : ""}${name ? ` (${name})` : ""}`
22480
+ );
22481
+ }
22482
+ const applied = asString(payload.status_filter_applied) ?? requestedStatus ?? "active";
22483
+ lines.push(
22484
+ `Status filter: ${applied}${requestedStatus === void 0 && applied === "active" ? " (default)" : ""} \u2014 use --status all to include disabled monitors.`
22485
+ );
22486
+ if (monitors.length > 0) {
22487
+ lines.push("", "Inspect one: deepline monitors get <key> --json");
22488
+ }
22489
+ return `${lines.join("\n")}
22490
+ `;
22491
+ }
22492
+ async function handleMonitorsList(options) {
22493
+ const http = buildHttpClient();
22494
+ const params = new URLSearchParams();
22495
+ if (options.status) params.set("status", options.status);
22496
+ if (options.limit) params.set("limit", options.limit);
22497
+ if (options.compact) params.set("compact", "true");
22498
+ const query = params.toString();
22499
+ const payload = await http.request(
22500
+ `/api/v2/monitors/deployed${query ? `?${query}` : ""}`,
22501
+ { method: "GET", ...FORBIDDEN_AS_API_ERROR }
22502
+ );
22503
+ printCommandEnvelope(payload, {
22504
+ json: options.json,
22505
+ text: renderDeployedListText(payload, options.status)
22506
+ });
21834
22507
  }
21835
22508
  async function handleMonitorsCheck(definition, options) {
21836
22509
  const http = buildHttpClient();
21837
- const body = parseJsonObjectArg(definition, "--definition");
22510
+ const body = resolveMonitorJsonBody({
22511
+ positional: definition,
22512
+ file: options.file,
22513
+ argLabel: "<definition>",
22514
+ command: "deepline monitors check"
22515
+ });
21838
22516
  const payload = await http.request(
21839
22517
  "/api/v2/monitors/check",
21840
22518
  { method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
@@ -21843,26 +22521,37 @@ async function handleMonitorsCheck(definition, options) {
21843
22521
  }
21844
22522
  async function handleMonitorsDeploy(definition, options) {
21845
22523
  const http = buildHttpClient();
21846
- const body = parseJsonObjectArg(definition, "--definition");
22524
+ const body = resolveMonitorJsonBody({
22525
+ positional: definition,
22526
+ file: options.file,
22527
+ argLabel: "<definition>",
22528
+ command: "deepline monitors deploy"
22529
+ });
22530
+ if (options.dryRun) {
22531
+ const payload2 = await http.request(
22532
+ "/api/v2/monitors/check",
22533
+ { method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
22534
+ );
22535
+ const valid = payload2.valid !== false;
22536
+ printCommandEnvelope(
22537
+ { dry_run: true, ...payload2 },
22538
+ { json: options.json, text: renderMonitorDeployPlan(payload2) }
22539
+ );
22540
+ if (!valid) {
22541
+ process.exitCode = 7;
22542
+ }
22543
+ return;
22544
+ }
21847
22545
  const payload = await http.request(
21848
22546
  "/api/v2/monitors/deploy",
21849
22547
  { method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
21850
22548
  );
21851
- printCommandEnvelope(payload, { json: options.json });
21852
- }
21853
- async function handleDeployedList(options) {
21854
- const http = buildHttpClient();
21855
- const params = new URLSearchParams();
21856
- if (options.status) params.set("status", options.status);
21857
- if (options.limit) params.set("limit", options.limit);
21858
- const query = params.toString();
21859
- const payload = await http.request(
21860
- `/api/v2/monitors/deployed${query ? `?${query}` : ""}`,
21861
- { method: "GET", ...FORBIDDEN_AS_API_ERROR }
21862
- );
21863
- printCommandEnvelope(payload, { json: options.json });
22549
+ printCommandEnvelope(payload, {
22550
+ json: options.json,
22551
+ text: renderMonitorDeployCompletion(payload)
22552
+ });
21864
22553
  }
21865
- async function handleDeployedGet(key, options) {
22554
+ async function handleMonitorsGet(key, options) {
21866
22555
  const http = buildHttpClient();
21867
22556
  const payload = await http.request(
21868
22557
  `/api/v2/monitors/deployed/${encodeKey(key)}`,
@@ -21870,10 +22559,58 @@ async function handleDeployedGet(key, options) {
21870
22559
  );
21871
22560
  printCommandEnvelope(payload, { json: options.json });
21872
22561
  }
21873
- async function handleDeployedDelete(key, options) {
22562
+ async function confirmMonitorDelete(key, options) {
22563
+ const rl = createInterface({
22564
+ input: process.stdin,
22565
+ output: process.stderr
22566
+ });
22567
+ try {
22568
+ const consequence = options.localOnly ? "This removes the Deepline record only (the upstream provider resource is left in place)." : "This deprovisions the upstream provider resource.";
22569
+ const answer = await rl.question(
22570
+ `Delete monitor "${key}"? ${consequence} [y/N] `
22571
+ );
22572
+ return /^y(es)?$/i.test(answer.trim());
22573
+ } finally {
22574
+ rl.close();
22575
+ }
22576
+ }
22577
+ async function handleMonitorsDelete(key, options) {
21874
22578
  const http = buildHttpClient();
21875
22579
  const params = new URLSearchParams();
21876
22580
  if (options.localOnly) params.set("local_only", "true");
22581
+ if (options.dryRun) {
22582
+ params.set("dry_run", "true");
22583
+ const payload2 = await http.request(
22584
+ `/api/v2/monitors/deployed/${encodeKey(key)}?${params.toString()}`,
22585
+ { method: "DELETE", ...FORBIDDEN_AS_API_ERROR }
22586
+ );
22587
+ assertMonitorDryRunAcknowledged(payload2, {
22588
+ command: "deepline monitors delete",
22589
+ mutation: "delete"
22590
+ });
22591
+ printCommandEnvelope(payload2, {
22592
+ json: options.json,
22593
+ text: renderMonitorDeletePlan(payload2, key)
22594
+ });
22595
+ return;
22596
+ }
22597
+ if (!options.yes) {
22598
+ const interactive = process.stdout.isTTY === true && process.stdin.isTTY === true;
22599
+ if (!interactive) {
22600
+ throw monitorDeleteRequiresYesError(key, {
22601
+ localOnly: options.localOnly
22602
+ });
22603
+ }
22604
+ const confirmed = await confirmMonitorDelete(key, {
22605
+ localOnly: options.localOnly
22606
+ });
22607
+ if (!confirmed) {
22608
+ process.stderr.write(`Aborted. Monitor "${key}" was not deleted.
22609
+ `);
22610
+ process.exitCode = 2;
22611
+ return;
22612
+ }
22613
+ }
21877
22614
  const query = params.toString();
21878
22615
  const payload = await http.request(
21879
22616
  `/api/v2/monitors/deployed/${encodeKey(key)}${query ? `?${query}` : ""}`,
@@ -21881,17 +22618,37 @@ async function handleDeployedDelete(key, options) {
21881
22618
  );
21882
22619
  printCommandEnvelope(payload, { json: options.json });
21883
22620
  }
21884
- async function handleDeployedUpdate(key, patch, options) {
22621
+ async function handleMonitorsUpdate(key, patch, options) {
21885
22622
  const http = buildHttpClient();
21886
- const body = parseJsonObjectArg(patch, "<patch>");
22623
+ const body = resolveMonitorJsonBody({
22624
+ positional: patch,
22625
+ file: options.file,
22626
+ argLabel: "<patch>",
22627
+ command: `deepline monitors update ${key}`
22628
+ });
21887
22629
  const payload = await http.request(
21888
22630
  `/api/v2/monitors/deployed/${encodeKey(key)}`,
21889
22631
  { method: "PATCH", body, ...FORBIDDEN_AS_API_ERROR }
21890
22632
  );
21891
22633
  printCommandEnvelope(payload, { json: options.json });
21892
22634
  }
21893
- async function handleReactivate(key, options) {
22635
+ async function handleMonitorsReactivate(key, options) {
21894
22636
  const http = buildHttpClient();
22637
+ if (options.dryRun) {
22638
+ const payload2 = await http.request(
22639
+ `/api/v2/monitors/deployed/${encodeKey(key)}/reactivate?dry_run=true`,
22640
+ { method: "POST", body: {}, ...FORBIDDEN_AS_API_ERROR }
22641
+ );
22642
+ assertMonitorDryRunAcknowledged(payload2, {
22643
+ command: "deepline monitors reactivate",
22644
+ mutation: "reactivate"
22645
+ });
22646
+ printCommandEnvelope(payload2, {
22647
+ json: options.json,
22648
+ text: renderMonitorReactivatePlan(payload2, key)
22649
+ });
22650
+ return;
22651
+ }
21895
22652
  const payload = await http.request(
21896
22653
  `/api/v2/monitors/deployed/${encodeKey(key)}/reactivate`,
21897
22654
  { method: "POST", body: {}, ...FORBIDDEN_AS_API_ERROR }
@@ -21899,116 +22656,240 @@ async function handleReactivate(key, options) {
21899
22656
  printCommandEnvelope(payload, { json: options.json });
21900
22657
  }
21901
22658
  function registerMonitorsCommands(program) {
21902
- const monitors = program.command("monitors").description("Discover, deploy, and manage Deepline monitors.").addHelpText(
22659
+ const monitors = program.command("monitors").description("Discover, deploy, and manage Deepline monitors.").showSuggestionAfterError(true).addHelpText(
21903
22660
  "after",
21904
22661
  `
21905
22662
  Notes:
21906
- Monitors are provider-backed signal feeds that deliver events into your
21907
- workspace. Access is granted by a Deepline admin via Admin -> Rollouts; until
21908
- then these commands return a clear monitor_access_required error.
22663
+ Deepline monitors are provider-backed signal feeds: a monitor writes provider
22664
+ events into a Customer DB table; a play reacts to each new row via a
22665
+ sqlListeners trigger \u2014 see \`deepline plays bootstrap monitor-triggered\`.
22666
+ Access is granted by a Deepline admin via Admin -> Rollouts; until then these
22667
+ commands return a clear monitor_access_required error.
22668
+
22669
+ Exit codes:
22670
+ 0 success; 2 usage/local input; 3 auth or permission; 4 not found;
22671
+ 5 server failure; 7 validation failed.
22672
+ \`monitors status\` exits 1 when you lack monitor access (documented contract).
21909
22673
 
21910
22674
  Examples:
21911
- deepline monitors tools --json
21912
- deepline monitors check '{"key":"my-monitor","tool":"...","payload":{}}'
21913
- deepline monitors deploy '{"key":"my-monitor","tool":"...","payload":{}}'
21914
- deepline monitors deployed --json
21915
- deepline monitors deployed get my-monitor --json
21916
- deepline monitors reactivate my-monitor --json
22675
+ deepline monitors status --json
22676
+ deepline monitors available --json # compact list by default
22677
+ deepline monitors available --full --json # complete catalog
22678
+ deepline monitors available theirstack.saved_search_webhook --json
22679
+ deepline monitors check '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
22680
+ deepline monitors deploy --dry-run --file monitor.json
22681
+ deepline monitors deploy --file monitor.json
22682
+ deepline monitors list --status all --json
22683
+ deepline monitors get my-monitor --json
22684
+ deepline monitors update my-monitor '{"controls":{"enabled":false}}' --json
22685
+ deepline monitors delete my-monitor --dry-run
22686
+ deepline monitors reactivate my-monitor --dry-run
21917
22687
  `
21918
22688
  );
21919
- monitors.command("tools").description("List or describe the monitor tools available to your org.").addHelpText(
21920
- "after",
21921
- `
22689
+ withJsonOption(
22690
+ monitors.command("status").description("Check whether you have access to Deepline monitors.").addHelpText(
22691
+ "after",
22692
+ `
21922
22693
  Notes:
21923
- Read-only. Filter the catalog with --provider, --tool, --search, or --limit.
21924
- Pass --tool to describe a single monitor tool.
22694
+ Read-only access check. Requires authentication but NOT monitor access, so it
22695
+ is safe to run first to confirm whether you can use the monitor commands. Exits
22696
+ 0 when you have access and 1 when you do not, so agents can branch on it (this
22697
+ exit-1 denial contract predates the typed monitor exit codes and is kept).
21925
22698
 
21926
22699
  Examples:
21927
- deepline monitors tools
21928
- deepline monitors tools --provider theirstack --json
21929
- deepline monitors tools --tool theirstack_jobs --json
22700
+ deepline monitors status
22701
+ deepline monitors status --json
21930
22702
  `
21931
- ).option("--provider <provider>", "Filter by provider").option("--tool <tool>", "Describe a single monitor tool by id").option("--search <query>", "Search monitor tools by text").option("--limit <n>", "Limit the number of monitor tools returned").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleMonitorsTools);
21932
- monitors.command("check <definition>").description("Validate a monitor definition without deploying it.").addHelpText(
21933
- "after",
21934
- `
22703
+ )
22704
+ ).action(monitorsAction(handleMonitorsStatus));
22705
+ withJsonOption(
22706
+ monitors.command("available [tool-id]").alias("tools").description(
22707
+ "List the monitor types you can deploy, or describe one by tool id."
22708
+ ).addHelpText(
22709
+ "after",
22710
+ `
21935
22711
  Notes:
21936
- Read-only validation. <definition> is a JSON object with key, tool, payload,
21937
- and optional controls. Does not deploy or spend credits.
22712
+ Read-only catalog of deployable monitor types. Pass a tool id positionally to
22713
+ describe one (payload schema + output streams). Filter the list with
22714
+ --provider, --search, or --limit. List entries include how many monitors you
22715
+ already have deployed on each tool ("deployed: N") when the server reports it.
22716
+ The list is compact by default (id + name + deployed count); pass --full to
22717
+ get the complete catalog (payload schemas + output streams for every tool).
22718
+ Describing a single tool by id always returns the full contract.
21938
22719
 
21939
22720
  Examples:
21940
- deepline monitors check '{"key":"my-monitor","tool":"theirstack_jobs","payload":{}}'
22721
+ deepline monitors available
22722
+ deepline monitors available --full --json
22723
+ deepline monitors available --provider theirstack --json
22724
+ deepline monitors available theirstack.saved_search_webhook --json
22725
+ deepline monitors available --search jobs --json
21941
22726
  `
21942
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleMonitorsCheck);
21943
- monitors.command("deploy <definition>").description("Deploy a monitor from a definition.").addHelpText(
21944
- "after",
21945
- `
22727
+ ).option("--provider <provider>", "Filter by provider").option(
22728
+ "--tool <tool>",
22729
+ "Describe a single monitor tool by id (same as the positional tool-id)"
22730
+ ).option("--search <query>", "Search monitor tools by text").option("--limit <n>", "Limit the number of monitor tools returned").option(
22731
+ "--full",
22732
+ "Return the complete monitor catalog (payload schemas + output streams). The list is compact by default."
22733
+ ).option("--compact", COMPACT_OPTION_DESCRIPTION)
22734
+ ).action(monitorsAction(handleMonitorsAvailable));
22735
+ withJsonOption(
22736
+ monitors.command("list").description("List the monitors you have deployed.").addHelpText(
22737
+ "after",
22738
+ `
21946
22739
  Notes:
21947
- Mutates workspace state and may spend Deepline credits. <definition> is a JSON
21948
- object with key, tool, payload, and optional controls.
22740
+ Read-only. --status filters by monitor status: active (default), disabled, or
22741
+ all. The output echoes the status filter that was applied. --compact returns
22742
+ high-signal fields only.
21949
22743
 
21950
22744
  Examples:
21951
- deepline monitors deploy '{"key":"my-monitor","tool":"theirstack_jobs","payload":{}}'
22745
+ deepline monitors list
22746
+ deepline monitors list --status all --json
22747
+ deepline monitors list --compact --json
21952
22748
  `
21953
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleMonitorsDeploy);
21954
- monitors.command("reactivate <key>").description("Reactivate a previously disabled deployed monitor.").addHelpText(
21955
- "after",
21956
- `
22749
+ ).option(
22750
+ "--status <status>",
22751
+ "Filter by monitor status: active (default), disabled, or all"
22752
+ ).option("--limit <n>", "Limit the number of deployed monitors returned").option("--compact", COMPACT_OPTION_DESCRIPTION)
22753
+ ).action(monitorsAction(handleMonitorsList));
22754
+ withJsonOption(
22755
+ monitors.command("get <key>").description("Show a single deployed monitor by its public key.").addHelpText(
22756
+ "after",
22757
+ `
21957
22758
  Notes:
21958
- Mutates workspace state and may spend Deepline credits.
22759
+ Read-only.
21959
22760
 
21960
22761
  Examples:
21961
- deepline monitors reactivate my-monitor --json
22762
+ deepline monitors get my-monitor --json
21962
22763
  `
21963
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleReactivate);
21964
- const deployed = monitors.command("deployed").description("List and manage your deployed monitors.").addHelpText(
21965
- "after",
21966
- `
22764
+ )
22765
+ ).action(monitorsAction(handleMonitorsGet));
22766
+ withJsonOption(
22767
+ monitors.command("check [definition]").description("Validate a monitor definition without deploying it.").addHelpText(
22768
+ "after",
22769
+ `
21967
22770
  Notes:
21968
- With no subcommand, lists deployed monitors. Use get/update/delete to manage a
21969
- single monitor by its public key.
22771
+ Read-only validation. <definition> is a JSON object with key, tool, payload,
22772
+ and optional controls (Deepline lifecycle metadata, e.g.
22773
+ "controls":{"enabled":false}). Pass it positionally, via --file <path>, or
22774
+ from stdin with --file -. Does not deploy or spend credits.
21970
22775
 
21971
22776
  Examples:
21972
- deepline monitors deployed --json
21973
- deepline monitors deployed --status all --json
21974
- deepline monitors deployed get my-monitor --json
22777
+ deepline monitors check '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
22778
+ deepline monitors check --file monitor.json
22779
+ cat monitor.json | deepline monitors check --file -
21975
22780
  `
21976
- ).option("--status <status>", 'Filter by monitor status (or "all")').option("--limit <n>", "Limit the number of deployed monitors returned").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleDeployedList);
21977
- deployed.command("get <key>").description("Show a single deployed monitor by its public key.").addHelpText(
21978
- "after",
21979
- `
22781
+ ).option(
22782
+ "-f, --file <path>",
22783
+ "Read the definition JSON from a file, or - for stdin"
22784
+ )
22785
+ ).action(monitorsAction(handleMonitorsCheck));
22786
+ withJsonOption(
22787
+ monitors.command("deploy [definition]").description("Deploy a monitor from a definition.").addHelpText(
22788
+ "after",
22789
+ `
21980
22790
  Notes:
21981
- Read-only.
22791
+ Mutates workspace state and may spend Deepline credits. <definition> is a JSON
22792
+ object with key, tool, payload, and optional controls (e.g.
22793
+ "controls":{"enabled":false}). Pass it positionally, via --file <path>, or
22794
+ from stdin with --file -.
22795
+ --dry-run validates the definition and shows the plan (deploy cost in Deepline
22796
+ credits when the server reports it, plus any existing monitors that may
22797
+ already cover this scope) WITHOUT deploying. Exits 0 when valid, 7 when not.
21982
22798
 
21983
22799
  Examples:
21984
- deepline monitors deployed get my-monitor --json
22800
+ deepline monitors deploy '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
22801
+ deepline monitors deploy --dry-run --file monitor.json
22802
+ deepline monitors deploy --file monitor.json
21985
22803
  `
21986
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleDeployedGet);
21987
- deployed.command("update <key> <patch>").description("Update a deployed monitor by its public key.").addHelpText(
21988
- "after",
21989
- `
22804
+ ).option(
22805
+ "-f, --file <path>",
22806
+ "Read the definition JSON from a file, or - for stdin"
22807
+ ).option(
22808
+ "--dry-run",
22809
+ "Validate and show the deploy plan (cost, reuse candidates) without deploying"
22810
+ )
22811
+ ).action(monitorsAction(handleMonitorsDeploy));
22812
+ withJsonOption(
22813
+ monitors.command("update <key> [patch]").description("Update a deployed monitor by its public key.").addHelpText(
22814
+ "after",
22815
+ `
21990
22816
  Notes:
21991
- Mutates workspace state. <patch> is a JSON object of fields to update.
22817
+ Mutates workspace state. <patch> is a JSON object of fields to update, e.g.
22818
+ '{"controls":{"enabled":false}}' to disable a monitor. Pass it positionally,
22819
+ via --file <path>, or from stdin with --file -.
21992
22820
 
21993
22821
  Examples:
21994
- deepline monitors deployed update my-monitor '{"controls":{"enabled":false}}' --json
22822
+ deepline monitors update my-monitor '{"controls":{"enabled":false}}' --json
22823
+ deepline monitors update my-monitor --file patch.json
21995
22824
  `
21996
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleDeployedUpdate);
21997
- deployed.command("delete <key>").description("Delete a deployed monitor by its public key.").addHelpText(
21998
- "after",
21999
- `
22825
+ ).option(
22826
+ "-f, --file <path>",
22827
+ "Read the patch JSON from a file, or - for stdin"
22828
+ )
22829
+ ).action(monitorsAction(handleMonitorsUpdate));
22830
+ withJsonOption(
22831
+ monitors.command("delete <key>").description("Delete a deployed monitor by its public key.").addHelpText(
22832
+ "after",
22833
+ `
22000
22834
  Notes:
22001
- Mutates workspace state. By default deprovisions the upstream provider
22002
- resource; pass --local-only to remove only the Deepline-managed record.
22835
+ DESTRUCTIVE. By default deprovisions the upstream provider resource; pass
22836
+ --local-only to remove only the Deepline-managed record.
22837
+ --dry-run shows the delete plan without deleting anything.
22838
+ Interactive terminals get a y/N confirmation; non-interactive runs (agents,
22839
+ scripts, pipes) must pass --yes.
22003
22840
 
22004
22841
  Examples:
22005
- deepline monitors deployed delete my-monitor --json
22006
- deepline monitors deployed delete my-monitor --local-only --json
22842
+ deepline monitors delete my-monitor --dry-run
22843
+ deepline monitors delete my-monitor --yes --json
22844
+ deepline monitors delete my-monitor --local-only --yes --json
22007
22845
  `
22008
- ).option(
22009
- "--local-only",
22010
- "Remove only the Deepline-managed record, leaving the upstream resource"
22011
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleDeployedDelete);
22846
+ ).option(
22847
+ "--local-only",
22848
+ "Remove only the Deepline-managed record, leaving the upstream resource"
22849
+ ).option("--dry-run", "Show the delete plan without deleting anything").option(
22850
+ "--yes",
22851
+ "Skip the confirmation prompt (required non-interactively)"
22852
+ )
22853
+ ).action(monitorsAction(handleMonitorsDelete));
22854
+ withJsonOption(
22855
+ monitors.command("reactivate <key>").description("Reactivate a previously disabled deployed monitor.").addHelpText(
22856
+ "after",
22857
+ `
22858
+ Notes:
22859
+ Mutates workspace state and may spend Deepline credits.
22860
+ --dry-run shows the reactivation cost (in Deepline credits) without changing
22861
+ anything.
22862
+
22863
+ Examples:
22864
+ deepline monitors reactivate my-monitor --dry-run
22865
+ deepline monitors reactivate my-monitor --json
22866
+ `
22867
+ ).option("--dry-run", "Show the reactivation cost without reactivating")
22868
+ ).action(monitorsAction(handleMonitorsReactivate));
22869
+ const deployed = withJsonOption(
22870
+ monitors.command("deployed", { hidden: true }).description("Alias of `monitors list` (plus get/update/delete aliases).").option(
22871
+ "--status <status>",
22872
+ "Filter by monitor status: active (default), disabled, or all"
22873
+ ).option("--limit <n>", "Limit the number of deployed monitors returned").option("--compact", COMPACT_OPTION_DESCRIPTION)
22874
+ ).action(monitorsAction(handleMonitorsList));
22875
+ withJsonOption(
22876
+ deployed.command("get <key>").description("Alias of `monitors get <key>`.")
22877
+ ).action(monitorsAction(handleMonitorsGet));
22878
+ withJsonOption(
22879
+ deployed.command("update <key> [patch]").description("Alias of `monitors update <key>`.").option(
22880
+ "-f, --file <path>",
22881
+ "Read the patch JSON from a file, or - for stdin"
22882
+ )
22883
+ ).action(monitorsAction(handleMonitorsUpdate));
22884
+ withJsonOption(
22885
+ deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option(
22886
+ "--local-only",
22887
+ "Remove only the Deepline-managed record, leaving the upstream resource"
22888
+ ).option("--dry-run", "Show the delete plan without deleting anything").option(
22889
+ "--yes",
22890
+ "Skip the confirmation prompt (required non-interactively)"
22891
+ )
22892
+ ).action(monitorsAction(handleMonitorsDelete));
22012
22893
  }
22013
22894
 
22014
22895
  // src/cli/commands/org.ts
@@ -23033,7 +23914,7 @@ Examples:
23033
23914
  }
23034
23915
 
23035
23916
  // src/cli/commands/switch.ts
23036
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync11 } from "fs";
23917
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
23037
23918
  import { homedir as homedir9 } from "os";
23038
23919
  import { dirname as dirname9, join as join9 } from "path";
23039
23920
  function hostSlugFromBaseUrl(baseUrl) {
@@ -23068,7 +23949,7 @@ function activeFamilyPath() {
23068
23949
  function readActiveFamily() {
23069
23950
  const path = activeFamilyPath();
23070
23951
  try {
23071
- return readFileSync9(path, "utf-8").trim() || "sdk";
23952
+ return readFileSync10(path, "utf-8").trim() || "sdk";
23072
23953
  } catch {
23073
23954
  return "sdk";
23074
23955
  }
@@ -23197,7 +24078,7 @@ import {
23197
24078
  chmodSync,
23198
24079
  existsSync as existsSync10,
23199
24080
  mkdtempSync,
23200
- readFileSync as readFileSync10,
24081
+ readFileSync as readFileSync11,
23201
24082
  writeFileSync as writeFileSync13
23202
24083
  } from "fs";
23203
24084
  import { tmpdir as tmpdir3 } from "os";
@@ -24600,7 +25481,7 @@ function readJsonArgument(raw, flagName) {
24600
25481
  throw new Error(`Invalid ${flagName} value: empty @file path.`);
24601
25482
  }
24602
25483
  try {
24603
- return readFileSync10(resolveAtFilePath(filePath), "utf8").replace(
25484
+ return readFileSync11(resolveAtFilePath(filePath), "utf8").replace(
24604
25485
  /^\uFEFF/,
24605
25486
  ""
24606
25487
  );
@@ -25270,9 +26151,10 @@ var PLAY_EQUIVALENT = {
25270
26151
  delete: "deepline plays delete <name>"
25271
26152
  };
25272
26153
  var MIGRATE_HINT = "deepline workflows transform <id>";
25273
- var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
25274
- function withJsonOption(command) {
25275
- return command.option("--json", JSON_OPTION_DESCRIPTION);
26154
+ var TRIGGER_REDIRECT = "Looking to trigger a play automatically? Use a trigger binding in definePlay \u2014 `cron`/`webhook`, or `sqlListeners` to run on a monitor\u2019s output (see `deepline monitors available <id>` for a monitor\u2019s streams and the deepline-monitors skill). `workflows` only manages prebuilt plays and is deprecated in favor of `plays`.";
26155
+ var JSON_OPTION_DESCRIPTION2 = "Emit JSON output. Also automatic when stdout is piped";
26156
+ function withJsonOption2(command) {
26157
+ return command.option("--json", JSON_OPTION_DESCRIPTION2);
25276
26158
  }
25277
26159
  var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
25278
26160
  "completed",
@@ -25297,6 +26179,7 @@ function noticeToStderr(command) {
25297
26179
  `note: \`deepline workflows ${command}\` is a legacy surface \u2014 workflows are becoming plays.
25298
26180
  equivalent: \`${deprecation.use}\`
25299
26181
  migrate this workflow: \`${MIGRATE_HINT}\`
26182
+ ${TRIGGER_REDIRECT}
25300
26183
  `
25301
26184
  );
25302
26185
  }
@@ -25530,7 +26413,7 @@ async function handleTail(id, runId, options) {
25530
26413
  }
25531
26414
  function registerWorkflowCommands(program) {
25532
26415
  const workflows = program.command("workflows").description(
25533
- "Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play."
26416
+ "Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play. " + TRIGGER_REDIRECT
25534
26417
  ).addHelpText(
25535
26418
  "after",
25536
26419
  `
@@ -25542,9 +26425,13 @@ keep working, but new work belongs in plays. Migrate a workflow with:
25542
26425
 
25543
26426
  Equivalents: list\u2192plays list \xB7 call\u2192plays run \xB7 apply\u2192plays publish \xB7
25544
26427
  lint\u2192plays check \xB7 runs\u2192runs list.
26428
+
26429
+ ${TRIGGER_REDIRECT}
26430
+ Add the binding as the third arg to definePlay in your .play.ts; the
26431
+ deepline-monitors skill has a monitor-triggered example.
25545
26432
  `
25546
26433
  );
25547
- withJsonOption(
26434
+ withJsonOption2(
25548
26435
  workflows.command("transform [id]").description("Compile a workflow into a reviewable V2 play (.play.ts).").option("--all", "Transform every workflow in the workspace").option("--out <dir>", "Output directory for generated plays", "./plays").option("--publish", "Publish each clean play immediately")
25549
26436
  ).addHelpText(
25550
26437
  "after",
@@ -25561,48 +26448,48 @@ Notes:
25561
26448
  ).action(async (id, options) => {
25562
26449
  process.exitCode = await handleTransform(id, options);
25563
26450
  });
25564
- withJsonOption(
26451
+ withJsonOption2(
25565
26452
  workflows.command("list").description("List workspace workflows.").option("--limit <n>", "Max workflows to return")
25566
26453
  ).action(handleList2);
25567
- withJsonOption(
26454
+ withJsonOption2(
25568
26455
  workflows.command("get <id>").description(
25569
26456
  "Fetch a workflow, including its published-revision config."
25570
26457
  )
25571
26458
  ).action(handleGet);
25572
- withJsonOption(
26459
+ withJsonOption2(
25573
26460
  workflows.command("disable <id>").description("Turn a workflow off.")
25574
26461
  ).action(handleDisable);
25575
- withJsonOption(
26462
+ withJsonOption2(
25576
26463
  workflows.command("enable <id>").description("Turn a workflow back on.")
25577
26464
  ).action(handleEnable);
25578
- withJsonOption(
26465
+ withJsonOption2(
25579
26466
  workflows.command("delete <id>").description("Delete a workflow and its revisions/runs.")
25580
26467
  ).action(handleDelete);
25581
- withJsonOption(
26468
+ withJsonOption2(
25582
26469
  workflows.command("apply").description("Create or update a workflow from config.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
25583
26470
  ).action(handleApply);
25584
- withJsonOption(
26471
+ withJsonOption2(
25585
26472
  workflows.command("lint").description("Validate a workflow config without saving.").option("--payload <json>", "Inline config JSON").option("--file <path>", "Path to a config JSON file")
25586
26473
  ).action(handleLint);
25587
- withJsonOption(
26474
+ withJsonOption2(
25588
26475
  workflows.command("schema").description("Fetch live workflow request schemas.").option(
25589
26476
  "--subject <subject>",
25590
26477
  "Schema subject (apply|call|trigger|all|\u2026)"
25591
26478
  )
25592
26479
  ).action(handleSchema);
25593
- withJsonOption(
26480
+ withJsonOption2(
25594
26481
  workflows.command("call").description("Queue a workflow run.").option("--workflow-id <id>", "Workflow id").option("--workflow-name <name>", "Workflow name").option("--payload <json>", "Run input JSON").option("--mode <mode>", "live | dry_run | smoke_test")
25595
26482
  ).action(handleCall);
25596
- withJsonOption(
26483
+ withJsonOption2(
25597
26484
  workflows.command("runs <id>").description("List a workflow\u2019s runs.").option("--limit <n>", "Max runs to return")
25598
26485
  ).action(handleRuns);
25599
- withJsonOption(
26486
+ withJsonOption2(
25600
26487
  workflows.command("run <id> <runId>").description("Fetch a single workflow run.")
25601
26488
  ).action(handleRun);
25602
- withJsonOption(
26489
+ withJsonOption2(
25603
26490
  workflows.command("tail <id> <runId>").description("Poll a workflow run until it reaches a terminal status.").option("--interval-ms <n>", "Poll interval in ms (default 2000)")
25604
26491
  ).action(handleTail);
25605
- withJsonOption(
26492
+ withJsonOption2(
25606
26493
  workflows.command("cancel <id> <runId>").description("Cancel a running workflow run.")
25607
26494
  ).action(handleCancel);
25608
26495
  }
@@ -25613,7 +26500,7 @@ import {
25613
26500
  existsSync as existsSync12,
25614
26501
  mkdirSync as mkdirSync10,
25615
26502
  realpathSync as realpathSync3,
25616
- readFileSync as readFileSync12,
26503
+ readFileSync as readFileSync13,
25617
26504
  renameSync,
25618
26505
  rmSync as rmSync4,
25619
26506
  unlinkSync,
@@ -25624,7 +26511,7 @@ import { dirname as dirname13, isAbsolute as isAbsolute2, join as join14, relati
25624
26511
 
25625
26512
  // src/cli/skills-sync.ts
25626
26513
  import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
25627
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync14 } from "fs";
26514
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync14 } from "fs";
25628
26515
  import { dirname as dirname12, join as join13 } from "path";
25629
26516
 
25630
26517
  // ../shared_libs/cli/install-commands.json
@@ -25753,7 +26640,7 @@ function readPluginSkillsVersion() {
25753
26640
  const dir = activePluginSkillsDir();
25754
26641
  if (!dir) return "";
25755
26642
  try {
25756
- return readFileSync11(join13(dir, ".version"), "utf-8").trim();
26643
+ return readFileSync12(join13(dir, ".version"), "utf-8").trim();
25757
26644
  } catch {
25758
26645
  return "";
25759
26646
  }
@@ -25770,7 +26657,7 @@ function readSdkSkillsLocalVersion(baseUrl) {
25770
26657
  const path = existsSync11(sdkSkillsVersionPath(baseUrl)) ? sdkSkillsVersionPath(baseUrl) : legacySdkSkillsVersionPath(baseUrl);
25771
26658
  if (!existsSync11(path)) return "";
25772
26659
  try {
25773
- return readFileSync11(path, "utf-8").trim();
26660
+ return readFileSync12(path, "utf-8").trim();
25774
26661
  } catch {
25775
26662
  return "";
25776
26663
  }
@@ -26065,7 +26952,7 @@ function sidecarStateDir(input2) {
26065
26952
  }
26066
26953
  function readOptionalText(path) {
26067
26954
  try {
26068
- return readFileSync12(path, "utf8").trim();
26955
+ return readFileSync13(path, "utf8").trim();
26069
26956
  } catch {
26070
26957
  return "";
26071
26958
  }
@@ -26200,7 +27087,7 @@ function readAutoUpdateFailure(plan) {
26200
27087
  if (!path) return null;
26201
27088
  try {
26202
27089
  const parsed = JSON.parse(
26203
- readFileSync12(path, "utf8")
27090
+ readFileSync13(path, "utf8")
26204
27091
  );
26205
27092
  if ((parsed.kind === "npm-global" || parsed.kind === "python-sidecar") && typeof parsed.packageSpec === "string" && typeof parsed.failedAt === "string" && typeof parsed.exitCode === "number" && typeof parsed.manualCommand === "string") {
26206
27093
  return parsed;
@@ -26284,7 +27171,7 @@ function installedPackageVersion(versionDir) {
26284
27171
  "package.json"
26285
27172
  );
26286
27173
  try {
26287
- const parsed = JSON.parse(readFileSync12(packageJsonPath, "utf8"));
27174
+ const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
26288
27175
  return typeof parsed.version === "string" ? safeVersionSegment(parsed.version) : "";
26289
27176
  } catch {
26290
27177
  return "";