struere 0.14.0 → 0.14.1

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.
@@ -2549,6 +2549,22 @@ import chalk3 from "chalk";
2549
2549
  function isInteractive() {
2550
2550
  return process.stdout.isTTY === true && !process.env.CI && !getApiKey();
2551
2551
  }
2552
+ function createSilentOutput() {
2553
+ return {
2554
+ start() {},
2555
+ succeed() {},
2556
+ fail() {},
2557
+ stop() {},
2558
+ info() {},
2559
+ warn() {},
2560
+ error(msg) {
2561
+ console.error(msg);
2562
+ },
2563
+ json(data) {
2564
+ console.log(JSON.stringify(data, null, 2));
2565
+ }
2566
+ };
2567
+ }
2552
2568
  function createOutput() {
2553
2569
  if (isInteractive()) {
2554
2570
  const spinner = ora3();
@@ -2658,15 +2674,15 @@ orgCommand.command("list").description("List your organizations").option("--json
2658
2674
  await refreshToken();
2659
2675
  const fresh = loadCredentials();
2660
2676
  const token = fresh?.token || credentials.token;
2661
- const spinner = ora4();
2662
- spinner.start("Fetching organizations");
2677
+ const spinner = options.json ? null : ora4();
2678
+ spinner?.start("Fetching organizations");
2663
2679
  const { organizations, error } = await listMyOrganizations(token);
2664
2680
  if (error) {
2665
- spinner.fail("Failed to fetch organizations");
2681
+ spinner?.fail("Failed to fetch organizations");
2666
2682
  console.log(chalk4.red(error));
2667
2683
  process.exit(1);
2668
2684
  }
2669
- spinner.stop();
2685
+ spinner?.stop();
2670
2686
  if (options.json) {
2671
2687
  console.log(JSON.stringify(organizations, null, 2));
2672
2688
  return;
@@ -2706,15 +2722,15 @@ orgCommand.command("create").argument("[name]", "Organization name").description
2706
2722
  }
2707
2723
  name = name.trim();
2708
2724
  const slug = options.slug || slugify(name);
2709
- const spinner = ora4();
2710
- spinner.start("Creating organization");
2725
+ const spinner = options.json ? null : ora4();
2726
+ spinner?.start("Creating organization");
2711
2727
  const { organization, error } = await createOrganization(token, name, slug);
2712
2728
  if (error || !organization) {
2713
- spinner.fail("Failed to create organization");
2729
+ spinner?.fail("Failed to create organization");
2714
2730
  console.log(chalk4.red(error || "Unknown error"));
2715
2731
  process.exit(1);
2716
2732
  }
2717
- spinner.stop();
2733
+ spinner?.stop();
2718
2734
  if (options.json) {
2719
2735
  console.log(JSON.stringify(organization, null, 2));
2720
2736
  return;
@@ -3110,6 +3126,11 @@ function extractAgentPayload(agent, customToolsMap) {
3110
3126
  const tools = (agent.tools || []).map((toolName) => {
3111
3127
  const isBuiltin = BUILTIN_TOOLS.includes(toolName);
3112
3128
  if (!isBuiltin && !customToolsMap.has(toolName)) {
3129
+ const builtinPrefixes = ["entity.", "calendar.", "whatsapp.", "agent.", "airtable.", "email.", "payment.", "web.", "voice.", "router."];
3130
+ const looksBuiltin = builtinPrefixes.some((prefix) => toolName.startsWith(prefix));
3131
+ if (looksBuiltin) {
3132
+ throw new Error(`Agent "${agent.name}" references tool "${toolName}" which looks like a built-in tool but is not recognized. Try updating the CLI: npm install -g struere@latest`);
3133
+ }
3113
3134
  const available = customToolsMap.size > 0 ? `Available custom tools: ${Array.from(customToolsMap.keys()).join(", ")}` : "No custom tools were loaded from tools/index.ts";
3114
3135
  throw new Error(`Agent "${agent.name}" references tool "${toolName}" but it was not found. ${available}`);
3115
3136
  }
@@ -3481,8 +3502,42 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
3481
3502
  output.start("Syncing to Convex");
3482
3503
  try {
3483
3504
  const result = await syncToEnvironment(cwd, project.organization.id, environment);
3484
- if (!jsonMode)
3505
+ if (!jsonMode) {
3485
3506
  output.succeed(`Synced to ${environment}`);
3507
+ console.log();
3508
+ const types = [
3509
+ { label: "agent", data: result.agents },
3510
+ { label: "entity type", data: result.entityTypes },
3511
+ { label: "role", data: result.roles },
3512
+ { label: "trigger", data: result.triggers },
3513
+ { label: "router", data: result.routers },
3514
+ { label: "eval suite", data: result.evalSuites }
3515
+ ];
3516
+ let hasChanges = false;
3517
+ for (const { label, data } of types) {
3518
+ if (!data)
3519
+ continue;
3520
+ for (const name of data.created) {
3521
+ console.log(chalk6.green(` + Created ${label}: ${name}`));
3522
+ hasChanges = true;
3523
+ }
3524
+ for (const name of data.updated) {
3525
+ console.log(chalk6.blue(` ~ Updated ${label}: ${name}`));
3526
+ hasChanges = true;
3527
+ }
3528
+ for (const name of data.deleted) {
3529
+ console.log(chalk6.red(` - Deleted ${label}: ${name}`));
3530
+ hasChanges = true;
3531
+ }
3532
+ }
3533
+ if (!hasChanges) {
3534
+ console.log(chalk6.gray(" No changes detected"));
3535
+ }
3536
+ if (result.evalSuites?.skipped && result.evalSuites.skipped.length > 0) {
3537
+ console.log(chalk6.yellow(` Skipped eval suites (agent not found): ${result.evalSuites.skipped.join(", ")}`));
3538
+ }
3539
+ console.log();
3540
+ }
3486
3541
  if (jsonMode) {
3487
3542
  console.log(JSON.stringify({
3488
3543
  success: true,
@@ -5338,17 +5393,17 @@ function flattenEntityForTable(entity) {
5338
5393
  var entitiesCommand = new Command13("data").description("Manage data records");
5339
5394
  entitiesCommand.command("types").description("List available data types").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (opts) => {
5340
5395
  await ensureAuth();
5341
- const spinner = ora11();
5396
+ const spinner = opts.json ? null : ora11();
5342
5397
  const env = opts.env;
5343
5398
  const orgId = getOrgId();
5344
- spinner.start("Fetching data types");
5399
+ spinner?.start("Fetching data types");
5345
5400
  const { data, error } = await queryEntityTypes(env, orgId);
5346
5401
  if (error || !data) {
5347
- spinner.fail("Failed to fetch data types");
5402
+ spinner?.fail("Failed to fetch data types");
5348
5403
  console.log(chalk15.red("Error:"), error);
5349
5404
  process.exit(1);
5350
5405
  }
5351
- spinner.succeed("Data types loaded");
5406
+ spinner?.succeed("Data types loaded");
5352
5407
  const types = data;
5353
5408
  if (opts.json) {
5354
5409
  console.log(JSON.stringify(types, null, 2));
@@ -5373,10 +5428,10 @@ entitiesCommand.command("types").description("List available data types").option
5373
5428
  });
5374
5429
  entitiesCommand.command("list <type>").description("List records of a type").option("--env <environment>", "Environment (development|production)", "development").option("--status <status>", "Filter by status").option("--limit <n>", "Maximum results", "50").option("--json", "Output raw JSON").action(async (type, opts) => {
5375
5430
  await ensureAuth();
5376
- const spinner = ora11();
5431
+ const spinner = opts.json ? null : ora11();
5377
5432
  const env = opts.env;
5378
5433
  const orgId = getOrgId();
5379
- spinner.start(`Fetching ${type} records`);
5434
+ spinner?.start(`Fetching ${type} records`);
5380
5435
  const [entitiesResult, typeResult] = await Promise.all([
5381
5436
  queryEntities(type, env, {
5382
5437
  status: opts.status,
@@ -5385,12 +5440,12 @@ entitiesCommand.command("list <type>").description("List records of a type").opt
5385
5440
  queryEntityTypeBySlug(type, env, orgId)
5386
5441
  ]);
5387
5442
  if (entitiesResult.error || !entitiesResult.data) {
5388
- spinner.fail(`Failed to fetch ${type} records`);
5443
+ spinner?.fail(`Failed to fetch ${type} records`);
5389
5444
  console.log(chalk15.red("Error:"), entitiesResult.error);
5390
5445
  process.exit(1);
5391
5446
  }
5392
5447
  const entities = entitiesResult.data;
5393
- spinner.succeed(`Found ${entities.length} ${type} records`);
5448
+ spinner?.succeed(`Found ${entities.length} ${type} records`);
5394
5449
  if (opts.json) {
5395
5450
  console.log(JSON.stringify(entities, null, 2));
5396
5451
  return;
@@ -5405,25 +5460,26 @@ entitiesCommand.command("list <type>").description("List records of a type").opt
5405
5460
  });
5406
5461
  entitiesCommand.command("get <id>").description("Get record details").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (rawId, opts) => {
5407
5462
  await ensureAuth();
5408
- const spinner = ora11();
5463
+ const spinner = opts.json ? null : ora11();
5409
5464
  const env = opts.env;
5410
5465
  const orgId = getOrgId();
5411
- spinner.start("Resolving record ID");
5466
+ spinner?.start("Resolving record ID");
5412
5467
  const resolved = await resolveEntityId(rawId, env, orgId);
5413
5468
  if (resolved.error || !resolved.data) {
5414
- spinner.fail("Record not found");
5469
+ spinner?.fail("Record not found");
5415
5470
  console.log(chalk15.red("Error:"), resolved.error || `No record matched "${rawId}"`);
5416
5471
  process.exit(1);
5417
5472
  }
5418
5473
  const id = resolved.data;
5419
- spinner.text = "Fetching record";
5474
+ if (spinner)
5475
+ spinner.text = "Fetching record";
5420
5476
  const { data, error } = await queryEntity(id, env, orgId);
5421
5477
  if (error || !data) {
5422
- spinner.fail("Failed to fetch record");
5478
+ spinner?.fail("Failed to fetch record");
5423
5479
  console.log(chalk15.red("Error:"), error || "Record not found");
5424
5480
  process.exit(1);
5425
5481
  }
5426
- spinner.succeed("Record loaded");
5482
+ spinner?.succeed("Record loaded");
5427
5483
  const result = data;
5428
5484
  if (opts.json) {
5429
5485
  console.log(JSON.stringify(result, null, 2));
@@ -5453,7 +5509,7 @@ entitiesCommand.command("get <id>").description("Get record details").option("--
5453
5509
  });
5454
5510
  entitiesCommand.command("create <type>").description("Create a new record").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Record data as JSON").option("--status <status>", "Initial status").option("--json", "Output raw JSON").action(async (type, opts) => {
5455
5511
  await ensureAuth();
5456
- const spinner = ora11();
5512
+ const spinner = opts.json ? null : ora11();
5457
5513
  const env = opts.env;
5458
5514
  const orgId = getOrgId();
5459
5515
  let data;
@@ -5468,14 +5524,14 @@ entitiesCommand.command("create <type>").description("Create a new record").opti
5468
5524
  console.log(chalk15.red("--data <json> is required in non-interactive mode"));
5469
5525
  process.exit(1);
5470
5526
  } else {
5471
- spinner.start(`Fetching ${type} schema`);
5527
+ spinner?.start(`Fetching ${type} schema`);
5472
5528
  const { data: typeData, error: error2 } = await queryEntityTypeBySlug(type, env, orgId);
5473
5529
  if (error2 || !typeData) {
5474
- spinner.fail(`Data type not found: ${type}`);
5530
+ spinner?.fail(`Data type not found: ${type}`);
5475
5531
  console.log(chalk15.red("Error:"), error2 || "Not found");
5476
5532
  process.exit(1);
5477
5533
  }
5478
- spinner.succeed(`Schema loaded for ${type}`);
5534
+ spinner?.succeed(`Schema loaded for ${type}`);
5479
5535
  console.log();
5480
5536
  const entityType = typeData;
5481
5537
  const schema = entityType.schema;
@@ -5509,14 +5565,14 @@ entitiesCommand.command("create <type>").description("Create a new record").opti
5509
5565
  }
5510
5566
  console.log();
5511
5567
  }
5512
- spinner.start(`Creating ${type} record`);
5568
+ spinner?.start(`Creating ${type} record`);
5513
5569
  const { data: result, error } = await createEntity(type, data, env, opts.status, orgId);
5514
5570
  if (error) {
5515
- spinner.fail("Failed to create record");
5571
+ spinner?.fail("Failed to create record");
5516
5572
  console.log(chalk15.red("Error:"), error);
5517
5573
  process.exit(1);
5518
5574
  }
5519
- spinner.succeed(`Record created`);
5575
+ spinner?.succeed(`Record created`);
5520
5576
  if (opts.json) {
5521
5577
  console.log(JSON.stringify({ id: result }, null, 2));
5522
5578
  } else {
@@ -5527,7 +5583,7 @@ entitiesCommand.command("create <type>").description("Create a new record").opti
5527
5583
  });
5528
5584
  entitiesCommand.command("update <id>").description("Update a record").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Update data as JSON").option("--status <status>", "New status").option("--json", "Output raw JSON").action(async (rawId, opts) => {
5529
5585
  await ensureAuth();
5530
- const spinner = ora11();
5586
+ const spinner = opts.json ? null : ora11();
5531
5587
  const env = opts.env;
5532
5588
  const orgId = getOrgId();
5533
5589
  if (!opts.data && !opts.status) {
@@ -5543,22 +5599,23 @@ entitiesCommand.command("update <id>").description("Update a record").option("--
5543
5599
  process.exit(1);
5544
5600
  }
5545
5601
  }
5546
- spinner.start("Resolving record ID");
5602
+ spinner?.start("Resolving record ID");
5547
5603
  const resolved = await resolveEntityId(rawId, env, orgId);
5548
5604
  if (resolved.error || !resolved.data) {
5549
- spinner.fail("Record not found");
5605
+ spinner?.fail("Record not found");
5550
5606
  console.log(chalk15.red("Error:"), resolved.error || `No record matched "${rawId}"`);
5551
5607
  process.exit(1);
5552
5608
  }
5553
5609
  const id = resolved.data;
5554
- spinner.text = "Updating record";
5610
+ if (spinner)
5611
+ spinner.text = "Updating record";
5555
5612
  const { data: result, error } = await updateEntity(id, data, env, opts.status, orgId);
5556
5613
  if (error) {
5557
- spinner.fail("Failed to update record");
5614
+ spinner?.fail("Failed to update record");
5558
5615
  console.log(chalk15.red("Error:"), error);
5559
5616
  process.exit(1);
5560
5617
  }
5561
- spinner.succeed("Record updated");
5618
+ spinner?.succeed("Record updated");
5562
5619
  if (opts.json) {
5563
5620
  console.log(JSON.stringify(result, null, 2));
5564
5621
  } else {
@@ -5646,21 +5703,21 @@ entitiesCommand.command("delete <id>").description("Delete a record").option("--
5646
5703
  });
5647
5704
  entitiesCommand.command("search <type> <query>").description("Search records").option("--env <environment>", "Environment (development|production)", "development").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (type, query, opts) => {
5648
5705
  await ensureAuth();
5649
- const spinner = ora11();
5706
+ const spinner = opts.json ? null : ora11();
5650
5707
  const env = opts.env;
5651
5708
  const orgId = getOrgId();
5652
- spinner.start(`Searching ${type} for "${query}"`);
5709
+ spinner?.start(`Searching ${type} for "${query}"`);
5653
5710
  const [searchResult, typeResult] = await Promise.all([
5654
5711
  searchEntities(type, query, env, parseInt(opts.limit, 10), orgId),
5655
5712
  queryEntityTypeBySlug(type, env, orgId)
5656
5713
  ]);
5657
5714
  if (searchResult.error || !searchResult.data) {
5658
- spinner.fail("Search failed");
5715
+ spinner?.fail("Search failed");
5659
5716
  console.log(chalk15.red("Error:"), searchResult.error);
5660
5717
  process.exit(1);
5661
5718
  }
5662
5719
  const entities = searchResult.data;
5663
- spinner.succeed(`Found ${entities.length} results`);
5720
+ spinner?.succeed(`Found ${entities.length} results`);
5664
5721
  if (opts.json) {
5665
5722
  console.log(JSON.stringify(entities, null, 2));
5666
5723
  return;
@@ -5839,21 +5896,21 @@ function formatTimestamp(ts) {
5839
5896
  var logsCommand = new Command14("logs").description("View and debug agent conversations");
5840
5897
  logsCommand.command("list", { isDefault: true }).description("List recent conversations").option("--env <environment>", "Environment (development|production|eval)", "development").option("--agent <slug>", "Filter by agent slug").option("--channel <channel>", "Filter by channel (api|whatsapp|widget|dashboard)").option("--phone <number>", "Filter by phone number").option("--limit <n>", "Maximum results", "20").option("--json", "Output raw JSON").action(async (opts) => {
5841
5898
  await ensureAuth2();
5842
- const spinner = ora12();
5899
+ const spinner = opts.json ? null : ora12();
5843
5900
  const orgId = getOrgId2();
5844
5901
  let agentId;
5845
5902
  if (opts.agent) {
5846
- spinner.start(`Resolving agent "${opts.agent}"`);
5903
+ spinner?.start(`Resolving agent "${opts.agent}"`);
5847
5904
  const resolved = await resolveAgentSlug(opts.agent, orgId);
5848
5905
  if (resolved.error || !resolved.data) {
5849
- spinner.fail("Agent not found");
5906
+ spinner?.fail("Agent not found");
5850
5907
  console.log(chalk16.red("Error:"), resolved.error || `No agent matched "${opts.agent}"`);
5851
5908
  process.exit(1);
5852
5909
  }
5853
5910
  agentId = resolved.data;
5854
- spinner.succeed(`Agent resolved: ${opts.agent}`);
5911
+ spinner?.succeed(`Agent resolved: ${opts.agent}`);
5855
5912
  }
5856
- spinner.start("Fetching conversations");
5913
+ spinner?.start("Fetching conversations");
5857
5914
  const { data, error } = await queryThreads({
5858
5915
  environment: opts.env,
5859
5916
  agentId,
@@ -5862,17 +5919,16 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
5862
5919
  limit: parseInt(opts.limit, 10)
5863
5920
  });
5864
5921
  if (error || !data) {
5865
- spinner.fail("Failed to fetch conversations");
5922
+ spinner?.fail("Failed to fetch conversations");
5866
5923
  console.log(chalk16.red("Error:"), error);
5867
5924
  process.exit(1);
5868
5925
  }
5869
5926
  const threads = data;
5870
5927
  if (opts.json) {
5871
- spinner.stop();
5872
5928
  console.log(JSON.stringify(threads, null, 2));
5873
5929
  return;
5874
5930
  }
5875
- spinner.succeed(`Found ${threads.length} conversations`);
5931
+ spinner?.succeed(`Found ${threads.length} conversations`);
5876
5932
  console.log();
5877
5933
  renderTable([
5878
5934
  { key: "id", label: "ID", width: 14 },
@@ -5896,25 +5952,25 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
5896
5952
  });
5897
5953
  logsCommand.command("view <thread-id>").description("View conversation messages").option("--env <environment>", "Environment hint for resolving short IDs").option("--exec", "Include execution details").option("--verbose", "Show full tool call arguments and results").option("--tail", "Show most recent messages (use with --limit)").option("--json", "Output raw JSON").option("--limit <n>", "Message limit", "100").action(async (rawThreadId, opts) => {
5898
5954
  await ensureAuth2();
5899
- const spinner = ora12();
5900
- spinner.start("Resolving thread");
5955
+ const spinner = opts.json ? null : ora12();
5956
+ spinner?.start("Resolving thread");
5901
5957
  const resolved = await resolveThreadId(rawThreadId, opts.env);
5902
5958
  if (resolved.error || !resolved.data) {
5903
- spinner.fail("Thread not found");
5959
+ spinner?.fail("Thread not found");
5904
5960
  console.log(chalk16.red("Error:"), resolved.error || `No thread matched "${rawThreadId}"`);
5905
5961
  process.exit(1);
5906
5962
  }
5907
5963
  const threadId = resolved.data;
5908
- spinner.stop();
5909
- spinner.start("Fetching conversation");
5964
+ spinner?.stop();
5965
+ spinner?.start("Fetching conversation");
5910
5966
  const fetchLimit = opts.tail ? 1000 : parseInt(opts.limit, 10);
5911
5967
  const { data, error } = await queryThreadDetail(threadId, fetchLimit);
5912
5968
  if (error || !data) {
5913
- spinner.fail("Failed to fetch conversation");
5969
+ spinner?.fail("Failed to fetch conversation");
5914
5970
  console.log(chalk16.red("Error:"), error || "Thread not found");
5915
5971
  process.exit(1);
5916
5972
  }
5917
- spinner.stop();
5973
+ spinner?.stop();
5918
5974
  const result = data;
5919
5975
  let executions = [];
5920
5976
  if (opts.exec) {
@@ -6795,7 +6851,7 @@ var templatesCommand = new Command16("templates").description("Manage WhatsApp m
6795
6851
  templatesCommand.command("list").description("List all message templates").option("--env <environment>", "Environment to find connection (development|production|eval)").option("--json", "Output raw JSON").action(async (opts) => {
6796
6852
  await ensureAuth3();
6797
6853
  const connectionId = await resolveConnectionId(opts.env ?? "production");
6798
- const out = createOutput();
6854
+ const out = opts.json ? createSilentOutput() : createOutput();
6799
6855
  out.start("Fetching templates");
6800
6856
  const { data, error } = await listTemplates(connectionId);
6801
6857
  if (error) {
@@ -6857,7 +6913,7 @@ templatesCommand.command("create <name>").description("Create a new message temp
6857
6913
  console.log(chalk18.red("Components must be a JSON array"));
6858
6914
  process.exit(1);
6859
6915
  }
6860
- const out = createOutput();
6916
+ const out = opts.json ? createSilentOutput() : createOutput();
6861
6917
  out.start(`Creating template "${name}"`);
6862
6918
  const { data, error } = await createTemplate(connectionId, name, opts.language, opts.category.toUpperCase(), components, opts.allowCategoryChange);
6863
6919
  const result = data;
@@ -6904,7 +6960,7 @@ templatesCommand.command("delete <name>").description("Delete a message template
6904
6960
  templatesCommand.command("status <name>").description("Check template approval status").option("--env <environment>", "Environment to find connection").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (name, opts) => {
6905
6961
  await ensureAuth3();
6906
6962
  const connectionId = await resolveConnectionId(opts.env ?? "production", opts.connection);
6907
- const out = createOutput();
6963
+ const out = opts.json ? createSilentOutput() : createOutput();
6908
6964
  out.start(`Checking status for "${name}"`);
6909
6965
  const { data, error } = await getTemplateStatus(connectionId, name);
6910
6966
  if (error) {
@@ -6942,7 +6998,7 @@ templatesCommand.command("status <name>").description("Check template approval s
6942
6998
  templatesCommand.command("edit <name>").description("Edit a message template").option("--env <environment>", "Environment to find connection").option("--connection <id>", "WhatsApp connection ID").option("--components <json>", "New components as JSON string").option("--file <path>", "Read components from a JSON file").option("--category <cat>", "New category (UTILITY|MARKETING|AUTHENTICATION)").option("--language <code>", "Language code").option("--json", "Output raw JSON").action(async (name, opts) => {
6943
6999
  await ensureAuth3();
6944
7000
  const connectionId = await resolveConnectionId(opts.env ?? "production", opts.connection);
6945
- const out = createOutput();
7001
+ const out = opts.json ? createSilentOutput() : createOutput();
6946
7002
  out.start(`Fetching template "${name}"`);
6947
7003
  const { data: statusData, error: statusError } = await getTemplateStatus(connectionId, name);
6948
7004
  if (statusError) {
@@ -7262,7 +7318,7 @@ function buildConfigFromOpts(provider, opts) {
7262
7318
  var integrationCommand = new Command17("integration").description("Manage integrations").argument("[provider]", "Integration provider (airtable, resend, flow)").option("--env <environment>", "Environment (development|production)", "development").option("--token <pat>", "Personal access token (airtable)").option("--base-id <id>", "Default base ID (airtable)").option("--from-email <email>", "From email address (resend)").option("--from-name <name>", "From display name (resend)").option("--reply-to <email>", "Reply-to address (resend)").option("--api-url <url>", "API URL (flow)").option("--api-key <key>", "API key (flow)").option("--secret-key <secret>", "Secret key (flow)").option("--return-url <url>", "Return URL after payment (flow)").option("--account-sid <sid>", "Twilio Account SID (twilio)").option("--auth-token <token>", "Twilio Auth Token (twilio)").option("--phone-number <number>", "Twilio phone number in E.164 format (twilio)").option("--router <slug>", "Router slug to assign to phone number (twilio)").option("--agent <slug>", "Agent slug to assign to phone number (twilio)").option("--test", "Test the connection after saving").option("--remove", "Remove integration config").option("--enable", "Enable integration").option("--disable", "Disable integration").option("--status", "Show current config status").option("--yes", "Skip confirmation prompts").option("--json", "Output raw JSON").action(async (provider, opts) => {
7263
7319
  await ensureAuth4();
7264
7320
  const env = opts.env;
7265
- const out = createOutput();
7321
+ const out = opts.json ? createSilentOutput() : createOutput();
7266
7322
  if (!provider || provider === "list") {
7267
7323
  out.start("Fetching integrations");
7268
7324
  const { data, error } = await listIntegrationConfigs(env);
@@ -7778,9 +7834,9 @@ function renderExecutionLog(executionLog, verbose) {
7778
7834
  var triggersCommand = new Command18("triggers").description("Manage triggers and automation runs");
7779
7835
  triggersCommand.command("list", { isDefault: true }).description("List all triggers").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--failed", "Show only triggers with recent failures").action(async (opts) => {
7780
7836
  await ensureAuth5();
7781
- const spinner = ora14();
7837
+ const spinner = opts.json ? null : ora14();
7782
7838
  try {
7783
- spinner.start("Fetching triggers");
7839
+ spinner?.start("Fetching triggers");
7784
7840
  const [triggers, statuses] = await Promise.all([
7785
7841
  listTriggers(opts.env),
7786
7842
  getLastRunStatuses(opts.env)
@@ -7789,7 +7845,7 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7789
7845
  if (opts.failed) {
7790
7846
  filtered = triggers.filter((t) => statuses[t.slug]?.status === "failed");
7791
7847
  }
7792
- spinner.succeed(`Found ${filtered.length} triggers${opts.failed ? " (failed only)" : ""}`);
7848
+ spinner?.succeed(`Found ${filtered.length} triggers${opts.failed ? " (failed only)" : ""}`);
7793
7849
  if (opts.json) {
7794
7850
  console.log(JSON.stringify(filtered, null, 2));
7795
7851
  return;
@@ -7815,7 +7871,7 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7815
7871
  console.log();
7816
7872
  } catch (err) {
7817
7873
  const message = err instanceof Error ? err.message : String(err);
7818
- spinner.fail("Failed to fetch triggers");
7874
+ spinner?.fail("Failed to fetch triggers");
7819
7875
  if (opts.json) {
7820
7876
  console.log(JSON.stringify({ success: false, error: message }));
7821
7877
  } else {
@@ -7826,18 +7882,18 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7826
7882
  });
7827
7883
  triggersCommand.command("get <slug>").description("View trigger details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (slug, opts) => {
7828
7884
  await ensureAuth5();
7829
- const spinner = ora14();
7885
+ const spinner = opts.json ? null : ora14();
7830
7886
  try {
7831
- spinner.start("Fetching trigger");
7887
+ spinner?.start("Fetching trigger");
7832
7888
  const trigger = await getTrigger(slug, opts.env);
7833
7889
  if (!trigger) {
7834
- spinner.fail("Trigger not found");
7890
+ spinner?.fail("Trigger not found");
7835
7891
  if (opts.json) {
7836
7892
  console.log(JSON.stringify({ success: false, error: "Trigger not found" }));
7837
7893
  }
7838
7894
  process.exit(1);
7839
7895
  }
7840
- spinner.succeed("Trigger loaded");
7896
+ spinner?.succeed("Trigger loaded");
7841
7897
  if (opts.json) {
7842
7898
  console.log(JSON.stringify(trigger, null, 2));
7843
7899
  return;
@@ -7894,7 +7950,7 @@ triggersCommand.command("get <slug>").description("View trigger details").option
7894
7950
  console.log();
7895
7951
  } catch (err) {
7896
7952
  const message = err instanceof Error ? err.message : String(err);
7897
- spinner.fail("Failed to fetch trigger");
7953
+ spinner?.fail("Failed to fetch trigger");
7898
7954
  if (opts.json) {
7899
7955
  console.log(JSON.stringify({ success: false, error: message }));
7900
7956
  } else {
@@ -7905,16 +7961,16 @@ triggersCommand.command("get <slug>").description("View trigger details").option
7905
7961
  });
7906
7962
  triggersCommand.command("runs [slug]").description("List trigger runs").option("--env <environment>", "Environment", "development").option("--status <status>", "Filter by status (pending|running|completed|failed|dead)").option("--limit <n>", "Maximum results", "20").option("--json", "Output raw JSON").action(async (slug, opts) => {
7907
7963
  await ensureAuth5();
7908
- const spinner = ora14();
7964
+ const spinner = opts.json ? null : ora14();
7909
7965
  try {
7910
- spinner.start("Fetching runs");
7966
+ spinner?.start("Fetching runs");
7911
7967
  const runs = await listTriggerRuns({
7912
7968
  environment: opts.env,
7913
7969
  status: opts.status,
7914
7970
  triggerSlug: slug,
7915
7971
  limit: parseInt(opts.limit, 10)
7916
7972
  });
7917
- spinner.succeed(`Found ${runs.length} runs`);
7973
+ spinner?.succeed(`Found ${runs.length} runs`);
7918
7974
  if (opts.json) {
7919
7975
  console.log(JSON.stringify(runs, null, 2));
7920
7976
  return;
@@ -7940,7 +7996,7 @@ triggersCommand.command("runs [slug]").description("List trigger runs").option("
7940
7996
  console.log();
7941
7997
  } catch (err) {
7942
7998
  const message = err instanceof Error ? err.message : String(err);
7943
- spinner.fail("Failed to fetch runs");
7999
+ spinner?.fail("Failed to fetch runs");
7944
8000
  if (opts.json) {
7945
8001
  console.log(JSON.stringify({ success: false, error: message }));
7946
8002
  } else {
@@ -7951,18 +8007,18 @@ triggersCommand.command("runs [slug]").description("List trigger runs").option("
7951
8007
  });
7952
8008
  triggersCommand.command("run <run-id>").description("View trigger run details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("-v, --verbose", "Show detailed agent tool calls").action(async (runId, opts) => {
7953
8009
  await ensureAuth5();
7954
- const spinner = ora14();
8010
+ const spinner = opts.json ? null : ora14();
7955
8011
  try {
7956
- spinner.start("Fetching run");
8012
+ spinner?.start("Fetching run");
7957
8013
  const run = await getTriggerRunDetail(runId, opts.env);
7958
8014
  if (!run) {
7959
- spinner.fail("Run not found");
8015
+ spinner?.fail("Run not found");
7960
8016
  if (opts.json) {
7961
8017
  console.log(JSON.stringify({ success: false, error: "Run not found" }));
7962
8018
  }
7963
8019
  process.exit(1);
7964
8020
  }
7965
- spinner.succeed("Run loaded");
8021
+ spinner?.succeed("Run loaded");
7966
8022
  if (opts.json) {
7967
8023
  console.log(JSON.stringify(run, null, 2));
7968
8024
  return;
@@ -7985,7 +8041,7 @@ triggersCommand.command("run <run-id>").description("View trigger run details").
7985
8041
  console.log();
7986
8042
  } catch (err) {
7987
8043
  const message = err instanceof Error ? err.message : String(err);
7988
- spinner.fail("Failed to fetch run");
8044
+ spinner?.fail("Failed to fetch run");
7989
8045
  if (opts.json) {
7990
8046
  console.log(JSON.stringify({ success: false, error: message }));
7991
8047
  } else {
@@ -7996,11 +8052,11 @@ triggersCommand.command("run <run-id>").description("View trigger run details").
7996
8052
  });
7997
8053
  triggersCommand.command("stats").description("Show trigger run statistics").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (opts) => {
7998
8054
  await ensureAuth5();
7999
- const spinner = ora14();
8055
+ const spinner = opts.json ? null : ora14();
8000
8056
  try {
8001
- spinner.start("Fetching statistics");
8057
+ spinner?.start("Fetching statistics");
8002
8058
  const stats = await getTriggerRunStats(opts.env);
8003
- spinner.succeed("Run statistics");
8059
+ spinner?.succeed("Run statistics");
8004
8060
  if (opts.json) {
8005
8061
  console.log(JSON.stringify(stats, null, 2));
8006
8062
  return;
@@ -8039,7 +8095,7 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
8039
8095
  console.log();
8040
8096
  } catch (err) {
8041
8097
  const message = err instanceof Error ? err.message : String(err);
8042
- spinner.fail("Failed to fetch statistics");
8098
+ spinner?.fail("Failed to fetch statistics");
8043
8099
  if (opts.json) {
8044
8100
  console.log(JSON.stringify({ success: false, error: message }));
8045
8101
  } else {
@@ -8050,15 +8106,15 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
8050
8106
  });
8051
8107
  triggersCommand.command("logs [slug]").description("View trigger execution history").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (slug, opts) => {
8052
8108
  await ensureAuth5();
8053
- const spinner = ora14();
8109
+ const spinner = opts.json ? null : ora14();
8054
8110
  try {
8055
- spinner.start("Fetching execution logs");
8111
+ spinner?.start("Fetching execution logs");
8056
8112
  const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
8057
8113
  environment: opts.env,
8058
8114
  triggerSlug: slug,
8059
8115
  limit: parseInt(opts.limit, 10)
8060
8116
  }));
8061
- spinner.succeed(`Found ${executions.length} executions`);
8117
+ spinner?.succeed(`Found ${executions.length} executions`);
8062
8118
  if (opts.json) {
8063
8119
  console.log(JSON.stringify(executions, null, 2));
8064
8120
  return;
@@ -8103,7 +8159,7 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
8103
8159
  console.log();
8104
8160
  } catch (err) {
8105
8161
  const message = err instanceof Error ? err.message : String(err);
8106
- spinner.fail("Failed to fetch execution logs");
8162
+ spinner?.fail("Failed to fetch execution logs");
8107
8163
  if (opts.json) {
8108
8164
  console.log(JSON.stringify({ success: false, error: message }));
8109
8165
  } else {
@@ -8114,7 +8170,7 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
8114
8170
  });
8115
8171
  triggersCommand.command("log <identifier>").description("View detailed trigger execution log (by event ID or trigger slug)").option("--env <environment>", "Environment", "development").option("--nth <n>", "Show nth most recent execution (when using slug)", "1").option("--json", "Output raw JSON").option("-v, --verbose", "Show detailed agent tool calls").action(async (identifier, opts) => {
8116
8172
  await ensureAuth5();
8117
- const spinner = ora14();
8173
+ const spinner = opts.json ? null : ora14();
8118
8174
  try {
8119
8175
  const nth = parseInt(opts.nth, 10);
8120
8176
  if (isNaN(nth) || nth < 1) {
@@ -8124,14 +8180,14 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8124
8180
  let eventId = identifier;
8125
8181
  const isConvexId = /^[0-9a-zA-Z]{20,}$/.test(identifier);
8126
8182
  if (!isConvexId) {
8127
- spinner.start("Resolving trigger slug to latest execution");
8183
+ spinner?.start("Resolving trigger slug to latest execution");
8128
8184
  const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
8129
8185
  environment: opts.env,
8130
8186
  triggerSlug: identifier,
8131
8187
  limit: nth
8132
8188
  }));
8133
8189
  if (!executions.length) {
8134
- spinner.fail(`No executions found for trigger "${identifier}" in ${opts.env}`);
8190
+ spinner?.fail(`No executions found for trigger "${identifier}" in ${opts.env}`);
8135
8191
  if (opts.json) {
8136
8192
  console.log(JSON.stringify({ success: false, error: `No executions found for trigger "${identifier}"` }));
8137
8193
  }
@@ -8139,22 +8195,22 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8139
8195
  }
8140
8196
  const idx = nth - 1;
8141
8197
  if (idx >= executions.length) {
8142
- spinner.fail(`Only ${executions.length} executions found, cannot get #${nth}`);
8198
+ spinner?.fail(`Only ${executions.length} executions found, cannot get #${nth}`);
8143
8199
  process.exit(1);
8144
8200
  }
8145
8201
  eventId = executions[idx]._id;
8146
- spinner.succeed(`Found execution for "${identifier}"`);
8202
+ spinner?.succeed(`Found execution for "${identifier}"`);
8147
8203
  }
8148
- spinner.start("Fetching execution detail");
8204
+ spinner?.start("Fetching execution detail");
8149
8205
  const event = await withTriggerAuthRetry(() => getTriggerExecutionDetail(eventId, opts.env));
8150
8206
  if (!event) {
8151
- spinner.fail("Execution not found");
8207
+ spinner?.fail("Execution not found");
8152
8208
  if (opts.json) {
8153
8209
  console.log(JSON.stringify({ success: false, error: "Execution not found" }));
8154
8210
  }
8155
8211
  process.exit(1);
8156
8212
  }
8157
- spinner.succeed("Execution loaded");
8213
+ spinner?.succeed("Execution loaded");
8158
8214
  if (opts.json) {
8159
8215
  console.log(JSON.stringify(event, null, 2));
8160
8216
  return;
@@ -8184,7 +8240,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8184
8240
  }
8185
8241
  } catch (err) {
8186
8242
  const message = err instanceof Error ? err.message : String(err);
8187
- spinner.fail("Failed to fetch execution detail");
8243
+ spinner?.fail("Failed to fetch execution detail");
8188
8244
  if (opts.json) {
8189
8245
  console.log(JSON.stringify({ success: false, error: message }));
8190
8246
  } else {
@@ -8195,7 +8251,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8195
8251
  });
8196
8252
  triggersCommand.command("retry <run-id>").description("Retry a failed or dead run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
8197
8253
  await ensureAuth5();
8198
- const spinner = ora14();
8254
+ const spinner = opts.json ? null : ora14();
8199
8255
  const environment = opts.env;
8200
8256
  if (environment === "production" && !opts.confirm && isInteractive()) {
8201
8257
  const readline = await import("readline");
@@ -8207,15 +8263,15 @@ triggersCommand.command("retry <run-id>").description("Retry a failed or dead ru
8207
8263
  rl.close();
8208
8264
  }
8209
8265
  try {
8210
- spinner.start("Retrying run...");
8266
+ spinner?.start("Retrying run...");
8211
8267
  await retryTriggerRun(runId, environment);
8212
- spinner.succeed(chalk20.green(`Run ${runId} queued for retry`));
8268
+ spinner?.succeed(chalk20.green(`Run ${runId} queued for retry`));
8213
8269
  if (opts.json) {
8214
8270
  console.log(JSON.stringify({ success: true, runId }));
8215
8271
  }
8216
8272
  } catch (err) {
8217
8273
  const message = err instanceof Error ? err.message : String(err);
8218
- spinner.fail("Failed to retry run");
8274
+ spinner?.fail("Failed to retry run");
8219
8275
  if (opts.json) {
8220
8276
  console.log(JSON.stringify({ success: false, error: message }));
8221
8277
  } else {
@@ -8226,7 +8282,7 @@ triggersCommand.command("retry <run-id>").description("Retry a failed or dead ru
8226
8282
  });
8227
8283
  triggersCommand.command("cancel <run-id>").description("Cancel a pending run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
8228
8284
  await ensureAuth5();
8229
- const spinner = ora14();
8285
+ const spinner = opts.json ? null : ora14();
8230
8286
  const environment = opts.env;
8231
8287
  if (environment === "production" && !opts.confirm && isInteractive()) {
8232
8288
  const readline = await import("readline");
@@ -8238,15 +8294,15 @@ triggersCommand.command("cancel <run-id>").description("Cancel a pending run").o
8238
8294
  rl.close();
8239
8295
  }
8240
8296
  try {
8241
- spinner.start("Cancelling run...");
8297
+ spinner?.start("Cancelling run...");
8242
8298
  await cancelTriggerRun(runId, environment);
8243
- spinner.succeed(chalk20.green(`Run ${runId} cancelled`));
8299
+ spinner?.succeed(chalk20.green(`Run ${runId} cancelled`));
8244
8300
  if (opts.json) {
8245
8301
  console.log(JSON.stringify({ success: true, runId }));
8246
8302
  }
8247
8303
  } catch (err) {
8248
8304
  const message = err instanceof Error ? err.message : String(err);
8249
- spinner.fail("Failed to cancel run");
8305
+ spinner?.fail("Failed to cancel run");
8250
8306
  if (opts.json) {
8251
8307
  console.log(JSON.stringify({ success: false, error: message }));
8252
8308
  } else {
@@ -8257,7 +8313,7 @@ triggersCommand.command("cancel <run-id>").description("Cancel a pending run").o
8257
8313
  });
8258
8314
  triggersCommand.command("enable <slug>").description("Enable a trigger").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
8259
8315
  await ensureAuth5();
8260
- const spinner = ora14();
8316
+ const spinner = opts.json ? null : ora14();
8261
8317
  const environment = opts.env;
8262
8318
  if (environment === "production" && !opts.confirm && isInteractive()) {
8263
8319
  const readline = await import("readline");
@@ -8269,15 +8325,15 @@ triggersCommand.command("enable <slug>").description("Enable a trigger").option(
8269
8325
  rl.close();
8270
8326
  }
8271
8327
  try {
8272
- spinner.start("Enabling trigger...");
8328
+ spinner?.start("Enabling trigger...");
8273
8329
  await toggleTrigger(slug, true, environment);
8274
- spinner.succeed(chalk20.green(`Trigger ${slug} enabled`));
8330
+ spinner?.succeed(chalk20.green(`Trigger ${slug} enabled`));
8275
8331
  if (opts.json) {
8276
8332
  console.log(JSON.stringify({ success: true, slug }));
8277
8333
  }
8278
8334
  } catch (err) {
8279
8335
  const message = err instanceof Error ? err.message : String(err);
8280
- spinner.fail("Failed to enable trigger");
8336
+ spinner?.fail("Failed to enable trigger");
8281
8337
  if (opts.json) {
8282
8338
  console.log(JSON.stringify({ success: false, error: message }));
8283
8339
  } else {
@@ -8288,7 +8344,7 @@ triggersCommand.command("enable <slug>").description("Enable a trigger").option(
8288
8344
  });
8289
8345
  triggersCommand.command("disable <slug>").description("Disable a trigger").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
8290
8346
  await ensureAuth5();
8291
- const spinner = ora14();
8347
+ const spinner = opts.json ? null : ora14();
8292
8348
  const environment = opts.env;
8293
8349
  if (environment === "production" && !opts.confirm && isInteractive()) {
8294
8350
  const readline = await import("readline");
@@ -8300,15 +8356,15 @@ triggersCommand.command("disable <slug>").description("Disable a trigger").optio
8300
8356
  rl.close();
8301
8357
  }
8302
8358
  try {
8303
- spinner.start("Disabling trigger...");
8359
+ spinner?.start("Disabling trigger...");
8304
8360
  await toggleTrigger(slug, false, environment);
8305
- spinner.succeed(chalk20.green(`Trigger ${slug} disabled`));
8361
+ spinner?.succeed(chalk20.green(`Trigger ${slug} disabled`));
8306
8362
  if (opts.json) {
8307
8363
  console.log(JSON.stringify({ success: true, slug }));
8308
8364
  }
8309
8365
  } catch (err) {
8310
8366
  const message = err instanceof Error ? err.message : String(err);
8311
- spinner.fail("Failed to disable trigger");
8367
+ spinner?.fail("Failed to disable trigger");
8312
8368
  if (opts.json) {
8313
8369
  console.log(JSON.stringify({ success: false, error: message }));
8314
8370
  } else {
@@ -8319,7 +8375,7 @@ triggersCommand.command("disable <slug>").description("Disable a trigger").optio
8319
8375
  });
8320
8376
  triggersCommand.command("retry-event <event-id>").description("Retry a failed immediate trigger execution").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (eventId, opts) => {
8321
8377
  await ensureAuth5();
8322
- const spinner = ora14();
8378
+ const spinner = opts.json ? null : ora14();
8323
8379
  const environment = opts.env;
8324
8380
  if (environment === "production" && !opts.confirm && isInteractive()) {
8325
8381
  const readline = await import("readline");
@@ -8331,19 +8387,19 @@ triggersCommand.command("retry-event <event-id>").description("Retry a failed im
8331
8387
  rl.close();
8332
8388
  }
8333
8389
  try {
8334
- spinner.start("Retrying failed execution...");
8390
+ spinner?.start("Retrying failed execution...");
8335
8391
  const result = await retryImmediateExecution(eventId, environment);
8336
8392
  if (result.success) {
8337
- spinner.succeed(chalk20.green(`Execution retried successfully`));
8393
+ spinner?.succeed(chalk20.green(`Execution retried successfully`));
8338
8394
  } else {
8339
- spinner.fail(chalk20.red(`Execution retry failed again`));
8395
+ spinner?.fail(chalk20.red(`Execution retry failed again`));
8340
8396
  }
8341
8397
  if (opts.json) {
8342
8398
  console.log(JSON.stringify(result));
8343
8399
  }
8344
8400
  } catch (err) {
8345
8401
  const message = err instanceof Error ? err.message : String(err);
8346
- spinner.fail("Failed to retry execution");
8402
+ spinner?.fail("Failed to retry execution");
8347
8403
  if (opts.json) {
8348
8404
  console.log(JSON.stringify({ success: false, error: message }));
8349
8405
  } else {
@@ -8354,7 +8410,7 @@ triggersCommand.command("retry-event <event-id>").description("Retry a failed im
8354
8410
  });
8355
8411
  triggersCommand.command("fire <slug>").description("Manually fire a trigger").option("--env <environment>", "Environment (development|production|eval)", "development").option("--entity <entityId>", "Entity ID to provide as context").option("--data <json>", "JSON data for template context").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").option("-v, --verbose", "Show detailed agent tool calls").action(async (slug, opts) => {
8356
8412
  await ensureAuth5();
8357
- const spinner = ora14();
8413
+ const spinner = opts.json ? null : ora14();
8358
8414
  const environment = opts.env;
8359
8415
  if (environment === "production" && !opts.confirm && isInteractive()) {
8360
8416
  const readline = await import("readline");
@@ -8375,7 +8431,7 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
8375
8431
  }
8376
8432
  }
8377
8433
  try {
8378
- spinner.start(`Firing trigger ${chalk20.cyan(slug)}...`);
8434
+ spinner?.start(`Firing trigger ${chalk20.cyan(slug)}...`);
8379
8435
  const { result, error } = await fireTrigger({
8380
8436
  slug,
8381
8437
  environment,
@@ -8383,7 +8439,7 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
8383
8439
  data
8384
8440
  });
8385
8441
  if (error) {
8386
- spinner.fail("Trigger execution failed");
8442
+ spinner?.fail("Trigger execution failed");
8387
8443
  if (opts.json) {
8388
8444
  console.log(JSON.stringify({ success: false, error }));
8389
8445
  } else {
@@ -8392,13 +8448,13 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
8392
8448
  process.exit(1);
8393
8449
  }
8394
8450
  if (!result) {
8395
- spinner.fail("No result returned");
8451
+ spinner?.fail("No result returned");
8396
8452
  process.exit(1);
8397
8453
  }
8398
8454
  if (result.success) {
8399
- spinner.succeed(chalk20.green(`Trigger ${chalk20.cyan(slug)} fired successfully`));
8455
+ spinner?.succeed(chalk20.green(`Trigger ${chalk20.cyan(slug)} fired successfully`));
8400
8456
  } else {
8401
- spinner.fail(chalk20.red(`Trigger ${chalk20.cyan(slug)} execution failed`));
8457
+ spinner?.fail(chalk20.red(`Trigger ${chalk20.cyan(slug)} execution failed`));
8402
8458
  }
8403
8459
  if (opts.json) {
8404
8460
  console.log(JSON.stringify(result, null, 2));
@@ -8416,7 +8472,7 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
8416
8472
  }
8417
8473
  } catch (err) {
8418
8474
  const message = err instanceof Error ? err.message : String(err);
8419
- spinner.fail("Failed to fire trigger");
8475
+ spinner?.fail("Failed to fire trigger");
8420
8476
  if (opts.json) {
8421
8477
  console.log(JSON.stringify({ success: false, error: message }));
8422
8478
  } else {
@@ -8654,6 +8710,8 @@ function channelColor(channel) {
8654
8710
  return chalk21.magenta(channel);
8655
8711
  case "dashboard":
8656
8712
  return chalk21.cyan(channel);
8713
+ case "voice":
8714
+ return chalk21.yellow(channel);
8657
8715
  default:
8658
8716
  return chalk21.gray(channel ?? "-");
8659
8717
  }
@@ -8673,17 +8731,17 @@ function roleColor(role) {
8673
8731
  }
8674
8732
  }
8675
8733
  var threadsCommand = new Command19("threads").description("Manage conversation threads");
8676
- threadsCommand.command("list", { isDefault: true }).description("List conversation threads").option("--env <environment>", "Environment (development|production|eval)", "development").option("--channel <channel>", "Filter by channel (whatsapp|api|widget|dashboard)").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (opts) => {
8734
+ threadsCommand.command("list", { isDefault: true }).description("List conversation threads").option("--env <environment>", "Environment (development|production|eval)", "development").option("--channel <channel>", "Filter by channel (whatsapp|api|widget|dashboard|voice)").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (opts) => {
8677
8735
  await ensureAuth6();
8678
- const spinner = ora15();
8736
+ const spinner = opts.json ? null : ora15();
8679
8737
  try {
8680
- spinner.start("Fetching threads");
8738
+ spinner?.start("Fetching threads");
8681
8739
  const threads = await listThreads({
8682
8740
  environment: opts.env,
8683
8741
  channel: opts.channel,
8684
8742
  limit: parseInt(opts.limit, 10)
8685
8743
  });
8686
- spinner.succeed(`Found ${threads.length} threads`);
8744
+ spinner?.succeed(`Found ${threads.length} threads`);
8687
8745
  if (opts.json) {
8688
8746
  console.log(JSON.stringify(threads, null, 2));
8689
8747
  return;
@@ -8707,7 +8765,7 @@ threadsCommand.command("list", { isDefault: true }).description("List conversati
8707
8765
  console.log();
8708
8766
  } catch (err) {
8709
8767
  const message = err instanceof Error ? err.message : String(err);
8710
- spinner.fail("Failed to fetch threads");
8768
+ spinner?.fail("Failed to fetch threads");
8711
8769
  if (opts.json) {
8712
8770
  console.log(JSON.stringify({ success: false, error: message }));
8713
8771
  } else {
@@ -8718,21 +8776,21 @@ threadsCommand.command("list", { isDefault: true }).description("List conversati
8718
8776
  });
8719
8777
  threadsCommand.command("view <id>").description("View thread details and messages").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (id, opts) => {
8720
8778
  await ensureAuth6();
8721
- const spinner = ora15();
8779
+ const spinner = opts.json ? null : ora15();
8722
8780
  try {
8723
- spinner.start("Fetching thread");
8781
+ spinner?.start("Fetching thread");
8724
8782
  const thread = await getThreadWithMessages({
8725
8783
  threadId: id,
8726
8784
  environment: opts.env
8727
8785
  });
8728
8786
  if (!thread) {
8729
- spinner.fail("Thread not found");
8787
+ spinner?.fail("Thread not found");
8730
8788
  if (opts.json) {
8731
8789
  console.log(JSON.stringify({ success: false, error: "Thread not found" }));
8732
8790
  }
8733
8791
  process.exit(1);
8734
8792
  }
8735
- spinner.succeed("Thread loaded");
8793
+ spinner?.succeed("Thread loaded");
8736
8794
  if (opts.json) {
8737
8795
  console.log(JSON.stringify(thread, null, 2));
8738
8796
  return;
@@ -8765,7 +8823,7 @@ threadsCommand.command("view <id>").description("View thread details and message
8765
8823
  console.log();
8766
8824
  } catch (err) {
8767
8825
  const message = err instanceof Error ? err.message : String(err);
8768
- spinner.fail("Failed to fetch thread");
8826
+ spinner?.fail("Failed to fetch thread");
8769
8827
  if (opts.json) {
8770
8828
  console.log(JSON.stringify({ success: false, error: message }));
8771
8829
  } else {
@@ -8776,7 +8834,7 @@ threadsCommand.command("view <id>").description("View thread details and message
8776
8834
  });
8777
8835
  threadsCommand.command("archive <id>").description("Archive a thread (frees its externalId)").option("--env <environment>", "Environment", "development").option("--confirm", "Skip production confirmation").option("--json", "Output raw JSON").action(async (id, opts) => {
8778
8836
  await ensureAuth6();
8779
- const spinner = ora15();
8837
+ const spinner = opts.json ? null : ora15();
8780
8838
  const environment = opts.env;
8781
8839
  if (environment === "production" && !opts.confirm && isInteractive()) {
8782
8840
  const readline = await import("readline");
@@ -8788,18 +8846,18 @@ threadsCommand.command("archive <id>").description("Archive a thread (frees its
8788
8846
  rl.close();
8789
8847
  }
8790
8848
  try {
8791
- spinner.start("Archiving thread...");
8849
+ spinner?.start("Archiving thread...");
8792
8850
  const result = await archiveThread({
8793
8851
  threadId: id,
8794
8852
  environment
8795
8853
  });
8796
- spinner.succeed(chalk21.green(`Thread ${id} archived`));
8854
+ spinner?.succeed(chalk21.green(`Thread ${id} archived`));
8797
8855
  if (opts.json) {
8798
8856
  console.log(JSON.stringify({ success: true, threadId: id }));
8799
8857
  }
8800
8858
  } catch (err) {
8801
8859
  const message = err instanceof Error ? err.message : String(err);
8802
- spinner.fail("Failed to archive thread");
8860
+ spinner?.fail("Failed to archive thread");
8803
8861
  if (opts.json) {
8804
8862
  console.log(JSON.stringify({ success: false, error: message }));
8805
8863
  } else {
@@ -8810,7 +8868,7 @@ threadsCommand.command("archive <id>").description("Archive a thread (frees its
8810
8868
  });
8811
8869
  threadsCommand.command("reset").description("Archive a thread by phone number").requiredOption("--phone <number>", "Phone number to find and archive").option("--env <environment>", "Environment", "production").option("--confirm", "Skip production confirmation").option("--json", "Output raw JSON").action(async (opts) => {
8812
8870
  await ensureAuth6();
8813
- const spinner = ora15();
8871
+ const spinner = opts.json ? null : ora15();
8814
8872
  const environment = opts.env;
8815
8873
  if (environment === "production" && !opts.confirm && isInteractive()) {
8816
8874
  const readline = await import("readline");
@@ -8822,30 +8880,31 @@ threadsCommand.command("reset").description("Archive a thread by phone number").
8822
8880
  rl.close();
8823
8881
  }
8824
8882
  try {
8825
- spinner.start(`Finding thread for phone ${opts.phone}...`);
8883
+ spinner?.start(`Finding thread for phone ${opts.phone}...`);
8826
8884
  const thread = await findThreadByPhone({
8827
8885
  phone: opts.phone,
8828
8886
  environment
8829
8887
  });
8830
8888
  if (!thread) {
8831
- spinner.fail(`No thread found for phone number ${opts.phone}`);
8889
+ spinner?.fail(`No thread found for phone number ${opts.phone}`);
8832
8890
  if (opts.json) {
8833
8891
  console.log(JSON.stringify({ success: false, error: "Thread not found for phone number" }));
8834
8892
  }
8835
8893
  process.exit(1);
8836
8894
  }
8837
- spinner.text = `Archiving thread ${thread._id}...`;
8895
+ if (spinner)
8896
+ spinner.text = `Archiving thread ${thread._id}...`;
8838
8897
  await archiveThread({
8839
8898
  threadId: thread._id,
8840
8899
  environment
8841
8900
  });
8842
- spinner.succeed(chalk21.green(`Thread ${thread._id} archived (phone: ${opts.phone})`));
8901
+ spinner?.succeed(chalk21.green(`Thread ${thread._id} archived (phone: ${opts.phone})`));
8843
8902
  if (opts.json) {
8844
8903
  console.log(JSON.stringify({ success: true, threadId: thread._id, phone: opts.phone }));
8845
8904
  }
8846
8905
  } catch (err) {
8847
8906
  const message = err instanceof Error ? err.message : String(err);
8848
- spinner.fail("Failed to reset thread");
8907
+ spinner?.fail("Failed to reset thread");
8849
8908
  if (opts.json) {
8850
8909
  console.log(JSON.stringify({ success: false, error: message }));
8851
8910
  } else {
@@ -9557,7 +9616,7 @@ var whatsappCommand = new Command23("whatsapp").description("Manage WhatsApp con
9557
9616
  whatsappCommand.command("list").description("List WhatsApp connections with routing assignments").option("--env <environment>", "Environment (development|production)", "production").option("--json", "Output raw JSON").action(async (opts) => {
9558
9617
  await ensureAuth7();
9559
9618
  const env = opts.env;
9560
- const out = createOutput();
9619
+ const out = opts.json ? createSilentOutput() : createOutput();
9561
9620
  out.start("Fetching WhatsApp connections");
9562
9621
  const { data, error } = await listWhatsAppConnections(env);
9563
9622
  if (error || !data) {
@@ -10503,14 +10562,14 @@ async function runCheck(fn) {
10503
10562
  }
10504
10563
  var doctorCommand = new Command25("doctor").description("Run diagnostic checks on your Struere project").option("--env <environment>", "Environment to check", "development").option("--json", "Output raw JSON").action(async (opts) => {
10505
10564
  await ensureAuth8();
10506
- const spinner = ora20();
10565
+ const spinner = opts.json ? null : ora20();
10507
10566
  const cwd = process.cwd();
10508
10567
  const environment = opts.env;
10509
10568
  try {
10510
- spinner.start("Loading resources...");
10569
+ spinner?.start("Loading resources...");
10511
10570
  const resources = await loadAllResources(cwd);
10512
- spinner.succeed("Resources loaded");
10513
- spinner.start("Running diagnostics...");
10571
+ spinner?.succeed("Resources loaded");
10572
+ spinner?.start("Running diagnostics...");
10514
10573
  const results = await Promise.allSettled([
10515
10574
  runCheck(() => checkWhatsAppConnection(resources)),
10516
10575
  runCheck(() => checkTemplateApprovals(resources)),
@@ -10520,7 +10579,7 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10520
10579
  runCheck(() => checkTriggerHealth(environment)),
10521
10580
  runCheck(() => checkSyncDrift(resources, environment))
10522
10581
  ]);
10523
- spinner.stop();
10582
+ spinner?.stop();
10524
10583
  const checks = results.map((r) => r.status === "fulfilled" ? r.value : { id: "unknown", label: "Unknown", status: "warn", message: `Check failed: ${r.reason}` });
10525
10584
  const summary = {
10526
10585
  ok: checks.filter((c) => c.status === "ok").length,
@@ -10563,7 +10622,7 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10563
10622
  }
10564
10623
  } catch (err) {
10565
10624
  const message = err instanceof Error ? err.message : String(err);
10566
- spinner.fail("Diagnostics failed");
10625
+ spinner?.fail("Diagnostics failed");
10567
10626
  if (opts.json) {
10568
10627
  console.log(JSON.stringify({ success: false, error: message }));
10569
10628
  } else {
@@ -10575,7 +10634,7 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10575
10634
  // package.json
10576
10635
  var package_default = {
10577
10636
  name: "struere",
10578
- version: "0.14.0",
10637
+ version: "0.14.1",
10579
10638
  description: "Build, test, and deploy AI agents",
10580
10639
  keywords: [
10581
10640
  "ai",