deepline 0.1.186 → 0.1.188
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/bundling-sources/apps/play-runner-workers/src/entry.ts +53 -17
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/workflow-preview.ts +26 -0
- package/dist/bundling-sources/sdk/src/play.ts +24 -3
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +88 -0
- package/dist/cli/index.js +1206 -216
- package/dist/cli/index.mjs +1152 -162
- package/dist/index.d.mts +113 -3
- package/dist/index.d.ts +113 -3
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -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.
|
|
611
|
+
version: "0.1.188",
|
|
612
612
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
613
613
|
supportPolicy: {
|
|
614
|
-
latest: "0.1.
|
|
614
|
+
latest: "0.1.188",
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -17347,11 +17614,12 @@ function helperSource() {
|
|
|
17347
17614
|
|
|
17348
17615
|
// src/cli/commands/enrich.ts
|
|
17349
17616
|
var ENRICH_EXPORT_PAGE_SIZE = 5e3;
|
|
17350
|
-
var ENRICH_AUTO_BATCH_ROWS =
|
|
17617
|
+
var ENRICH_AUTO_BATCH_ROWS = 200;
|
|
17351
17618
|
var ENRICH_HEAVY_PLAY_AUTO_BATCH_ROWS = 25;
|
|
17352
17619
|
var ENRICH_NESTED_PROVIDER_AUTO_BATCH_ROWS = 50;
|
|
17353
17620
|
var ENRICH_EXPORT_BACKING_ROWS_WAIT_MS = 6e4;
|
|
17354
17621
|
var ENRICH_EXPORT_BACKING_ROWS_POLL_MS = 1e3;
|
|
17622
|
+
var ENRICH_CUSTOMER_DB_FAILURE_LOOKUP_RETRY_DELAYS_MS = [1e3, 3e3, 7e3];
|
|
17355
17623
|
var ENRICH_SOURCE_ROW_INDEX_COLUMN = "__deeplineSourceRowIndex";
|
|
17356
17624
|
var ENRICH_ORIGINAL_SOURCE_ROW_INDEX_COLUMN = "__deeplineOriginalSourceRowIndex";
|
|
17357
17625
|
var ENRICH_CELL_META_FIELD = "__deeplineCellMeta";
|
|
@@ -17502,6 +17770,13 @@ function enrichExportBackingRowsWaitMs() {
|
|
|
17502
17770
|
const parsed = Number.parseInt(raw, 10);
|
|
17503
17771
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : ENRICH_EXPORT_BACKING_ROWS_WAIT_MS;
|
|
17504
17772
|
}
|
|
17773
|
+
function enrichCustomerDbFailureLookupRetryDelaysMs() {
|
|
17774
|
+
const raw = process.env.DEEPLINE_ENRICH_CUSTOMER_DB_LOOKUP_RETRY_DELAYS_MS?.trim();
|
|
17775
|
+
if (!raw) {
|
|
17776
|
+
return ENRICH_CUSTOMER_DB_FAILURE_LOOKUP_RETRY_DELAYS_MS;
|
|
17777
|
+
}
|
|
17778
|
+
return raw.split(",").map((part) => Number.parseInt(part.trim(), 10)).filter((value) => Number.isFinite(value) && value >= 0);
|
|
17779
|
+
}
|
|
17505
17780
|
function emitEnrichDebugValidationLines(config) {
|
|
17506
17781
|
for (const line of buildEnrichDebugValidationLines(config)) {
|
|
17507
17782
|
emitEnrichDebug(line);
|
|
@@ -18442,13 +18717,71 @@ function failureAliasFromCellMeta(cellMeta) {
|
|
|
18442
18717
|
}
|
|
18443
18718
|
return null;
|
|
18444
18719
|
}
|
|
18720
|
+
function formatEnrichDiagnosticError(error) {
|
|
18721
|
+
if (error instanceof DeeplineError) {
|
|
18722
|
+
const status = error.statusCode ? `HTTP ${error.statusCode}: ` : "";
|
|
18723
|
+
const code = error.code && error.code !== "API_ERROR" ? ` [${error.code}]` : "";
|
|
18724
|
+
return `${status}${error.message}${code}`;
|
|
18725
|
+
}
|
|
18726
|
+
return error instanceof Error ? error.message : String(error);
|
|
18727
|
+
}
|
|
18728
|
+
function isRetryableCustomerDbLookupError(error) {
|
|
18729
|
+
if (error instanceof DeeplineError) {
|
|
18730
|
+
const status = error.statusCode;
|
|
18731
|
+
if (status === 408 || status === 425 || status === 429 || status === 503 || typeof status === "number" && status >= 500) {
|
|
18732
|
+
return true;
|
|
18733
|
+
}
|
|
18734
|
+
if (error.code === "NETWORK_TIMEOUT" || error.code === "NETWORK_ABORTED" || error.code === "NETWORK_ERROR" || error.code === "42P01") {
|
|
18735
|
+
return true;
|
|
18736
|
+
}
|
|
18737
|
+
}
|
|
18738
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18739
|
+
return /Internal Server Error|fetch failed|ECONNRESET|ETIMEDOUT|timed out after \d+ms while waiting for a response|Unable to connect|relation .* does not exist|Neon API request failed \(404\)|ingestion plane .*not ready/i.test(
|
|
18740
|
+
message
|
|
18741
|
+
);
|
|
18742
|
+
}
|
|
18743
|
+
function customerDbLookupUnavailableIssue(input2) {
|
|
18744
|
+
const command = input2.customerDb.command ?? (input2.customerDb.sql ? `deepline db query --sql ${shellSingleQuote2(input2.customerDb.sql)} --json` : void 0);
|
|
18745
|
+
return {
|
|
18746
|
+
issue_id: `customer-db-failure-lookup-${input2.customerDb.runId ?? "run"}`,
|
|
18747
|
+
kind: "customer_db_lookup_unavailable",
|
|
18748
|
+
severity: "needs_attention",
|
|
18749
|
+
message: "Enrich completed, but Deepline could not inspect the Customer DB backing table for failed row details yet.",
|
|
18750
|
+
next_steps: [
|
|
18751
|
+
"Use the Customer DB query command below to inspect the durable rows after the data plane settles.",
|
|
18752
|
+
"If the command keeps failing, inspect the run output and runtime sheet export instead of rerunning provider enrichments blindly."
|
|
18753
|
+
],
|
|
18754
|
+
follow_up_commands: command ? [
|
|
18755
|
+
{
|
|
18756
|
+
label: "query customer db",
|
|
18757
|
+
command,
|
|
18758
|
+
path: input2.customerDb.datasetPath
|
|
18759
|
+
}
|
|
18760
|
+
] : [],
|
|
18761
|
+
...input2.customerDb.runId ? { run_id: input2.customerDb.runId } : {},
|
|
18762
|
+
error: formatEnrichDiagnosticError(input2.error),
|
|
18763
|
+
attempts: input2.attempts
|
|
18764
|
+
};
|
|
18765
|
+
}
|
|
18766
|
+
function emitCustomerDbLookupUnavailableWarning(issue) {
|
|
18767
|
+
process.stderr.write(`Warning: ${issue.message}
|
|
18768
|
+
`);
|
|
18769
|
+
if (issue.error) {
|
|
18770
|
+
process.stderr.write(`Warning detail: ${issue.error}
|
|
18771
|
+
`);
|
|
18772
|
+
}
|
|
18773
|
+
for (const command of issue.follow_up_commands) {
|
|
18774
|
+
process.stderr.write(`Command: ${command.label}: ${command.command}
|
|
18775
|
+
`);
|
|
18776
|
+
}
|
|
18777
|
+
}
|
|
18445
18778
|
async function collectCustomerDbFailureJobs(input2) {
|
|
18446
18779
|
const customerDb = enrichCustomerDbJson({
|
|
18447
18780
|
status: input2.status,
|
|
18448
18781
|
client: input2.client
|
|
18449
18782
|
});
|
|
18450
18783
|
if (!customerDb?.runId || !customerDb.sqlQualifiedTableName) {
|
|
18451
|
-
return [];
|
|
18784
|
+
return { jobs: [], issue: null };
|
|
18452
18785
|
}
|
|
18453
18786
|
const fallbackAliases = collectHardFailureAliasSpecs(input2.config);
|
|
18454
18787
|
const fallbackAlias = fallbackAliases[0]?.alias ?? "enrich";
|
|
@@ -18456,16 +18789,49 @@ async function collectCustomerDbFailureJobs(input2) {
|
|
|
18456
18789
|
fallbackAliases.map((spec, index) => [normalizeAlias2(spec.alias), index])
|
|
18457
18790
|
);
|
|
18458
18791
|
const sql = `select _input_index, _status, _error, _cell_meta from ${customerDb.sqlQualifiedTableName} where _run_id = ${sqlStringLiteral(customerDb.runId)} and (_error is not null or lower(coalesce(_status, '')) in ('failed', 'error')) order by _input_index limit 1000`;
|
|
18459
|
-
const
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18792
|
+
const retryDelays = enrichCustomerDbFailureLookupRetryDelaysMs();
|
|
18793
|
+
let lastError = null;
|
|
18794
|
+
let attempts = 0;
|
|
18795
|
+
let result = null;
|
|
18796
|
+
for (let attempt = 0; attempt <= retryDelays.length; attempt += 1) {
|
|
18797
|
+
try {
|
|
18798
|
+
attempts = attempt + 1;
|
|
18799
|
+
result = await input2.client.queryCustomerDb({
|
|
18800
|
+
sql,
|
|
18801
|
+
maxRows: 1e3
|
|
18802
|
+
});
|
|
18803
|
+
lastError = null;
|
|
18804
|
+
break;
|
|
18805
|
+
} catch (error) {
|
|
18806
|
+
lastError = error;
|
|
18807
|
+
if (attempt >= retryDelays.length || !isRetryableCustomerDbLookupError(error)) {
|
|
18808
|
+
break;
|
|
18809
|
+
}
|
|
18810
|
+
const delayMs = retryDelays[attempt] ?? 0;
|
|
18811
|
+
emitEnrichDebug(
|
|
18812
|
+
`customer_db failure lookup retrying after ${delayMs}ms: ${formatEnrichDiagnosticError(error)}`
|
|
18813
|
+
);
|
|
18814
|
+
if (delayMs > 0) {
|
|
18815
|
+
await sleep6(delayMs);
|
|
18816
|
+
}
|
|
18817
|
+
}
|
|
18818
|
+
}
|
|
18819
|
+
if (!result) {
|
|
18820
|
+
return {
|
|
18821
|
+
jobs: [],
|
|
18822
|
+
issue: customerDbLookupUnavailableIssue({
|
|
18823
|
+
customerDb,
|
|
18824
|
+
error: lastError,
|
|
18825
|
+
attempts
|
|
18826
|
+
})
|
|
18827
|
+
};
|
|
18828
|
+
}
|
|
18463
18829
|
const rows = Array.isArray(result.rows) ? result.rows : [];
|
|
18464
18830
|
const failedRows = rows.filter((row) => {
|
|
18465
18831
|
const status = typeof row._status === "string" ? row._status.trim().toLowerCase() : "";
|
|
18466
18832
|
return typeof row._error === "string" && row._error.trim() || status === "failed" || status === "error";
|
|
18467
18833
|
});
|
|
18468
|
-
|
|
18834
|
+
const jobs = failedRows.map((row, index) => {
|
|
18469
18835
|
const metaFailure = failureAliasFromCellMeta(row._cell_meta);
|
|
18470
18836
|
const alias = metaFailure?.alias ?? fallbackAlias;
|
|
18471
18837
|
const normalizedAlias = normalizeAlias2(alias);
|
|
@@ -18489,6 +18855,7 @@ async function collectCustomerDbFailureJobs(input2) {
|
|
|
18489
18855
|
...metaFailure?.provider ? { provider: metaFailure.provider } : {}
|
|
18490
18856
|
};
|
|
18491
18857
|
});
|
|
18858
|
+
return { jobs, issue: null };
|
|
18492
18859
|
}
|
|
18493
18860
|
async function buildEnrichFailureReport(input2) {
|
|
18494
18861
|
const fallbackRowsInfo = input2.exportResult ? null : extractCanonicalRowsInfo(input2.status);
|
|
@@ -19016,9 +19383,7 @@ function collectWaterfallAliasSpecs(config) {
|
|
|
19016
19383
|
alias,
|
|
19017
19384
|
childAliases: activeChildren.map((child) => child.alias).filter(Boolean),
|
|
19018
19385
|
childRunIfJsByAlias: new Map(
|
|
19019
|
-
activeChildren.map(
|
|
19020
|
-
(child) => [child.alias, child.run_if_js]
|
|
19021
|
-
)
|
|
19386
|
+
activeChildren.map((child) => [child.alias, child.run_if_js])
|
|
19022
19387
|
),
|
|
19023
19388
|
childToolsByAlias: new Map(
|
|
19024
19389
|
activeChildren.map(
|
|
@@ -19794,21 +20159,26 @@ async function maybeEmitEnrichFailureReport(input2) {
|
|
|
19794
20159
|
status: input2.status,
|
|
19795
20160
|
rowRange: input2.statusRowRange ?? input2.rowRange
|
|
19796
20161
|
});
|
|
19797
|
-
const
|
|
20162
|
+
const customerDbDiagnostics = input2.status === void 0 || rowJobs.length > 0 || statusJobs.length > 0 ? { jobs: [], issue: null } : await collectCustomerDbFailureJobs({
|
|
19798
20163
|
client: input2.client,
|
|
19799
20164
|
status: input2.status,
|
|
19800
20165
|
config: input2.config
|
|
19801
20166
|
});
|
|
20167
|
+
const customerDbJobs = customerDbDiagnostics.jobs;
|
|
20168
|
+
if (customerDbDiagnostics.issue) {
|
|
20169
|
+
emitCustomerDbLookupUnavailableWarning(customerDbDiagnostics.issue);
|
|
20170
|
+
}
|
|
20171
|
+
const allEnrichIssues = enrichIssues;
|
|
19802
20172
|
const jobs = rowJobs.length > 0 || statusJobs.length > 0 || customerDbJobs.length > 0 || input2.status === void 0 ? mergeEnrichFailureJobs(
|
|
19803
20173
|
mergeEnrichFailureJobs(rowJobs, statusJobs),
|
|
19804
20174
|
customerDbJobs
|
|
19805
20175
|
) : [];
|
|
19806
|
-
if (jobs.length === 0 &&
|
|
20176
|
+
if (jobs.length === 0 && allEnrichIssues.length === 0) {
|
|
19807
20177
|
return null;
|
|
19808
20178
|
}
|
|
19809
20179
|
const reportPath = await persistEnrichFailureReport({
|
|
19810
20180
|
jobs,
|
|
19811
|
-
issues:
|
|
20181
|
+
issues: allEnrichIssues,
|
|
19812
20182
|
apiUrl: input2.client.baseUrl,
|
|
19813
20183
|
outputPath: input2.outputPath,
|
|
19814
20184
|
rows: input2.rowRange
|
|
@@ -19828,13 +20198,13 @@ async function maybeEmitEnrichFailureReport(input2) {
|
|
|
19828
20198
|
`);
|
|
19829
20199
|
}
|
|
19830
20200
|
}
|
|
19831
|
-
if (
|
|
20201
|
+
if (allEnrichIssues.length > 0) {
|
|
19832
20202
|
process.stderr.write("Enrichment needs attention.\n");
|
|
19833
20203
|
process.stderr.write("Issue preview (up to 5 enrichment issues):\n");
|
|
19834
20204
|
process.stderr.write(
|
|
19835
20205
|
"issue_id column severity selected_rows rows_with_value message\n"
|
|
19836
20206
|
);
|
|
19837
|
-
for (const issue of
|
|
20207
|
+
for (const issue of allEnrichIssues.slice(0, 5)) {
|
|
19838
20208
|
process.stderr.write(
|
|
19839
20209
|
`${issue.issue_id} ${issue.column} ${issue.severity} ${issue.selected_rows} ${issue.rows_with_value} ${issue.message}
|
|
19840
20210
|
`
|
|
@@ -19848,9 +20218,9 @@ async function maybeEmitEnrichFailureReport(input2) {
|
|
|
19848
20218
|
`);
|
|
19849
20219
|
}
|
|
19850
20220
|
}
|
|
19851
|
-
if (
|
|
20221
|
+
if (allEnrichIssues.length > 5) {
|
|
19852
20222
|
process.stderr.write(
|
|
19853
|
-
`Showing 5 of ${
|
|
20223
|
+
`Showing 5 of ${allEnrichIssues.length} enrichment issues.
|
|
19854
20224
|
`
|
|
19855
20225
|
);
|
|
19856
20226
|
}
|
|
@@ -19868,12 +20238,12 @@ async function maybeEmitEnrichFailureReport(input2) {
|
|
|
19868
20238
|
process.stderr.write(
|
|
19869
20239
|
jobs.length > 0 ? "Enrichment failed. See report above.\n" : "Enrichment produced no values for at least one requested waterfall. See issue preview above.\n"
|
|
19870
20240
|
);
|
|
19871
|
-
return { path: reportPath, jobs, issues:
|
|
20241
|
+
return { path: reportPath, jobs, issues: allEnrichIssues };
|
|
19872
20242
|
}
|
|
19873
20243
|
process.stderr.write(
|
|
19874
20244
|
jobs.length > 0 ? "Enrichment failed.\n" : "Enrichment needs attention.\n"
|
|
19875
20245
|
);
|
|
19876
|
-
return { path: "", jobs, issues:
|
|
20246
|
+
return { path: "", jobs, issues: allEnrichIssues };
|
|
19877
20247
|
}
|
|
19878
20248
|
function mergePreferredColumns(preferredColumns, rows) {
|
|
19879
20249
|
const columns = [];
|
|
@@ -21797,44 +22167,455 @@ Examples:
|
|
|
21797
22167
|
}
|
|
21798
22168
|
|
|
21799
22169
|
// src/cli/commands/monitors.ts
|
|
22170
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
22171
|
+
import { createInterface } from "readline/promises";
|
|
21800
22172
|
var FORBIDDEN_AS_API_ERROR = { forbiddenAsApiError: true };
|
|
22173
|
+
var JSON_OPTION_DESCRIPTION = "Emit JSON output. Also automatic when stdout is piped";
|
|
22174
|
+
function withJsonOption(command) {
|
|
22175
|
+
return command.option("--json", JSON_OPTION_DESCRIPTION);
|
|
22176
|
+
}
|
|
22177
|
+
var COMPACT_OPTION_DESCRIPTION = "Ask the server for high-signal fields only (smaller output for agent loops)";
|
|
21801
22178
|
function buildHttpClient() {
|
|
21802
22179
|
return new HttpClient(resolveConfig());
|
|
21803
22180
|
}
|
|
21804
|
-
|
|
22181
|
+
var MonitorsUsageError = class extends Error {
|
|
22182
|
+
code = "MONITORS_USAGE_ERROR";
|
|
22183
|
+
constructor(message) {
|
|
22184
|
+
super(message);
|
|
22185
|
+
this.name = "MonitorsUsageError";
|
|
22186
|
+
}
|
|
22187
|
+
};
|
|
22188
|
+
var MonitorDryRunUnsupportedError = class extends Error {
|
|
22189
|
+
code = "MONITOR_DRY_RUN_UNSUPPORTED";
|
|
22190
|
+
constructor(message) {
|
|
22191
|
+
super(message);
|
|
22192
|
+
this.name = "MonitorDryRunUnsupportedError";
|
|
22193
|
+
}
|
|
22194
|
+
};
|
|
22195
|
+
function monitorsErrorExitCode(error) {
|
|
22196
|
+
if (error instanceof MonitorsUsageError) return 2;
|
|
22197
|
+
if (error instanceof AuthError) return 3;
|
|
22198
|
+
if (error instanceof DeeplineError) {
|
|
22199
|
+
const status = error.statusCode;
|
|
22200
|
+
if (status === 401 || status === 403) return 3;
|
|
22201
|
+
if (status === 404) return 4;
|
|
22202
|
+
if (status === 400 || status === 422) return 7;
|
|
22203
|
+
return 5;
|
|
22204
|
+
}
|
|
22205
|
+
return 5;
|
|
22206
|
+
}
|
|
22207
|
+
function monitorsFailureNextCommand(error, exitCode) {
|
|
22208
|
+
if (error instanceof DeeplineError && error.code === "monitor_access_required") {
|
|
22209
|
+
return "deepline monitors status";
|
|
22210
|
+
}
|
|
22211
|
+
if (exitCode === 3) return "deepline auth status --json";
|
|
22212
|
+
if (exitCode === 4) return "deepline monitors list --status all --json";
|
|
22213
|
+
return void 0;
|
|
22214
|
+
}
|
|
22215
|
+
function readValidationIssues(error) {
|
|
22216
|
+
if (!(error instanceof DeeplineError)) return [];
|
|
22217
|
+
const response = asRecord(asRecord(error.details)?.response);
|
|
22218
|
+
const issues = Array.isArray(response?.issues) ? response.issues : [];
|
|
22219
|
+
return issues.flatMap((raw) => {
|
|
22220
|
+
const issue = asRecord(raw);
|
|
22221
|
+
if (!issue) return [];
|
|
22222
|
+
return [
|
|
22223
|
+
{
|
|
22224
|
+
...asString(issue.path) ? { path: asString(issue.path) } : {},
|
|
22225
|
+
...asString(issue.message) ? { message: asString(issue.message) } : {}
|
|
22226
|
+
}
|
|
22227
|
+
];
|
|
22228
|
+
});
|
|
22229
|
+
}
|
|
22230
|
+
function reportMonitorsFailure(error) {
|
|
22231
|
+
const exitCode = monitorsErrorExitCode(error);
|
|
22232
|
+
const next = monitorsFailureNextCommand(error, exitCode);
|
|
22233
|
+
const wantsJson = shouldEmitJson(process.argv.includes("--json"));
|
|
22234
|
+
if (wantsJson) {
|
|
22235
|
+
const payload = errorToJsonPayload(error);
|
|
22236
|
+
printJson({
|
|
22237
|
+
ok: false,
|
|
22238
|
+
exitCode,
|
|
22239
|
+
...next ? { next } : {},
|
|
22240
|
+
error: payload.error
|
|
22241
|
+
});
|
|
22242
|
+
} else {
|
|
22243
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22244
|
+
process.stderr.write(`Error: ${message}
|
|
22245
|
+
`);
|
|
22246
|
+
for (const issue of readValidationIssues(error)) {
|
|
22247
|
+
process.stderr.write(
|
|
22248
|
+
` - ${issue.path ? `${issue.path}: ` : ""}${issue.message ?? ""}
|
|
22249
|
+
`
|
|
22250
|
+
);
|
|
22251
|
+
}
|
|
22252
|
+
if (next) process.stderr.write(`Next: ${next}
|
|
22253
|
+
`);
|
|
22254
|
+
}
|
|
22255
|
+
process.exitCode = exitCode;
|
|
22256
|
+
}
|
|
22257
|
+
function monitorsAction(handler) {
|
|
22258
|
+
return async (...args) => {
|
|
22259
|
+
try {
|
|
22260
|
+
await handler(...args);
|
|
22261
|
+
} catch (error) {
|
|
22262
|
+
reportMonitorsFailure(error);
|
|
22263
|
+
}
|
|
22264
|
+
};
|
|
22265
|
+
}
|
|
22266
|
+
function parseJsonObjectArg(raw, argLabel) {
|
|
21805
22267
|
let parsed;
|
|
21806
22268
|
try {
|
|
21807
22269
|
parsed = JSON.parse(raw);
|
|
21808
22270
|
} catch (error) {
|
|
21809
|
-
throw new
|
|
21810
|
-
`${
|
|
22271
|
+
throw new MonitorsUsageError(
|
|
22272
|
+
`${argLabel} must be a JSON object. ${error instanceof Error ? error.message : String(error)}`
|
|
21811
22273
|
);
|
|
21812
22274
|
}
|
|
21813
22275
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
21814
|
-
throw new
|
|
22276
|
+
throw new MonitorsUsageError(`${argLabel} must be a JSON object.`);
|
|
21815
22277
|
}
|
|
21816
22278
|
return parsed;
|
|
21817
22279
|
}
|
|
22280
|
+
function resolveMonitorJsonBody(input2) {
|
|
22281
|
+
const readFile3 = input2.readFile ?? ((path) => readFileSync9(path, "utf-8"));
|
|
22282
|
+
const readStdin = input2.readStdin ?? (() => readFileSync9(0, "utf-8"));
|
|
22283
|
+
if (input2.positional !== void 0 && input2.file !== void 0) {
|
|
22284
|
+
throw new MonitorsUsageError(
|
|
22285
|
+
`Pass exactly one source for ${input2.argLabel}: the positional JSON or --file, not both.`
|
|
22286
|
+
);
|
|
22287
|
+
}
|
|
22288
|
+
if (input2.positional !== void 0) {
|
|
22289
|
+
return parseJsonObjectArg(input2.positional, input2.argLabel);
|
|
22290
|
+
}
|
|
22291
|
+
if (input2.file !== void 0) {
|
|
22292
|
+
if (input2.file === "-") {
|
|
22293
|
+
return parseJsonObjectArg(readStdin(), `${input2.argLabel} (stdin)`);
|
|
22294
|
+
}
|
|
22295
|
+
let raw;
|
|
22296
|
+
try {
|
|
22297
|
+
raw = readFile3(input2.file);
|
|
22298
|
+
} catch (error) {
|
|
22299
|
+
throw new MonitorsUsageError(
|
|
22300
|
+
`Could not read --file ${input2.file}: ${error instanceof Error ? error.message : String(error)}`
|
|
22301
|
+
);
|
|
22302
|
+
}
|
|
22303
|
+
return parseJsonObjectArg(raw, `${input2.argLabel} (--file ${input2.file})`);
|
|
22304
|
+
}
|
|
22305
|
+
throw new MonitorsUsageError(
|
|
22306
|
+
`${input2.command} needs ${input2.argLabel} (a JSON object). Pass it positionally:
|
|
22307
|
+
${input2.command} '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
|
|
22308
|
+
or from a file / stdin:
|
|
22309
|
+
${input2.command} --file monitor.json
|
|
22310
|
+
cat monitor.json | ${input2.command} --file -`
|
|
22311
|
+
);
|
|
22312
|
+
}
|
|
21818
22313
|
function encodeKey(key) {
|
|
21819
22314
|
return encodeURIComponent(key);
|
|
21820
22315
|
}
|
|
21821
|
-
|
|
22316
|
+
function asRecord(value) {
|
|
22317
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
22318
|
+
}
|
|
22319
|
+
function asString(value) {
|
|
22320
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
22321
|
+
}
|
|
22322
|
+
function asFiniteNumber(value) {
|
|
22323
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
22324
|
+
}
|
|
22325
|
+
function yesNo(value) {
|
|
22326
|
+
if (value === true) return "yes";
|
|
22327
|
+
if (value === false) return "no";
|
|
22328
|
+
return "unknown";
|
|
22329
|
+
}
|
|
22330
|
+
function readDeployOutputContract(payload) {
|
|
22331
|
+
const contract = asRecord(payload.output_contract);
|
|
22332
|
+
if (!contract) return { streams: [] };
|
|
22333
|
+
const rawOutputs = Array.isArray(contract.outputs) ? contract.outputs : [];
|
|
22334
|
+
const streams = rawOutputs.flatMap((raw) => {
|
|
22335
|
+
const output2 = asRecord(raw);
|
|
22336
|
+
const stream = output2 ? asString(output2.stream) : void 0;
|
|
22337
|
+
const table = output2 ? asString(output2.table) : void 0;
|
|
22338
|
+
if (!output2 || !stream || !table) return [];
|
|
22339
|
+
const columns = Array.isArray(output2.columns) ? output2.columns.flatMap((rawColumn) => {
|
|
22340
|
+
const column = asRecord(rawColumn);
|
|
22341
|
+
const name = column ? asString(column.name) : void 0;
|
|
22342
|
+
return name ? [name] : [];
|
|
22343
|
+
}) : [];
|
|
22344
|
+
return [
|
|
22345
|
+
{
|
|
22346
|
+
stream,
|
|
22347
|
+
table,
|
|
22348
|
+
columns,
|
|
22349
|
+
isEvent: output2.is_event_stream === true,
|
|
22350
|
+
matchesPayload: output2.matches_payload === true
|
|
22351
|
+
}
|
|
22352
|
+
];
|
|
22353
|
+
});
|
|
22354
|
+
return { tool: asString(contract.tool), streams };
|
|
22355
|
+
}
|
|
22356
|
+
function renderMonitorDeployCompletion(payload) {
|
|
22357
|
+
const monitor = asRecord(payload.monitor);
|
|
22358
|
+
const key = monitor ? asString(monitor.key) : void 0;
|
|
22359
|
+
const { tool, streams } = readDeployOutputContract(payload);
|
|
22360
|
+
if (!key || streams.length === 0) return void 0;
|
|
22361
|
+
const toolId = tool ?? (monitor ? asString(monitor.tool) : void 0) ?? "<tool>";
|
|
22362
|
+
const name = monitor ? asString(monitor.name) : void 0;
|
|
22363
|
+
const status = monitor ? asString(monitor.status) : void 0;
|
|
22364
|
+
const eventStreams = streams.filter((stream) => stream.isEvent);
|
|
22365
|
+
const matched = eventStreams.filter((stream) => stream.matchesPayload);
|
|
22366
|
+
const shown = matched.length ? matched : eventStreams.length ? eventStreams : streams;
|
|
22367
|
+
const primary = shown[0];
|
|
22368
|
+
const lines = [
|
|
22369
|
+
`\u2713 Deployed monitor "${key}"${name ? ` (${name})` : ""} \u2014 tool ${toolId}${status ? `, status ${status}` : ""}`,
|
|
22370
|
+
"",
|
|
22371
|
+
"Output \u2192 Customer DB:"
|
|
22372
|
+
];
|
|
22373
|
+
for (const stream of shown) {
|
|
22374
|
+
lines.push(` ${stream.table} (stream: ${stream.stream})`);
|
|
22375
|
+
lines.push(
|
|
22376
|
+
` columns: ${stream.columns.length ? stream.columns.join(", ") : "(promoted dynamically as events arrive)"}`
|
|
22377
|
+
);
|
|
22378
|
+
}
|
|
22379
|
+
lines.push(
|
|
22380
|
+
"",
|
|
22381
|
+
"This monitor streams new rows into your Customer DB \u2014 there is no manual run.",
|
|
22382
|
+
"Next:",
|
|
22383
|
+
" \u2022 Query the table above directly, or",
|
|
22384
|
+
" \u2022 Run a play on each new row (third arg to definePlay in your .play.ts):",
|
|
22385
|
+
` sqlListeners: [{ id: '${primary.stream}', tool: '${toolId}', stream: '${primary.stream}', operations: ['INSERT'] }]`,
|
|
22386
|
+
" The changed row arrives to your handler as the sqlListener event's `after`.",
|
|
22387
|
+
"",
|
|
22388
|
+
"Reuse: many plays can bind to this one stream \u2014 you do NOT need a separate",
|
|
22389
|
+
"monitor per play. Before deploying another monitor on this tool, run",
|
|
22390
|
+
"`deepline monitors list` and reuse an existing one that covers your scope."
|
|
22391
|
+
);
|
|
22392
|
+
return `${lines.join("\n")}
|
|
22393
|
+
`;
|
|
22394
|
+
}
|
|
22395
|
+
function renderMonitorDeployPlan(payload) {
|
|
22396
|
+
const valid = payload.valid !== false;
|
|
22397
|
+
const lines = [
|
|
22398
|
+
"DRY RUN \u2014 nothing was deployed.",
|
|
22399
|
+
valid ? "Definition: valid" : "Definition: INVALID"
|
|
22400
|
+
];
|
|
22401
|
+
if (!valid) {
|
|
22402
|
+
const issues = Array.isArray(payload.issues) ? payload.issues : [];
|
|
22403
|
+
for (const raw of issues) {
|
|
22404
|
+
const issue = asRecord(raw);
|
|
22405
|
+
const path = issue ? asString(issue.path) : void 0;
|
|
22406
|
+
const message = issue ? asString(issue.message) : void 0;
|
|
22407
|
+
if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
|
|
22408
|
+
}
|
|
22409
|
+
}
|
|
22410
|
+
const estimate = asRecord(payload.deploy_cost_estimate);
|
|
22411
|
+
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
22412
|
+
if (credits !== void 0) {
|
|
22413
|
+
const note = estimate ? asString(estimate.note) : void 0;
|
|
22414
|
+
lines.push(
|
|
22415
|
+
`Deploy cost: would cost ${credits} Deepline credits${note ? ` \u2014 ${note}` : ""}.`
|
|
22416
|
+
);
|
|
22417
|
+
} else {
|
|
22418
|
+
lines.push(
|
|
22419
|
+
"Deploy cost: estimate unavailable from this server (validation only)."
|
|
22420
|
+
);
|
|
22421
|
+
}
|
|
22422
|
+
const candidates = Array.isArray(payload.reuse_candidates) ? payload.reuse_candidates : [];
|
|
22423
|
+
const candidateLines = candidates.flatMap((raw) => {
|
|
22424
|
+
const candidate = asRecord(raw);
|
|
22425
|
+
const key = candidate ? asString(candidate.key) : void 0;
|
|
22426
|
+
if (!candidate || !key) return [];
|
|
22427
|
+
const name = asString(candidate.name);
|
|
22428
|
+
const status = asString(candidate.status);
|
|
22429
|
+
const covers = asString(candidate.covers);
|
|
22430
|
+
return [
|
|
22431
|
+
` ${key}${name ? ` (${name})` : ""}${status ? ` \u2014 ${status}` : ""}${covers ? `; covers ${covers}` : ""}`
|
|
22432
|
+
];
|
|
22433
|
+
});
|
|
22434
|
+
if (candidateLines.length > 0) {
|
|
22435
|
+
lines.push(
|
|
22436
|
+
"",
|
|
22437
|
+
"You may already have a monitor covering this \u2014 check before deploying:",
|
|
22438
|
+
...candidateLines,
|
|
22439
|
+
" Inspect: deepline monitors get <key> --json"
|
|
22440
|
+
);
|
|
22441
|
+
}
|
|
22442
|
+
lines.push(
|
|
22443
|
+
"",
|
|
22444
|
+
valid ? "Deploy for real: re-run this command without --dry-run." : "Fix the definition, then re-run this command."
|
|
22445
|
+
);
|
|
22446
|
+
return `${lines.join("\n")}
|
|
22447
|
+
`;
|
|
22448
|
+
}
|
|
22449
|
+
function renderMonitorDeletePlan(payload, fallbackKey) {
|
|
22450
|
+
const plan = asRecord(payload.plan);
|
|
22451
|
+
const key = (plan ? asString(plan.monitor_key) : void 0) ?? fallbackKey;
|
|
22452
|
+
const lines = [
|
|
22453
|
+
"DRY RUN \u2014 nothing was deleted.",
|
|
22454
|
+
`Delete plan for monitor "${key}":`,
|
|
22455
|
+
` delete Deepline record: ${yesNo(plan?.would_delete_record)}`,
|
|
22456
|
+
` deprovision upstream provider resource: ${yesNo(plan?.would_deprovision_upstream)}`,
|
|
22457
|
+
"",
|
|
22458
|
+
`Delete for real: deepline monitors delete ${key} --yes`
|
|
22459
|
+
];
|
|
22460
|
+
return `${lines.join("\n")}
|
|
22461
|
+
`;
|
|
22462
|
+
}
|
|
22463
|
+
function renderMonitorReactivatePlan(payload, key) {
|
|
22464
|
+
const estimate = asRecord(payload.cost_estimate);
|
|
22465
|
+
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
22466
|
+
const lines = [
|
|
22467
|
+
"DRY RUN \u2014 nothing was reactivated.",
|
|
22468
|
+
credits !== void 0 ? `Reactivate cost: would cost ${credits} Deepline credits.` : "Reactivate cost: estimate unavailable from this server.",
|
|
22469
|
+
"",
|
|
22470
|
+
`Reactivate for real: deepline monitors reactivate ${key}`
|
|
22471
|
+
];
|
|
22472
|
+
return `${lines.join("\n")}
|
|
22473
|
+
`;
|
|
22474
|
+
}
|
|
22475
|
+
function assertMonitorDryRunAcknowledged(payload, context) {
|
|
22476
|
+
if (payload.dry_run === true) return;
|
|
22477
|
+
throw new MonitorDryRunUnsupportedError(
|
|
22478
|
+
`${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}.`
|
|
22479
|
+
);
|
|
22480
|
+
}
|
|
22481
|
+
function monitorDeleteRequiresYesError(key, options = {}) {
|
|
22482
|
+
const flags = options.localOnly ? " --local-only" : "";
|
|
22483
|
+
return new MonitorsUsageError(
|
|
22484
|
+
`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:
|
|
22485
|
+
deepline monitors delete ${key}${flags} --yes
|
|
22486
|
+
Preview the plan first with:
|
|
22487
|
+
deepline monitors delete ${key}${flags} --dry-run`
|
|
22488
|
+
);
|
|
22489
|
+
}
|
|
22490
|
+
async function handleMonitorsStatus(options) {
|
|
22491
|
+
const http = buildHttpClient();
|
|
22492
|
+
const payload = await http.request(
|
|
22493
|
+
"/api/v2/monitors/access",
|
|
22494
|
+
{ method: "GET" }
|
|
22495
|
+
);
|
|
22496
|
+
const hasAccess = payload.has_access === true;
|
|
22497
|
+
const reason = typeof payload.reason === "string" && payload.reason.trim() ? payload.reason.trim() : void 0;
|
|
22498
|
+
const text = hasAccess ? `\u2713 You have access to Deepline Monitors${reason ? `
|
|
22499
|
+
${reason}` : ""}
|
|
22500
|
+
` : `\u2717 No access to Deepline Monitors${reason ? ` \u2014 ${reason}` : ""}
|
|
22501
|
+
`;
|
|
22502
|
+
printCommandEnvelope(
|
|
22503
|
+
{ has_access: hasAccess, ...reason ? { reason } : {} },
|
|
22504
|
+
{ json: options.json, text }
|
|
22505
|
+
);
|
|
22506
|
+
if (!hasAccess) {
|
|
22507
|
+
process.exitCode = 1;
|
|
22508
|
+
}
|
|
22509
|
+
}
|
|
22510
|
+
function renderAvailableToolsText(payload) {
|
|
22511
|
+
const tools = Array.isArray(payload.tools) ? payload.tools : null;
|
|
22512
|
+
if (!tools) return void 0;
|
|
22513
|
+
const returned = asFiniteNumber(payload.returned) ?? tools.length;
|
|
22514
|
+
const total = asFiniteNumber(payload.total);
|
|
22515
|
+
const lines = [
|
|
22516
|
+
total !== void 0 ? `Monitor tools you can deploy (${returned} of ${total}):` : "Monitor tools you can deploy:"
|
|
22517
|
+
];
|
|
22518
|
+
for (const raw of tools) {
|
|
22519
|
+
const entry = asRecord(raw);
|
|
22520
|
+
const id = entry ? asString(entry.tool) : void 0;
|
|
22521
|
+
if (!entry || !id) continue;
|
|
22522
|
+
const name = asString(entry.name) ?? asString(entry.display_name);
|
|
22523
|
+
const deployedCount = asFiniteNumber(entry.deployed_count);
|
|
22524
|
+
lines.push(
|
|
22525
|
+
` ${id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (deployed: ${deployedCount})` : ""}`
|
|
22526
|
+
);
|
|
22527
|
+
}
|
|
22528
|
+
if (payload.is_truncated === true) {
|
|
22529
|
+
lines.push("List truncated; raise --limit to see more.");
|
|
22530
|
+
}
|
|
22531
|
+
lines.push(
|
|
22532
|
+
"",
|
|
22533
|
+
"Describe one (payload schema + output streams): deepline monitors available <tool-id> --json"
|
|
22534
|
+
);
|
|
22535
|
+
return `${lines.join("\n")}
|
|
22536
|
+
`;
|
|
22537
|
+
}
|
|
22538
|
+
async function handleMonitorsAvailable(toolId, options) {
|
|
22539
|
+
if (toolId !== void 0 && options.tool !== void 0) {
|
|
22540
|
+
if (toolId !== options.tool) {
|
|
22541
|
+
throw new MonitorsUsageError(
|
|
22542
|
+
`Pass the tool id once: got positional "${toolId}" and --tool "${options.tool}". Use: deepline monitors available ${toolId}`
|
|
22543
|
+
);
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
const tool = toolId ?? options.tool;
|
|
21822
22547
|
const http = buildHttpClient();
|
|
21823
22548
|
const params = new URLSearchParams();
|
|
21824
22549
|
if (options.provider) params.set("provider", options.provider);
|
|
21825
|
-
if (
|
|
22550
|
+
if (tool) params.set("tool", tool);
|
|
21826
22551
|
if (options.search) params.set("search", options.search);
|
|
21827
22552
|
if (options.limit) params.set("limit", options.limit);
|
|
22553
|
+
const compactList = !tool && !options.full;
|
|
22554
|
+
if (compactList || options.compact) params.set("compact", "true");
|
|
21828
22555
|
const query = params.toString();
|
|
21829
22556
|
const payload = await http.request(
|
|
21830
22557
|
`/api/v2/monitors/tools${query ? `?${query}` : ""}`,
|
|
21831
22558
|
{ method: "GET", ...FORBIDDEN_AS_API_ERROR }
|
|
21832
22559
|
);
|
|
21833
|
-
printCommandEnvelope(payload, {
|
|
22560
|
+
printCommandEnvelope(payload, {
|
|
22561
|
+
json: options.json,
|
|
22562
|
+
// Human list view shows id + name + deployed_count ("deployed: N") so
|
|
22563
|
+
// can-deploy vs already-deployed is visible in one view. Describing a
|
|
22564
|
+
// single tool keeps the full JSON contract (payload schema, streams).
|
|
22565
|
+
text: tool ? void 0 : renderAvailableToolsText(payload)
|
|
22566
|
+
});
|
|
22567
|
+
}
|
|
22568
|
+
function renderDeployedListText(payload, requestedStatus) {
|
|
22569
|
+
const monitors = Array.isArray(payload.monitors) ? payload.monitors : null;
|
|
22570
|
+
if (!monitors) return void 0;
|
|
22571
|
+
const lines = [
|
|
22572
|
+
monitors.length === 0 ? "No deployed monitors matched." : `Deployed monitors (${monitors.length}):`
|
|
22573
|
+
];
|
|
22574
|
+
for (const raw of monitors) {
|
|
22575
|
+
const entry = asRecord(raw);
|
|
22576
|
+
const key = entry ? asString(entry.key) ?? asString(entry.monitor_key) : void 0;
|
|
22577
|
+
if (!entry || !key) continue;
|
|
22578
|
+
const status = asString(entry.status);
|
|
22579
|
+
const tool = asString(entry.tool);
|
|
22580
|
+
const name = asString(entry.name);
|
|
22581
|
+
lines.push(
|
|
22582
|
+
` ${key}${status ? ` ${status}` : ""}${tool ? ` ${tool}` : ""}${name ? ` (${name})` : ""}`
|
|
22583
|
+
);
|
|
22584
|
+
}
|
|
22585
|
+
const applied = asString(payload.status_filter_applied) ?? requestedStatus ?? "active";
|
|
22586
|
+
lines.push(
|
|
22587
|
+
`Status filter: ${applied}${requestedStatus === void 0 && applied === "active" ? " (default)" : ""} \u2014 use --status all to include disabled monitors.`
|
|
22588
|
+
);
|
|
22589
|
+
if (monitors.length > 0) {
|
|
22590
|
+
lines.push("", "Inspect one: deepline monitors get <key> --json");
|
|
22591
|
+
}
|
|
22592
|
+
return `${lines.join("\n")}
|
|
22593
|
+
`;
|
|
22594
|
+
}
|
|
22595
|
+
async function handleMonitorsList(options) {
|
|
22596
|
+
const http = buildHttpClient();
|
|
22597
|
+
const params = new URLSearchParams();
|
|
22598
|
+
if (options.status) params.set("status", options.status);
|
|
22599
|
+
if (options.limit) params.set("limit", options.limit);
|
|
22600
|
+
if (options.compact) params.set("compact", "true");
|
|
22601
|
+
const query = params.toString();
|
|
22602
|
+
const payload = await http.request(
|
|
22603
|
+
`/api/v2/monitors/deployed${query ? `?${query}` : ""}`,
|
|
22604
|
+
{ method: "GET", ...FORBIDDEN_AS_API_ERROR }
|
|
22605
|
+
);
|
|
22606
|
+
printCommandEnvelope(payload, {
|
|
22607
|
+
json: options.json,
|
|
22608
|
+
text: renderDeployedListText(payload, options.status)
|
|
22609
|
+
});
|
|
21834
22610
|
}
|
|
21835
22611
|
async function handleMonitorsCheck(definition, options) {
|
|
21836
22612
|
const http = buildHttpClient();
|
|
21837
|
-
const body =
|
|
22613
|
+
const body = resolveMonitorJsonBody({
|
|
22614
|
+
positional: definition,
|
|
22615
|
+
file: options.file,
|
|
22616
|
+
argLabel: "<definition>",
|
|
22617
|
+
command: "deepline monitors check"
|
|
22618
|
+
});
|
|
21838
22619
|
const payload = await http.request(
|
|
21839
22620
|
"/api/v2/monitors/check",
|
|
21840
22621
|
{ method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
|
|
@@ -21843,26 +22624,37 @@ async function handleMonitorsCheck(definition, options) {
|
|
|
21843
22624
|
}
|
|
21844
22625
|
async function handleMonitorsDeploy(definition, options) {
|
|
21845
22626
|
const http = buildHttpClient();
|
|
21846
|
-
const body =
|
|
22627
|
+
const body = resolveMonitorJsonBody({
|
|
22628
|
+
positional: definition,
|
|
22629
|
+
file: options.file,
|
|
22630
|
+
argLabel: "<definition>",
|
|
22631
|
+
command: "deepline monitors deploy"
|
|
22632
|
+
});
|
|
22633
|
+
if (options.dryRun) {
|
|
22634
|
+
const payload2 = await http.request(
|
|
22635
|
+
"/api/v2/monitors/check",
|
|
22636
|
+
{ method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
|
|
22637
|
+
);
|
|
22638
|
+
const valid = payload2.valid !== false;
|
|
22639
|
+
printCommandEnvelope(
|
|
22640
|
+
{ dry_run: true, ...payload2 },
|
|
22641
|
+
{ json: options.json, text: renderMonitorDeployPlan(payload2) }
|
|
22642
|
+
);
|
|
22643
|
+
if (!valid) {
|
|
22644
|
+
process.exitCode = 7;
|
|
22645
|
+
}
|
|
22646
|
+
return;
|
|
22647
|
+
}
|
|
21847
22648
|
const payload = await http.request(
|
|
21848
22649
|
"/api/v2/monitors/deploy",
|
|
21849
22650
|
{ method: "POST", body, ...FORBIDDEN_AS_API_ERROR }
|
|
21850
22651
|
);
|
|
21851
|
-
printCommandEnvelope(payload, {
|
|
21852
|
-
|
|
21853
|
-
|
|
21854
|
-
|
|
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 });
|
|
22652
|
+
printCommandEnvelope(payload, {
|
|
22653
|
+
json: options.json,
|
|
22654
|
+
text: renderMonitorDeployCompletion(payload)
|
|
22655
|
+
});
|
|
21864
22656
|
}
|
|
21865
|
-
async function
|
|
22657
|
+
async function handleMonitorsGet(key, options) {
|
|
21866
22658
|
const http = buildHttpClient();
|
|
21867
22659
|
const payload = await http.request(
|
|
21868
22660
|
`/api/v2/monitors/deployed/${encodeKey(key)}`,
|
|
@@ -21870,10 +22662,58 @@ async function handleDeployedGet(key, options) {
|
|
|
21870
22662
|
);
|
|
21871
22663
|
printCommandEnvelope(payload, { json: options.json });
|
|
21872
22664
|
}
|
|
21873
|
-
async function
|
|
22665
|
+
async function confirmMonitorDelete(key, options) {
|
|
22666
|
+
const rl = createInterface({
|
|
22667
|
+
input: process.stdin,
|
|
22668
|
+
output: process.stderr
|
|
22669
|
+
});
|
|
22670
|
+
try {
|
|
22671
|
+
const consequence = options.localOnly ? "This removes the Deepline record only (the upstream provider resource is left in place)." : "This deprovisions the upstream provider resource.";
|
|
22672
|
+
const answer = await rl.question(
|
|
22673
|
+
`Delete monitor "${key}"? ${consequence} [y/N] `
|
|
22674
|
+
);
|
|
22675
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
22676
|
+
} finally {
|
|
22677
|
+
rl.close();
|
|
22678
|
+
}
|
|
22679
|
+
}
|
|
22680
|
+
async function handleMonitorsDelete(key, options) {
|
|
21874
22681
|
const http = buildHttpClient();
|
|
21875
22682
|
const params = new URLSearchParams();
|
|
21876
22683
|
if (options.localOnly) params.set("local_only", "true");
|
|
22684
|
+
if (options.dryRun) {
|
|
22685
|
+
params.set("dry_run", "true");
|
|
22686
|
+
const payload2 = await http.request(
|
|
22687
|
+
`/api/v2/monitors/deployed/${encodeKey(key)}?${params.toString()}`,
|
|
22688
|
+
{ method: "DELETE", ...FORBIDDEN_AS_API_ERROR }
|
|
22689
|
+
);
|
|
22690
|
+
assertMonitorDryRunAcknowledged(payload2, {
|
|
22691
|
+
command: "deepline monitors delete",
|
|
22692
|
+
mutation: "delete"
|
|
22693
|
+
});
|
|
22694
|
+
printCommandEnvelope(payload2, {
|
|
22695
|
+
json: options.json,
|
|
22696
|
+
text: renderMonitorDeletePlan(payload2, key)
|
|
22697
|
+
});
|
|
22698
|
+
return;
|
|
22699
|
+
}
|
|
22700
|
+
if (!options.yes) {
|
|
22701
|
+
const interactive = process.stdout.isTTY === true && process.stdin.isTTY === true;
|
|
22702
|
+
if (!interactive) {
|
|
22703
|
+
throw monitorDeleteRequiresYesError(key, {
|
|
22704
|
+
localOnly: options.localOnly
|
|
22705
|
+
});
|
|
22706
|
+
}
|
|
22707
|
+
const confirmed = await confirmMonitorDelete(key, {
|
|
22708
|
+
localOnly: options.localOnly
|
|
22709
|
+
});
|
|
22710
|
+
if (!confirmed) {
|
|
22711
|
+
process.stderr.write(`Aborted. Monitor "${key}" was not deleted.
|
|
22712
|
+
`);
|
|
22713
|
+
process.exitCode = 2;
|
|
22714
|
+
return;
|
|
22715
|
+
}
|
|
22716
|
+
}
|
|
21877
22717
|
const query = params.toString();
|
|
21878
22718
|
const payload = await http.request(
|
|
21879
22719
|
`/api/v2/monitors/deployed/${encodeKey(key)}${query ? `?${query}` : ""}`,
|
|
@@ -21881,17 +22721,37 @@ async function handleDeployedDelete(key, options) {
|
|
|
21881
22721
|
);
|
|
21882
22722
|
printCommandEnvelope(payload, { json: options.json });
|
|
21883
22723
|
}
|
|
21884
|
-
async function
|
|
22724
|
+
async function handleMonitorsUpdate(key, patch, options) {
|
|
21885
22725
|
const http = buildHttpClient();
|
|
21886
|
-
const body =
|
|
22726
|
+
const body = resolveMonitorJsonBody({
|
|
22727
|
+
positional: patch,
|
|
22728
|
+
file: options.file,
|
|
22729
|
+
argLabel: "<patch>",
|
|
22730
|
+
command: `deepline monitors update ${key}`
|
|
22731
|
+
});
|
|
21887
22732
|
const payload = await http.request(
|
|
21888
22733
|
`/api/v2/monitors/deployed/${encodeKey(key)}`,
|
|
21889
22734
|
{ method: "PATCH", body, ...FORBIDDEN_AS_API_ERROR }
|
|
21890
22735
|
);
|
|
21891
22736
|
printCommandEnvelope(payload, { json: options.json });
|
|
21892
22737
|
}
|
|
21893
|
-
async function
|
|
22738
|
+
async function handleMonitorsReactivate(key, options) {
|
|
21894
22739
|
const http = buildHttpClient();
|
|
22740
|
+
if (options.dryRun) {
|
|
22741
|
+
const payload2 = await http.request(
|
|
22742
|
+
`/api/v2/monitors/deployed/${encodeKey(key)}/reactivate?dry_run=true`,
|
|
22743
|
+
{ method: "POST", body: {}, ...FORBIDDEN_AS_API_ERROR }
|
|
22744
|
+
);
|
|
22745
|
+
assertMonitorDryRunAcknowledged(payload2, {
|
|
22746
|
+
command: "deepline monitors reactivate",
|
|
22747
|
+
mutation: "reactivate"
|
|
22748
|
+
});
|
|
22749
|
+
printCommandEnvelope(payload2, {
|
|
22750
|
+
json: options.json,
|
|
22751
|
+
text: renderMonitorReactivatePlan(payload2, key)
|
|
22752
|
+
});
|
|
22753
|
+
return;
|
|
22754
|
+
}
|
|
21895
22755
|
const payload = await http.request(
|
|
21896
22756
|
`/api/v2/monitors/deployed/${encodeKey(key)}/reactivate`,
|
|
21897
22757
|
{ method: "POST", body: {}, ...FORBIDDEN_AS_API_ERROR }
|
|
@@ -21899,116 +22759,240 @@ async function handleReactivate(key, options) {
|
|
|
21899
22759
|
printCommandEnvelope(payload, { json: options.json });
|
|
21900
22760
|
}
|
|
21901
22761
|
function registerMonitorsCommands(program) {
|
|
21902
|
-
const monitors = program.command("monitors").description("Discover, deploy, and manage Deepline monitors.").addHelpText(
|
|
22762
|
+
const monitors = program.command("monitors").description("Discover, deploy, and manage Deepline monitors.").showSuggestionAfterError(true).addHelpText(
|
|
21903
22763
|
"after",
|
|
21904
22764
|
`
|
|
21905
22765
|
Notes:
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
22766
|
+
Deepline monitors are provider-backed signal feeds: a monitor writes provider
|
|
22767
|
+
events into a Customer DB table; a play reacts to each new row via a
|
|
22768
|
+
sqlListeners trigger \u2014 see \`deepline plays bootstrap monitor-triggered\`.
|
|
22769
|
+
Access is granted by a Deepline admin via Admin -> Rollouts; until then these
|
|
22770
|
+
commands return a clear monitor_access_required error.
|
|
22771
|
+
|
|
22772
|
+
Exit codes:
|
|
22773
|
+
0 success; 2 usage/local input; 3 auth or permission; 4 not found;
|
|
22774
|
+
5 server failure; 7 validation failed.
|
|
22775
|
+
\`monitors status\` exits 1 when you lack monitor access (documented contract).
|
|
21909
22776
|
|
|
21910
22777
|
Examples:
|
|
21911
|
-
deepline monitors
|
|
21912
|
-
deepline monitors
|
|
21913
|
-
deepline monitors
|
|
21914
|
-
deepline monitors
|
|
21915
|
-
deepline monitors
|
|
21916
|
-
deepline monitors
|
|
22778
|
+
deepline monitors status --json
|
|
22779
|
+
deepline monitors available --json # compact list by default
|
|
22780
|
+
deepline monitors available --full --json # complete catalog
|
|
22781
|
+
deepline monitors available theirstack.saved_search_webhook --json
|
|
22782
|
+
deepline monitors check '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
|
|
22783
|
+
deepline monitors deploy --dry-run --file monitor.json
|
|
22784
|
+
deepline monitors deploy --file monitor.json
|
|
22785
|
+
deepline monitors list --status all --json
|
|
22786
|
+
deepline monitors get my-monitor --json
|
|
22787
|
+
deepline monitors update my-monitor '{"controls":{"enabled":false}}' --json
|
|
22788
|
+
deepline monitors delete my-monitor --dry-run
|
|
22789
|
+
deepline monitors reactivate my-monitor --dry-run
|
|
21917
22790
|
`
|
|
21918
22791
|
);
|
|
21919
|
-
|
|
21920
|
-
"
|
|
21921
|
-
|
|
22792
|
+
withJsonOption(
|
|
22793
|
+
monitors.command("status").description("Check whether you have access to Deepline monitors.").addHelpText(
|
|
22794
|
+
"after",
|
|
22795
|
+
`
|
|
21922
22796
|
Notes:
|
|
21923
|
-
Read-only.
|
|
21924
|
-
|
|
22797
|
+
Read-only access check. Requires authentication but NOT monitor access, so it
|
|
22798
|
+
is safe to run first to confirm whether you can use the monitor commands. Exits
|
|
22799
|
+
0 when you have access and 1 when you do not, so agents can branch on it (this
|
|
22800
|
+
exit-1 denial contract predates the typed monitor exit codes and is kept).
|
|
21925
22801
|
|
|
21926
22802
|
Examples:
|
|
21927
|
-
deepline monitors
|
|
21928
|
-
deepline monitors
|
|
21929
|
-
deepline monitors tools --tool theirstack_jobs --json
|
|
22803
|
+
deepline monitors status
|
|
22804
|
+
deepline monitors status --json
|
|
21930
22805
|
`
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
22806
|
+
)
|
|
22807
|
+
).action(monitorsAction(handleMonitorsStatus));
|
|
22808
|
+
withJsonOption(
|
|
22809
|
+
monitors.command("available [tool-id]").alias("tools").description(
|
|
22810
|
+
"List the monitor types you can deploy, or describe one by tool id."
|
|
22811
|
+
).addHelpText(
|
|
22812
|
+
"after",
|
|
22813
|
+
`
|
|
21935
22814
|
Notes:
|
|
21936
|
-
Read-only
|
|
21937
|
-
|
|
22815
|
+
Read-only catalog of deployable monitor types. Pass a tool id positionally to
|
|
22816
|
+
describe one (payload schema + output streams). Filter the list with
|
|
22817
|
+
--provider, --search, or --limit. List entries include how many monitors you
|
|
22818
|
+
already have deployed on each tool ("deployed: N") when the server reports it.
|
|
22819
|
+
The list is compact by default (id + name + deployed count); pass --full to
|
|
22820
|
+
get the complete catalog (payload schemas + output streams for every tool).
|
|
22821
|
+
Describing a single tool by id always returns the full contract.
|
|
21938
22822
|
|
|
21939
22823
|
Examples:
|
|
21940
|
-
deepline monitors
|
|
22824
|
+
deepline monitors available
|
|
22825
|
+
deepline monitors available --full --json
|
|
22826
|
+
deepline monitors available --provider theirstack --json
|
|
22827
|
+
deepline monitors available theirstack.saved_search_webhook --json
|
|
22828
|
+
deepline monitors available --search jobs --json
|
|
21941
22829
|
`
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
21945
|
-
|
|
22830
|
+
).option("--provider <provider>", "Filter by provider").option(
|
|
22831
|
+
"--tool <tool>",
|
|
22832
|
+
"Describe a single monitor tool by id (same as the positional tool-id)"
|
|
22833
|
+
).option("--search <query>", "Search monitor tools by text").option("--limit <n>", "Limit the number of monitor tools returned").option(
|
|
22834
|
+
"--full",
|
|
22835
|
+
"Return the complete monitor catalog (payload schemas + output streams). The list is compact by default."
|
|
22836
|
+
).option("--compact", COMPACT_OPTION_DESCRIPTION)
|
|
22837
|
+
).action(monitorsAction(handleMonitorsAvailable));
|
|
22838
|
+
withJsonOption(
|
|
22839
|
+
monitors.command("list").description("List the monitors you have deployed.").addHelpText(
|
|
22840
|
+
"after",
|
|
22841
|
+
`
|
|
21946
22842
|
Notes:
|
|
21947
|
-
|
|
21948
|
-
|
|
22843
|
+
Read-only. --status filters by monitor status: active (default), disabled, or
|
|
22844
|
+
all. The output echoes the status filter that was applied. --compact returns
|
|
22845
|
+
high-signal fields only.
|
|
21949
22846
|
|
|
21950
22847
|
Examples:
|
|
21951
|
-
deepline monitors
|
|
22848
|
+
deepline monitors list
|
|
22849
|
+
deepline monitors list --status all --json
|
|
22850
|
+
deepline monitors list --compact --json
|
|
21952
22851
|
`
|
|
21953
|
-
|
|
21954
|
-
|
|
21955
|
-
|
|
21956
|
-
|
|
22852
|
+
).option(
|
|
22853
|
+
"--status <status>",
|
|
22854
|
+
"Filter by monitor status: active (default), disabled, or all"
|
|
22855
|
+
).option("--limit <n>", "Limit the number of deployed monitors returned").option("--compact", COMPACT_OPTION_DESCRIPTION)
|
|
22856
|
+
).action(monitorsAction(handleMonitorsList));
|
|
22857
|
+
withJsonOption(
|
|
22858
|
+
monitors.command("get <key>").description("Show a single deployed monitor by its public key.").addHelpText(
|
|
22859
|
+
"after",
|
|
22860
|
+
`
|
|
21957
22861
|
Notes:
|
|
21958
|
-
|
|
22862
|
+
Read-only.
|
|
21959
22863
|
|
|
21960
22864
|
Examples:
|
|
21961
|
-
deepline monitors
|
|
22865
|
+
deepline monitors get my-monitor --json
|
|
21962
22866
|
`
|
|
21963
|
-
|
|
21964
|
-
|
|
21965
|
-
|
|
21966
|
-
|
|
22867
|
+
)
|
|
22868
|
+
).action(monitorsAction(handleMonitorsGet));
|
|
22869
|
+
withJsonOption(
|
|
22870
|
+
monitors.command("check [definition]").description("Validate a monitor definition without deploying it.").addHelpText(
|
|
22871
|
+
"after",
|
|
22872
|
+
`
|
|
21967
22873
|
Notes:
|
|
21968
|
-
|
|
21969
|
-
|
|
22874
|
+
Read-only validation. <definition> is a JSON object with key, tool, payload,
|
|
22875
|
+
and optional controls (Deepline lifecycle metadata, e.g.
|
|
22876
|
+
"controls":{"enabled":false}). Pass it positionally, via --file <path>, or
|
|
22877
|
+
from stdin with --file -. Does not deploy or spend credits.
|
|
21970
22878
|
|
|
21971
22879
|
Examples:
|
|
21972
|
-
deepline monitors
|
|
21973
|
-
deepline monitors
|
|
21974
|
-
deepline monitors
|
|
22880
|
+
deepline monitors check '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
|
|
22881
|
+
deepline monitors check --file monitor.json
|
|
22882
|
+
cat monitor.json | deepline monitors check --file -
|
|
21975
22883
|
`
|
|
21976
|
-
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
|
|
22884
|
+
).option(
|
|
22885
|
+
"-f, --file <path>",
|
|
22886
|
+
"Read the definition JSON from a file, or - for stdin"
|
|
22887
|
+
)
|
|
22888
|
+
).action(monitorsAction(handleMonitorsCheck));
|
|
22889
|
+
withJsonOption(
|
|
22890
|
+
monitors.command("deploy [definition]").description("Deploy a monitor from a definition.").addHelpText(
|
|
22891
|
+
"after",
|
|
22892
|
+
`
|
|
21980
22893
|
Notes:
|
|
21981
|
-
|
|
22894
|
+
Mutates workspace state and may spend Deepline credits. <definition> is a JSON
|
|
22895
|
+
object with key, tool, payload, and optional controls (e.g.
|
|
22896
|
+
"controls":{"enabled":false}). Pass it positionally, via --file <path>, or
|
|
22897
|
+
from stdin with --file -.
|
|
22898
|
+
--dry-run validates the definition and shows the plan (deploy cost in Deepline
|
|
22899
|
+
credits when the server reports it, plus any existing monitors that may
|
|
22900
|
+
already cover this scope) WITHOUT deploying. Exits 0 when valid, 7 when not.
|
|
21982
22901
|
|
|
21983
22902
|
Examples:
|
|
21984
|
-
deepline monitors
|
|
22903
|
+
deepline monitors deploy '{"key":"my-monitor","tool":"theirstack.saved_search_webhook","payload":{}}'
|
|
22904
|
+
deepline monitors deploy --dry-run --file monitor.json
|
|
22905
|
+
deepline monitors deploy --file monitor.json
|
|
21985
22906
|
`
|
|
21986
|
-
|
|
21987
|
-
|
|
21988
|
-
|
|
21989
|
-
|
|
22907
|
+
).option(
|
|
22908
|
+
"-f, --file <path>",
|
|
22909
|
+
"Read the definition JSON from a file, or - for stdin"
|
|
22910
|
+
).option(
|
|
22911
|
+
"--dry-run",
|
|
22912
|
+
"Validate and show the deploy plan (cost, reuse candidates) without deploying"
|
|
22913
|
+
)
|
|
22914
|
+
).action(monitorsAction(handleMonitorsDeploy));
|
|
22915
|
+
withJsonOption(
|
|
22916
|
+
monitors.command("update <key> [patch]").description("Update a deployed monitor by its public key.").addHelpText(
|
|
22917
|
+
"after",
|
|
22918
|
+
`
|
|
21990
22919
|
Notes:
|
|
21991
|
-
Mutates workspace state. <patch> is a JSON object of fields to update.
|
|
22920
|
+
Mutates workspace state. <patch> is a JSON object of fields to update, e.g.
|
|
22921
|
+
'{"controls":{"enabled":false}}' to disable a monitor. Pass it positionally,
|
|
22922
|
+
via --file <path>, or from stdin with --file -.
|
|
21992
22923
|
|
|
21993
22924
|
Examples:
|
|
21994
|
-
deepline monitors
|
|
22925
|
+
deepline monitors update my-monitor '{"controls":{"enabled":false}}' --json
|
|
22926
|
+
deepline monitors update my-monitor --file patch.json
|
|
21995
22927
|
`
|
|
21996
|
-
|
|
21997
|
-
|
|
21998
|
-
|
|
21999
|
-
|
|
22928
|
+
).option(
|
|
22929
|
+
"-f, --file <path>",
|
|
22930
|
+
"Read the patch JSON from a file, or - for stdin"
|
|
22931
|
+
)
|
|
22932
|
+
).action(monitorsAction(handleMonitorsUpdate));
|
|
22933
|
+
withJsonOption(
|
|
22934
|
+
monitors.command("delete <key>").description("Delete a deployed monitor by its public key.").addHelpText(
|
|
22935
|
+
"after",
|
|
22936
|
+
`
|
|
22000
22937
|
Notes:
|
|
22001
|
-
|
|
22002
|
-
|
|
22938
|
+
DESTRUCTIVE. By default deprovisions the upstream provider resource; pass
|
|
22939
|
+
--local-only to remove only the Deepline-managed record.
|
|
22940
|
+
--dry-run shows the delete plan without deleting anything.
|
|
22941
|
+
Interactive terminals get a y/N confirmation; non-interactive runs (agents,
|
|
22942
|
+
scripts, pipes) must pass --yes.
|
|
22003
22943
|
|
|
22004
22944
|
Examples:
|
|
22005
|
-
deepline monitors
|
|
22006
|
-
deepline monitors
|
|
22945
|
+
deepline monitors delete my-monitor --dry-run
|
|
22946
|
+
deepline monitors delete my-monitor --yes --json
|
|
22947
|
+
deepline monitors delete my-monitor --local-only --yes --json
|
|
22007
22948
|
`
|
|
22008
|
-
|
|
22009
|
-
|
|
22010
|
-
|
|
22011
|
-
|
|
22949
|
+
).option(
|
|
22950
|
+
"--local-only",
|
|
22951
|
+
"Remove only the Deepline-managed record, leaving the upstream resource"
|
|
22952
|
+
).option("--dry-run", "Show the delete plan without deleting anything").option(
|
|
22953
|
+
"--yes",
|
|
22954
|
+
"Skip the confirmation prompt (required non-interactively)"
|
|
22955
|
+
)
|
|
22956
|
+
).action(monitorsAction(handleMonitorsDelete));
|
|
22957
|
+
withJsonOption(
|
|
22958
|
+
monitors.command("reactivate <key>").description("Reactivate a previously disabled deployed monitor.").addHelpText(
|
|
22959
|
+
"after",
|
|
22960
|
+
`
|
|
22961
|
+
Notes:
|
|
22962
|
+
Mutates workspace state and may spend Deepline credits.
|
|
22963
|
+
--dry-run shows the reactivation cost (in Deepline credits) without changing
|
|
22964
|
+
anything.
|
|
22965
|
+
|
|
22966
|
+
Examples:
|
|
22967
|
+
deepline monitors reactivate my-monitor --dry-run
|
|
22968
|
+
deepline monitors reactivate my-monitor --json
|
|
22969
|
+
`
|
|
22970
|
+
).option("--dry-run", "Show the reactivation cost without reactivating")
|
|
22971
|
+
).action(monitorsAction(handleMonitorsReactivate));
|
|
22972
|
+
const deployed = withJsonOption(
|
|
22973
|
+
monitors.command("deployed", { hidden: true }).description("Alias of `monitors list` (plus get/update/delete aliases).").option(
|
|
22974
|
+
"--status <status>",
|
|
22975
|
+
"Filter by monitor status: active (default), disabled, or all"
|
|
22976
|
+
).option("--limit <n>", "Limit the number of deployed monitors returned").option("--compact", COMPACT_OPTION_DESCRIPTION)
|
|
22977
|
+
).action(monitorsAction(handleMonitorsList));
|
|
22978
|
+
withJsonOption(
|
|
22979
|
+
deployed.command("get <key>").description("Alias of `monitors get <key>`.")
|
|
22980
|
+
).action(monitorsAction(handleMonitorsGet));
|
|
22981
|
+
withJsonOption(
|
|
22982
|
+
deployed.command("update <key> [patch]").description("Alias of `monitors update <key>`.").option(
|
|
22983
|
+
"-f, --file <path>",
|
|
22984
|
+
"Read the patch JSON from a file, or - for stdin"
|
|
22985
|
+
)
|
|
22986
|
+
).action(monitorsAction(handleMonitorsUpdate));
|
|
22987
|
+
withJsonOption(
|
|
22988
|
+
deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option(
|
|
22989
|
+
"--local-only",
|
|
22990
|
+
"Remove only the Deepline-managed record, leaving the upstream resource"
|
|
22991
|
+
).option("--dry-run", "Show the delete plan without deleting anything").option(
|
|
22992
|
+
"--yes",
|
|
22993
|
+
"Skip the confirmation prompt (required non-interactively)"
|
|
22994
|
+
)
|
|
22995
|
+
).action(monitorsAction(handleMonitorsDelete));
|
|
22012
22996
|
}
|
|
22013
22997
|
|
|
22014
22998
|
// src/cli/commands/org.ts
|
|
@@ -23033,7 +24017,7 @@ Examples:
|
|
|
23033
24017
|
}
|
|
23034
24018
|
|
|
23035
24019
|
// src/cli/commands/switch.ts
|
|
23036
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as
|
|
24020
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
23037
24021
|
import { homedir as homedir9 } from "os";
|
|
23038
24022
|
import { dirname as dirname9, join as join9 } from "path";
|
|
23039
24023
|
function hostSlugFromBaseUrl(baseUrl) {
|
|
@@ -23068,7 +24052,7 @@ function activeFamilyPath() {
|
|
|
23068
24052
|
function readActiveFamily() {
|
|
23069
24053
|
const path = activeFamilyPath();
|
|
23070
24054
|
try {
|
|
23071
|
-
return
|
|
24055
|
+
return readFileSync10(path, "utf-8").trim() || "sdk";
|
|
23072
24056
|
} catch {
|
|
23073
24057
|
return "sdk";
|
|
23074
24058
|
}
|
|
@@ -23197,7 +24181,7 @@ import {
|
|
|
23197
24181
|
chmodSync,
|
|
23198
24182
|
existsSync as existsSync10,
|
|
23199
24183
|
mkdtempSync,
|
|
23200
|
-
readFileSync as
|
|
24184
|
+
readFileSync as readFileSync11,
|
|
23201
24185
|
writeFileSync as writeFileSync13
|
|
23202
24186
|
} from "fs";
|
|
23203
24187
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -24600,7 +25584,7 @@ function readJsonArgument(raw, flagName) {
|
|
|
24600
25584
|
throw new Error(`Invalid ${flagName} value: empty @file path.`);
|
|
24601
25585
|
}
|
|
24602
25586
|
try {
|
|
24603
|
-
return
|
|
25587
|
+
return readFileSync11(resolveAtFilePath(filePath), "utf8").replace(
|
|
24604
25588
|
/^\uFEFF/,
|
|
24605
25589
|
""
|
|
24606
25590
|
);
|
|
@@ -25270,9 +26254,10 @@ var PLAY_EQUIVALENT = {
|
|
|
25270
26254
|
delete: "deepline plays delete <name>"
|
|
25271
26255
|
};
|
|
25272
26256
|
var MIGRATE_HINT = "deepline workflows transform <id>";
|
|
25273
|
-
var
|
|
25274
|
-
|
|
25275
|
-
|
|
26257
|
+
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`.";
|
|
26258
|
+
var JSON_OPTION_DESCRIPTION2 = "Emit JSON output. Also automatic when stdout is piped";
|
|
26259
|
+
function withJsonOption2(command) {
|
|
26260
|
+
return command.option("--json", JSON_OPTION_DESCRIPTION2);
|
|
25276
26261
|
}
|
|
25277
26262
|
var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
25278
26263
|
"completed",
|
|
@@ -25297,6 +26282,7 @@ function noticeToStderr(command) {
|
|
|
25297
26282
|
`note: \`deepline workflows ${command}\` is a legacy surface \u2014 workflows are becoming plays.
|
|
25298
26283
|
equivalent: \`${deprecation.use}\`
|
|
25299
26284
|
migrate this workflow: \`${MIGRATE_HINT}\`
|
|
26285
|
+
${TRIGGER_REDIRECT}
|
|
25300
26286
|
`
|
|
25301
26287
|
);
|
|
25302
26288
|
}
|
|
@@ -25530,7 +26516,7 @@ async function handleTail(id, runId, options) {
|
|
|
25530
26516
|
}
|
|
25531
26517
|
function registerWorkflowCommands(program) {
|
|
25532
26518
|
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."
|
|
26519
|
+
"Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play. " + TRIGGER_REDIRECT
|
|
25534
26520
|
).addHelpText(
|
|
25535
26521
|
"after",
|
|
25536
26522
|
`
|
|
@@ -25542,9 +26528,13 @@ keep working, but new work belongs in plays. Migrate a workflow with:
|
|
|
25542
26528
|
|
|
25543
26529
|
Equivalents: list\u2192plays list \xB7 call\u2192plays run \xB7 apply\u2192plays publish \xB7
|
|
25544
26530
|
lint\u2192plays check \xB7 runs\u2192runs list.
|
|
26531
|
+
|
|
26532
|
+
${TRIGGER_REDIRECT}
|
|
26533
|
+
Add the binding as the third arg to definePlay in your .play.ts; the
|
|
26534
|
+
deepline-monitors skill has a monitor-triggered example.
|
|
25545
26535
|
`
|
|
25546
26536
|
);
|
|
25547
|
-
|
|
26537
|
+
withJsonOption2(
|
|
25548
26538
|
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
26539
|
).addHelpText(
|
|
25550
26540
|
"after",
|
|
@@ -25561,48 +26551,48 @@ Notes:
|
|
|
25561
26551
|
).action(async (id, options) => {
|
|
25562
26552
|
process.exitCode = await handleTransform(id, options);
|
|
25563
26553
|
});
|
|
25564
|
-
|
|
26554
|
+
withJsonOption2(
|
|
25565
26555
|
workflows.command("list").description("List workspace workflows.").option("--limit <n>", "Max workflows to return")
|
|
25566
26556
|
).action(handleList2);
|
|
25567
|
-
|
|
26557
|
+
withJsonOption2(
|
|
25568
26558
|
workflows.command("get <id>").description(
|
|
25569
26559
|
"Fetch a workflow, including its published-revision config."
|
|
25570
26560
|
)
|
|
25571
26561
|
).action(handleGet);
|
|
25572
|
-
|
|
26562
|
+
withJsonOption2(
|
|
25573
26563
|
workflows.command("disable <id>").description("Turn a workflow off.")
|
|
25574
26564
|
).action(handleDisable);
|
|
25575
|
-
|
|
26565
|
+
withJsonOption2(
|
|
25576
26566
|
workflows.command("enable <id>").description("Turn a workflow back on.")
|
|
25577
26567
|
).action(handleEnable);
|
|
25578
|
-
|
|
26568
|
+
withJsonOption2(
|
|
25579
26569
|
workflows.command("delete <id>").description("Delete a workflow and its revisions/runs.")
|
|
25580
26570
|
).action(handleDelete);
|
|
25581
|
-
|
|
26571
|
+
withJsonOption2(
|
|
25582
26572
|
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
26573
|
).action(handleApply);
|
|
25584
|
-
|
|
26574
|
+
withJsonOption2(
|
|
25585
26575
|
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
26576
|
).action(handleLint);
|
|
25587
|
-
|
|
26577
|
+
withJsonOption2(
|
|
25588
26578
|
workflows.command("schema").description("Fetch live workflow request schemas.").option(
|
|
25589
26579
|
"--subject <subject>",
|
|
25590
26580
|
"Schema subject (apply|call|trigger|all|\u2026)"
|
|
25591
26581
|
)
|
|
25592
26582
|
).action(handleSchema);
|
|
25593
|
-
|
|
26583
|
+
withJsonOption2(
|
|
25594
26584
|
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
26585
|
).action(handleCall);
|
|
25596
|
-
|
|
26586
|
+
withJsonOption2(
|
|
25597
26587
|
workflows.command("runs <id>").description("List a workflow\u2019s runs.").option("--limit <n>", "Max runs to return")
|
|
25598
26588
|
).action(handleRuns);
|
|
25599
|
-
|
|
26589
|
+
withJsonOption2(
|
|
25600
26590
|
workflows.command("run <id> <runId>").description("Fetch a single workflow run.")
|
|
25601
26591
|
).action(handleRun);
|
|
25602
|
-
|
|
26592
|
+
withJsonOption2(
|
|
25603
26593
|
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
26594
|
).action(handleTail);
|
|
25605
|
-
|
|
26595
|
+
withJsonOption2(
|
|
25606
26596
|
workflows.command("cancel <id> <runId>").description("Cancel a running workflow run.")
|
|
25607
26597
|
).action(handleCancel);
|
|
25608
26598
|
}
|
|
@@ -25613,7 +26603,7 @@ import {
|
|
|
25613
26603
|
existsSync as existsSync12,
|
|
25614
26604
|
mkdirSync as mkdirSync10,
|
|
25615
26605
|
realpathSync as realpathSync3,
|
|
25616
|
-
readFileSync as
|
|
26606
|
+
readFileSync as readFileSync13,
|
|
25617
26607
|
renameSync,
|
|
25618
26608
|
rmSync as rmSync4,
|
|
25619
26609
|
unlinkSync,
|
|
@@ -25624,7 +26614,7 @@ import { dirname as dirname13, isAbsolute as isAbsolute2, join as join14, relati
|
|
|
25624
26614
|
|
|
25625
26615
|
// src/cli/skills-sync.ts
|
|
25626
26616
|
import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
|
|
25627
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as
|
|
26617
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync14 } from "fs";
|
|
25628
26618
|
import { dirname as dirname12, join as join13 } from "path";
|
|
25629
26619
|
|
|
25630
26620
|
// ../shared_libs/cli/install-commands.json
|
|
@@ -25753,7 +26743,7 @@ function readPluginSkillsVersion() {
|
|
|
25753
26743
|
const dir = activePluginSkillsDir();
|
|
25754
26744
|
if (!dir) return "";
|
|
25755
26745
|
try {
|
|
25756
|
-
return
|
|
26746
|
+
return readFileSync12(join13(dir, ".version"), "utf-8").trim();
|
|
25757
26747
|
} catch {
|
|
25758
26748
|
return "";
|
|
25759
26749
|
}
|
|
@@ -25770,7 +26760,7 @@ function readSdkSkillsLocalVersion(baseUrl) {
|
|
|
25770
26760
|
const path = existsSync11(sdkSkillsVersionPath(baseUrl)) ? sdkSkillsVersionPath(baseUrl) : legacySdkSkillsVersionPath(baseUrl);
|
|
25771
26761
|
if (!existsSync11(path)) return "";
|
|
25772
26762
|
try {
|
|
25773
|
-
return
|
|
26763
|
+
return readFileSync12(path, "utf-8").trim();
|
|
25774
26764
|
} catch {
|
|
25775
26765
|
return "";
|
|
25776
26766
|
}
|
|
@@ -26065,7 +27055,7 @@ function sidecarStateDir(input2) {
|
|
|
26065
27055
|
}
|
|
26066
27056
|
function readOptionalText(path) {
|
|
26067
27057
|
try {
|
|
26068
|
-
return
|
|
27058
|
+
return readFileSync13(path, "utf8").trim();
|
|
26069
27059
|
} catch {
|
|
26070
27060
|
return "";
|
|
26071
27061
|
}
|
|
@@ -26200,7 +27190,7 @@ function readAutoUpdateFailure(plan) {
|
|
|
26200
27190
|
if (!path) return null;
|
|
26201
27191
|
try {
|
|
26202
27192
|
const parsed = JSON.parse(
|
|
26203
|
-
|
|
27193
|
+
readFileSync13(path, "utf8")
|
|
26204
27194
|
);
|
|
26205
27195
|
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
27196
|
return parsed;
|
|
@@ -26284,7 +27274,7 @@ function installedPackageVersion(versionDir) {
|
|
|
26284
27274
|
"package.json"
|
|
26285
27275
|
);
|
|
26286
27276
|
try {
|
|
26287
|
-
const parsed = JSON.parse(
|
|
27277
|
+
const parsed = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
|
|
26288
27278
|
return typeof parsed.version === "string" ? safeVersionSegment(parsed.version) : "";
|
|
26289
27279
|
} catch {
|
|
26290
27280
|
return "";
|