@signaliz/cli 1.0.16 → 1.0.18

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.
Files changed (3) hide show
  1. package/README.md +47 -4
  2. package/dist/bin.js +1176 -72
  3. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -9,18 +9,23 @@ var import_node_readline = require("readline");
9
9
  var import_node_child_process = require("child_process");
10
10
  var CONFIG_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".signaliz");
11
11
  var CONFIG_FILE = (0, import_node_path.join)(CONFIG_DIR, "config.json");
12
- function loadConfig() {
12
+ var cachedConfig;
13
+ function loadConfig(options = {}) {
14
+ if (cachedConfig && !options.refresh) return cachedConfig;
13
15
  try {
14
16
  if ((0, import_node_fs.existsSync)(CONFIG_FILE)) {
15
- return JSON.parse((0, import_node_fs.readFileSync)(CONFIG_FILE, "utf8"));
17
+ cachedConfig = JSON.parse((0, import_node_fs.readFileSync)(CONFIG_FILE, "utf8"));
18
+ return cachedConfig;
16
19
  }
17
20
  } catch {
18
21
  }
19
- return {};
22
+ cachedConfig = {};
23
+ return cachedConfig;
20
24
  }
21
25
  function saveConfig(config) {
22
26
  (0, import_node_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
23
27
  (0, import_node_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
28
+ cachedConfig = config;
24
29
  }
25
30
  function arg(name) {
26
31
  const found = process.argv.find((a) => a.startsWith(`--${name}=`));
@@ -98,6 +103,7 @@ function normalizeCampaignBuildInput(raw) {
98
103
  const signals = nested("signals", "signals");
99
104
  const copy = nested("copy", "copy");
100
105
  const delivery = nested("delivery", "delivery");
106
+ const learningHoldout = nested("learning_holdout", "learningHoldout");
101
107
  return {
102
108
  name: get("name", "name"),
103
109
  prompt: get("prompt", "prompt"),
@@ -110,6 +116,14 @@ function normalizeCampaignBuildInput(raw) {
110
116
  dedupKeys: get("dedup_keys", "dedupKeys"),
111
117
  brainPreflight: get("brain_preflight", "brainPreflight"),
112
118
  brainDefaults: get("brain_defaults", "brainDefaults"),
119
+ learningHoldout: learningHoldout ? {
120
+ ...learningHoldout,
121
+ controlPercentage: learningHoldout.control_percentage ?? learningHoldout.controlPercentage,
122
+ minControlSize: learningHoldout.min_control_size ?? learningHoldout.minControlSize,
123
+ experimentKey: learningHoldout.experiment_key ?? learningHoldout.experimentKey,
124
+ sourceCampaignId: learningHoldout.source_campaign_id ?? learningHoldout.sourceCampaignId,
125
+ primaryMetric: learningHoldout.primary_metric ?? learningHoldout.primaryMetric
126
+ } : void 0,
113
127
  deliveryRisk: get("delivery_risk", "deliveryRisk"),
114
128
  icp: nested("icp", "icp"),
115
129
  policy: policy ? {
@@ -145,7 +159,6 @@ var promptValueFlags = /* @__PURE__ */ new Set([
145
159
  "action-name",
146
160
  "actor-id",
147
161
  "actor-type",
148
- "analysis-models",
149
162
  "attachment-field",
150
163
  "attachment-fields",
151
164
  "attachment-file",
@@ -193,6 +206,8 @@ var promptValueFlags = /* @__PURE__ */ new Set([
193
206
  "domains",
194
207
  "enhancers-file",
195
208
  "enhancers-json",
209
+ "field-map-file",
210
+ "field-map-json",
196
211
  "file-url",
197
212
  "integration-id",
198
213
  "image-url",
@@ -203,13 +218,14 @@ var promptValueFlags = /* @__PURE__ */ new Set([
203
218
  "icp",
204
219
  "integration-display-name",
205
220
  "integration-route",
206
- "judge-model",
221
+ "interval-ms",
207
222
  "layer",
208
223
  "layers",
209
224
  "limit",
210
225
  "lead-count",
211
226
  "log-limit",
212
227
  "max-concurrency",
228
+ "max-polls",
213
229
  "max-retries",
214
230
  "max-tokens",
215
231
  "max-credits",
@@ -218,9 +234,18 @@ var promptValueFlags = /* @__PURE__ */ new Set([
218
234
  "memory-type",
219
235
  "model",
220
236
  "name",
237
+ "holdout-min-sample-size",
238
+ "holdout-control-percentage",
239
+ "holdout-experiment-key",
240
+ "holdout-min-control-size",
241
+ "holdout-primary-metric",
242
+ "holdout-source-campaign-id",
243
+ "http-method",
221
244
  "min-sample-size",
222
245
  "min-workspace-count",
223
246
  "nango-connection-id",
247
+ "nango-action",
248
+ "nango-proxy-path",
224
249
  "network-days",
225
250
  "output-format",
226
251
  "output-file",
@@ -245,11 +270,14 @@ var promptValueFlags = /* @__PURE__ */ new Set([
245
270
  "provider-link-id",
246
271
  "provider-name",
247
272
  "provider-workspace-id",
273
+ "proxy-path",
248
274
  "qualification-file",
249
275
  "qualification-json",
250
276
  "request-file",
251
277
  "records-file",
252
278
  "records-json",
279
+ "required-field",
280
+ "required-fields",
253
281
  "revenue-motion",
254
282
  "readiness-json",
255
283
  "rationale",
@@ -275,12 +303,20 @@ var promptValueFlags = /* @__PURE__ */ new Set([
275
303
  "target-icp-file",
276
304
  "target-icp-json",
277
305
  "temperature",
306
+ "timeout-ms",
278
307
  "tool-name",
279
308
  "url",
280
309
  "user-display-name",
281
310
  "user-email",
282
311
  "video-url",
312
+ "wait-interval-ms",
313
+ "wait-max-polls",
314
+ "wait-timeout-ms",
283
315
  "write-mode",
316
+ "timezone",
317
+ "wake-event",
318
+ "wake-events",
319
+ "wake-on-events",
284
320
  "workspace-connection-id",
285
321
  "workspace-integration-id",
286
322
  "workspace-label",
@@ -291,6 +327,7 @@ var promptValueFlags = /* @__PURE__ */ new Set([
291
327
  "brain-defaults-file",
292
328
  "memory-dimension-filters-file",
293
329
  "memory-dimension-filters-json",
330
+ "method",
294
331
  "metadata-file",
295
332
  "metadata-json",
296
333
  "plan-file",
@@ -361,6 +398,9 @@ function commandBooleanFlag(flags, name, defaultValue = false) {
361
398
  if (Array.isArray(value)) return value.length > 0 ? parseCommandBoolean(value.at(-1), defaultValue) : defaultValue;
362
399
  return parseCommandBoolean(value, defaultValue);
363
400
  }
401
+ function commandOptionalBooleanFlag(flags, name) {
402
+ return commandFlagValue(flags, name) === void 0 ? void 0 : commandBooleanFlag(flags, name);
403
+ }
364
404
  function parseCommandBoolean(value, defaultValue) {
365
405
  if (value === void 0) return defaultValue;
366
406
  if (typeof value === "boolean") return value;
@@ -439,6 +479,32 @@ function layerCommand(layer) {
439
479
  const normalized = String(layer || "sender").trim() || "sender";
440
480
  return `signaliz connect --layer ${normalized}`;
441
481
  }
482
+ function campaignSourcePlanRecord(result) {
483
+ const root = record(result);
484
+ const plan = record(root.sourcePlan ?? root.source_plan);
485
+ if (Object.keys(plan).length > 0) return plan;
486
+ return record(record(root.plan).source_plan);
487
+ }
488
+ function campaignSourceRouteRecords(result) {
489
+ const root = record(result);
490
+ const plan = campaignSourcePlanRecord(result);
491
+ return firstArray(root.sourceRoutes, root.source_routes, plan.routes).filter((route) => route && typeof route === "object" && !Array.isArray(route)).sort((a, b) => Number(a.priority ?? 0) - Number(b.priority ?? 0));
492
+ }
493
+ function campaignSourceSummary(result) {
494
+ const plan = campaignSourcePlanRecord(result);
495
+ if (Object.keys(plan).length === 0) return void 0;
496
+ const decision = record(plan.route_decision);
497
+ const intent = capitalizeLabel(plan.intent);
498
+ const reason = String(decision.selected_reason || "").trim();
499
+ return [intent, reason].filter(Boolean).join(" - ");
500
+ }
501
+ function campaignSourceRouteLine(route) {
502
+ const role = capitalizeLabel(route.role);
503
+ const tool = labelize(route.tool || "source");
504
+ const providers = firstArray(route.providers).map(labelize).filter(Boolean).join(" + ");
505
+ const fallback = typeof route.fallback_when === "string" && route.fallback_when.trim() ? ` (${route.fallback_when.trim()})` : "";
506
+ return `${role ? `${role}: ` : ""}${tool}${providers ? ` via ${providers}` : ""}${fallback}`;
507
+ }
442
508
  function blockedRouteLayers(result) {
443
509
  const root = record(result);
444
510
  const providerActivation = record(root.provider_activation);
@@ -609,6 +675,50 @@ function printExecutionRefs(refs) {
609
675
  if (ref.replay_command) console.log(` replay: ${ref.replay_command}`);
610
676
  }
611
677
  }
678
+ function combineExecutionRefs(...groups) {
679
+ const refs = /* @__PURE__ */ new Map();
680
+ for (const group of groups) {
681
+ if (!Array.isArray(group)) continue;
682
+ for (const ref of group) {
683
+ if (!ref?.kind || !ref?.id) continue;
684
+ refs.set(`${ref.kind}:${ref.id}`, ref);
685
+ }
686
+ }
687
+ return Array.from(refs.values());
688
+ }
689
+ function opsWaitOptions() {
690
+ return {
691
+ interval_ms: numberFlag("interval-ms") ?? numberFlag("wait-interval-ms"),
692
+ max_polls: numberFlag("max-polls") ?? numberFlag("wait-max-polls"),
693
+ timeout_ms: numberFlag("timeout-ms") ?? numberFlag("wait-timeout-ms"),
694
+ limit: numberFlag("limit"),
695
+ cursor: flagArg("cursor"),
696
+ include_failed_runs: hasFlag("include-failed-runs"),
697
+ include_results: !hasFlag("no-results")
698
+ };
699
+ }
700
+ function printOpsWaitForResults(result) {
701
+ const status = result.status || {};
702
+ console.log(`Op: ${result.op_id || result.opId || status.op_id || status.opId || "unknown"}`);
703
+ console.log(`Status: ${status.status || "unknown"}`);
704
+ console.log(`Phase: ${status.phase || "unknown"}`);
705
+ console.log(`Polls: ${result.polls ?? 0}${result.timed_out || result.timedOut ? " (timed out)" : ""}`);
706
+ if (result.results) {
707
+ const summary = result.results.summary || {};
708
+ const count = summary.results_count ?? summary.resultsCount ?? result.results.results_count ?? result.results.resultsCount ?? 0;
709
+ const total = summary.results_total ?? summary.resultsTotal ?? result.results.results_total ?? result.results.resultsTotal;
710
+ const delivery = summary.delivery_status ?? summary.deliveryStatus ?? result.results.delivery_status ?? result.results.deliveryStatus;
711
+ console.log(`Results: ${count}`);
712
+ if (total !== void 0 && total !== null) console.log(`Total: ${total}`);
713
+ if (delivery) console.log(`Deliver: ${delivery}`);
714
+ if (Array.isArray(result.results.results) && result.results.results.length) {
715
+ console.log("\nRows");
716
+ for (const row of result.results.results.slice(0, 10)) console.log(JSON.stringify(row));
717
+ }
718
+ }
719
+ if (result.next_action || result.nextAction) console.log(`Next: ${result.next_action || result.nextAction}`);
720
+ printExecutionRefs(result.execution_refs || result.executionRefs);
721
+ }
612
722
  function printQueueProducerEnvelope(envelope) {
613
723
  if (!envelope || typeof envelope !== "object") return;
614
724
  const queueName = envelope.queue_name ?? envelope.queueName;
@@ -635,6 +745,52 @@ function printPrimitiveGraph(primitives) {
635
745
  console.log(`- ${primitive.id || "unknown"}@${primitive.version || "unknown"} (${primitive.permission_level || "unknown"}, ${retry}, ${concurrency}, ${rate})`);
636
746
  }
637
747
  }
748
+ function printOpsExecutionContract(plan) {
749
+ const contract = record(plan?.execution_contract || plan?.executionContract);
750
+ if (!Object.keys(contract).length) return;
751
+ const runNow = record(contract.run_now || contract.runNow);
752
+ const schedule = record(contract.schedule);
753
+ const approval = record(contract.approval);
754
+ const monitor = record(contract.monitor);
755
+ const resultContract = record(contract.result_contract || contract.resultContract);
756
+ const sequence = firstArray(contract.sequence);
757
+ const nangoRoute = record(contract.nango_route || contract.nangoRoute);
758
+ const createTool = schedule.create_tool || schedule.createTool || "ops_create";
759
+ const activateTool = schedule.activate_tool || schedule.activateTool || "ops_execute";
760
+ const schedulePath = createTool === activateTool ? String(createTool) : `${createTool} -> ${activateTool}`;
761
+ const statusTool = monitor.status_tool || monitor.statusTool || "ops_status";
762
+ const waitTool = monitor.wait_tool || monitor.waitTool || resultContract.wait_tool || resultContract.waitTool || "ops_wait";
763
+ const resultsTool = monitor.results_tool || monitor.resultsTool || "ops_results";
764
+ const resultShape = firstArray(resultContract.shape).map(String);
765
+ console.log("\nExecution contract");
766
+ console.log(`- Mode: ${contract.mode || "run_once"}`);
767
+ console.log(`- Run now: ${runNow.tool || "ops_execute"}`);
768
+ console.log(`- Schedule: ${schedule.cadence || "manual"} via ${schedulePath}`);
769
+ if (schedule.recurrence) console.log(`- Recurrence: ${schedule.recurrence}`);
770
+ console.log(`- Monitor/results: ${waitTool} (or ${statusTool} -> ${resultsTool})`);
771
+ if (resultShape.length) console.log(`- Result shape: ${resultShape.join(", ")}`);
772
+ if (approval.tool) {
773
+ console.log(`- Approval: ${approval.required ? `${approval.tool} required` : "not required"}${approval.reason ? ` - ${approval.reason}` : ""}`);
774
+ }
775
+ if (sequence.length) {
776
+ console.log(`- Sequence: ${sequence.map((step) => step.tool || "unknown_tool").join(" -> ")}`);
777
+ }
778
+ if (Object.keys(nangoRoute).length) {
779
+ console.log("\nNango API route");
780
+ console.log(`- Connect: ${nangoRoute.connect_tool || nangoRoute.connectTool || "nango_connect_session_create"}`);
781
+ console.log(`- Discover: ${nangoRoute.discover_tool || nangoRoute.discoverTool || "nango_mcp_tools_list"}`);
782
+ console.log(`- Dry run: ${nangoRoute.dry_run_tool || nangoRoute.dryRunTool || "nango_mcp_tool_call"}`);
783
+ console.log(`- Execute: ${nangoRoute.execute_tool || nangoRoute.executeTool || "nango_mcp_tool_call"} after approval`);
784
+ if (nangoRoute.execute_and_wait_tool || nangoRoute.executeAndWaitTool) {
785
+ console.log(`- Execute and wait: ${nangoRoute.execute_and_wait_tool || nangoRoute.executeAndWaitTool}`);
786
+ }
787
+ const nangoScheduleTool = nangoRoute.schedule_and_wait_tool || nangoRoute.scheduleAndWaitTool || nangoRoute.schedule_tool || nangoRoute.scheduleTool;
788
+ if (nangoScheduleTool) {
789
+ console.log(`- Schedule: ${nangoScheduleTool} after approval`);
790
+ }
791
+ if (nangoRoute.write_gate || nangoRoute.writeGate) console.log(`- Gate: ${nangoRoute.write_gate || nangoRoute.writeGate}`);
792
+ }
793
+ }
638
794
  function normalizeSavedCommandArgs(args) {
639
795
  const separator = args.indexOf("--");
640
796
  const raw = separator === -1 ? args : args.slice(separator + 1);
@@ -758,7 +914,7 @@ function opsSaved(sub, rest) {
758
914
  stdio: "inherit",
759
915
  env: process.env
760
916
  });
761
- const latest = loadConfig();
917
+ const latest = loadConfig({ refresh: true });
762
918
  const latestSaved = latest.saved_commands || {};
763
919
  const latestCommand = latestSaved[name];
764
920
  if (latestCommand) {
@@ -943,11 +1099,9 @@ Discovery:
943
1099
  --category NAME Optional category filter, e.g. ops or enrichment
944
1100
 
945
1101
  AI:
946
- ai multi-model Run Custom AI Enrichment - Multi Model
947
- --prompt "..." Prompt/template for each record (required)
948
- --model provider/model
949
- OpenRouter model id, e.g. google/gemini-2.5-flash
950
- --records-file FILE JSON array, or object with records/inputs/data
1102
+ ai multi-model Run Custom AI Enrichment - Multi Model
1103
+ --prompt "..." Prompt/template for each record (required)
1104
+ --records-file FILE JSON array, or object with records/inputs/data
951
1105
  --input-file FILE Single JSON record
952
1106
  --output-fields a:text,b:number
953
1107
  Structured fields to return
@@ -955,12 +1109,10 @@ AI:
955
1109
  --pdf-url URL Attach a PDF URL to all records
956
1110
  --attachment-file FILE
957
1111
  Attach a local image/PDF/audio/video as base64
958
- --attachment-fields a,b
959
- Record fields containing attachment URLs/arrays
960
- --max-concurrency N Record concurrency, default 3, maximum 5
961
- --analysis-models a,b Optional Fusion analysis model panel
962
- --judge-model MODEL Optional Fusion judge model
963
- --confirm-spend Required to run spendful Fusion enrichment
1112
+ --attachment-fields a,b
1113
+ Record fields containing attachment URLs/arrays
1114
+ --max-concurrency N Record concurrency, default 3, maximum 5
1115
+ --confirm-spend Required to run spendful AI enrichment
964
1116
 
965
1117
  Lead Generation:
966
1118
  lead generate Start a B2B lead-generation job
@@ -1046,9 +1198,13 @@ GTM Kernel:
1046
1198
  gtm learning <id> Plan Brain learning for a campaign
1047
1199
  --include-network Include privacy-safe network lanes
1048
1200
  --write-mode MODE dry_run or write
1201
+ --holdout-min-sample-size N
1202
+ Minimum control/treatment subjects for measured lift
1049
1203
  gtm learning-run <id> Queue ready Brain learning phases
1050
1204
  --phases a,b Optional phase allow-list
1051
1205
  --write-mode MODE dry_run or write, default dry_run
1206
+ --holdout-min-sample-size N
1207
+ Minimum control/treatment subjects for measured lift
1052
1208
  gtm calibrate <id> Calibrate deliverability predictions
1053
1209
  --min-sample-size N Default server-side
1054
1210
  --write Write calibrations/patterns; otherwise dry-run
@@ -1072,6 +1228,12 @@ GTM Kernel:
1072
1228
  --provider NAME instantly, smartlead, heyreach, airbyte, custom_webhook
1073
1229
  --campaign-id ID Optional campaign scope
1074
1230
  --write-mode MODE Brain-cycle mode, default dry_run
1231
+ gtm feedback-pull Pull Instantly feedback into the learning loop
1232
+ --source SOURCE webhook_events or emails, default webhook_events
1233
+ --campaign-id ID Optional campaign scope
1234
+ --provider-campaign-id ID
1235
+ Instantly campaign id
1236
+ --confirm Write feedback/memory rows; otherwise dry-run
1075
1237
 
1076
1238
  Native GTM Data Sources:
1077
1239
  Use these when you already know the exact source you want to run.
@@ -1164,8 +1326,12 @@ Campaign Builder:
1164
1326
  campaign rows <id> Retrieve build rows
1165
1327
  --limit N Page size (default: 50, max: 500)
1166
1328
  --cursor <id> Pagination cursor
1329
+ --include-data Include generated copy and rich row data
1330
+ --include-raw Include raw provider payloads
1167
1331
  --all Dump all rows (caution: unbounded)
1168
1332
  campaign artifacts <id> List artifacts with download URLs
1333
+ campaign download <id> Generate fresh signed artifact URLs
1334
+ --format csv|ndjson Artifact format (default: csv)
1169
1335
  campaign approve <id> Approve pending delivery
1170
1336
  --destination-type json|csv|webhook
1171
1337
  --destination-id ID Optional destination/sink ID
@@ -1173,12 +1339,18 @@ Campaign Builder:
1173
1339
  campaign cancel <id> Cancel a running build
1174
1340
 
1175
1341
  Ops:
1176
- Canonical path: ops plan -> ops create -> ops run -> ops status -> ops results
1342
+ Canonical path: ops plan -> ops execute -> ops wait
1177
1343
  ops plan "..." Preview a prompt-first Op plan
1178
1344
  --blueprint NAME monitor_companies|build_leads|enrich_list|route_signals|launch_campaign
1179
1345
  --target-count N Target row count
1180
1346
  --cadence NAME manual|hourly|daily|weekly
1347
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1181
1348
  --destinations a,b Destinations such as slack,csv,google_sheets,webhook,airbyte,nango
1349
+ --workspace-connection-id ID
1350
+ Nango workspace connection for API delivery
1351
+ --provider-config-key KEY
1352
+ Nango integration/provider key
1353
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1182
1354
  --company-domains a,b
1183
1355
  Company domains for monitor/list-based Ops
1184
1356
  --ai-prompt TEXT Custom AI enrichment or scoring instruction
@@ -1195,7 +1367,21 @@ Ops:
1195
1367
  --confirm-spend Acknowledge estimated credits/external writes
1196
1368
  --auto-run Run immediately after create when safe
1197
1369
  --no-auto-run Create only
1370
+ --wait Auto-run, poll status, and fetch results
1371
+ --interval-ms N Polling interval for --wait, default 5000
1372
+ --max-polls N Polling limit for --wait, default 12, max 30
1373
+ --timeout-ms N Total wait timeout for --wait, default 60000
1374
+ --limit N Result rows after terminal status
1375
+ --no-results Stop after final status without fetching results
1376
+ --cadence NAME manual|hourly|daily|weekly
1377
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
1378
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1198
1379
  --destinations a,b Destinations such as slack,csv,google_sheets,webhook,airbyte,nango
1380
+ --workspace-connection-id ID
1381
+ Nango workspace connection for API delivery
1382
+ --provider-config-key KEY
1383
+ Nango integration/provider key
1384
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1199
1385
  --company-domains a,b
1200
1386
  Company domains for monitor/list-based Ops
1201
1387
  --ai-prompt TEXT Custom AI enrichment or scoring instruction
@@ -1204,19 +1390,78 @@ Ops:
1204
1390
  ops create "..." Create an Op from a prompt
1205
1391
  --confirm-spend Acknowledge estimated credits/external writes
1206
1392
  --activate Create as active instead of draft
1393
+ --cadence NAME manual|hourly|daily|weekly
1394
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
1395
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1396
+ --destinations a,b Destinations such as slack,csv,google_sheets,webhook,airbyte,nango
1397
+ --workspace-connection-id ID
1398
+ Nango workspace connection for API delivery
1399
+ --provider-config-key KEY
1400
+ Nango integration/provider key
1401
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1402
+ --company-domains a,b
1403
+ Company domains for monitor/list-based Ops
1404
+ --ai-prompt TEXT Custom AI enrichment or scoring instruction
1405
+ --signal-prompt TEXT Custom signal-finding instruction
1406
+ --output-prompt TEXT Clean result formatting instruction
1407
+ ops schedule "..." Create and activate a recurring Op, daily by default
1408
+ ops recur "..." Alias for ops schedule
1409
+ --confirm-spend Acknowledge estimated credits/external writes
1410
+ --wait Run first tick and fetch results through ops_schedule_and_wait
1411
+ --interval-ms N Polling interval for --wait, default 5000
1412
+ --max-polls N Polling limit for --wait, default 12, max 30
1413
+ --timeout-ms N Total wait timeout for --wait, default 60000
1414
+ --limit N Result rows after terminal status
1415
+ --cadence NAME hourly|daily|weekly (default: daily)
1416
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
1417
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1207
1418
  --destinations a,b Destinations such as slack,csv,google_sheets,webhook,airbyte,nango
1419
+ --workspace-connection-id ID
1420
+ Nango workspace connection for API delivery
1421
+ --provider-config-key KEY
1422
+ Nango integration/provider key
1423
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1208
1424
  --company-domains a,b
1209
1425
  Company domains for monitor/list-based Ops
1210
1426
  --ai-prompt TEXT Custom AI enrichment or scoring instruction
1211
1427
  --signal-prompt TEXT Custom signal-finding instruction
1212
1428
  --output-prompt TEXT Clean result formatting instruction
1213
1429
  ops run <op_id> Run an Op immediately
1430
+ --wait Poll status and fetch results after queueing
1431
+ --interval-ms N Polling interval for --wait, default 5000
1432
+ --max-polls N Polling limit for --wait, default 12, max 30
1433
+ --timeout-ms N Total wait timeout for --wait, default 60000
1434
+ --limit N Result rows after terminal status
1435
+ --no-results Stop after final status without fetching results
1214
1436
  ops status <op_id> Check simple Op status
1215
1437
  ops results <op_id> Retrieve latest Op results
1216
1438
  --limit N Page size, default 100, max 500
1217
1439
  --cursor ID Pagination cursor
1218
1440
  --include-failed-runs
1219
1441
  Include failed run output when supported
1442
+ ops wait <op_id> Wait for an Op and return results (MCP parity: ops_wait)
1443
+ --interval-ms N Polling interval, default 5000
1444
+ --max-polls N Polling limit, default 12, max 30
1445
+ --timeout-ms N Total wait timeout, default 60000
1446
+ --limit N Result page size after terminal status
1447
+ --no-results Stop after final status without fetching results
1448
+ ops nango <subcommand> Connect, discover, call, schedule, and poll Nango tools
1449
+ flow|search Prepare a provider search/connect/Ops route plan
1450
+ connect Start Nango Connect auth for a provider
1451
+ tools List action tools for a workspace connection
1452
+ call <action> Dry-run or execute a Nango action
1453
+ proof|audit|read-write
1454
+ Audit connected rows and optional live read/write probes
1455
+ --read-proxy-path PATH
1456
+ Provider API path for approved live read proof
1457
+ --write-proxy-path PATH
1458
+ Provider API path for approved live write proof
1459
+ --execute-read-probe --confirm
1460
+ Run the read probe
1461
+ --execute-write-probe --confirm-write
1462
+ Run the write probe with safe test input
1463
+ schedule|recur Create a recurring Nango-backed Op; add --wait for first results
1464
+ result Poll async Nango action result
1220
1465
  ops run-status <run_id>
1221
1466
  Inspect a Trigger.dev run or batch with --run-ids a,b
1222
1467
  --watch Poll until all runs reach a terminal state
@@ -1367,6 +1612,7 @@ Status and repair:
1367
1612
  provider-prepare Build read-only recipe, route, and preview arguments
1368
1613
  activate-route Dry-run or confirm provider route setup
1369
1614
  feedback-webhook Prepare feedback ingress
1615
+ feedback-pull Dry-run or confirm Instantly feedback pull
1370
1616
  learning <id> Plan Brain learning
1371
1617
  learning-run <id> Queue Brain learning phases
1372
1618
  calibrate <id> Calibrate deliverability
@@ -1770,6 +2016,35 @@ Options:
1770
2016
  readiness: `signaliz gtm readiness <campaign_id> [options]
1771
2017
 
1772
2018
  Alias for signaliz gtm execution.
2019
+ `,
2020
+ "feedback-pull": `signaliz gtm feedback-pull [options]
2021
+
2022
+ Dry-run or confirm an Instantly feedback pull. Dry-run is the default and
2023
+ counts provider records without writing feedback rows, memory, routine outcomes,
2024
+ or sync cursors. Confirmed writes require --confirm or --confirm-write.
2025
+
2026
+ Options:
2027
+ --source SOURCE webhook_events or emails, default webhook_events
2028
+ --provider-campaign-id ID
2029
+ Instantly campaign id
2030
+ --campaign-id ID Optional linked GTM campaign id
2031
+ --campaign-build-id ID Optional Campaign Builder build id
2032
+ --provider-link-id ID Optional provider link id
2033
+ --integration-id ID Optional Instantly integration id
2034
+ --from DATE Webhook-event pull window start
2035
+ --to DATE Webhook-event pull window end
2036
+ --limit N Page size, default server-side, max 100
2037
+ --max-pages N Max pages, default server-side, max 20
2038
+ --email-type TYPE received, sent, or manual when source=emails
2039
+ --mode MODE emode_focused, emode_others, or emode_all
2040
+ --sort-order ORDER asc or desc
2041
+ --no-memory Do not create memory rows on confirmed writes
2042
+ --no-routine-outcomes Do not write routine outcomes
2043
+ --no-brain-cycle Do not queue feedback-triggered Brain learning
2044
+ --write-mode MODE Brain-cycle mode, default dry_run
2045
+ --confirm Write feedback/memory rows; otherwise dry-run
2046
+ --confirm-write Alias for --confirm
2047
+ --json Machine-readable output
1773
2048
  `,
1774
2049
  campaigns: `signaliz gtm campaigns [options]
1775
2050
 
@@ -1834,7 +2109,13 @@ Options:
1834
2109
  --blueprint NAME monitor_companies|build_leads|enrich_list|route_signals|launch_campaign
1835
2110
  --target-count N Target row count
1836
2111
  --cadence NAME manual|hourly|daily|weekly
2112
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1837
2113
  --destinations a,b Destinations such as json,csv,slack,airbyte,nango
2114
+ --workspace-connection-id ID
2115
+ Nango workspace connection for API delivery
2116
+ --provider-config-key KEY
2117
+ Nango integration/provider key
2118
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1838
2119
  --company-domains a,b Explicit account/domain list
1839
2120
  --json Machine-readable output
1840
2121
 
@@ -1850,12 +2131,52 @@ Options:
1850
2131
  --confirm-spend Acknowledge estimated credits/external writes
1851
2132
  --activate Create as active instead of draft
1852
2133
  --target-count N Target row count
2134
+ --cadence NAME manual|hourly|daily|weekly
2135
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
2136
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1853
2137
  --destinations a,b Destinations such as json,csv,slack,airbyte,nango
2138
+ --workspace-connection-id ID
2139
+ Nango workspace connection for API delivery
2140
+ --provider-config-key KEY
2141
+ Nango integration/provider key
2142
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1854
2143
  --company-domains a,b Explicit account/domain list
1855
2144
  --json Machine-readable output
1856
2145
 
1857
2146
  Safe next step:
1858
2147
  signaliz ops run <op_id>
2148
+ `,
2149
+ schedule: `signaliz ops schedule "plain-English recurring GTM goal" [options]
2150
+
2151
+ Create and activate a recurring Op from a prompt. Defaults to daily cadence
2152
+ unless --cadence is provided. Requires --confirm-spend for spendful work.
2153
+
2154
+ Options:
2155
+ --confirm-spend Acknowledge estimated credits/external writes
2156
+ --wait Create the recurring Op, run the first tick, and fetch results
2157
+ --interval-ms N Polling interval for --wait, default 5000
2158
+ --max-polls N Polling limit for --wait, default 12, max 30
2159
+ --timeout-ms N Total wait timeout for --wait, default 60000
2160
+ --limit N Result rows after terminal status
2161
+ --target-count N Target row count
2162
+ --cadence NAME hourly|daily|weekly (default: daily)
2163
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
2164
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
2165
+ --destinations a,b Destinations such as json,csv,slack,airbyte,nango
2166
+ --workspace-connection-id ID
2167
+ Nango workspace connection for API delivery
2168
+ --provider-config-key KEY
2169
+ Nango integration/provider key
2170
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
2171
+ --company-domains a,b Explicit account/domain list
2172
+ --json Machine-readable output
2173
+
2174
+ Safe next step:
2175
+ signaliz ops status <op_id>
2176
+ `,
2177
+ recur: `signaliz ops recur "plain-English recurring GTM goal" [options]
2178
+
2179
+ Alias for signaliz ops schedule.
1859
2180
  `,
1860
2181
  execute: `signaliz ops execute "plain-English GTM goal" [options]
1861
2182
 
@@ -1866,13 +2187,38 @@ Options:
1866
2187
  --confirm-spend Acknowledge estimated credits/external writes
1867
2188
  --auto-run Run immediately after create when safe
1868
2189
  --no-auto-run Create only
2190
+ --wait Auto-run, poll status, and fetch results
2191
+ --interval-ms N Polling interval for --wait, default 5000
2192
+ --max-polls N Polling limit for --wait, default 12, max 30
2193
+ --timeout-ms N Total wait timeout for --wait, default 60000
2194
+ --limit N Result rows to return after terminal status
2195
+ --no-results Stop after final status without fetching results
1869
2196
  --target-count N Target row count
2197
+ --cadence NAME manual|hourly|daily|weekly
2198
+ --timezone TZ IANA timezone for recurring Ops, e.g. America/Phoenix
2199
+ --wake-on-events a,b Event triggers such as company_funded,crm.updated
1870
2200
  --destinations a,b Destinations such as json,csv,slack,airbyte,nango
2201
+ --workspace-connection-id ID
2202
+ Nango workspace connection for API delivery
2203
+ --provider-config-key KEY
2204
+ Nango integration/provider key
2205
+ --action-name NAME Nango action; use --proxy-path PATH for proxy mode
1871
2206
  --json Machine-readable output
1872
2207
  `,
1873
2208
  run: `signaliz ops run <op_id> [options]
1874
2209
 
1875
2210
  Run a created Op immediately.
2211
+
2212
+ Options:
2213
+ --force Force a run when supported
2214
+ --instruction TEXT Optional run instruction
2215
+ --wait Poll status and fetch results after queueing
2216
+ --interval-ms N Polling interval for --wait, default 5000
2217
+ --max-polls N Polling limit for --wait, default 12, max 30
2218
+ --timeout-ms N Total wait timeout for --wait, default 60000
2219
+ --limit N Result rows to return after terminal status
2220
+ --no-results Stop after final status without fetching results
2221
+ --json Machine-readable output
1876
2222
  `,
1877
2223
  status: `signaliz ops status <op_id> [options]
1878
2224
 
@@ -1887,6 +2233,51 @@ Options:
1887
2233
  --cursor ID Pagination cursor
1888
2234
  --include-failed-runs Include failed run output when supported
1889
2235
  --json Machine-readable output
2236
+ `,
2237
+ wait: `signaliz ops wait <op_id> [options]
2238
+
2239
+ Poll Op status until it completes, blocks, fails, or needs action, then fetch
2240
+ the latest results automatically.
2241
+
2242
+ Options:
2243
+ --interval-ms N Polling interval, default 5000
2244
+ --max-polls N Polling limit, default 12, max 30
2245
+ --timeout-ms N Total wait timeout, default 60000
2246
+ --limit N Result rows to return after terminal status
2247
+ --include-failed-runs Include failed run output when fetching results
2248
+ --no-results Only wait for terminal status; do not fetch results
2249
+ --json Machine-readable output
2250
+ `,
2251
+ nango: `signaliz ops nango <flow|search|connect|tools|call|proof|schedule|result> [options]
2252
+
2253
+ Connect and operate customer-owned vendor APIs through Nango from the Ops
2254
+ namespace. This is the Ops-first route for "connect to any tool, do the work,
2255
+ and get the result" flows.
2256
+
2257
+ Examples:
2258
+ signaliz ops nango search hubspot
2259
+ signaliz ops nango connect --provider-id hubspot
2260
+ signaliz ops nango tools --workspace-connection-id <id> --provider-config-key hubspot
2261
+ signaliz ops nango call upsert-contact --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' --dry-run
2262
+ signaliz ops nango call --workspace-connection-id <id> --proxy-path /v2/leads/list --method GET --dry-run
2263
+ signaliz ops nango call upsert-contact --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' --execute --confirm --wait
2264
+ signaliz ops nango proof --json
2265
+ signaliz ops nango proof --workspace-connection-id <id> --read-proxy-path /v2/accounts --execute-read-probe --confirm
2266
+ signaliz ops nango proof --workspace-connection-id <id> --write-proxy-path /v2/custom-tags --method POST --write-input-json '{"label":"safe-proof"}' --execute-write-probe --confirm-write
2267
+ signaliz ops nango schedule "Monitor accounts daily and upsert approved rows" --workspace-connection-id <id> --provider-config-key hubspot --action-name upsert-contact --confirm-spend --wait
2268
+ signaliz ops nango result --status-url /action/<action_id>
2269
+
2270
+ Subcommands:
2271
+ flow|search Prepare provider search, connect, action, and Ops route calls
2272
+ connect Start Nango Connect auth for a provider
2273
+ tools List Nango action tools for a workspace connection
2274
+ call <action> Dry-run by default; add --execute --confirm for approved writes, --wait for result readback
2275
+ accepts --proxy-path for direct Nango proxy mode
2276
+ proof|audit|read-write Audit connected Nango rows, tools, proxy readiness, and optional live read/write probes
2277
+ accepts --read-proxy-path, --write-proxy-path, --execute-read-probe, --execute-write-probe
2278
+ schedule|recur PROMPT Create a recurring Nango-backed Op through ops_nango_schedule; --wait uses ops_nango_schedule_and_wait
2279
+ accepts --input-json/--input-file, --field-map-json, and --required-fields
2280
+ result Poll async Nango action result
1890
2281
  `
1891
2282
  };
1892
2283
  function opsHelpFor(sub) {
@@ -1901,7 +2292,7 @@ Canonical path:
1901
2292
  signaliz ops results <op_id>
1902
2293
 
1903
2294
  Common commands:
1904
- proof, autopilot, plan, execute, create, run, status, results, doctor, connections
2295
+ proof, autopilot, plan, execute, create, schedule, run, status, results, wait, nango, doctor, connections
1905
2296
 
1906
2297
  Run: signaliz ops help <command>
1907
2298
  `;
@@ -1932,6 +2323,13 @@ Options:
1932
2323
  --outputs json,csv Delivery outputs, default json
1933
2324
  --dry-run Validate and plan without launching spendful work
1934
2325
  --allow-downscale Allow oversized target counts to be clamped
2326
+ --learning-holdout Assign explicit control vs learned cohorts
2327
+ --holdout-control-percentage N
2328
+ Control share, e.g. 0.2 or 20 for 20%
2329
+ --holdout-min-control-size N
2330
+ Minimum control rows for large builds, default 50
2331
+ --holdout-experiment-key KEY
2332
+ Stable experiment key for feedback attribution
1935
2333
  --confirm-spend Acknowledge spendful generation
1936
2334
  --input-file FILE JSON config file from campaign scope or gtm prepare-build
1937
2335
  --wait Poll until completion
@@ -1953,12 +2351,22 @@ Options:
1953
2351
  --cursor ID Pagination cursor
1954
2352
  --qualified Only qualified rows
1955
2353
  --disqualified Only disqualified rows
2354
+ --include-data Include rich row data such as generated copy
2355
+ --include-raw Include raw provider/qualification payloads
1956
2356
  --all Dump all rows
1957
2357
  --json Machine-readable output
1958
2358
  `,
1959
2359
  artifacts: `signaliz campaign artifacts <campaign_build_id> [options]
1960
2360
 
1961
2361
  List generated artifacts and signed download URLs.
2362
+ `,
2363
+ download: `signaliz campaign download <campaign_build_id> [options]
2364
+
2365
+ Generate fresh signed URLs for a downloadable artifact.
2366
+
2367
+ Options:
2368
+ --format csv|ndjson Artifact format, default csv
2369
+ --json Machine-readable output
1962
2370
  `,
1963
2371
  approve: `signaliz campaign approve <campaign_build_id> --destination-type <json|csv|webhook>
1964
2372
 
@@ -1979,9 +2387,10 @@ Canonical path:
1979
2387
  signaliz campaign build --prompt "VP Sales at B2B SaaS" --confirm-spend --wait
1980
2388
  signaliz campaign rows <campaign_build_id> --limit 100
1981
2389
  signaliz campaign artifacts <campaign_build_id>
2390
+ signaliz campaign download <campaign_build_id> --format csv
1982
2391
 
1983
2392
  Commands:
1984
- scope, build, status, rows, artifacts, approve, cancel
2393
+ scope, build, status, rows, artifacts, download, approve, cancel
1985
2394
 
1986
2395
  Run: signaliz campaign help <command>
1987
2396
  `;
@@ -2394,20 +2803,20 @@ function aiAttachmentsFromFlags() {
2394
2803
  }
2395
2804
  async function ai(sub, rest) {
2396
2805
  if (sub !== "multi-model" && sub !== "multimodel") {
2397
- die('Usage: signaliz ai multi-model --prompt "..." --model provider/model --confirm-spend');
2806
+ die('Usage: signaliz ai multi-model --prompt "..." --confirm-spend');
2807
+ }
2808
+ if (flagArg("model") !== void 0 || hasFlag("model") || flagArg("analysis-models") !== void 0 || hasFlag("analysis-models") || flagArg("judge-model") !== void 0 || hasFlag("judge-model")) {
2809
+ die("Custom AI model selection is no longer supported. Signaliz-hosted custom AI uses the default model and returns model readback metadata.");
2398
2810
  }
2399
2811
  const promptText = promptArg(rest);
2400
- const model = flagArg("model");
2401
- if (!promptText) die('Usage: signaliz ai multi-model --prompt "..." --model provider/model --confirm-spend');
2402
- if (!model) die("--model is required. Use an OpenRouter id such as google/gemini-2.5-flash.");
2812
+ if (!promptText) die('Usage: signaliz ai multi-model --prompt "..." --confirm-spend');
2403
2813
  if (!hasFlag("confirm-spend")) {
2404
- die("This uses OpenRouter Fusion and can spend credits. Re-run with --confirm-spend to approve.");
2814
+ die("This runs custom AI enrichment and can spend credits. Re-run with --confirm-spend to approve.");
2405
2815
  }
2406
2816
  const sdk = getSdk();
2407
2817
  const records = aiRecordsFromFlags();
2408
2818
  const result = await sdk.ai.multiModel({
2409
2819
  prompt: promptText,
2410
- model,
2411
2820
  records,
2412
2821
  systemPrompt: flagArg("system-prompt"),
2413
2822
  temperature: numberFlag("temperature"),
@@ -2418,15 +2827,12 @@ async function ai(sub, rest) {
2418
2827
  attachmentFields: csvFlag("attachment-fields") || csvFlag("attachment-field"),
2419
2828
  pdfEngine: flagArg("pdf-engine"),
2420
2829
  fusion: {
2421
- required: !hasFlag("no-require-fusion"),
2422
- analysis_models: csvFlag("analysis-models"),
2423
- judge_model: flagArg("judge-model"),
2424
2830
  include_raw_analysis: hasFlag("include-raw-analysis")
2425
2831
  }
2426
2832
  });
2427
2833
  return output(result, () => {
2428
2834
  console.log("Custom AI Enrichment - Multi Model complete");
2429
- console.log(` Model: ${result.model || model}`);
2835
+ console.log(` Model: ${result.model || "google/gemini-2.5-flash"}`);
2430
2836
  console.log(` Records: ${result.results.length}`);
2431
2837
  console.log(` Credits: ${result.creditsUsed}`);
2432
2838
  console.log(` Attachments: ${result.multimodalCount}`);
@@ -2462,6 +2868,16 @@ async function campaignBuild() {
2462
2868
  if (hasFlag("dry-run")) config.dryRun = true;
2463
2869
  if (hasFlag("allow-downscale")) config.allowDownscale = true;
2464
2870
  if (hasFlag("confirm-spend")) config.confirmSpend = true;
2871
+ if (hasFlag("learning-holdout") || flagArg("holdout-control-percentage") || flagArg("holdout-min-control-size") || flagArg("holdout-experiment-key")) {
2872
+ config.learningHoldout = {
2873
+ enabled: true,
2874
+ controlPercentage: numberFlag("holdout-control-percentage") ?? 0.2,
2875
+ minControlSize: numberFlag("holdout-min-control-size") ?? 50,
2876
+ experimentKey: flagArg("holdout-experiment-key") || void 0,
2877
+ sourceCampaignId: flagArg("holdout-source-campaign-id") || void 0,
2878
+ primaryMetric: flagArg("holdout-primary-metric") || void 0
2879
+ };
2880
+ }
2465
2881
  const outputs = (flagArg("outputs") || "json").split(",").map((s) => s.trim());
2466
2882
  const webhookUrl = flagArg("webhook-url");
2467
2883
  if (outputs.includes("csv") || outputs.includes("webhook") || webhookUrl) {
@@ -2502,8 +2918,14 @@ async function campaignBuild() {
2502
2918
  console.log("\u2713 Campaign dry-run plan ready");
2503
2919
  console.log(` Requested: ${result.requestedTargetCount ?? config.targetCount ?? 50} leads`);
2504
2920
  console.log(` Planned: ${result.plannedTargetCount ?? result.requestedTargetCount ?? config.targetCount ?? 50} leads`);
2921
+ const sourceSummary = campaignSourceSummary(result);
2922
+ if (sourceSummary) console.log(` Source: ${sourceSummary}`);
2923
+ for (const [index, route] of campaignSourceRouteRecords(result).slice(0, 4).entries()) {
2924
+ console.log(` Route ${index + 1}: ${campaignSourceRouteLine(route)}`);
2925
+ }
2505
2926
  if (result.estimatedCredits !== void 0) console.log(` Credits: ~${result.estimatedCredits}`);
2506
2927
  if (result.estimatedDurationSeconds !== void 0) console.log(` Duration: ~${result.estimatedDurationSeconds}s`);
2928
+ if (result.learningHoldout?.enabled) console.log(` Holdout: enabled (${Math.round(Number(result.learningHoldout.control_percentage || 0) * 100)}% control)`);
2507
2929
  if (result.targetLimitApplied) console.log(` Note: ${result.targetLimitReason || "target count downscaled"}`);
2508
2930
  hint("Remove --dry-run and add --confirm-spend when ready to launch.");
2509
2931
  });
@@ -2517,6 +2939,7 @@ async function campaignBuild() {
2517
2939
  console.log(` Status: ${result.status}`);
2518
2940
  console.log(` Phase: ${result.currentPhase}`);
2519
2941
  console.log(` Target: ${config.targetCount || 50} leads`);
2942
+ if (result.learningHoldout?.enabled) console.log(` Holdout: enabled (${Math.round(Number(result.learningHoldout.control_percentage || 0) * 100)}% control)`);
2520
2943
  hint(`signaliz campaign status ${result.campaignBuildId}`);
2521
2944
  });
2522
2945
  }
@@ -2546,6 +2969,7 @@ async function campaignBuild() {
2546
2969
  \u2713 Build ${finalStatus.status}`);
2547
2970
  console.log(` Rows: ${finalStatus.recordsSucceeded}/${finalStatus.recordsTotal} succeeded`);
2548
2971
  console.log(` Artifacts: ${finalStatus.artifactCount}`);
2972
+ printCampaignArtifactDownloadHint(finalStatus, result.campaignBuildId);
2549
2973
  if (finalStatus.status === "completed") {
2550
2974
  hint(`signaliz campaign rows ${result.campaignBuildId} --limit 100`);
2551
2975
  }
@@ -2589,6 +3013,35 @@ async function campaignScope() {
2589
3013
  hint("Next: run signaliz campaign build --input-file with the buildCampaignArgs JSON, or call build_campaign with the returned dry-run args.");
2590
3014
  });
2591
3015
  }
3016
+ function campaignArtifactDownloads(status) {
3017
+ const downloads = Array.isArray(status?.artifactDownloads) ? status.artifactDownloads : Array.isArray(status?.artifact_downloads) ? status.artifact_downloads : [];
3018
+ return downloads.map(record);
3019
+ }
3020
+ function campaignArtifactDownloadUrl(download) {
3021
+ const url = download.downloadUrl || download.download_url || download.signedUrl || download.signed_url;
3022
+ return typeof url === "string" && url.trim() ? url.trim() : null;
3023
+ }
3024
+ function campaignPreferredArtifactDownload(status) {
3025
+ const downloads = campaignArtifactDownloads(status);
3026
+ if (downloads.length === 0) return null;
3027
+ return downloads.find((download) => {
3028
+ const format = String(download.format || download.artifactType || download.artifact_type || "").toLowerCase();
3029
+ return format === "csv";
3030
+ }) || downloads[0];
3031
+ }
3032
+ function printCampaignArtifactDownloadHint(status, buildId) {
3033
+ const download = campaignPreferredArtifactDownload(status);
3034
+ const url = download ? campaignArtifactDownloadUrl(download) : null;
3035
+ if (url) {
3036
+ const format = String(download?.format || download?.artifactType || download?.artifact_type || "artifact");
3037
+ const rowCount = Number(download?.rowCount ?? download?.row_count ?? 0);
3038
+ console.log(`Download: ${format}${rowCount > 0 ? `, ${rowCount} rows` : ""}`);
3039
+ console.log(` ${url}`);
3040
+ return;
3041
+ }
3042
+ const artifactCount = Number(status?.artifactCount ?? status?.artifact_count ?? 0);
3043
+ if (artifactCount > 0) hint(`signaliz campaign download ${buildId} --format csv`);
3044
+ }
2592
3045
  async function campaignStatus() {
2593
3046
  const buildId = positionalAfter("status") || flagArg("campaign-build-id");
2594
3047
  if (!buildId) die("Usage: signaliz campaign status <campaign_build_id>");
@@ -2600,7 +3053,9 @@ async function campaignStatus() {
2600
3053
  console.log(`Status: ${status.status}`);
2601
3054
  console.log(`Phase: ${status.currentPhase || "N/A"}`);
2602
3055
  console.log(`Rows: ${status.recordsSucceeded}/${status.recordsTotal} succeeded, ${status.recordsFailed} failed`);
3056
+ printCampaignCustomerRowCounts(status);
2603
3057
  console.log(`Artifacts: ${status.artifactCount}`);
3058
+ printCampaignArtifactDownloadHint(status, buildId);
2604
3059
  if (status.triggerRunId) console.log(`Trigger: ${status.triggerRunId}`);
2605
3060
  if (status.staleRunningPhase) {
2606
3061
  const age = typeof status.phaseAgeSeconds === "number" ? `${status.phaseAgeSeconds}s` : "unknown age";
@@ -2614,12 +3069,18 @@ async function campaignStatus() {
2614
3069
  console.log(`Errors: ${status.errors.length}`);
2615
3070
  for (const error of status.errors.slice(0, 3)) console.log(` - ${error}`);
2616
3071
  }
2617
- if (status.status === "completed") {
3072
+ if (status.status === "completed" && status.nextAction) {
3073
+ hint(status.nextAction);
3074
+ } else if (status.status === "completed") {
2618
3075
  hint(`signaliz campaign rows ${buildId} --limit 100`);
2619
3076
  } else if (status.staleRunningPhase && status.triggerRunId) {
2620
3077
  hint(`signaliz ops run-status ${status.triggerRunId} --watch`);
2621
3078
  } else if (status.status === "running" || status.status === "queued") {
2622
3079
  hint(`signaliz campaign status ${buildId} (poll again in ~10s)`);
3080
+ } else if (status.status === "pending_approval" && status.approvalAction) {
3081
+ hint(status.approvalAction);
3082
+ } else if (status.status === "pending_approval" && status.nextAction) {
3083
+ hint(status.nextAction);
2623
3084
  } else if (status.status === "pending_approval") {
2624
3085
  hint(`signaliz campaign approve ${buildId} --destination-type webhook`);
2625
3086
  } else if (status.status === "failed" && status.nextAction) {
@@ -2627,6 +3088,22 @@ async function campaignStatus() {
2627
3088
  }
2628
3089
  });
2629
3090
  }
3091
+ function printCampaignCustomerRowCounts(status) {
3092
+ const counts = status?.customerRowCounts || status?.customer_row_counts;
3093
+ if (!counts || typeof counts !== "object") return;
3094
+ const value = (camel, snake) => counts[camel] ?? counts[snake] ?? "n/a";
3095
+ const requested = value("requestedTarget", "requested_target");
3096
+ console.log(`Customer rows: accepted ${value("acceptedRows", "accepted_rows")}/${requested}, copied ${value("copiedRows", "copied_rows")}, approval-ready ${value("approvalReadyRows", "approval_ready_rows")}, QA-ready ${value("qaReadyRows", "qa_ready_rows")}, delivered ${value("deliveredRows", "delivered_rows")}`);
3097
+ const shortfalls = [
3098
+ ["accepted", value("acceptedShortfall", "accepted_shortfall")],
3099
+ ["usable", value("usableShortfall", "usable_shortfall")],
3100
+ ["QA-ready", value("qaReadyShortfall", "qa_ready_shortfall")],
3101
+ ["delivery", value("deliveryShortfall", "delivery_shortfall")]
3102
+ ].filter(([, count]) => typeof count === "number" && count > 0);
3103
+ if (shortfalls.length > 0) {
3104
+ console.log(`Shortfalls: ${shortfalls.map(([label, count]) => `${label} ${count}`).join(", ")}`);
3105
+ }
3106
+ }
2630
3107
  async function campaignRows() {
2631
3108
  const buildId = positionalAfter("rows") || flagArg("campaign-build-id");
2632
3109
  if (!buildId) die("Usage: signaliz campaign rows <campaign_build_id>");
@@ -2641,6 +3118,8 @@ async function campaignRows() {
2641
3118
  do {
2642
3119
  const opts = { limit: Math.min(limit, 500) };
2643
3120
  if (nextCursor) opts.cursor = nextCursor;
3121
+ if (hasFlag("include-data")) opts.includeData = true;
3122
+ if (hasFlag("include-raw")) opts.includeRaw = true;
2644
3123
  const segment = flagArg("segment");
2645
3124
  if (segment) opts.segment = segment;
2646
3125
  const rowStatus = flagArg("row-status");
@@ -2665,6 +3144,8 @@ async function campaignRows() {
2665
3144
  } else {
2666
3145
  const opts = { limit: Math.min(limit, 500) };
2667
3146
  if (cursor) opts.cursor = cursor;
3147
+ if (hasFlag("include-data")) opts.includeData = true;
3148
+ if (hasFlag("include-raw")) opts.includeRaw = true;
2668
3149
  const segment = flagArg("segment");
2669
3150
  if (segment) opts.segment = segment;
2670
3151
  const rowStatus = flagArg("row-status");
@@ -2690,6 +3171,7 @@ async function campaignRows() {
2690
3171
  status: r.status || "",
2691
3172
  score: r.score ?? ""
2692
3173
  })));
3174
+ printCampaignCopySnippets(operatorResult.rows);
2693
3175
  console.log(`
2694
3176
  Showing ${operatorResult.rows.length} row(s)${operatorResult.hasMore ? " (more available)" : ""}`);
2695
3177
  if (operatorResult.nextCursor) {
@@ -2702,14 +3184,15 @@ function formatCampaignRowsForOperator(rows) {
2702
3184
  return rows.map((row) => {
2703
3185
  const rowRecord = record(row);
2704
3186
  const data = record(rowRecord.data);
3187
+ const copyData = record(data.copy);
2705
3188
  const rawCopy = record(data.raw_copy);
2706
3189
  const copy = {
2707
- subject: data.subject || rawCopy.subject || null,
2708
- opener: data.opener || rawCopy.opener || null,
2709
- body: data.body || rawCopy.body || null,
2710
- cta: data.cta || rawCopy.cta || null,
2711
- engine: data.copy_engine || rawCopy.engine || null,
2712
- model: data.copy_model || null
3190
+ subject: data.subject || copyData.subject || rawCopy.subject || null,
3191
+ opener: data.opener || copyData.opener || rawCopy.opener || null,
3192
+ body: data.body || copyData.body || rawCopy.body || null,
3193
+ cta: data.cta || copyData.cta || rawCopy.cta || null,
3194
+ engine: data.copy_engine || copyData.engine || rawCopy.engine || null,
3195
+ model: data.copy_model || copyData.model || null
2713
3196
  };
2714
3197
  return {
2715
3198
  ...rowRecord,
@@ -2739,6 +3222,26 @@ function formatCampaignRowsForOperator(rows) {
2739
3222
  };
2740
3223
  });
2741
3224
  }
3225
+ function printCampaignCopySnippets(rows, limit = 5) {
3226
+ const rowsWithCopy = rows.filter((row) => {
3227
+ const copy = record(row.copy);
3228
+ return Boolean(copy.subject || copy.opener || copy.body || copy.cta);
3229
+ });
3230
+ if (rowsWithCopy.length === 0) return;
3231
+ console.log("\nCopy:");
3232
+ for (const row of rowsWithCopy.slice(0, limit)) {
3233
+ const copy = record(row.copy);
3234
+ const label = row.email || row.name || row.company || `row ${row.index ?? ""}`.toString().trim();
3235
+ console.log(`- ${label}`);
3236
+ if (copy.subject) console.log(` Subject: ${copy.subject}`);
3237
+ if (copy.opener) console.log(` Opener: ${copy.opener}`);
3238
+ if (copy.body) console.log(` Body: ${copy.body}`);
3239
+ if (copy.cta) console.log(` CTA: ${copy.cta}`);
3240
+ }
3241
+ if (rowsWithCopy.length > limit) {
3242
+ console.log(` ...${rowsWithCopy.length - limit} more row(s) with copy`);
3243
+ }
3244
+ }
2742
3245
  async function campaignArtifacts() {
2743
3246
  const buildId = positionalAfter("artifacts") || flagArg("campaign-build-id");
2744
3247
  if (!buildId) die("Usage: signaliz campaign artifacts <campaign_build_id>");
@@ -2756,6 +3259,24 @@ async function campaignArtifacts() {
2756
3259
  }
2757
3260
  });
2758
3261
  }
3262
+ async function campaignDownload() {
3263
+ const buildId = positionalAfter("download") || flagArg("campaign-build-id");
3264
+ if (!buildId) die("Usage: signaliz campaign download <campaign_build_id> [--format csv|ndjson]");
3265
+ const sdk = getSdk();
3266
+ const format = flagArg("format") || "csv";
3267
+ const result = await sdk.campaigns.downloadCampaignArtifact(buildId, { format });
3268
+ output(result, () => {
3269
+ console.log(`Artifact: ${result.format} (${result.rowCount} rows)`);
3270
+ if (result.partCount > 1) console.log(`Parts: ${result.partCount}`);
3271
+ const parts = Array.isArray(result.parts) ? result.parts : [];
3272
+ for (const part of parts) {
3273
+ const url = part.signedUrl;
3274
+ if (!url) continue;
3275
+ const label = result.partCount > 1 ? `part ${part.partIndex}` : "download";
3276
+ console.log(`${label}: ${url}`);
3277
+ }
3278
+ });
3279
+ }
2759
3280
  async function campaignApprove() {
2760
3281
  const buildId = positionalAfter("approve") || positionalAfter("approve-delivery") || flagArg("campaign-build-id");
2761
3282
  if (!buildId) die("Usage: signaliz campaign approve <campaign_build_id> --destination-type <json|csv|webhook>");
@@ -2811,6 +3332,8 @@ async function campaign(sub) {
2811
3332
  return campaignRows();
2812
3333
  case "artifacts":
2813
3334
  return campaignArtifacts();
3335
+ case "download":
3336
+ return campaignDownload();
2814
3337
  case "approve":
2815
3338
  case "approve-delivery":
2816
3339
  return campaignApprove();
@@ -2818,7 +3341,7 @@ async function campaign(sub) {
2818
3341
  return campaignCancel();
2819
3342
  default:
2820
3343
  die(`Unknown campaign subcommand: ${sub}
2821
- Available: scope, build, status, rows, artifacts, approve, cancel`);
3344
+ Available: scope, build, status, rows, artifacts, download, approve, cancel`);
2822
3345
  }
2823
3346
  }
2824
3347
  var CAMPAIGN_AGENT_APPROVAL_TYPES = [
@@ -2864,9 +3387,9 @@ Examples:
2864
3387
  signaliz campaign-agent dry-run --goal "Build a local services campaign" --target-count 250 --use-local-leads --json
2865
3388
  signaliz campaign-agent build-approved --goal "Build a strategy-template campaign" --confirm-launch --approve-all --approved-by operator@example.com --spend-limit 500
2866
3389
  signaliz campaign-agent build-approved --request-file campaign-builder-agent-request.json --confirm-launch --approve-all --approved-by operator@example.com --spend-limit 500 --wait --approve-delivery --delivery-destination-type json
2867
- signaliz campaign-agent review <campaign_build_id> --limit 100 --json
3390
+ signaliz campaign-agent review <campaign_build_id> --limit 100 --include-data --json
2868
3391
  signaliz campaign-agent status <campaign_build_id> --json
2869
- signaliz campaign-agent rows <campaign_build_id> --limit 100 --json
3392
+ signaliz campaign-agent rows <campaign_build_id> --limit 100 --include-data --json
2870
3393
 
2871
3394
  Plan options:
2872
3395
  --goal TEXT Campaign goal or brief
@@ -2874,7 +3397,7 @@ Plan options:
2874
3397
  --kit-file FILE Write campaign-agent kit output to a JSON file
2875
3398
  --seed-manifest-file FILE
2876
3399
  Manifest path for memory-kit seed dry-runs
2877
- --source NAME memory-kit seed source: agency, workflow-patterns, instantly-feedback, or all
3400
+ --source NAME memory-kit seed source: north-star, agency, workflow-patterns, instantly-feedback, or all
2878
3401
  --request-output-file FILE
2879
3402
  Request filename to reference inside kit commands
2880
3403
  --request-file FILE Reusable JSON CampaignBuilderAgentRequest; flags override file fields
@@ -3118,6 +3641,8 @@ async function campaignAgentReview(sub, rest) {
3118
3641
  cursor: commandStringFlag(flags, "cursor"),
3119
3642
  segment: commandStringFlag(flags, "segment"),
3120
3643
  rowStatus: commandStringFlag(flags, "row-status"),
3644
+ includeData: commandOptionalBooleanFlag(flags, "include-data"),
3645
+ includeRaw: commandOptionalBooleanFlag(flags, "include-raw"),
3121
3646
  qualified: commandBooleanFlag(flags, "qualified") ? true : commandBooleanFlag(flags, "disqualified") ? false : void 0
3122
3647
  };
3123
3648
  const review = await agent.reviewBuild?.(buildId, rowOptions) || await campaignAgentBuildReviewFromCampaignApis(agent, campaigns, buildId, rowOptions);
@@ -3141,6 +3666,8 @@ async function campaignAgentReview(sub, rest) {
3141
3666
  cursor: commandStringFlag(flags, "cursor"),
3142
3667
  segment: commandStringFlag(flags, "segment"),
3143
3668
  rowStatus: commandStringFlag(flags, "row-status"),
3669
+ includeData: commandOptionalBooleanFlag(flags, "include-data"),
3670
+ includeRaw: commandOptionalBooleanFlag(flags, "include-raw"),
3144
3671
  qualified: commandBooleanFlag(flags, "qualified") ? true : commandBooleanFlag(flags, "disqualified") ? false : void 0
3145
3672
  };
3146
3673
  const result2 = await (agent.getBuildRows?.bind(agent) || campaigns.rows?.bind(campaigns) || campaigns.getCampaignBuildRows?.bind(campaigns))?.(buildId, rowOptions);
@@ -3731,8 +4258,11 @@ function printCampaignAgentBuildStatus(status) {
3731
4258
  if (status.campaignId) console.log(`Campaign ID: ${status.campaignId}`);
3732
4259
  console.log(`Status: ${status.status}`);
3733
4260
  console.log(`Phase: ${status.currentPhase || "N/A"}`);
4261
+ if (status.terminalState?.kind) console.log(`Terminal: ${status.terminalState.kind}`);
3734
4262
  console.log(`Rows: ${status.recordsSucceeded}/${status.recordsTotal} succeeded, ${status.recordsFailed} failed`);
4263
+ printCampaignCustomerRowCounts(status);
3735
4264
  console.log(`Artifacts: ${status.artifactCount}`);
4265
+ if (status.campaignBuildId) printCampaignArtifactDownloadHint(status, status.campaignBuildId);
3736
4266
  if (status.triggerRunId) console.log(`Trigger: ${status.triggerRunId}`);
3737
4267
  if (Array.isArray(status.warnings) && status.warnings.length) {
3738
4268
  console.log(`Warnings: ${status.warnings.length}`);
@@ -3742,8 +4272,14 @@ function printCampaignAgentBuildStatus(status) {
3742
4272
  console.log(`Errors: ${status.errors.length}`);
3743
4273
  for (const error of status.errors.slice(0, 3)) console.log(` - ${error}`);
3744
4274
  }
3745
- if (status.status === "completed") {
4275
+ if (status.status === "completed" && status.nextAction) {
4276
+ hint(status.nextAction);
4277
+ } else if (status.status === "completed") {
3746
4278
  hint(`signaliz campaign-agent rows ${status.campaignBuildId} --limit 100`);
4279
+ } else if (status.status === "pending_approval" && status.approvalAction) {
4280
+ hint(status.approvalAction);
4281
+ } else if (status.status === "pending_approval" && status.nextAction) {
4282
+ hint(status.nextAction);
3747
4283
  } else if (status.status === "pending_approval") {
3748
4284
  hint(`signaliz campaign-agent approve ${status.campaignBuildId} --destination-type webhook`);
3749
4285
  } else if ((status.status === "running" || status.status === "queued") && status.triggerRunId) {
@@ -3756,11 +4292,14 @@ function printCampaignAgentBuildReview(review) {
3756
4292
  console.log(`Campaign: ${status.name || review.campaignBuildId}`);
3757
4293
  console.log(`Status: ${summary.status || status.status}`);
3758
4294
  console.log(`Phase: ${summary.phase || status.currentPhase || "N/A"}`);
4295
+ console.log(`Terminal state: ${summary.terminalState || status.terminalState?.kind || "unknown"}`);
3759
4296
  console.log(`Rows: ${summary.sampledRows || 0} sampled, ${summary.availableRows || 0} available`);
3760
4297
  console.log(`Qualified: ${summary.qualifiedRows || 0}`);
3761
4298
  console.log(`Emails: ${summary.rowsWithEmail || 0}`);
3762
4299
  console.log(`Copy ready: ${summary.copyReadyRows || 0}`);
3763
4300
  console.log(`Artifacts: ${summary.artifactCount || 0}`);
4301
+ printCampaignAgentNorthStarScorecard(review.northStarScorecard);
4302
+ if (review.campaignBuildId) printCampaignArtifactDownloadHint(status, review.campaignBuildId);
3764
4303
  console.log(`Delivery approval ready: ${summary.readyForDeliveryApproval === true ? "yes" : "no"}`);
3765
4304
  const gates = Array.isArray(review.gates) ? review.gates : [];
3766
4305
  if (gates.length) {
@@ -3780,6 +4319,21 @@ function printCampaignAgentBuildReview(review) {
3780
4319
  for (const action of nextActions) console.log(`- ${action}`);
3781
4320
  }
3782
4321
  }
4322
+ function printCampaignAgentNorthStarScorecard(scorecard) {
4323
+ const card = record(scorecard);
4324
+ if (!card.version) return;
4325
+ const score = card.score === null || card.score === void 0 ? "pending" : `${card.score}%`;
4326
+ console.log(`North Star: ${String(card.status || "unknown")} (${score})`);
4327
+ if (card.moatState) console.log(`Moat state: ${card.moatState}`);
4328
+ const metrics = Array.isArray(card.metrics) ? card.metrics : [];
4329
+ if (metrics.length) {
4330
+ console.log("\nNorth Star gates:");
4331
+ for (const metric of metrics.slice(0, 12)) {
4332
+ const item = record(metric);
4333
+ console.log(`- ${String(item.status || "review").toUpperCase()} ${item.label || item.id}: ${item.detail || ""}`);
4334
+ }
4335
+ }
4336
+ }
3783
4337
  function createCampaignAgentBuildReview(status, rows, artifacts) {
3784
4338
  const rowList = Array.isArray(rows?.rows) ? rows.rows : [];
3785
4339
  const sampledRows = rowList.length;
@@ -3898,6 +4452,7 @@ function printCampaignAgentRows(result) {
3898
4452
  status: row.status || "",
3899
4453
  score: row.score ?? ""
3900
4454
  })));
4455
+ printCampaignCopySnippets(result.rows);
3901
4456
  console.log(`
3902
4457
  Showing ${result.rows.length} row(s)${result.hasMore ? " (more available)" : ""}`);
3903
4458
  if (result.nextCursor) {
@@ -4690,6 +5245,7 @@ async function gtm(sub, rest) {
4690
5245
  days: numberFlag("days"),
4691
5246
  networkDays: numberFlag("network-days"),
4692
5247
  minSampleSize: numberFlag("min-sample-size"),
5248
+ holdoutMinSampleSize: numberFlag("holdout-min-sample-size"),
4693
5249
  minWorkspaceCount: numberFlag("min-workspace-count"),
4694
5250
  limit: numberFlag("limit")
4695
5251
  });
@@ -4700,6 +5256,10 @@ async function gtm(sub, rest) {
4700
5256
  console.log("GTM Brain learning plan");
4701
5257
  console.log(` Campaign: ${campaignId}`);
4702
5258
  console.log(` Lanes: ${ready}/${lanes.length} ready`);
5259
+ if (result.holdout_lift?.status) {
5260
+ const delta = result.holdout_lift?.lift?.primary_metric_delta;
5261
+ console.log(` Holdout: ${result.holdout_lift.status}${delta !== void 0 ? ` (${delta} ${result.holdout_lift.primary_metric || "primary_metric"})` : ""}`);
5262
+ }
4703
5263
  console.log(` Calls: ${calls.length}`);
4704
5264
  for (const call of calls.slice(0, 5)) {
4705
5265
  const state = call.ready === false ? "blocked" : "ready";
@@ -4721,6 +5281,7 @@ async function gtm(sub, rest) {
4721
5281
  days: numberFlag("days"),
4722
5282
  networkDays: numberFlag("network-days"),
4723
5283
  minSampleSize: numberFlag("min-sample-size"),
5284
+ holdoutMinSampleSize: numberFlag("holdout-min-sample-size"),
4724
5285
  minWorkspaceCount: numberFlag("min-workspace-count"),
4725
5286
  minPrivacyK: numberFlag("min-privacy-k"),
4726
5287
  limit: numberFlag("limit")
@@ -4974,6 +5535,103 @@ async function gtm(sub, rest) {
4974
5535
  hint(`signaliz gtm bootstrap${common.campaignId ? ` --campaign-id ${common.campaignId}` : ""} --include-samples`);
4975
5536
  });
4976
5537
  }
5538
+ if (sub === "feedback-pull" || sub === "instantly-feedback-pull" || sub === "pull-feedback") {
5539
+ const provider = (flagArg("provider") || "instantly").toLowerCase();
5540
+ if (provider !== "instantly") {
5541
+ die("gtm feedback-pull currently supports --provider instantly. Use gtm feedback-webhook for provider-agnostic ingress.");
5542
+ }
5543
+ const source = flagArg("source") || flagArg("pull-source") || "webhook_events";
5544
+ if (!["webhook_events", "emails"].includes(source)) {
5545
+ die("--source must be webhook_events or emails");
5546
+ }
5547
+ if (flagArg("source") && flagArg("pull-source") && flagArg("source") !== flagArg("pull-source")) {
5548
+ die("--source and --pull-source must match when both are provided");
5549
+ }
5550
+ const campaignId = flagArg("campaign-id");
5551
+ const campaignBuildId = flagArg("campaign-build-id") || flagArg("build-id");
5552
+ const providerLinkId = flagArg("provider-link-id");
5553
+ const providerCampaignId = flagArg("provider-campaign-id") || flagArg("instantly-campaign-id") || positionalAfter(sub);
5554
+ if (!campaignId && !campaignBuildId && !providerLinkId && !providerCampaignId) {
5555
+ die("Usage: signaliz gtm feedback-pull --provider-campaign-id <instantly_campaign_id> [--source webhook_events|emails] [--confirm]");
5556
+ }
5557
+ const hasEmailHistoryFlags = Boolean(flagArg("email-type") || flagArg("mode") || flagArg("sort-order"));
5558
+ if (hasEmailHistoryFlags && source !== "emails") {
5559
+ die("--email-type, --mode, and --sort-order require --source emails");
5560
+ }
5561
+ const emailType = source === "emails" ? flagArg("email-type") || "received" : void 0;
5562
+ if (emailType !== void 0 && !["received", "sent", "manual"].includes(emailType)) {
5563
+ die("--email-type must be received, sent, or manual");
5564
+ }
5565
+ const mode = source === "emails" ? flagArg("mode") || "emode_all" : void 0;
5566
+ if (mode !== void 0 && !["emode_focused", "emode_others", "emode_all"].includes(mode)) {
5567
+ die("--mode must be emode_focused, emode_others, or emode_all");
5568
+ }
5569
+ const sortOrder = source === "emails" ? flagArg("sort-order") || "asc" : void 0;
5570
+ if (sortOrder !== void 0 && !["asc", "desc"].includes(sortOrder)) {
5571
+ die("--sort-order must be asc or desc");
5572
+ }
5573
+ if (hasFlag("write") && !hasFlag("confirm") && !hasFlag("confirm-write")) {
5574
+ die("Write-mode feedback pulls require --confirm or --confirm-write. Run without --confirm first for a dry-run count.");
5575
+ }
5576
+ const confirmed = hasFlag("confirm") || hasFlag("confirm-write");
5577
+ const dryRun = !confirmed;
5578
+ const result = await sdk.gtm.pullInstantlyFeedback({
5579
+ campaignId,
5580
+ campaignBuildId,
5581
+ providerLinkId,
5582
+ providerCampaignId,
5583
+ instantlyCampaignId: flagArg("instantly-campaign-id"),
5584
+ integrationId: flagArg("integration-id"),
5585
+ source,
5586
+ instantlyProfile: flagArg("instantly-profile") || flagArg("profile"),
5587
+ cursor: flagArg("cursor"),
5588
+ from: flagArg("from") || flagArg("since"),
5589
+ to: flagArg("to") || flagArg("until"),
5590
+ limit: numberFlag("limit"),
5591
+ maxPages: numberFlag("max-pages"),
5592
+ emailType,
5593
+ mode,
5594
+ sortOrder,
5595
+ createMemory: !hasFlag("no-memory"),
5596
+ writeRoutineOutcomes: !hasFlag("no-routine-outcomes"),
5597
+ dryRun,
5598
+ runBrainCycle: !hasFlag("no-brain-cycle"),
5599
+ brainCycleWriteMode: flagArg("write-mode") || "dry_run",
5600
+ brainCyclePhases: csvFlag("brain-cycle-phases"),
5601
+ brainCycleMinIngested: numberFlag("brain-cycle-min-ingested"),
5602
+ brainCycleMinIntervalMinutes: numberFlag("brain-cycle-min-interval-minutes")
5603
+ });
5604
+ return output(result, () => {
5605
+ console.log("GTM Instantly feedback pull");
5606
+ console.log(` Mode: ${dryRun ? "dry_run" : "write"}`);
5607
+ console.log(` Source: ${result.source || source}`);
5608
+ if (result.provider_campaign_id || providerCampaignId) console.log(` Campaign: ${result.provider_campaign_id || providerCampaignId}`);
5609
+ if (result.run_id) console.log(` Run ID: ${result.run_id}`);
5610
+ if (result.status) console.log(` Status: ${result.status}`);
5611
+ if (result.max_pages !== void 0) console.log(` Pages: ${result.max_pages}`);
5612
+ const countFields = [
5613
+ ["Seen", result.webhook_events_seen ?? result.email_records_seen ?? result.source_events],
5614
+ ["Normalized", result.feedback_events_synced ?? result.events_normalized],
5615
+ ["Ingested", result.ingested],
5616
+ ["Duplicates", result.duplicates],
5617
+ ["Unmatched", result.unmatched]
5618
+ ];
5619
+ for (const [label, value] of countFields) {
5620
+ if (value !== void 0 && value !== null) console.log(` ${label}: ${value}`);
5621
+ }
5622
+ if (Array.isArray(result.blockers) && result.blockers.length) console.log(` Blockers: ${result.blockers.join("; ")}`);
5623
+ if (dryRun) {
5624
+ if (result.run_id) hint(`signaliz ops run-status ${result.run_id} --watch`);
5625
+ hint("Review the dry-run counts before writing feedback, memory, and outcomes: rerun with --confirm.");
5626
+ } else if (campaignId) {
5627
+ hint(`signaliz gtm learning ${campaignId} --include-network`);
5628
+ } else if (campaignBuildId) {
5629
+ hint(`signaliz gtm execution --campaign-build-id ${campaignBuildId}`);
5630
+ } else {
5631
+ hint("After the pull completes, run signaliz gtm bootstrap --include-samples to verify feedback and memory readiness.");
5632
+ }
5633
+ });
5634
+ }
4977
5635
  if (sub === "nango") return gtmNango(rest[0], rest.slice(1));
4978
5636
  if (!sub || sub === "list" || sub === "sources") {
4979
5637
  const result = await sdk.leads.listNativeGtmCapabilities(flagArg("category"));
@@ -5034,7 +5692,7 @@ ${i + 1}. ${m.capability_id} (score ${m.score}) \u2014 ${m.label}`);
5034
5692
  const result = await sdk.leads.checkStatus(jobId);
5035
5693
  return output(result);
5036
5694
  }
5037
- die("Usage: signaliz gtm <context|bootstrap|strategy-memory|agent-template|agent-plan|plan|commit-plan|prepare-build|campaigns|campaign|existing-campaigns|audit-existing|memory|learning|execution|integrations|provider-prepare|activate-route|list|find|run|status|nango>");
5695
+ die("Usage: signaliz gtm <context|bootstrap|strategy-memory|agent-template|agent-plan|plan|commit-plan|prepare-build|campaigns|campaign|existing-campaigns|audit-existing|memory|learning|execution|integrations|provider-prepare|activate-route|feedback-webhook|feedback-pull|list|find|run|status|nango>");
5038
5696
  }
5039
5697
  function nangoActionInput() {
5040
5698
  const fromFile = readJsonFlag("input-file");
@@ -5051,30 +5709,146 @@ function nangoBaseOptions() {
5051
5709
  nangoConnectionId: flagArg("nango-connection-id")
5052
5710
  };
5053
5711
  }
5054
- function nangoConnectOptions() {
5712
+ function nangoConnectOptions(defaultSurface = "gtm_kernel") {
5055
5713
  return {
5056
5714
  providerId: flagArg("provider-id") || flagArg("provider"),
5057
5715
  integrationId: flagArg("integration-id") || flagArg("provider-config-key"),
5058
5716
  providerDisplayName: flagArg("provider-display-name") || flagArg("provider-name"),
5059
5717
  integrationDisplayName: flagArg("integration-display-name"),
5060
5718
  allowedIntegrations: csvFlag("allowed-integrations"),
5061
- surface: flagArg("surface") || "gtm_kernel",
5719
+ surface: flagArg("surface") || defaultSurface,
5062
5720
  source: flagArg("source") || "signaliz_cli",
5063
5721
  userEmail: flagArg("user-email"),
5064
5722
  userDisplayName: flagArg("user-display-name")
5065
5723
  };
5066
5724
  }
5725
+ function nangoPositionalValues(rest = []) {
5726
+ const values = [];
5727
+ for (let index = 0; index < rest.length; index++) {
5728
+ const value = rest[index];
5729
+ if (value.startsWith("--")) {
5730
+ if (!value.includes("=") && rest[index + 1] && !rest[index + 1].startsWith("--")) index++;
5731
+ continue;
5732
+ }
5733
+ values.push(value);
5734
+ }
5735
+ return values;
5736
+ }
5737
+ function nangoFlowOptions(rest = []) {
5738
+ const positionalSearch = nangoPositionalValues(rest).join(" ");
5739
+ const search = flagArg("search") || flagArg("query") || positionalSearch || void 0;
5740
+ return {
5741
+ ...nangoBaseOptions(),
5742
+ query: flagArg("query"),
5743
+ search,
5744
+ providerId: flagArg("provider-id") || flagArg("provider"),
5745
+ provider: flagArg("provider"),
5746
+ intent: flagArg("intent"),
5747
+ actionName: flagArg("action-name") || flagArg("action"),
5748
+ toolName: flagArg("tool-name"),
5749
+ proxyPath: flagArg("proxy-path") || flagArg("nango-proxy-path"),
5750
+ method: flagArg("method"),
5751
+ input: nangoActionInput(),
5752
+ cadence: flagArg("cadence"),
5753
+ fieldMap: readInlineJsonFlag("field-map-json") || readJsonFlag("field-map-file"),
5754
+ requiredFields: csvFlag("required-fields") || csvFlag("required-field"),
5755
+ confirmSpend: hasFlag("confirm-spend"),
5756
+ writeConfirmed: hasFlag("write-confirmed") || hasFlag("confirm-write") || void 0,
5757
+ layer: flagArg("layer"),
5758
+ campaignId: flagArg("campaign-id"),
5759
+ limit: numberFlag("limit"),
5760
+ includeProviderCatalog: hasFlag("skip-provider-catalog") ? false : hasFlag("include-provider-catalog") ? true : void 0
5761
+ };
5762
+ }
5067
5763
  function nangoStatusUrl(result) {
5068
5764
  return result?.status_url || result?.statusUrl || result?.nango_result?.status_url || result?.nango_result?.statusUrl;
5069
5765
  }
5070
- async function gtmNango(sub, rest) {
5766
+ function nangoActionId(result) {
5767
+ return result?.action_id || result?.actionId || result?.nango_result?.action_id || result?.nango_result?.actionId || result?.nango_result?.id;
5768
+ }
5769
+ function nangoActionStatus(result) {
5770
+ return result?.status || result?.nango_result?.status;
5771
+ }
5772
+ function isTerminalNangoActionStatus(status) {
5773
+ return ["completed", "succeeded", "success", "failed", "error", "cancelled", "canceled"].includes(String(status || "").toLowerCase());
5774
+ }
5775
+ async function waitForNangoActionResult(nango, call, opts = {}) {
5776
+ const intervalMs = Math.max(250, Number(opts.intervalMs ?? 5e3));
5777
+ const maxPolls = Math.max(1, Math.min(60, Math.trunc(Number(opts.maxPolls ?? 12))));
5778
+ const timeoutMs = Math.max(250, Number(opts.timeoutMs ?? 6e4));
5779
+ let statusUrl = nangoStatusUrl(call);
5780
+ let actionId = nangoActionId(call);
5781
+ let latest = statusUrl || actionId ? void 0 : call;
5782
+ let polls = 0;
5783
+ const startedAt = Date.now();
5784
+ while ((statusUrl || actionId) && polls < maxPolls && Date.now() - startedAt <= timeoutMs) {
5785
+ polls += 1;
5786
+ latest = await nango.getNangoActionResult({ actionId, statusUrl });
5787
+ statusUrl = nangoStatusUrl(latest) || statusUrl;
5788
+ actionId = nangoActionId(latest) || actionId;
5789
+ if (isTerminalNangoActionStatus(nangoActionStatus(latest))) break;
5790
+ if (polls < maxPolls && Date.now() - startedAt < timeoutMs) await sleep(intervalMs);
5791
+ }
5792
+ const status = nangoActionStatus(latest || call);
5793
+ const timedOut = Boolean((statusUrl || actionId) && !isTerminalNangoActionStatus(status) && (polls >= maxPolls || Date.now() - startedAt > timeoutMs));
5794
+ return {
5795
+ call,
5796
+ result: latest,
5797
+ status,
5798
+ action_id: actionId,
5799
+ actionId,
5800
+ status_url: statusUrl,
5801
+ statusUrl,
5802
+ polls,
5803
+ max_polls: maxPolls,
5804
+ maxPolls,
5805
+ interval_ms: intervalMs,
5806
+ intervalMs,
5807
+ timeout_ms: timeoutMs,
5808
+ timeoutMs,
5809
+ timed_out: timedOut,
5810
+ timedOut
5811
+ };
5812
+ }
5813
+ async function gtmNango(sub, rest, namespace = "gtm") {
5071
5814
  const sdk = getSdk();
5815
+ const nango = namespace === "ops" ? sdk.ops : sdk.gtm;
5816
+ const commandPrefix = namespace === "ops" ? "signaliz ops nango" : "signaliz gtm nango";
5817
+ const defaultSurface = namespace === "ops" ? "ops_destinations" : "gtm_kernel";
5818
+ if (sub === "flow" || sub === "plan" || sub === "prepare" || sub === "search") {
5819
+ const result = await nango.prepareNangoIntegrationFlow(nangoFlowOptions(rest));
5820
+ return output(result, () => {
5821
+ console.log(`Nango integration flow: ${result?.status || "prepared"}`);
5822
+ if (result?.provider_id) console.log(` Provider: ${result.provider_id}`);
5823
+ if (result?.integration_id) console.log(` Integration: ${result.integration_id}`);
5824
+ if (result?.selected_connection?.id) console.log(` Workspace connection: ${result.selected_connection.id}`);
5825
+ const providers = Array.isArray(result?.provider_library?.providers) ? result.provider_library.providers : [];
5826
+ if (providers.length) {
5827
+ console.log(" Provider matches:");
5828
+ for (const provider of providers.slice(0, 5)) console.log(` - ${provider.name || provider.id} (${provider.id})`);
5829
+ }
5830
+ const connections = Array.isArray(result?.matched_connections) ? result.matched_connections : [];
5831
+ if (connections.length) {
5832
+ console.log(" Saved connections:");
5833
+ for (const connection of connections.slice(0, 5)) {
5834
+ const ready = connection.ready_for_route ? "ready" : connection.needs_attention ? "needs reconnect" : "not ready";
5835
+ console.log(` - ${connection.connector_name || connection.provider_config_key || connection.id}: ${ready} (${connection.id})`);
5836
+ }
5837
+ }
5838
+ const nextActions = Array.isArray(result?.next_actions) ? result.next_actions : [];
5839
+ if (nextActions.length) {
5840
+ console.log(" Next calls:");
5841
+ for (const action of nextActions.slice(0, 5)) console.log(` - ${action.tool}: ${action.reason || "next step"}`);
5842
+ }
5843
+ if (result?.next_action) hint(result.next_action);
5844
+ });
5845
+ }
5072
5846
  if (sub === "connect" || sub === "auth" || sub === "authorize") {
5073
- const options = nangoConnectOptions();
5847
+ const options = nangoConnectOptions(defaultSurface);
5074
5848
  if (!options.providerId && !options.integrationId && (!options.allowedIntegrations || options.allowedIntegrations.length === 0)) {
5075
- die("Usage: signaliz gtm nango connect --provider-id <provider> [--integration-id <key>] [--allowed-integrations a,b]");
5849
+ die(`Usage: ${commandPrefix} connect --provider-id <provider> [--integration-id <key>] [--allowed-integrations a,b]`);
5076
5850
  }
5077
- const result = await sdk.gtm.createNangoConnectSession(options);
5851
+ const result = await nango.createNangoConnectSession(options);
5078
5852
  return output(result, () => {
5079
5853
  console.log(`Nango connect session: ${result?.status || "created"}`);
5080
5854
  if (result?.provider_id || options.providerId) console.log(` Provider: ${result?.provider_id || options.providerId}`);
@@ -5082,11 +5856,11 @@ async function gtmNango(sub, rest) {
5082
5856
  if (result?.connect_link || result?.auth_window_url) console.log(` Auth link: ${result.connect_link || result.auth_window_url}`);
5083
5857
  if (result?.expires_at) console.log(` Expires: ${result.expires_at}`);
5084
5858
  if (result?.next_action) console.log(` Next: ${result.next_action}`);
5085
- hint("After authorization finishes, run signaliz gtm nango tools --workspace-connection-id <id> --provider-config-key <key>");
5859
+ hint(`After authorization finishes, run ${commandPrefix} tools --workspace-connection-id <id> --provider-config-key <key>`);
5086
5860
  });
5087
5861
  }
5088
5862
  if (!sub || sub === "tools" || sub === "list") {
5089
- const result = await sdk.gtm.listNangoTools({
5863
+ const result = await nango.listNangoTools({
5090
5864
  ...nangoBaseOptions(),
5091
5865
  format: flagArg("format"),
5092
5866
  includeRaw: hasFlag("include-raw")
@@ -5097,39 +5871,153 @@ async function gtmNango(sub, rest) {
5097
5871
  for (const tool of tools2.slice(0, 10)) {
5098
5872
  console.log(`- ${tool.name || tool.action_name || tool.id}: ${tool.description || tool.type || "action"}`);
5099
5873
  }
5100
- hint(`signaliz gtm nango call <action_name> --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' --dry-run`);
5874
+ hint(`${commandPrefix} call <action_name> --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' --dry-run`);
5875
+ });
5876
+ }
5877
+ if (namespace === "ops" && (sub === "proof" || sub === "audit" || sub === "read-write")) {
5878
+ const result = await nango.proveNangoReadWrite({
5879
+ ...nangoBaseOptions(),
5880
+ limit: numberFlag("limit"),
5881
+ includeRaw: hasFlag("include-raw"),
5882
+ readProxyPath: flagArg("read-proxy-path") || flagArg("proxy-path"),
5883
+ writeProxyPath: flagArg("write-proxy-path"),
5884
+ method: flagArg("method") || flagArg("http-method"),
5885
+ executeReadProbe: hasFlag("execute-read-probe"),
5886
+ executeWriteProbe: hasFlag("execute-write-probe"),
5887
+ confirm: hasFlag("confirm"),
5888
+ confirmWrite: hasFlag("confirm-write") || hasFlag("confirm"),
5889
+ input: nangoActionInput(),
5890
+ writeInput: readInlineJsonFlag("write-input-json") || readJsonFlag("write-input-file")
5891
+ });
5892
+ return output(result, () => {
5893
+ const summary = result?.summary || {};
5894
+ const connectedRows = summary.connected_connection_rows ?? summary.connection_rows ?? 0;
5895
+ const connectedUnique = summary.connected_unique_connections ?? summary.unique_connections ?? 0;
5896
+ console.log(`Nango proof: ${result?.status || "unknown"}`);
5897
+ console.log(`Connections: ${summary.connection_rows ?? 0} rows / ${summary.unique_connections ?? 0} unique`);
5898
+ console.log(`Connected: ${connectedRows} rows / ${connectedUnique} unique`);
5899
+ console.log(`Reconnect required: ${summary.reconnect_required ?? summary.needs_attention_connection_rows ?? 0}`);
5900
+ console.log(`Action tools: ${summary.action_tools ?? 0}`);
5901
+ console.log(`Runtime reads: ${summary.runtime_read_verified ?? 0}/${connectedRows}`);
5902
+ console.log(`Writes: ${summary.write_verified ?? 0}/${connectedRows}`);
5903
+ if (summary.duplicate_connection_rows) console.log(`Duplicates: ${summary.duplicate_connection_rows}`);
5904
+ if (summary.duplicate_connected_connection_rows) console.log(`Connected duplicates: ${summary.duplicate_connected_connection_rows}`);
5905
+ if (result?.next_action) console.log(`Next: ${result.next_action}`);
5906
+ });
5907
+ }
5908
+ if (namespace === "ops" && (sub === "schedule" || sub === "recur")) {
5909
+ const promptText = promptArg(rest);
5910
+ if (!promptText) {
5911
+ die(`Usage: ${commandPrefix} ${sub} "plain-English recurring goal" --workspace-connection-id <id> --action-name <action> [--confirm-spend] [--wait]`);
5912
+ }
5913
+ const scheduleInput = {
5914
+ prompt: promptText,
5915
+ blueprint: flagArg("blueprint"),
5916
+ targetCount: numberFlag("target-count"),
5917
+ cadence: flagArg("cadence") || "daily",
5918
+ wakeOnEvents: csvFlag("wake-on-events") || csvFlag("wake-events") || csvFlag("wake-event"),
5919
+ timezone: flagArg("timezone"),
5920
+ companyDomains: csvFlag("company-domains") || csvFlag("domains"),
5921
+ customAiPrompt: flagArg("ai-prompt") || flagArg("custom-ai-prompt"),
5922
+ signalPrompt: flagArg("signal-prompt"),
5923
+ outputPrompt: flagArg("output-prompt") || flagArg("result-prompt"),
5924
+ confirmSpend: hasFlag("confirm-spend"),
5925
+ ...nangoBaseOptions(),
5926
+ actionName: flagArg("action-name") || flagArg("action") || flagArg("nango-action"),
5927
+ toolName: flagArg("tool-name"),
5928
+ proxyPath: flagArg("proxy-path") || flagArg("nango-proxy-path"),
5929
+ method: flagArg("method") || flagArg("http-method"),
5930
+ input: nangoActionInput(),
5931
+ fieldMap: readInlineJsonFlag("field-map-json") || readJsonFlag("field-map-file"),
5932
+ requiredFields: csvFlag("required-fields") || csvFlag("required-field"),
5933
+ writeConfirmed: hasFlag("write-confirmed") || hasFlag("confirm") || hasFlag("confirm-write") || void 0,
5934
+ agentWriteConfirmed: hasFlag("agent-write-confirmed") || void 0
5935
+ };
5936
+ if (hasFlag("wait")) {
5937
+ const combined = await nango.scheduleNangoActionAndWait({
5938
+ ...scheduleInput,
5939
+ force: hasFlag("force"),
5940
+ instruction: flagArg("instruction"),
5941
+ intervalMs: numberFlag("interval-ms") ?? numberFlag("wait-interval-ms"),
5942
+ maxPolls: numberFlag("max-polls") ?? numberFlag("wait-max-polls"),
5943
+ timeoutMs: numberFlag("timeout-ms") ?? numberFlag("wait-timeout-ms"),
5944
+ limit: numberFlag("limit"),
5945
+ cursor: flagArg("cursor"),
5946
+ includeFailedRuns: hasFlag("include-failed-runs"),
5947
+ includeResults: !hasFlag("no-results")
5948
+ });
5949
+ return output(combined, () => {
5950
+ console.log(`Nango Op scheduled: ${combined.schedule?.op_id || combined.op_id}`);
5951
+ printOpsWaitForResults(combined);
5952
+ });
5953
+ }
5954
+ const result = await nango.scheduleNangoAction(scheduleInput);
5955
+ return output(result, () => {
5956
+ if (result.approval_required || result.error_code === "APPROVAL_REQUIRED") {
5957
+ console.log("Approval required");
5958
+ if (result.plan) {
5959
+ console.log(`Plan: ${result.plan.title || result.plan.plan_id || "pending approval"}`);
5960
+ if (result.plan.ui_summary) console.log(`Summary: ${result.plan.ui_summary}`);
5961
+ printPlanSchedule(result.plan);
5962
+ printOpsExecutionContract(result.plan);
5963
+ }
5964
+ console.log(`Next: ${result.next_action || "Retry with --confirm-spend after review."}`);
5965
+ hint(`${commandPrefix} ${sub} ${JSON.stringify(promptText)} --confirm-spend`);
5966
+ return;
5967
+ }
5968
+ console.log(`Nango Op scheduled: ${result.op_id}`);
5969
+ console.log(`Status: ${result.status || "scheduled"}`);
5970
+ if (result.plan) printOpsExecutionContract(result.plan);
5971
+ console.log(`Next: ${result.next_action || "Run or wait for the next scheduled tick."}`);
5972
+ if (result.op_id) hint(`signaliz ops wait ${result.op_id}`);
5101
5973
  });
5102
5974
  }
5103
5975
  if (sub === "call" || sub === "run") {
5104
- const actionName = rest[0] || flagArg("action-name");
5976
+ const actionName = nangoPositionalValues(rest)[0] || flagArg("action-name");
5105
5977
  const toolName = flagArg("tool-name");
5106
- if (!actionName && !toolName) {
5107
- die(`Usage: signaliz gtm nango call <action_name> --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' [--execute --confirm]`);
5978
+ const proxyPath = flagArg("proxy-path") || flagArg("nango-proxy-path");
5979
+ if (!actionName && !toolName && !proxyPath) {
5980
+ die(`Usage: ${commandPrefix} call <action_name> --workspace-connection-id <id> --input-json '{"email":"buyer@example.com"}' [--execute --confirm], or ${commandPrefix} call --workspace-connection-id <id> --proxy-path /provider/path --method GET`);
5108
5981
  }
5109
- const result = await sdk.gtm.callNangoTool({
5982
+ const callOptions = {
5110
5983
  ...nangoBaseOptions(),
5111
5984
  actionName,
5112
5985
  toolName,
5986
+ deliveryMode: proxyPath ? "proxy" : void 0,
5987
+ proxyPath,
5988
+ method: flagArg("method") || flagArg("http-method"),
5113
5989
  input: nangoActionInput(),
5114
- async: hasFlag("async") || void 0,
5990
+ async: hasFlag("async") || hasFlag("wait") || void 0,
5115
5991
  maxRetries: numberFlag("max-retries"),
5116
5992
  dryRun: hasFlag("execute") ? false : hasFlag("dry-run") ? true : void 0,
5117
5993
  confirm: hasFlag("confirm") || void 0,
5118
5994
  confirmWrite: hasFlag("confirm-write") || void 0
5119
- });
5120
- return output(result, () => {
5995
+ };
5996
+ const waitOptions = {
5997
+ intervalMs: numberFlag("interval-ms") ?? numberFlag("wait-interval-ms"),
5998
+ maxPolls: numberFlag("max-polls") ?? numberFlag("wait-max-polls"),
5999
+ timeoutMs: numberFlag("timeout-ms") ?? numberFlag("wait-timeout-ms")
6000
+ };
6001
+ const waited = hasFlag("wait") && namespace === "ops" ? await nango.callNangoToolAndWait({ ...callOptions, ...waitOptions }) : null;
6002
+ const result = waited?.call || await nango.callNangoTool(callOptions);
6003
+ const finalResult = waited || (hasFlag("wait") ? await waitForNangoActionResult(nango, result, waitOptions) : result);
6004
+ return output(finalResult, () => {
5121
6005
  console.log(`Nango action ${result?.status || "submitted"}: ${actionName || toolName}`);
5122
6006
  const statusUrl = nangoStatusUrl(result);
5123
6007
  if (statusUrl) console.log(` Status URL: ${statusUrl}`);
6008
+ if (finalResult !== result) {
6009
+ console.log(` Result: ${finalResult.status || "unknown"} (${finalResult.polls}/${finalResult.max_polls} polls)`);
6010
+ if (finalResult.timed_out) console.log(" Timed out before a terminal Nango result.");
6011
+ }
5124
6012
  if (result?.next_action) console.log(` Next: ${result.next_action}`);
5125
- if (statusUrl) hint(`signaliz gtm nango result --status-url ${statusUrl}`);
6013
+ if (statusUrl && finalResult === result) hint(`${commandPrefix} result --status-url ${statusUrl}`);
5126
6014
  });
5127
6015
  }
5128
6016
  if (sub === "result" || sub === "status") {
5129
- const actionId = rest[0] || flagArg("action-id");
6017
+ const actionId = nangoPositionalValues(rest)[0] || flagArg("action-id");
5130
6018
  const statusUrl = flagArg("status-url");
5131
- if (!actionId && !statusUrl) die("Usage: signaliz gtm nango result --action-id <id> OR --status-url /action/<id>");
5132
- const result = await sdk.gtm.getNangoActionResult({ actionId, statusUrl });
6019
+ if (!actionId && !statusUrl) die(`Usage: ${commandPrefix} result --action-id <id> OR --status-url /action/<id>`);
6020
+ const result = await nango.getNangoActionResult({ actionId, statusUrl });
5133
6021
  return output(result, () => {
5134
6022
  console.log(`Nango action result: ${result?.status || "unknown"}`);
5135
6023
  if (result?.action_id || actionId) console.log(` Action ID: ${result?.action_id || actionId}`);
@@ -5137,7 +6025,7 @@ async function gtmNango(sub, rest) {
5137
6025
  if (nextStatusUrl) console.log(` Status URL: ${nextStatusUrl}`);
5138
6026
  });
5139
6027
  }
5140
- die("Usage: signaliz gtm nango <connect|tools|call|result>");
6028
+ die(`Usage: ${commandPrefix} <flow|search|connect|tools|call|schedule|result>`);
5141
6029
  }
5142
6030
  async function email(sub, rest) {
5143
6031
  const sdk = getSdk();
@@ -5342,26 +6230,74 @@ async function ops(sub, rest) {
5342
6230
  const savedRest = sub === "save" ? rest : rest.slice(1);
5343
6231
  return opsSaved(savedSub, savedRest);
5344
6232
  }
6233
+ if (sub === "nango") return gtmNango(rest[0], rest.slice(1), "ops");
5345
6234
  if (sub === "plan" && !promptArg(rest)) {
5346
6235
  die('Usage: signaliz ops plan "monitor these companies daily..."');
5347
6236
  }
5348
6237
  if (sub === "create" && !promptArg(rest)) {
5349
6238
  die('Usage: signaliz ops create "build 100 leads that fit..." [--confirm-spend]');
5350
6239
  }
6240
+ if ((sub === "schedule" || sub === "recur") && !promptArg(rest)) {
6241
+ die('Usage: signaliz ops schedule "monitor these companies daily..." [--confirm-spend]');
6242
+ }
5351
6243
  if (sub === "execute" && !promptArg(rest)) {
5352
6244
  die('Usage: signaliz ops execute "launch a campaign for this ICP..." [--dry-run|--confirm-spend]');
5353
6245
  }
5354
6246
  const sdk = getSdk();
6247
+ const opsOptionalBooleanFlag = (name) => {
6248
+ const value = flagArg(name);
6249
+ if (value !== void 0) return !["0", "false", "no", "off"].includes(value.toLowerCase());
6250
+ return hasFlag(name) ? true : void 0;
6251
+ };
6252
+ const opsNangoDestination = () => {
6253
+ const destination = { type: "nango" };
6254
+ let hasDetails = false;
6255
+ const set = (key, value) => {
6256
+ if (value === void 0 || value === null || value === "") return;
6257
+ destination[key] = value;
6258
+ hasDetails = true;
6259
+ };
6260
+ const nangoAction = flagArg("nango-action");
6261
+ const nangoProxyPath = flagArg("nango-proxy-path");
6262
+ set("connectionId", flagArg("workspace-connection-id") || flagArg("connection-id"));
6263
+ set("providerConfigKey", flagArg("provider-config-key"));
6264
+ set("integrationId", flagArg("integration-id"));
6265
+ set("nangoConnectionId", flagArg("nango-connection-id"));
6266
+ set("actionName", flagArg("action-name") || nangoAction);
6267
+ set("nangoAction", nangoAction);
6268
+ set("proxyPath", flagArg("proxy-path") || nangoProxyPath);
6269
+ set("nangoProxyPath", nangoProxyPath);
6270
+ set("method", flagArg("method") || flagArg("http-method"));
6271
+ set("writeConfirmed", opsOptionalBooleanFlag("write-confirmed"));
6272
+ set("agentWriteConfirmed", opsOptionalBooleanFlag("agent-write-confirmed"));
6273
+ return hasDetails ? destination : void 0;
6274
+ };
6275
+ const isNangoDestinationType = (type) => {
6276
+ const normalized = type.toLowerCase().replace(/[-\s]/g, "_");
6277
+ return ["nango", "api", "managed_api", "customer_api", "external_api", "nango_api"].includes(normalized);
6278
+ };
5355
6279
  const opsDestinations = () => {
5356
6280
  const destinations = csvFlag("destinations") || csvFlag("destination");
5357
- return destinations?.map((type) => ({ type }));
6281
+ const nangoDestination = opsNangoDestination();
6282
+ const out = (destinations || []).map((type) => nangoDestination && isNangoDestinationType(type) ? { ...nangoDestination, type } : { type });
6283
+ if (nangoDestination && !out.some((destination) => isNangoDestinationType(String(destination.type)))) {
6284
+ out.push(nangoDestination);
6285
+ }
6286
+ return out.length > 0 ? out : void 0;
5358
6287
  };
6288
+ const opsWakeEvents = () => csvFlag("wake-on-events") || csvFlag("wake-events") || csvFlag("wake-event");
5359
6289
  const opsCompanyDomains = () => csvFlag("company-domains") || csvFlag("domains");
5360
6290
  const opsCustomPrompts = () => ({
5361
6291
  custom_ai_prompt: flagArg("ai-prompt") || flagArg("custom-ai-prompt"),
5362
6292
  signal_prompt: flagArg("signal-prompt"),
5363
6293
  output_prompt: flagArg("output-prompt") || flagArg("result-prompt")
5364
6294
  });
6295
+ const printPlanSchedule2 = (plan) => {
6296
+ if (!plan || typeof plan !== "object") return;
6297
+ const wakeEvents = Array.isArray(plan.wake_on_events) ? plan.wake_on_events : plan.wakeOnEvents;
6298
+ console.log(`Schedule: ${plan.cadence || "manual"}`);
6299
+ console.log(`Wake: ${Array.isArray(wakeEvents) && wakeEvents.length ? wakeEvents.join(", ") : "cadence only"}`);
6300
+ };
5365
6301
  if (sub === "plan") {
5366
6302
  const promptText = promptArg(rest);
5367
6303
  const result = await sdk.ops.plan({
@@ -5369,6 +6305,7 @@ async function ops(sub, rest) {
5369
6305
  blueprint: flagArg("blueprint"),
5370
6306
  target_count: numberFlag("target-count"),
5371
6307
  cadence: flagArg("cadence"),
6308
+ wake_on_events: opsWakeEvents(),
5372
6309
  destinations: opsDestinations(),
5373
6310
  company_domains: opsCompanyDomains(),
5374
6311
  ...opsCustomPrompts()
@@ -5378,10 +6315,11 @@ async function ops(sub, rest) {
5378
6315
  if (result.ui_summary) console.log(`Summary: ${result.ui_summary}`);
5379
6316
  console.log(`Status: ${result.status}`);
5380
6317
  console.log(`Outcome: ${result.blueprint}`);
5381
- console.log(`Cadence: ${result.cadence}`);
6318
+ printPlanSchedule2(result);
5382
6319
  console.log(`Target: ${result.target_count}`);
5383
6320
  console.log(`Credits: ${result.estimated_credits}`);
5384
6321
  if (result.destination_status) console.log(`Deliver: ${result.destination_status}`);
6322
+ printOpsExecutionContract(result);
5385
6323
  printPrimitiveGraph(result.primitive_graph);
5386
6324
  console.log(`Next: ${result.next_action}`);
5387
6325
  });
@@ -5400,6 +6338,26 @@ async function ops(sub, rest) {
5400
6338
  console.log(`${primary.title}: ${primary.outcome}`);
5401
6339
  console.log(`Prompt: ${primary.prompt}`);
5402
6340
  console.log(`CLI: ${primary.cli_command}`);
6341
+ const packet = primary.operating_packet || primary.operatingPacket || result.operating_packet || result.operatingPacket;
6342
+ if (packet?.schedule) {
6343
+ console.log(`Schedule: ${packet.schedule.cadence} - ${packet.schedule.recurrence}`);
6344
+ console.log(`Results: ${packet.result_contract?.wait_tool || packet.resultContract?.waitTool || packet.result_contract?.retrieve_tool || packet.resultContract?.retrieveTool || "ops_wait"}`);
6345
+ }
6346
+ const nangoRoute = packet?.nango_route || packet?.nangoRoute;
6347
+ if (nangoRoute?.enabled) {
6348
+ console.log("\nNango route");
6349
+ console.log(`- Discover: ${nangoRoute.discover_tool || nangoRoute.discoverTool}`);
6350
+ console.log(`- Dry run: ${nangoRoute.dry_run_tool || nangoRoute.dryRunTool}`);
6351
+ console.log(`- Execute: ${nangoRoute.execute_tool || nangoRoute.executeTool} after confirm=true`);
6352
+ if (nangoRoute.execute_and_wait_tool || nangoRoute.executeAndWaitTool) {
6353
+ console.log(`- Wait: ${nangoRoute.execute_and_wait_tool || nangoRoute.executeAndWaitTool}`);
6354
+ }
6355
+ const nangoScheduleTool = nangoRoute.schedule_and_wait_tool || nangoRoute.scheduleAndWaitTool || nangoRoute.schedule_tool || nangoRoute.scheduleTool;
6356
+ if (nangoScheduleTool) {
6357
+ console.log(`- Schedule: ${nangoScheduleTool} after approval`);
6358
+ }
6359
+ console.log(`- Gate: ${nangoRoute.write_gate || nangoRoute.writeGate}`);
6360
+ }
5403
6361
  if (primary.mcp_sequence?.length) {
5404
6362
  console.log("\nMCP flow");
5405
6363
  for (const [index, step] of primary.mcp_sequence.entries()) {
@@ -5411,22 +6369,49 @@ async function ops(sub, rest) {
5411
6369
  }
5412
6370
  if (sub === "execute") {
5413
6371
  const promptText = promptArg(rest);
6372
+ const shouldWait = hasFlag("wait");
5414
6373
  const result = await sdk.ops.execute({
5415
6374
  prompt: promptText,
5416
6375
  blueprint: flagArg("blueprint"),
5417
6376
  target_count: numberFlag("target-count"),
5418
6377
  cadence: flagArg("cadence"),
6378
+ wake_on_events: opsWakeEvents(),
6379
+ timezone: flagArg("timezone"),
5419
6380
  destinations: opsDestinations(),
5420
6381
  company_domains: opsCompanyDomains(),
5421
6382
  ...opsCustomPrompts(),
5422
6383
  confirm_spend: hasFlag("confirm-spend"),
5423
6384
  dry_run: hasFlag("dry-run"),
5424
- auto_run: hasFlag("auto-run") ? true : hasFlag("no-auto-run") ? false : void 0,
6385
+ auto_run: hasFlag("auto-run") ? true : hasFlag("no-auto-run") ? false : shouldWait ? true : void 0,
5425
6386
  output_format: flagArg("output-format") === "markdown" ? "markdown" : "json"
5426
6387
  });
6388
+ if (shouldWait && result.op_id && !result.approval_required && result.error_code !== "APPROVAL_REQUIRED" && !hasFlag("dry-run")) {
6389
+ const waited = await sdk.ops.waitForResults({
6390
+ op_id: result.op_id,
6391
+ ...opsWaitOptions()
6392
+ });
6393
+ const combined = {
6394
+ ...waited,
6395
+ execute: result,
6396
+ run_id: result.run_id ?? waited.status?.run_id ?? waited.status?.runId,
6397
+ runId: result.runId ?? result.run_id ?? waited.status?.runId ?? waited.status?.run_id,
6398
+ execution_refs: combineExecutionRefs(result.execution_refs, waited.execution_refs)
6399
+ };
6400
+ return output(combined, () => {
6401
+ if (result.run_id || result.runId) console.log(`Run: ${result.run_id || result.runId}`);
6402
+ printOpsWaitForResults(combined);
6403
+ });
6404
+ }
5427
6405
  return output(result, () => {
5428
6406
  if (result.approval_required || result.error_code === "APPROVAL_REQUIRED") {
5429
6407
  console.log("Approval required");
6408
+ if (result.plan) {
6409
+ console.log(`Plan: ${result.plan.title || result.plan.plan_id || "pending approval"}`);
6410
+ if (result.plan.ui_summary) console.log(`Summary: ${result.plan.ui_summary}`);
6411
+ console.log(`Status: ${result.plan.status || result.status || "needs_approval"}`);
6412
+ printPlanSchedule2(result.plan);
6413
+ printOpsExecutionContract(result.plan);
6414
+ }
5430
6415
  if (result.estimated_credits !== void 0) console.log(`Credits: ${result.estimated_credits}`);
5431
6416
  console.log(`Next: ${result.next_action || "Retry with --confirm-spend after approval."}`);
5432
6417
  hint(`signaliz ops execute ${JSON.stringify(promptText)} --confirm-spend`);
@@ -5436,13 +6421,19 @@ async function ops(sub, rest) {
5436
6421
  console.log(`Dry run: ${result.plan.title}`);
5437
6422
  if (result.plan.ui_summary) console.log(`Summary: ${result.plan.ui_summary}`);
5438
6423
  console.log(`Status: ${result.plan.status}`);
6424
+ printPlanSchedule2(result.plan);
5439
6425
  console.log(`Credits: ${result.plan.estimated_credits}`);
6426
+ printOpsExecutionContract(result.plan);
5440
6427
  console.log(`Next: ${result.next_action || result.plan.next_action}`);
5441
6428
  return;
5442
6429
  }
5443
6430
  console.log(`Op: ${result.op_id || "not created"}`);
5444
6431
  if (result.run_id) console.log(`Run: ${result.run_id}`);
5445
6432
  console.log(`Status: ${result.status || "unknown"}`);
6433
+ if (result.plan) {
6434
+ printPlanSchedule2(result.plan);
6435
+ printOpsExecutionContract(result.plan);
6436
+ }
5446
6437
  console.log(`Next: ${result.next_action || "Check status for progress."}`);
5447
6438
  if (result.op_id) hint(`signaliz ops status ${result.op_id}`);
5448
6439
  });
@@ -5505,6 +6496,8 @@ Available: ${result.available_types.join(", ")}`);
5505
6496
  blueprint: flagArg("blueprint"),
5506
6497
  target_count: numberFlag("target-count"),
5507
6498
  cadence: flagArg("cadence"),
6499
+ wake_on_events: opsWakeEvents(),
6500
+ timezone: flagArg("timezone"),
5508
6501
  destinations: opsDestinations(),
5509
6502
  company_domains: opsCompanyDomains(),
5510
6503
  ...opsCustomPrompts(),
@@ -5512,15 +6505,99 @@ Available: ${result.available_types.join(", ")}`);
5512
6505
  activate: hasFlag("activate")
5513
6506
  });
5514
6507
  return output(result, () => {
6508
+ if (result.approval_required || result.error_code === "APPROVAL_REQUIRED") {
6509
+ console.log("Approval required");
6510
+ if (result.plan) {
6511
+ console.log(`Plan: ${result.plan.title || result.plan.plan_id || "pending approval"}`);
6512
+ if (result.plan.ui_summary) console.log(`Summary: ${result.plan.ui_summary}`);
6513
+ console.log(`Status: ${result.plan.status || result.status || "needs_approval"}`);
6514
+ printPlanSchedule2(result.plan);
6515
+ printOpsExecutionContract(result.plan);
6516
+ }
6517
+ if (result.estimated_credits !== void 0) console.log(`Credits: ${result.estimated_credits}`);
6518
+ console.log(`Next: ${result.next_action || "Retry with --confirm-spend after approval."}`);
6519
+ hint(`signaliz ops create ${JSON.stringify(promptText)} --confirm-spend`);
6520
+ return;
6521
+ }
5515
6522
  console.log(`Op created: ${result.op_id}`);
5516
6523
  console.log(`Status: ${result.status}`);
6524
+ if (result.plan) {
6525
+ printPlanSchedule2(result.plan);
6526
+ printOpsExecutionContract(result.plan);
6527
+ }
5517
6528
  console.log(`Next: ${result.next_action}`);
5518
6529
  hint(`signaliz ops run ${result.op_id}`);
5519
6530
  });
5520
6531
  }
6532
+ if (sub === "schedule" || sub === "recur") {
6533
+ const promptText = promptArg(rest);
6534
+ const shouldWait = hasFlag("wait");
6535
+ const scheduleInput = {
6536
+ prompt: promptText,
6537
+ blueprint: flagArg("blueprint"),
6538
+ target_count: numberFlag("target-count"),
6539
+ cadence: flagArg("cadence") || "daily",
6540
+ wake_on_events: opsWakeEvents(),
6541
+ timezone: flagArg("timezone"),
6542
+ destinations: opsDestinations(),
6543
+ company_domains: opsCompanyDomains(),
6544
+ ...opsCustomPrompts(),
6545
+ confirm_spend: hasFlag("confirm-spend")
6546
+ };
6547
+ if (shouldWait) {
6548
+ const combined = await sdk.ops.scheduleAndWait({
6549
+ ...scheduleInput,
6550
+ force: hasFlag("force"),
6551
+ instruction: flagArg("instruction"),
6552
+ ...opsWaitOptions()
6553
+ });
6554
+ return output(combined, () => {
6555
+ console.log(`Op scheduled: ${combined.schedule?.op_id || combined.op_id}`);
6556
+ printOpsWaitForResults(combined);
6557
+ });
6558
+ }
6559
+ const result = await sdk.ops.schedule(scheduleInput);
6560
+ return output(result, () => {
6561
+ if (result.approval_required || result.error_code === "APPROVAL_REQUIRED") {
6562
+ console.log("Approval required");
6563
+ if (result.plan) {
6564
+ console.log(`Plan: ${result.plan.title || result.plan.plan_id || "pending approval"}`);
6565
+ if (result.plan.ui_summary) console.log(`Summary: ${result.plan.ui_summary}`);
6566
+ console.log(`Status: ${result.plan.status || result.status || "needs_approval"}`);
6567
+ printPlanSchedule2(result.plan);
6568
+ printOpsExecutionContract(result.plan);
6569
+ }
6570
+ if (result.estimated_credits !== void 0) console.log(`Credits: ${result.estimated_credits}`);
6571
+ console.log(`Next: ${result.next_action || "Retry with --confirm-spend after approval."}`);
6572
+ hint(`signaliz ops schedule ${JSON.stringify(promptText)} --confirm-spend`);
6573
+ return;
6574
+ }
6575
+ console.log(`Op scheduled: ${result.op_id}`);
6576
+ if (result.run_id) console.log(`Run: ${result.run_id}`);
6577
+ console.log(`Status: ${result.status}`);
6578
+ if (result.plan) {
6579
+ printPlanSchedule2(result.plan);
6580
+ printOpsExecutionContract(result.plan);
6581
+ }
6582
+ console.log(`Next: ${result.next_action}`);
6583
+ if (result.op_id) hint(`signaliz ops run ${result.op_id} --wait --limit 100`);
6584
+ });
6585
+ }
5521
6586
  if (sub === "run") {
5522
6587
  const opId = rest[0];
5523
6588
  if (!opId) die("Usage: signaliz ops run <op_id>");
6589
+ if (hasFlag("wait")) {
6590
+ const result2 = await sdk.ops.runAndWait({
6591
+ op_id: opId,
6592
+ force: hasFlag("force"),
6593
+ instruction: flagArg("instruction"),
6594
+ ...opsWaitOptions()
6595
+ });
6596
+ return output(result2, () => {
6597
+ if (result2.run?.run_id || result2.run?.runId) console.log(`Run: ${result2.run.run_id || result2.run.runId}`);
6598
+ printOpsWaitForResults(result2);
6599
+ });
6600
+ }
5524
6601
  const result = await sdk.ops.run({ op_id: opId, force: hasFlag("force"), instruction: flagArg("instruction") });
5525
6602
  return output(result, () => {
5526
6603
  console.log(`Op running: ${result.op_id}`);
@@ -5553,14 +6630,38 @@ Available: ${result.available_types.join(", ")}`);
5553
6630
  include_failed_runs: hasFlag("include-failed-runs")
5554
6631
  });
5555
6632
  return output(result, () => {
5556
- const hasMore = result.has_more || result.hasMore;
5557
- const nextCursor = result.next_cursor ?? result.nextCursor;
5558
- console.log(`Results: ${result.results_count}${hasMore ? " (more available)" : ""}`);
5559
- for (const row of result.results.slice(0, 10)) console.log(JSON.stringify(row));
6633
+ const summary = result.summary || {};
6634
+ const hasMore = summary.has_more ?? summary.hasMore ?? result.has_more ?? result.hasMore;
6635
+ const nextCursor = summary.next_cursor ?? summary.nextCursor ?? result.next_cursor ?? result.nextCursor;
6636
+ const resultsCount = summary.results_count ?? summary.resultsCount ?? result.results_count ?? result.resultsCount ?? 0;
6637
+ const resultsTotal = summary.results_total ?? summary.resultsTotal ?? result.results_total ?? result.resultsTotal;
6638
+ const deliveryStatus = summary.delivery_status ?? summary.deliveryStatus ?? result.delivery_status ?? result.deliveryStatus;
6639
+ const resultsUrl = summary.results_url ?? summary.resultsUrl ?? result.results_url ?? result.resultsUrl;
6640
+ const nextAction = summary.next_action ?? summary.nextAction ?? result.next_action ?? result.nextAction;
6641
+ console.log(`Results: ${resultsCount}${hasMore ? " (more available)" : ""}`);
6642
+ if (summary.label) console.log(`Summary: ${summary.label}`);
6643
+ console.log(`Status: ${summary.status || result.status || "unknown"} (${summary.phase || result.phase || "unknown"})`);
6644
+ if (resultsTotal !== void 0 && resultsTotal !== null) console.log(`Total: ${resultsTotal}`);
6645
+ if (deliveryStatus) console.log(`Deliver: ${deliveryStatus}`);
6646
+ if (resultsUrl) console.log(`Open: ${resultsUrl}`);
6647
+ if (nextAction) console.log(`Next: ${nextAction}`);
6648
+ if (result.results.length) {
6649
+ console.log("\nRows");
6650
+ for (const row of result.results.slice(0, 10)) console.log(JSON.stringify(row));
6651
+ }
5560
6652
  if (hasMore && nextCursor) hint(`signaliz ops results ${opId} --cursor ${nextCursor}`);
5561
6653
  printExecutionRefs(result.execution_refs);
5562
6654
  });
5563
6655
  }
6656
+ if (sub === "wait" || sub === "await") {
6657
+ const opId = rest[0] || flagArg("op-id");
6658
+ if (!opId) die("Usage: signaliz ops wait <op_id>");
6659
+ const result = await sdk.ops.waitForResults({
6660
+ op_id: opId,
6661
+ ...opsWaitOptions()
6662
+ });
6663
+ return output(result, () => printOpsWaitForResults(result));
6664
+ }
5564
6665
  if (sub === "run-status" || sub === "trigger-status" || sub === "watch") {
5565
6666
  const runIds = csvFlag("run-ids");
5566
6667
  const runId = rest[0] || flagArg("run-id");
@@ -5889,13 +6990,15 @@ Available: ${result.available_types.join(", ")}`);
5889
6990
  const result = await sdk.ops.attachSinkToRoutine({ routine_id: routineId, sink_id: sinkId });
5890
6991
  return output(result);
5891
6992
  }
5892
- die("Usage: signaliz ops <proof|autopilot|plan|execute|quickstart|create|run|status|results|run-status|queue|logs|replay|dashboard|doctor|connections|sinks|routines|routine|attach-sink|saved>");
6993
+ die("Usage: signaliz ops <proof|autopilot|plan|execute|quickstart|create|schedule|run|status|results|wait|nango|run-status|queue|logs|replay|dashboard|doctor|connections|sinks|routines|routine|attach-sink|saved>");
5893
6994
  }
5894
6995
  var OPS_SHORTCUTS = {
5895
6996
  "/plan": "plan",
5896
6997
  plan: "plan",
5897
6998
  "/goal": "create",
5898
6999
  goal: "create",
7000
+ schedule: "schedule",
7001
+ recur: "schedule",
5899
7002
  quickstart: "quickstart",
5900
7003
  template: "template",
5901
7004
  proof: "proof",
@@ -5904,6 +7007,7 @@ var OPS_SHORTCUTS = {
5904
7007
  run: "run",
5905
7008
  status: "status",
5906
7009
  results: "results",
7010
+ wait: "wait",
5907
7011
  queue: "queue",
5908
7012
  debug: "debug",
5909
7013
  watch: "watch",