@agent-nexus/cli 0.1.11 → 0.1.13

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 +1 -1
  2. package/dist/index.js +1196 -91
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -166,7 +166,7 @@ var require_package = __commonJS({
166
166
  "package.json"(exports2, module2) {
167
167
  module2.exports = {
168
168
  name: "@agent-nexus/cli",
169
- version: "0.1.11",
169
+ version: "0.1.13",
170
170
  description: "Official CLI for the Nexus AI agent platform.",
171
171
  license: "MIT",
172
172
  keywords: [
@@ -695,6 +695,24 @@ var AgentsResource = class extends BaseResource {
695
695
  return this.http.request("POST", `/agents/${agentId}/generate-profile-picture`, { body });
696
696
  }
697
697
  };
698
+ var AnalyticsResource = class extends BaseResource {
699
+ async getOverview(params) {
700
+ return this.http.request("GET", "/analytics/overview", {
701
+ query: params
702
+ });
703
+ }
704
+ async exportCsv(params) {
705
+ return this.http.requestRaw("GET", "/analytics/export", {
706
+ query: params
707
+ });
708
+ }
709
+ async listFeedback(params) {
710
+ const { data, meta } = await this.http.requestWithMeta("GET", "/analytics/feedback", {
711
+ query: params
712
+ });
713
+ return { data, meta };
714
+ }
715
+ };
698
716
  var ChannelsResource = class extends BaseResource {
699
717
  // ===========================================================================
700
718
  // Setup Orchestrator
@@ -748,23 +766,37 @@ var ChannelsResource = class extends BaseResource {
748
766
  async getWhatsAppSender(senderId) {
749
767
  return this.http.request("GET", `/channels/whatsapp-senders/${senderId}`);
750
768
  }
751
- };
752
- var AnalyticsResource = class extends BaseResource {
753
- async getOverview(params) {
754
- return this.http.request("GET", "/analytics/overview", {
769
+ // ===========================================================================
770
+ // WhatsApp Templates
771
+ // ===========================================================================
772
+ async listWhatsAppTemplates(params) {
773
+ return this.http.request("GET", "/channels/whatsapp-templates", {
755
774
  query: params
756
775
  });
757
776
  }
758
- async exportCsv(params) {
759
- return this.http.requestRaw("GET", "/analytics/export", {
760
- query: params
761
- });
777
+ async createWhatsAppTemplate(body) {
778
+ return this.http.request("POST", "/channels/whatsapp-templates", { body });
762
779
  }
763
- async listFeedback(params) {
764
- const { data, meta } = await this.http.requestWithMeta("GET", "/analytics/feedback", {
780
+ async getWhatsAppTemplate(templateId) {
781
+ return this.http.request("GET", `/channels/whatsapp-templates/${templateId}`);
782
+ }
783
+ async deleteWhatsAppTemplate(templateId) {
784
+ return this.http.request(
785
+ "DELETE",
786
+ `/channels/whatsapp-templates/${templateId}`
787
+ );
788
+ }
789
+ async listTemplateApprovals(params) {
790
+ return this.http.request("GET", "/channels/whatsapp-templates/approvals", {
765
791
  query: params
766
792
  });
767
- return { data, meta };
793
+ }
794
+ async submitTemplateApproval(body) {
795
+ return this.http.request(
796
+ "POST",
797
+ "/channels/whatsapp-templates/approvals",
798
+ { body }
799
+ );
768
800
  }
769
801
  };
770
802
  var CloudImportsResource = class extends BaseResource {
@@ -898,6 +930,42 @@ var DeploymentsResource = class extends BaseResource {
898
930
  async updateEmbedConfig(deploymentId, body) {
899
931
  return this.http.request("PATCH", `/deployments/${deploymentId}/embed-config`, { body });
900
932
  }
933
+ // ===========================================================================
934
+ // WhatsApp Deployment Templates
935
+ // ===========================================================================
936
+ async listDeploymentTemplates(deploymentId) {
937
+ return this.http.request(
938
+ "GET",
939
+ `/deployments/${deploymentId}/whatsapp-templates`
940
+ );
941
+ }
942
+ async attachDeploymentTemplate(deploymentId, body) {
943
+ return this.http.request(
944
+ "POST",
945
+ `/deployments/${deploymentId}/whatsapp-templates`,
946
+ { body }
947
+ );
948
+ }
949
+ async updateDeploymentTemplate(deploymentId, templateId, body) {
950
+ return this.http.request(
951
+ "PATCH",
952
+ `/deployments/${deploymentId}/whatsapp-templates/${templateId}`,
953
+ { body }
954
+ );
955
+ }
956
+ async detachDeploymentTemplate(deploymentId, templateId) {
957
+ return this.http.request(
958
+ "DELETE",
959
+ `/deployments/${deploymentId}/whatsapp-templates/${templateId}`
960
+ );
961
+ }
962
+ async updateDeploymentTemplateSettings(deploymentId, body) {
963
+ return this.http.request(
964
+ "PATCH",
965
+ `/deployments/${deploymentId}/whatsapp-templates/settings`,
966
+ { body }
967
+ );
968
+ }
901
969
  };
902
970
  var DocumentTemplateFoldersResource = class extends BaseResource {
903
971
  async list() {
@@ -1988,6 +2056,64 @@ var ToolDiscoveryResource = class extends BaseResource {
1988
2056
  );
1989
2057
  }
1990
2058
  };
2059
+ var TracingResource = class extends BaseResource {
2060
+ // ── Traces ──────────────────────────────────────────────────────────────
2061
+ async listTraces(params) {
2062
+ const { data, meta } = await this.http.requestWithMeta(
2063
+ "GET",
2064
+ "/tracing/traces",
2065
+ { query: params }
2066
+ );
2067
+ return { data, meta };
2068
+ }
2069
+ async getTrace(traceId) {
2070
+ return this.http.request("GET", `/tracing/traces/${traceId}`);
2071
+ }
2072
+ async deleteTrace(traceId) {
2073
+ return this.http.request("DELETE", `/tracing/traces/${traceId}`);
2074
+ }
2075
+ // ── Generations ─────────────────────────────────────────────────────────
2076
+ async listGenerations(params) {
2077
+ const { data, meta } = await this.http.requestWithMeta(
2078
+ "GET",
2079
+ "/tracing/generations",
2080
+ { query: params }
2081
+ );
2082
+ return { data, meta };
2083
+ }
2084
+ async getGeneration(generationId) {
2085
+ return this.http.request("GET", `/tracing/generations/${generationId}`);
2086
+ }
2087
+ // ── Models ──────────────────────────────────────────────────────────────
2088
+ async listModels() {
2089
+ return this.http.request("GET", "/tracing/models");
2090
+ }
2091
+ // ── Analytics ───────────────────────────────────────────────────────────
2092
+ async getSummary(params) {
2093
+ return this.http.request("GET", "/tracing/analytics/summary", {
2094
+ query: params
2095
+ });
2096
+ }
2097
+ async getCostBreakdown(params) {
2098
+ return this.http.request("GET", "/tracing/analytics/cost-breakdown", {
2099
+ query: params
2100
+ });
2101
+ }
2102
+ async getTimeline(params) {
2103
+ return this.http.request("GET", "/tracing/analytics/timeline", {
2104
+ query: params
2105
+ });
2106
+ }
2107
+ // ── Export ──────────────────────────────────────────────────────────────
2108
+ async exportTrace(traceId, params) {
2109
+ return this.http.request("POST", `/tracing/traces/${traceId}/export`, {
2110
+ body: params
2111
+ });
2112
+ }
2113
+ async bulkExport(params) {
2114
+ return this.http.request("POST", "/tracing/export", { body: params });
2115
+ }
2116
+ };
1991
2117
  var WorkflowExecutionsResource = class extends BaseResource {
1992
2118
  async list(params) {
1993
2119
  const { data, meta } = await this.http.requestWithMeta("GET", "/workflows/executions", {
@@ -2460,6 +2586,7 @@ var NexusClient = class {
2460
2586
  this.phoneNumbers = new PhoneNumbersResource(http);
2461
2587
  this.tickets = new TicketsResource(http);
2462
2588
  this.channels = new ChannelsResource(http);
2589
+ this.tracing = new TracingResource(http);
2463
2590
  }
2464
2591
  };
2465
2592
 
@@ -2471,6 +2598,10 @@ var URL_MAP = {
2471
2598
  production: "https://api.nexusgpt.io",
2472
2599
  dev: "http://localhost:3001"
2473
2600
  };
2601
+ var DASHBOARD_URL_MAP = {
2602
+ production: "https://gpt.nexus",
2603
+ dev: "http://localhost:3000"
2604
+ };
2474
2605
  var CONFIG_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".nexus-mcp");
2475
2606
  var CONFIG_FILE = import_node_path.default.join(CONFIG_DIR, "config.json");
2476
2607
  var NEXUSRC_FILENAME = ".nexusrc";
@@ -2657,6 +2788,17 @@ function resolveBaseUrl(override) {
2657
2788
  const env = process.env.NEXUS_ENV ?? "production";
2658
2789
  return URL_MAP[env] ?? URL_MAP.production;
2659
2790
  }
2791
+ function resolveDashboardUrl(override) {
2792
+ if (override) return override;
2793
+ if (process.env.NEXUS_DASHBOARD_URL) return process.env.NEXUS_DASHBOARD_URL;
2794
+ try {
2795
+ const resolved = resolveProfile();
2796
+ if (resolved.profile.dashboardUrl) return resolved.profile.dashboardUrl;
2797
+ } catch {
2798
+ }
2799
+ const env = process.env.NEXUS_ENV ?? "production";
2800
+ return DASHBOARD_URL_MAP[env] ?? DASHBOARD_URL_MAP.production;
2801
+ }
2660
2802
 
2661
2803
  // src/client.ts
2662
2804
  var _lastResolved = null;
@@ -3275,8 +3417,10 @@ Notes:
3275
3417
  Run "nexus auth list" to see all profiles, "nexus auth switch <name>" to change active.`
3276
3418
  ).action(async (opts) => {
3277
3419
  let baseUrl;
3420
+ let dashboardUrl;
3278
3421
  if (opts.env === "dev") {
3279
3422
  baseUrl = "http://localhost:3001";
3423
+ dashboardUrl = "http://localhost:3000";
3280
3424
  }
3281
3425
  const resolvedBaseUrl = baseUrl ?? resolveBaseUrl();
3282
3426
  let apiKey = opts.apiKey;
@@ -3368,6 +3512,7 @@ Notes:
3368
3512
  saveProfile(profileName, {
3369
3513
  apiKey,
3370
3514
  ...baseUrl ? { baseUrl } : {},
3515
+ ...dashboardUrl ? { dashboardUrl } : {},
3371
3516
  ...orgName ? { orgName } : {},
3372
3517
  ...orgId ? { orgId } : {}
3373
3518
  });
@@ -3570,7 +3715,43 @@ function openUrl(url) {
3570
3715
 
3571
3716
  // src/commands/channel.ts
3572
3717
  var import_node_child_process2 = require("child_process");
3718
+ var import_node_fs2 = require("fs");
3573
3719
  init_output();
3720
+ var VARIABLE_PATTERN = /\{\{\d+\}\}/g;
3721
+ function warnIfHighVariableDensity(types) {
3722
+ let warned = false;
3723
+ function checkField(text, fieldLabel) {
3724
+ const matches = text.match(VARIABLE_PATTERN) ?? [];
3725
+ if (matches.length === 0) return;
3726
+ const variableCharsLength = matches.reduce((sum, m) => sum + m.length, 0);
3727
+ const staticLength = text.length - variableCharsLength;
3728
+ if (staticLength < matches.length * 3) {
3729
+ console.warn(
3730
+ color.yellow("\u26A0 Warning:") + ` ${fieldLabel} has very high variable density (${staticLength} static chars, ${matches.length} variable(s)). Meta may reject this with "too many variables for its length."`
3731
+ );
3732
+ warned = true;
3733
+ }
3734
+ }
3735
+ for (const [typeKey, typeValue] of Object.entries(types)) {
3736
+ if (!typeValue || typeof typeValue !== "object") continue;
3737
+ const tv = typeValue;
3738
+ if (typeof tv.body === "string") checkField(tv.body, `${typeKey} body`);
3739
+ if (typeof tv.title === "string") checkField(tv.title, `${typeKey} title`);
3740
+ if (typeof tv.subtitle === "string") checkField(tv.subtitle, `${typeKey} subtitle`);
3741
+ if (Array.isArray(tv.cards)) {
3742
+ tv.cards.forEach((card, i) => {
3743
+ if (typeof card?.body === "string") checkField(card.body, `${typeKey} card[${i}] body`);
3744
+ if (typeof card?.title === "string") checkField(card.title, `${typeKey} card[${i}] title`);
3745
+ });
3746
+ }
3747
+ }
3748
+ if (warned) {
3749
+ console.warn(
3750
+ color.yellow(" Tip:") + " Add more descriptive static text around {{N}} placeholders to avoid Meta rejection.\n"
3751
+ );
3752
+ }
3753
+ return warned;
3754
+ }
3574
3755
  function openUrl2(url) {
3575
3756
  const platform = process.platform;
3576
3757
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
@@ -3578,7 +3759,10 @@ function openUrl2(url) {
3578
3759
  }
3579
3760
  function registerChannelCommands(program2) {
3580
3761
  const channel = program2.command("channel").description("Set up deployment channels: connections, phone numbers, WhatsApp senders");
3581
- channel.command("setup").description("Check or auto-provision channel setup prerequisites").requiredOption("--type <type>", "Deployment type (WHATSAPP, TWILIO_SMS, TWILIO_VOICE, EMBED, etc.)").option("--auto", "Auto-provision what is possible (e.g., create messaging connection)").option("--region <region>", "Region for auto-provisioning (us1 or ie1)", "us1").addHelpText(
3762
+ channel.command("setup").description("Check or auto-provision channel setup prerequisites").requiredOption(
3763
+ "--type <type>",
3764
+ "Deployment type (WHATSAPP, TWILIO_SMS, TWILIO_VOICE, EMBED, etc.)"
3765
+ ).option("--auto", "Auto-provision what is possible (e.g., create messaging connection)").option("--region <region>", "Region for auto-provisioning (us1 or ie1)", "us1").addHelpText(
3582
3766
  "after",
3583
3767
  `
3584
3768
  Examples:
@@ -3622,8 +3806,8 @@ Examples:
3622
3806
  $ nexus channel connect-waba`
3623
3807
  ).action(async () => {
3624
3808
  try {
3625
- const dashboardUrl = process.env.NEXUS_DASHBOARD_URL ?? "https://gpt.nexus";
3626
- const url = `${dashboardUrl}/app/settings/phone-number`;
3809
+ const dashboardUrl = resolveDashboardUrl(program2.optsWithGlobals().dashboardUrl);
3810
+ const url = `${dashboardUrl}/app/connect-waba`;
3627
3811
  console.log(`Opening ${color.cyan(url)} ...`);
3628
3812
  console.log("");
3629
3813
  console.log('Complete the "Connect with Meta" flow in your browser, then verify:');
@@ -3727,6 +3911,292 @@ Examples:
3727
3911
  process.exitCode = handleError(err);
3728
3912
  }
3729
3913
  });
3914
+ const waTemplate = channel.command("whatsapp-template").description("Manage WhatsApp message templates (Twilio Content API)");
3915
+ waTemplate.command("list").description("List WhatsApp message templates").option("--connection-id <id>", "Filter by messaging connection ID").addHelpText(
3916
+ "after",
3917
+ `
3918
+ Examples:
3919
+ $ nexus channel whatsapp-template list
3920
+ $ nexus channel whatsapp-template list --connection-id abc --json`
3921
+ ).action(async (opts) => {
3922
+ try {
3923
+ const client = createClient(program2.optsWithGlobals());
3924
+ const result = await client.channels.listWhatsAppTemplates({
3925
+ connectionId: opts.connectionId
3926
+ });
3927
+ const data = result?.data ?? result;
3928
+ printTable(Array.isArray(data) ? data : [data], [
3929
+ { key: "id", label: "ID", width: 38 },
3930
+ { key: "friendly_name", label: "FRIENDLY NAME", width: 25 },
3931
+ { key: "language", label: "LANG", width: 8 }
3932
+ ]);
3933
+ } catch (err) {
3934
+ process.exitCode = handleError(err);
3935
+ }
3936
+ });
3937
+ waTemplate.command("get").description("Get WhatsApp template details").argument("<templateId>", "Template ID (Twilio SID)").action(async (templateId) => {
3938
+ try {
3939
+ const client = createClient(program2.optsWithGlobals());
3940
+ const result = await client.channels.getWhatsAppTemplate(templateId);
3941
+ const data = result?.data ?? result;
3942
+ printRecord(data, [
3943
+ { key: "id", label: "ID" },
3944
+ { key: "friendly_name", label: "Friendly Name" },
3945
+ { key: "language", label: "Language" },
3946
+ { key: "types", label: "Types" },
3947
+ { key: "variables", label: "Variables" },
3948
+ { key: "created_at", label: "Created At" }
3949
+ ]);
3950
+ } catch (err) {
3951
+ process.exitCode = handleError(err);
3952
+ }
3953
+ });
3954
+ waTemplate.command("create").description("Create a WhatsApp message template").requiredOption("--connection-id <id>", "Messaging connection ID").requiredOption("--friendly-name <name>", "Template name (e.g., order_confirmation)").requiredOption("--language <lang>", "Language code (e.g., en, en_US)").option("--body <text>", "Template body text (auto-wraps as twilio/text type)").option("--body-file <path>", "Path to JSON file with full Twilio Types object").option("--type <type>", "Twilio type key when using --body", "twilio/text").option("--variables <json>", `Template variables as JSON (e.g., '{"1":"default"}')`).option("--submit", "Also submit for Meta approval after creation").option(
3955
+ "--category <category>",
3956
+ "Approval category (required with --submit): UTILITY, MARKETING, AUTHENTICATION"
3957
+ ).addHelpText(
3958
+ "after",
3959
+ `
3960
+ Examples:
3961
+ $ nexus channel whatsapp-template create --connection-id abc --friendly-name welcome --language en --body "Hello {{1}}, welcome!"
3962
+ $ nexus channel whatsapp-template create --connection-id abc --friendly-name promo --language en --body-file template.json
3963
+ $ nexus channel whatsapp-template create --connection-id abc --friendly-name order --language en --body "Order {{1}} confirmed" --submit --category UTILITY`
3964
+ ).action(async (opts) => {
3965
+ try {
3966
+ if (!opts.body && !opts.bodyFile) {
3967
+ console.error("Error: Either --body or --body-file is required.");
3968
+ process.exitCode = 1;
3969
+ return;
3970
+ }
3971
+ if (opts.body && opts.bodyFile) {
3972
+ console.error("Error: Cannot use both --body and --body-file.");
3973
+ process.exitCode = 1;
3974
+ return;
3975
+ }
3976
+ if (opts.submit && !opts.category) {
3977
+ console.error("Error: --category is required when using --submit.");
3978
+ process.exitCode = 1;
3979
+ return;
3980
+ }
3981
+ let types;
3982
+ if (opts.bodyFile) {
3983
+ try {
3984
+ const content = (0, import_node_fs2.readFileSync)(opts.bodyFile, "utf-8");
3985
+ types = JSON.parse(content);
3986
+ } catch (e) {
3987
+ console.error(
3988
+ `Error reading --body-file: ${e instanceof Error ? e.message : String(e)}`
3989
+ );
3990
+ process.exitCode = 1;
3991
+ return;
3992
+ }
3993
+ } else {
3994
+ types = { [opts.type]: { body: opts.body } };
3995
+ }
3996
+ let variables;
3997
+ if (opts.variables) {
3998
+ try {
3999
+ variables = JSON.parse(opts.variables);
4000
+ } catch {
4001
+ console.error("Error: --variables must be valid JSON.");
4002
+ process.exitCode = 1;
4003
+ return;
4004
+ }
4005
+ }
4006
+ warnIfHighVariableDensity(types);
4007
+ const client = createClient(program2.optsWithGlobals());
4008
+ const result = await client.channels.createWhatsAppTemplate({
4009
+ connectionId: opts.connectionId,
4010
+ friendlyName: opts.friendlyName,
4011
+ language: opts.language,
4012
+ types,
4013
+ variables
4014
+ });
4015
+ const data = result?.data ?? result;
4016
+ printRecord(data, [
4017
+ { key: "id", label: "ID" },
4018
+ { key: "friendly_name", label: "Friendly Name" },
4019
+ { key: "language", label: "Language" },
4020
+ { key: "created_at", label: "Created At" }
4021
+ ]);
4022
+ printSuccess("WhatsApp template created.");
4023
+ if (opts.submit) {
4024
+ console.log("");
4025
+ console.log("Submitting for Meta approval...");
4026
+ const approval = await client.channels.submitTemplateApproval({
4027
+ connectionId: opts.connectionId,
4028
+ templateId: data.id,
4029
+ name: opts.friendlyName,
4030
+ category: opts.category
4031
+ });
4032
+ const approvalData = approval?.data ?? approval;
4033
+ printRecord(approvalData, [
4034
+ { key: "sid", label: "Approval SID" },
4035
+ { key: "status", label: "Status" }
4036
+ ]);
4037
+ printSuccess("Template submitted for Meta approval.");
4038
+ const pollMaxMs = 3e4;
4039
+ const pollIntervalMs = 5e3;
4040
+ const pollStart = Date.now();
4041
+ let resolved = false;
4042
+ console.log("Checking approval status...");
4043
+ while (Date.now() - pollStart < pollMaxMs) {
4044
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
4045
+ try {
4046
+ const approvals = await client.channels.listTemplateApprovals({
4047
+ connectionId: opts.connectionId
4048
+ });
4049
+ const approvalsArr = approvals?.data ?? approvals;
4050
+ const items = Array.isArray(approvalsArr) ? approvalsArr : [approvalsArr];
4051
+ const match = items.find((a) => a.sid === data.id);
4052
+ if (match?.approvalRequests?.status) {
4053
+ const status = match.approvalRequests.status;
4054
+ if (status === "rejected") {
4055
+ console.log(color.red(`\u2717 Template rejected by Meta: ${status}`));
4056
+ if (match.approvalRequests.rejection_reason) {
4057
+ console.log(` Reason: ${match.approvalRequests.rejection_reason}`);
4058
+ }
4059
+ resolved = true;
4060
+ process.exitCode = 1;
4061
+ break;
4062
+ } else if (status === "approved") {
4063
+ console.log(color.green(`\u2713 Template approved by Meta.`));
4064
+ resolved = true;
4065
+ break;
4066
+ }
4067
+ }
4068
+ } catch {
4069
+ }
4070
+ }
4071
+ if (!resolved) {
4072
+ console.log(
4073
+ `Status still pending. Check later: ${color.dim("nexus channel whatsapp-template approvals")}`
4074
+ );
4075
+ }
4076
+ }
4077
+ } catch (err) {
4078
+ process.exitCode = handleError(err);
4079
+ }
4080
+ });
4081
+ waTemplate.command("delete").description("Delete a WhatsApp template").argument("<templateId>", "Template ID (Twilio SID)").option("--yes", "Skip confirmation prompt").action(async (templateId, opts) => {
4082
+ try {
4083
+ if (!opts.yes && process.stdin.isTTY) {
4084
+ const readline2 = await import("readline");
4085
+ const rl = readline2.createInterface({
4086
+ input: process.stdin,
4087
+ output: process.stdout
4088
+ });
4089
+ const answer = await new Promise((resolve) => {
4090
+ rl.question(`Delete template ${templateId}? (y/N) `, resolve);
4091
+ });
4092
+ rl.close();
4093
+ if (answer.toLowerCase() !== "y") {
4094
+ console.log("Cancelled.");
4095
+ return;
4096
+ }
4097
+ }
4098
+ const client = createClient(program2.optsWithGlobals());
4099
+ await client.channels.deleteWhatsAppTemplate(templateId);
4100
+ printSuccess("WhatsApp template deleted.", { id: templateId });
4101
+ } catch (err) {
4102
+ process.exitCode = handleError(err);
4103
+ }
4104
+ });
4105
+ waTemplate.command("approvals").description("List template approval status from Meta").option("--connection-id <id>", "Filter by messaging connection ID").addHelpText(
4106
+ "after",
4107
+ `
4108
+ Examples:
4109
+ $ nexus channel whatsapp-template approvals
4110
+ $ nexus channel whatsapp-template approvals --json`
4111
+ ).action(async (opts) => {
4112
+ try {
4113
+ const client = createClient(program2.optsWithGlobals());
4114
+ const result = await client.channels.listTemplateApprovals({
4115
+ connectionId: opts.connectionId
4116
+ });
4117
+ const data = result?.data ?? result;
4118
+ const items = Array.isArray(data) ? data : [data];
4119
+ const rows = items.map((item) => ({
4120
+ sid: item.sid,
4121
+ name: item.approvalRequests?.name ?? "",
4122
+ category: item.approvalRequests?.category ?? "",
4123
+ status: item.approvalRequests?.status ?? "",
4124
+ rejection_reason: item.approvalRequests?.rejection_reason ?? ""
4125
+ }));
4126
+ printTable(rows, [
4127
+ { key: "sid", label: "SID", width: 38 },
4128
+ { key: "name", label: "NAME", width: 20 },
4129
+ { key: "category", label: "CATEGORY", width: 15 },
4130
+ { key: "status", label: "STATUS", width: 12 },
4131
+ { key: "rejection_reason", label: "REJECTION REASON", width: 30 }
4132
+ ]);
4133
+ } catch (err) {
4134
+ process.exitCode = handleError(err);
4135
+ }
4136
+ });
4137
+ waTemplate.command("submit-approval").description("Submit a template for Meta WhatsApp approval").requiredOption("--connection-id <id>", "Messaging connection ID").requiredOption("--template-id <id>", "Template ID (Twilio SID)").requiredOption("--name <name>", "Template name for approval").requiredOption(
4138
+ "--category <category>",
4139
+ "Approval category: UTILITY, MARKETING, or AUTHENTICATION"
4140
+ ).option("--wait", "Poll approval status until resolved (up to 2 minutes)").addHelpText(
4141
+ "after",
4142
+ `
4143
+ Examples:
4144
+ $ nexus channel whatsapp-template submit-approval --connection-id abc --template-id HX123 --name welcome --category UTILITY
4145
+ $ nexus channel whatsapp-template submit-approval --connection-id abc --template-id HX123 --name promo --category MARKETING --wait`
4146
+ ).action(async (opts) => {
4147
+ try {
4148
+ const client = createClient(program2.optsWithGlobals());
4149
+ const result = await client.channels.submitTemplateApproval({
4150
+ connectionId: opts.connectionId,
4151
+ templateId: opts.templateId,
4152
+ name: opts.name,
4153
+ category: opts.category
4154
+ });
4155
+ const data = result?.data ?? result;
4156
+ printRecord(data, [
4157
+ { key: "sid", label: "Approval SID" },
4158
+ { key: "status", label: "Status" }
4159
+ ]);
4160
+ printSuccess("Template submitted for Meta approval.");
4161
+ if (opts.wait) {
4162
+ const maxWaitMs = 12e4;
4163
+ const intervalMs = 5e3;
4164
+ const startTime = Date.now();
4165
+ let finalStatus = data.status;
4166
+ console.log("Waiting for approval...");
4167
+ while (Date.now() - startTime < maxWaitMs) {
4168
+ await new Promise((r) => setTimeout(r, intervalMs));
4169
+ const approvals = await client.channels.listTemplateApprovals({
4170
+ connectionId: opts.connectionId
4171
+ });
4172
+ const approvalsData = approvals?.data ?? approvals;
4173
+ const items = Array.isArray(approvalsData) ? approvalsData : [approvalsData];
4174
+ const match = items.find((a) => a.sid === opts.templateId);
4175
+ if (match?.approvalRequests?.status) {
4176
+ finalStatus = match.approvalRequests.status;
4177
+ if (finalStatus !== "pending" && finalStatus !== "unsubmitted") {
4178
+ console.log(`Approval resolved: ${color.cyan(finalStatus)}`);
4179
+ if (finalStatus === "rejected" && match.approvalRequests.rejection_reason) {
4180
+ console.log(`Reason: ${match.approvalRequests.rejection_reason}`);
4181
+ }
4182
+ break;
4183
+ }
4184
+ }
4185
+ }
4186
+ if (finalStatus === "pending" || finalStatus === "unsubmitted") {
4187
+ console.log(
4188
+ `Still ${finalStatus} after 2m. Check again: ${color.dim("nexus channel whatsapp-template approvals")}`
4189
+ );
4190
+ }
4191
+ }
4192
+ console.log(
4193
+ `
4194
+ Next: Attach to deployment: ${color.dim("nexus deployment template attach <depId> --template-id ...")}`
4195
+ );
4196
+ } catch (err) {
4197
+ process.exitCode = handleError(err);
4198
+ }
4199
+ });
3730
4200
  }
3731
4201
 
3732
4202
  // src/commands/collection.ts
@@ -4371,57 +4841,294 @@ Examples:
4371
4841
  ).action(async (id, opts) => {
4372
4842
  try {
4373
4843
  const client = createClient(program2.optsWithGlobals());
4374
- const base = await resolveBody(opts.body);
4375
- const body = mergeBodyWithFlags(base, {
4376
- ...opts.name !== void 0 && { name: opts.name }
4844
+ const base = await resolveBody(opts.body);
4845
+ const body = mergeBodyWithFlags(base, {
4846
+ ...opts.name !== void 0 && { name: opts.name }
4847
+ });
4848
+ await client.deploymentFolders.update(id, body);
4849
+ printSuccess("Deployment folder updated.", { id });
4850
+ } catch (err) {
4851
+ process.exitCode = handleError(err);
4852
+ }
4853
+ });
4854
+ depFolder.command("delete").description("Delete a deployment folder").argument("<id>", "Folder ID").option("--yes", "Skip confirmation").addHelpText(
4855
+ "after",
4856
+ `
4857
+ Examples:
4858
+ $ nexus deployment folder delete fld-123
4859
+ $ nexus deployment folder delete fld-123 --yes`
4860
+ ).action(async (id, opts) => {
4861
+ try {
4862
+ const client = createClient(program2.optsWithGlobals());
4863
+ if (!opts.yes && process.stdout.isTTY) {
4864
+ const readline2 = await import("readline/promises");
4865
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4866
+ const answer = await rl.question(`Delete deployment folder ${id}? [y/N] `);
4867
+ rl.close();
4868
+ if (answer.toLowerCase() !== "y") {
4869
+ console.log("Aborted.");
4870
+ return;
4871
+ }
4872
+ }
4873
+ await client.deploymentFolders.delete(id);
4874
+ printSuccess("Deployment folder deleted.", { id });
4875
+ } catch (err) {
4876
+ process.exitCode = handleError(err);
4877
+ }
4878
+ });
4879
+ depFolder.command("assign").description("Assign a deployment to a folder").requiredOption("--deployment-id <id>", "Deployment ID").requiredOption("--folder-id <id>", "Folder ID").addHelpText(
4880
+ "after",
4881
+ `
4882
+ Examples:
4883
+ $ nexus deployment folder assign --deployment-id dep-123 --folder-id fld-456`
4884
+ ).action(async (opts) => {
4885
+ try {
4886
+ const client = createClient(program2.optsWithGlobals());
4887
+ await client.deploymentFolders.assign({
4888
+ deploymentId: opts.deploymentId,
4889
+ folderId: opts.folderId
4890
+ });
4891
+ printSuccess("Deployment assigned to folder.", {
4892
+ deploymentId: opts.deploymentId,
4893
+ folderId: opts.folderId
4894
+ });
4895
+ } catch (err) {
4896
+ process.exitCode = handleError(err);
4897
+ }
4898
+ });
4899
+ const depTemplate = deployment.command("template").description("Manage WhatsApp templates attached to a deployment");
4900
+ depTemplate.command("list").description("List templates attached to a WhatsApp deployment").argument("<deploymentId>", "Deployment ID").addHelpText(
4901
+ "after",
4902
+ `
4903
+ Examples:
4904
+ $ nexus deployment template list dep-123
4905
+ $ nexus deployment template list dep-123 --json`
4906
+ ).action(async (deploymentId) => {
4907
+ try {
4908
+ const client = createClient(program2.optsWithGlobals());
4909
+ const result = await client.deployments.listDeploymentTemplates(deploymentId);
4910
+ const data = result?.data ?? result;
4911
+ const items = Array.isArray(data) ? data : [data];
4912
+ const rows = items.map((t) => ({
4913
+ templateId: t.templateId,
4914
+ name: t.name,
4915
+ type: t.type ?? "template",
4916
+ variables: Object.keys(t.variables ?? {}).length,
4917
+ multiLang: t.enableMultiLanguage ? "yes" : "no"
4918
+ }));
4919
+ printTable(rows, [
4920
+ { key: "templateId", label: "TEMPLATE ID", width: 38 },
4921
+ { key: "name", label: "NAME", width: 20 },
4922
+ { key: "type", label: "TYPE", width: 10 },
4923
+ { key: "variables", label: "VARS", width: 6 },
4924
+ { key: "multiLang", label: "MULTI-LANG", width: 10 }
4925
+ ]);
4926
+ } catch (err) {
4927
+ process.exitCode = handleError(err);
4928
+ }
4929
+ });
4930
+ depTemplate.command("attach").description("Attach a WhatsApp template to a deployment").argument("<deploymentId>", "Deployment ID").requiredOption("--template-id <id>", "Template ID (Twilio SID)").requiredOption("--name <name>", "Display name for this template").requiredOption("--description <text>", "Description of what this template does").option(
4931
+ "--variables <json>",
4932
+ 'Variables JSON: {"1":{"description":"Name","isBodyVariable":true}}'
4933
+ ).option("--type <type>", "Template type: template, card, or carousel", "template").option("--enable-multi-language", "Enable multi-language support").option("--enable-dynamic-size", "Enable dynamic carousel size (carousel only)").option(
4934
+ "--carousel-template-group <json>",
4935
+ "Carousel template group JSON with size variants (carousel only)"
4936
+ ).option(
4937
+ "--single-item-card-template-id <id>",
4938
+ "Fallback card template ID for single-item carousels"
4939
+ ).option(
4940
+ "--single-item-card-template-group <json>",
4941
+ "Single-item card template group JSON for multi-language fallback"
4942
+ ).addHelpText(
4943
+ "after",
4944
+ `
4945
+ Examples:
4946
+ $ nexus deployment template attach dep-123 --template-id HX456 --name welcome --description "Welcome message"
4947
+ $ nexus deployment template attach dep-123 --template-id HX456 --name order --description "Order confirmation" --variables '{"1":{"description":"Customer name","isBodyVariable":true}}'
4948
+ $ nexus deployment template attach dep-123 --template-id HX456 --name products --description "Product carousel" --type carousel --enable-dynamic-size --carousel-template-group '{"baseName":"products","availableTemplates":[{"language":"en","carouselSize":3,"templateId":"HX111"},{"language":"en","carouselSize":5,"templateId":"HX222"}],"minCarouselSize":3,"maxCarouselSize":5}'`
4949
+ ).action(async (deploymentId, opts) => {
4950
+ try {
4951
+ let variables;
4952
+ if (opts.variables) {
4953
+ try {
4954
+ variables = JSON.parse(opts.variables);
4955
+ } catch {
4956
+ console.error("Error: --variables must be valid JSON.");
4957
+ process.exitCode = 1;
4958
+ return;
4959
+ }
4960
+ }
4961
+ let carouselTemplateGroup;
4962
+ if (opts.carouselTemplateGroup) {
4963
+ try {
4964
+ carouselTemplateGroup = JSON.parse(opts.carouselTemplateGroup);
4965
+ } catch {
4966
+ console.error("Error: --carousel-template-group must be valid JSON.");
4967
+ process.exitCode = 1;
4968
+ return;
4969
+ }
4970
+ }
4971
+ let singleItemCardTemplateGroup;
4972
+ if (opts.singleItemCardTemplateGroup) {
4973
+ try {
4974
+ singleItemCardTemplateGroup = JSON.parse(opts.singleItemCardTemplateGroup);
4975
+ } catch {
4976
+ console.error("Error: --single-item-card-template-group must be valid JSON.");
4977
+ process.exitCode = 1;
4978
+ return;
4979
+ }
4980
+ }
4981
+ const client = createClient(program2.optsWithGlobals());
4982
+ const result = await client.deployments.attachDeploymentTemplate(deploymentId, {
4983
+ templateId: opts.templateId,
4984
+ name: opts.name,
4985
+ description: opts.description,
4986
+ variables,
4987
+ type: opts.type,
4988
+ enableMultiLanguage: opts.enableMultiLanguage,
4989
+ enableDynamicSize: opts.enableDynamicSize,
4990
+ carouselTemplateGroup,
4991
+ singleItemCardTemplateId: opts.singleItemCardTemplateId,
4992
+ singleItemCardTemplateGroup
4377
4993
  });
4378
- await client.deploymentFolders.update(id, body);
4379
- printSuccess("Deployment folder updated.", { id });
4994
+ const data = result?.data ?? result;
4995
+ printRecord(data, [
4996
+ { key: "templateId", label: "Template ID" },
4997
+ { key: "name", label: "Name" },
4998
+ { key: "description", label: "Description" },
4999
+ { key: "type", label: "Type" }
5000
+ ]);
5001
+ printSuccess("Template attached to deployment.");
5002
+ } catch (err) {
5003
+ process.exitCode = handleError(err);
5004
+ }
5005
+ });
5006
+ depTemplate.command("update").description("Update a template's configuration on a deployment").argument("<deploymentId>", "Deployment ID").argument("<templateId>", "Template ID (Twilio SID)").option("--name <name>", "New display name").option("--description <text>", "New description").option("--variables <json>", "Updated variables JSON").option("--enable-multi-language", "Enable multi-language support").option("--no-multi-language", "Disable multi-language support").option("--enable-dynamic-size", "Enable dynamic carousel size (carousel only)").option("--no-dynamic-size", "Disable dynamic carousel size").option(
5007
+ "--carousel-template-group <json>",
5008
+ "Carousel template group JSON with size variants (carousel only)"
5009
+ ).option(
5010
+ "--single-item-card-template-id <id>",
5011
+ "Fallback card template ID for single-item carousels"
5012
+ ).option(
5013
+ "--single-item-card-template-group <json>",
5014
+ "Single-item card template group JSON for multi-language fallback"
5015
+ ).addHelpText(
5016
+ "after",
5017
+ `
5018
+ Examples:
5019
+ $ nexus deployment template update dep-123 HX456 --name "Updated Welcome"
5020
+ $ nexus deployment template update dep-123 HX456 --variables '{"1":{"description":"Full name"}}'
5021
+ $ nexus deployment template update dep-123 HX456 --enable-dynamic-size --carousel-template-group '{"baseName":"products","availableTemplates":[...]}'`
5022
+ ).action(async (deploymentId, templateId, opts) => {
5023
+ try {
5024
+ const body = {};
5025
+ if (opts.name !== void 0) body.name = opts.name;
5026
+ if (opts.description !== void 0) body.description = opts.description;
5027
+ if (opts.enableMultiLanguage !== void 0)
5028
+ body.enableMultiLanguage = opts.enableMultiLanguage;
5029
+ if (opts.enableDynamicSize !== void 0)
5030
+ body.enableDynamicSize = opts.enableDynamicSize;
5031
+ if (opts.singleItemCardTemplateId !== void 0)
5032
+ body.singleItemCardTemplateId = opts.singleItemCardTemplateId;
5033
+ if (opts.variables) {
5034
+ try {
5035
+ body.variables = JSON.parse(opts.variables);
5036
+ } catch {
5037
+ console.error("Error: --variables must be valid JSON.");
5038
+ process.exitCode = 1;
5039
+ return;
5040
+ }
5041
+ }
5042
+ if (opts.carouselTemplateGroup) {
5043
+ try {
5044
+ body.carouselTemplateGroup = JSON.parse(opts.carouselTemplateGroup);
5045
+ } catch {
5046
+ console.error("Error: --carousel-template-group must be valid JSON.");
5047
+ process.exitCode = 1;
5048
+ return;
5049
+ }
5050
+ }
5051
+ if (opts.singleItemCardTemplateGroup) {
5052
+ try {
5053
+ body.singleItemCardTemplateGroup = JSON.parse(opts.singleItemCardTemplateGroup);
5054
+ } catch {
5055
+ console.error("Error: --single-item-card-template-group must be valid JSON.");
5056
+ process.exitCode = 1;
5057
+ return;
5058
+ }
5059
+ }
5060
+ const client = createClient(program2.optsWithGlobals());
5061
+ const result = await client.deployments.updateDeploymentTemplate(
5062
+ deploymentId,
5063
+ templateId,
5064
+ body
5065
+ );
5066
+ const data = result?.data ?? result;
5067
+ printRecord(data, [
5068
+ { key: "templateId", label: "Template ID" },
5069
+ { key: "name", label: "Name" },
5070
+ { key: "description", label: "Description" }
5071
+ ]);
5072
+ printSuccess("Deployment template updated.");
4380
5073
  } catch (err) {
4381
5074
  process.exitCode = handleError(err);
4382
5075
  }
4383
5076
  });
4384
- depFolder.command("delete").description("Delete a deployment folder").argument("<id>", "Folder ID").option("--yes", "Skip confirmation").addHelpText(
4385
- "after",
4386
- `
4387
- Examples:
4388
- $ nexus deployment folder delete fld-123
4389
- $ nexus deployment folder delete fld-123 --yes`
4390
- ).action(async (id, opts) => {
5077
+ depTemplate.command("detach").description("Detach a template from a deployment").argument("<deploymentId>", "Deployment ID").argument("<templateId>", "Template ID (Twilio SID)").option("--yes", "Skip confirmation prompt").action(async (deploymentId, templateId, opts) => {
4391
5078
  try {
4392
- const client = createClient(program2.optsWithGlobals());
4393
- if (!opts.yes && process.stdout.isTTY) {
4394
- const readline2 = await import("readline/promises");
4395
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4396
- const answer = await rl.question(`Delete deployment folder ${id}? [y/N] `);
5079
+ if (!opts.yes && process.stdin.isTTY) {
5080
+ const readline2 = await import("readline");
5081
+ const rl = readline2.createInterface({
5082
+ input: process.stdin,
5083
+ output: process.stdout
5084
+ });
5085
+ const answer = await new Promise((resolve) => {
5086
+ rl.question(
5087
+ `Detach template ${templateId} from deployment ${deploymentId}? (y/N) `,
5088
+ resolve
5089
+ );
5090
+ });
4397
5091
  rl.close();
4398
5092
  if (answer.toLowerCase() !== "y") {
4399
- console.log("Aborted.");
5093
+ console.log("Cancelled.");
4400
5094
  return;
4401
5095
  }
4402
5096
  }
4403
- await client.deploymentFolders.delete(id);
4404
- printSuccess("Deployment folder deleted.", { id });
5097
+ const client = createClient(program2.optsWithGlobals());
5098
+ await client.deployments.detachDeploymentTemplate(deploymentId, templateId);
5099
+ printSuccess("Template detached from deployment.", { templateId });
4405
5100
  } catch (err) {
4406
5101
  process.exitCode = handleError(err);
4407
5102
  }
4408
5103
  });
4409
- depFolder.command("assign").description("Assign a deployment to a folder").requiredOption("--deployment-id <id>", "Deployment ID").requiredOption("--folder-id <id>", "Folder ID").addHelpText(
5104
+ depTemplate.command("settings").description("View or update deployment template settings").argument("<deploymentId>", "Deployment ID").option(
5105
+ "--allow-dynamic-templates <bool>",
5106
+ "Allow agent to dynamically create and send templates (true/false)"
5107
+ ).addHelpText(
4410
5108
  "after",
4411
5109
  `
4412
5110
  Examples:
4413
- $ nexus deployment folder assign --deployment-id dep-123 --folder-id fld-456`
4414
- ).action(async (opts) => {
5111
+ $ nexus deployment template settings dep-123
5112
+ $ nexus deployment template settings dep-123 --allow-dynamic-templates true`
5113
+ ).action(async (deploymentId, opts) => {
4415
5114
  try {
4416
5115
  const client = createClient(program2.optsWithGlobals());
4417
- await client.deploymentFolders.assign({
4418
- deploymentId: opts.deploymentId,
4419
- folderId: opts.folderId
4420
- });
4421
- printSuccess("Deployment assigned to folder.", {
4422
- deploymentId: opts.deploymentId,
4423
- folderId: opts.folderId
4424
- });
5116
+ if (opts.allowDynamicTemplates !== void 0) {
5117
+ const value = opts.allowDynamicTemplates === "true";
5118
+ await client.deployments.updateDeploymentTemplateSettings(deploymentId, {
5119
+ allowAgentToCreateAndSendTemplates: value
5120
+ });
5121
+ printSuccess(
5122
+ `Dynamic template creation ${value ? "enabled" : "disabled"} for deployment.`
5123
+ );
5124
+ } else {
5125
+ const result = await client.deployments.listDeploymentTemplates(deploymentId);
5126
+ const data = result?.data ?? result;
5127
+ console.log(`Templates attached: ${Array.isArray(data) ? data.length : 0}`);
5128
+ console.log(
5129
+ `Tip: Use --allow-dynamic-templates true/false to toggle agent template creation.`
5130
+ );
5131
+ }
4425
5132
  } catch (err) {
4426
5133
  process.exitCode = handleError(err);
4427
5134
  }
@@ -4905,7 +5612,7 @@ All commands follow the pattern: \`nexus <group> <action> [arguments] [options]\
4905
5612
  | [\`eval\`](docs/command-reference.md#nexus-eval) | (subgroups: \`session\`, \`dataset\`, \`execute\`, \`judge\`, \`results\`, \`formats\`, \`judges\`) | AI task evaluation |
4906
5613
  | [\`ticket\`](docs/command-reference.md#nexus-ticket) | \`list\` \`get\` \`create\` \`update\` \`comment\` \`comments\` | Bug and feature tracking |
4907
5614
  | [\`phone-number\`](docs/command-reference.md#nexus-phone-number) | \`search\` \`buy\` \`list\` \`get\` \`release\` | Phone number management |
4908
- | [\`channel\`](docs/command-reference.md#nexus-channel) | \`setup\` \`connection list\\|create\` \`whatsapp-sender list\\|create\\|get\` | Channel setup orchestrator |
5615
+ | [\`channel\`](docs/command-reference.md#nexus-channel) | \`setup\` \`connection list\\|create\` \`whatsapp-sender list\\|create\\|get\` | Channel setup orchestrator |
4909
5616
  | [\`prompt-assistant\`](docs/command-reference.md#nexus-prompt-assistant) | \`chat\` \`get-thread\` \`delete-thread\` | AI-assisted prompt writing |
4910
5617
 
4911
5618
  ### Utility
@@ -5933,12 +6640,12 @@ List deployments.
5933
6640
  nexus deployment list [options]
5934
6641
  \`\`\`
5935
6642
 
5936
- | Option | Description |
5937
- | ------------------ | --------------------------------------------- |
5938
- | \`--agent-id <id>\` | Filter by agent ID |
6643
+ | Option | Description |
6644
+ | ------------------ | --------------------------------------------------------------- |
6645
+ | \`--agent-id <id>\` | Filter by agent ID |
5939
6646
  | \`--type <type>\` | Filter by type (\`EMBED\`, \`API\`, \`WHATSAPP\`, \`TWILIO_SMS\`, etc.) |
5940
- | \`--page <number>\` | Page number |
5941
- | \`--limit <number>\` | Items per page |
6647
+ | \`--page <number>\` | Page number |
6648
+ | \`--limit <number>\` | Items per page |
5942
6649
 
5943
6650
  \`\`\`bash
5944
6651
  nexus deployment list
@@ -5975,13 +6682,13 @@ Create a new deployment.
5975
6682
  nexus deployment create [options]
5976
6683
  \`\`\`
5977
6684
 
5978
- | Option | Required | Description |
5979
- | ---------------------- | -------- | --------------------------------------------------------------------------- |
5980
- | \`--name <name>\` | Yes | Deployment name |
6685
+ | Option | Required | Description |
6686
+ | ---------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
6687
+ | \`--name <name>\` | Yes | Deployment name |
5981
6688
  | \`--type <type>\` | Yes | Deployment type: \`EMBED\`, \`API\`, \`WHATSAPP\`, \`TWILIO_SMS\`, \`TWILIO_VOICE\`, \`TELEGRAM\`, \`SLACK\`, \`GMAIL\`, \`OUTLOOK\`, \`TEAMS\` |
5982
- | \`--agent-id <id>\` | No | Agent ID |
5983
- | \`--description <text>\` | No | Deployment description |
5984
- | \`--body <json>\` | No | Request body as JSON, \`.json\` file, or \`-\` for stdin |
6689
+ | \`--agent-id <id>\` | No | Agent ID |
6690
+ | \`--description <text>\` | No | Deployment description |
6691
+ | \`--body <json>\` | No | Request body as JSON, \`.json\` file, or \`-\` for stdin |
5985
6692
 
5986
6693
  **Important:** Channel deployments (WhatsApp, SMS, Voice, etc.) have prerequisites. Run \`nexus channel setup --type <TYPE>\` first to see what's needed.
5987
6694
 
@@ -8697,11 +9404,11 @@ Check or auto-provision channel setup prerequisites. Returns a step-by-step chec
8697
9404
  nexus channel setup [options]
8698
9405
  \`\`\`
8699
9406
 
8700
- | Option | Required | Description |
8701
- | ----------------- | -------- | ---------------------------------------------------------------- |
8702
- | \`--type <type>\` | Yes | Deployment type (\`WHATSAPP\`, \`TWILIO_SMS\`, \`TWILIO_VOICE\`, etc.) |
8703
- | \`--auto\` | No | Auto-provision what is possible (e.g., create messaging connection) |
8704
- | \`--region <region>\` | No | Region for auto-provisioning: \`us1\` or \`ie1\` (default: \`us1\`) |
9407
+ | Option | Required | Description |
9408
+ | ------------------- | -------- | ------------------------------------------------------------------- |
9409
+ | \`--type <type>\` | Yes | Deployment type (\`WHATSAPP\`, \`TWILIO_SMS\`, \`TWILIO_VOICE\`, etc.) |
9410
+ | \`--auto\` | No | Auto-provision what is possible (e.g., create messaging connection) |
9411
+ | \`--region <region>\` | No | Region for auto-provisioning: \`us1\` or \`ie1\` (default: \`us1\`) |
8705
9412
 
8706
9413
  \`\`\`bash
8707
9414
  nexus channel setup --type WHATSAPP
@@ -8719,7 +9426,7 @@ Open the browser to connect your WhatsApp Business Account via Meta's Embedded S
8719
9426
  nexus channel connect-waba
8720
9427
  \`\`\`
8721
9428
 
8722
- Opens \`{NEXUS_DASHBOARD_URL}/app/settings/phone-number\` in your default browser. Complete the "Connect with Meta" flow, then verify with \`nexus channel setup --type WHATSAPP\`.
9429
+ Opens \`{NEXUS_DASHBOARD_URL}/app/connect-waba\` in your default browser \u2014 a dedicated page with a single "Connect with Meta" button. Complete the flow, then verify with \`nexus channel setup --type WHATSAPP\`.
8723
9430
 
8724
9431
  \`\`\`bash
8725
9432
  nexus channel connect-waba
@@ -8749,8 +9456,8 @@ Create a messaging connection (max 1 per organization via the API).
8749
9456
  nexus channel connection create [options]
8750
9457
  \`\`\`
8751
9458
 
8752
- | Option | Description |
8753
- | ------------------ | ------------------------------- |
9459
+ | Option | Description |
9460
+ | ------------------- | --------------------------------------- |
8754
9461
  | \`--region <region>\` | Region: \`us1\` or \`ie1\` (default: \`us1\`) |
8755
9462
 
8756
9463
  \`\`\`bash
@@ -8782,12 +9489,12 @@ Create a WhatsApp sender (registers a phone number with WhatsApp Business via Me
8782
9489
  nexus channel whatsapp-sender create [options]
8783
9490
  \`\`\`
8784
9491
 
8785
- | Option | Required | Description |
8786
- | -------------------------- | -------- | ----------------------------------------------------- |
8787
- | \`--connection-id <id>\` | Yes | Messaging connection ID |
8788
- | \`--phone-number-id <id>\` | Yes | Phone number ID |
8789
- | \`--sender-name <name>\` | Yes | Display name for the WhatsApp sender |
8790
- | \`--waba-id <id>\` | No | WhatsApp Business Account ID (reads from connection if omitted) |
9492
+ | Option | Required | Description |
9493
+ | ------------------------ | -------- | --------------------------------------------------------------- |
9494
+ | \`--connection-id <id>\` | Yes | Messaging connection ID |
9495
+ | \`--phone-number-id <id>\` | Yes | Phone number ID |
9496
+ | \`--sender-name <name>\` | Yes | Display name for the WhatsApp sender |
9497
+ | \`--waba-id <id>\` | No | WhatsApp Business Account ID (reads from connection if omitted) |
8791
9498
 
8792
9499
  \`\`\`bash
8793
9500
  nexus channel whatsapp-sender create \\
@@ -10257,7 +10964,7 @@ ${"\u2550".repeat(72)}`);
10257
10964
  }
10258
10965
 
10259
10966
  // src/commands/document.ts
10260
- var import_node_fs2 = __toESM(require("fs"));
10967
+ var import_node_fs3 = __toESM(require("fs"));
10261
10968
  var import_node_path2 = __toESM(require("path"));
10262
10969
  init_output();
10263
10970
  function registerDocumentCommands(program2) {
@@ -10323,12 +11030,12 @@ Examples:
10323
11030
  try {
10324
11031
  const client = createClient(program2.optsWithGlobals());
10325
11032
  const absPath = import_node_path2.default.resolve(filePath);
10326
- if (!import_node_fs2.default.existsSync(absPath)) {
11033
+ if (!import_node_fs3.default.existsSync(absPath)) {
10327
11034
  console.error(`Error: File not found: ${absPath}`);
10328
11035
  process.exitCode = 1;
10329
11036
  return;
10330
11037
  }
10331
- const buffer = import_node_fs2.default.readFileSync(absPath);
11038
+ const buffer = import_node_fs3.default.readFileSync(absPath);
10332
11039
  const blob = new Blob([buffer]);
10333
11040
  const fileName = import_node_path2.default.basename(absPath);
10334
11041
  const doc = await client.documents.uploadFile(blob, fileName, opts.description);
@@ -11626,7 +12333,7 @@ Notes:
11626
12333
  }
11627
12334
 
11628
12335
  // src/commands/template.ts
11629
- var import_node_fs3 = __toESM(require("fs"));
12336
+ var import_node_fs4 = __toESM(require("fs"));
11630
12337
  var import_node_path3 = __toESM(require("path"));
11631
12338
  init_output();
11632
12339
  function registerTemplateCommands(program2) {
@@ -11712,12 +12419,12 @@ Examples:
11712
12419
  try {
11713
12420
  const client = createClient(program2.optsWithGlobals());
11714
12421
  const absPath = import_node_path3.default.resolve(opts.file);
11715
- if (!import_node_fs3.default.existsSync(absPath)) {
12422
+ if (!import_node_fs4.default.existsSync(absPath)) {
11716
12423
  console.error(`Error: File not found: ${absPath}`);
11717
12424
  process.exitCode = 1;
11718
12425
  return;
11719
12426
  }
11720
- const buffer = import_node_fs3.default.readFileSync(absPath);
12427
+ const buffer = import_node_fs4.default.readFileSync(absPath);
11721
12428
  const blob = new Blob([buffer]);
11722
12429
  const fileName = import_node_path3.default.basename(absPath);
11723
12430
  const result = await client.skills.uploadDocumentTemplateFile(id, blob, fileName);
@@ -12030,15 +12737,387 @@ Examples:
12030
12737
  });
12031
12738
  }
12032
12739
 
12740
+ // src/commands/tracing.ts
12741
+ init_output();
12742
+ function registerTracingCommands(program2) {
12743
+ const tracing = program2.command("tracing").description("View LLM traces and analytics");
12744
+ addPaginationOptions(
12745
+ tracing.command("traces").description("List LLM traces").option("--status <status>", "Filter by status (IN_PROGRESS, COMPLETED, FAILED)").option("--agent-id <id>", "Filter by agent ID").option("--workflow-id <id>", "Filter by workflow ID").option("--model <name>", "Filter by model name").option("--start-date <iso>", "Filter from date (ISO 8601)").option("--end-date <iso>", "Filter to date (ISO 8601)").option(
12746
+ "--sort-by <field>",
12747
+ "Sort by field (startedAt, totalCostUsd, totalDurationMs)",
12748
+ "startedAt"
12749
+ ).option("--order <dir>", "Sort order (asc, desc)", "desc").addHelpText(
12750
+ "after",
12751
+ `
12752
+ Examples:
12753
+ $ nexus tracing traces
12754
+ $ nexus tracing traces --status FAILED --limit 10
12755
+ $ nexus tracing traces --agent-id abc --start-date 2026-03-01 --json`
12756
+ )
12757
+ ).action(async (opts) => {
12758
+ try {
12759
+ const client = createClient(program2.optsWithGlobals());
12760
+ const { data, meta } = await client.tracing.listTraces({
12761
+ ...getPaginationParams(opts),
12762
+ status: opts.status,
12763
+ agentId: opts.agentId,
12764
+ workflowId: opts.workflowId,
12765
+ model: opts.model,
12766
+ startDate: opts.startDate,
12767
+ endDate: opts.endDate,
12768
+ sortBy: opts.sortBy,
12769
+ order: opts.order
12770
+ });
12771
+ printList(data, meta, [
12772
+ { key: "id", label: "ID", width: 36 },
12773
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12774
+ { key: "agentName", label: "AGENT", width: 20 },
12775
+ { key: "workflowName", label: "WORKFLOW", width: 20 },
12776
+ {
12777
+ key: "totalCostUsd",
12778
+ label: "COST ($)",
12779
+ width: 10,
12780
+ format: (v) => v != null ? `$${Number(v).toFixed(4)}` : "-"
12781
+ },
12782
+ {
12783
+ key: "totalDurationMs",
12784
+ label: "DURATION",
12785
+ width: 10,
12786
+ format: (v) => v != null ? `${Number(v)}ms` : "-"
12787
+ },
12788
+ { key: "generationCount", label: "GENS", width: 5 },
12789
+ { key: "startedAt", label: "STARTED", width: 20 }
12790
+ ]);
12791
+ } catch (err) {
12792
+ process.exitCode = handleError(err);
12793
+ }
12794
+ });
12795
+ tracing.command("trace").description("Get trace details with generations").argument("<id>", "Trace ID").addHelpText(
12796
+ "after",
12797
+ `
12798
+ Examples:
12799
+ $ nexus tracing trace abc-123
12800
+ $ nexus tracing trace abc-123 --json`
12801
+ ).action(async (id) => {
12802
+ try {
12803
+ const client = createClient(program2.optsWithGlobals());
12804
+ const trace = await client.tracing.getTrace(id);
12805
+ printRecord(trace, [
12806
+ { key: "id", label: "ID" },
12807
+ { key: "status", label: "Status" },
12808
+ { key: "agentName", label: "Agent" },
12809
+ { key: "workflowName", label: "Workflow" },
12810
+ { key: "totalCostUsd", label: "Cost ($)", format: (v) => v != null ? `$${Number(v).toFixed(4)}` : "-" },
12811
+ { key: "totalInputTokens", label: "Input Tokens" },
12812
+ { key: "totalOutputTokens", label: "Output Tokens" },
12813
+ { key: "totalDurationMs", label: "Duration (ms)" },
12814
+ { key: "startedAt", label: "Started" },
12815
+ { key: "completedAt", label: "Completed" }
12816
+ ]);
12817
+ const gens = trace.generations;
12818
+ if (gens && gens.length > 0) {
12819
+ console.log(`
12820
+ ${color.bold("Generations")} (${gens.length}):
12821
+ `);
12822
+ printList(gens, void 0, [
12823
+ { key: "id", label: "ID", width: 36 },
12824
+ { key: "modelName", label: "MODEL", width: 25 },
12825
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12826
+ {
12827
+ key: "costUsd",
12828
+ label: "COST ($)",
12829
+ width: 10,
12830
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
12831
+ },
12832
+ {
12833
+ key: "durationMs",
12834
+ label: "DURATION",
12835
+ width: 10,
12836
+ format: (v) => v != null ? `${v}ms` : "-"
12837
+ }
12838
+ ]);
12839
+ }
12840
+ } catch (err) {
12841
+ process.exitCode = handleError(err);
12842
+ }
12843
+ });
12844
+ tracing.command("delete").description("Delete a trace and its generations").argument("<id>", "Trace ID").addHelpText(
12845
+ "after",
12846
+ `
12847
+ Examples:
12848
+ $ nexus tracing delete abc-123`
12849
+ ).action(async (id) => {
12850
+ try {
12851
+ const client = createClient(program2.optsWithGlobals());
12852
+ await client.tracing.deleteTrace(id);
12853
+ printSuccess("Trace deleted.", { id });
12854
+ } catch (err) {
12855
+ process.exitCode = handleError(err);
12856
+ }
12857
+ });
12858
+ addPaginationOptions(
12859
+ tracing.command("generations").description("List LLM generations across traces").option("--trace-id <id>", "Filter by trace ID").option("--provider <provider>", "Filter by provider (OPEN_AI, ANTHROPIC, GOOGLE_AI)").option("--model <name>", "Filter by model name").option("--status <status>", "Filter by status (PENDING, RUNNING, COMPLETED, FAILED)").option("--agent-id <id>", "Filter by agent ID").option("--task-id <id>", "Filter by task ID").option("--start-date <iso>", "Filter from date").option("--end-date <iso>", "Filter to date").option("--min-cost <usd>", "Minimum cost in USD").option("--max-cost <usd>", "Maximum cost in USD").addHelpText(
12860
+ "after",
12861
+ `
12862
+ Examples:
12863
+ $ nexus tracing generations
12864
+ $ nexus tracing generations --provider ANTHROPIC --status FAILED
12865
+ $ nexus tracing generations --trace-id abc-123 --json`
12866
+ )
12867
+ ).action(async (opts) => {
12868
+ try {
12869
+ const client = createClient(program2.optsWithGlobals());
12870
+ const { data, meta } = await client.tracing.listGenerations({
12871
+ ...getPaginationParams(opts),
12872
+ traceId: opts.traceId,
12873
+ provider: opts.provider,
12874
+ modelName: opts.model,
12875
+ status: opts.status,
12876
+ agentId: opts.agentId,
12877
+ taskId: opts.taskId,
12878
+ startDate: opts.startDate,
12879
+ endDate: opts.endDate,
12880
+ minCostUsd: opts.minCost ? parseFloat(opts.minCost) : void 0,
12881
+ maxCostUsd: opts.maxCost ? parseFloat(opts.maxCost) : void 0
12882
+ });
12883
+ printList(data, meta, [
12884
+ { key: "id", label: "ID", width: 36 },
12885
+ { key: "traceId", label: "TRACE", width: 36 },
12886
+ { key: "modelName", label: "MODEL", width: 25 },
12887
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12888
+ {
12889
+ key: "costUsd",
12890
+ label: "COST ($)",
12891
+ width: 10,
12892
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
12893
+ },
12894
+ {
12895
+ key: "durationMs",
12896
+ label: "DURATION",
12897
+ width: 10,
12898
+ format: (v) => v != null ? `${v}ms` : "-"
12899
+ }
12900
+ ]);
12901
+ } catch (err) {
12902
+ process.exitCode = handleError(err);
12903
+ }
12904
+ });
12905
+ tracing.command("generation").description("Get generation details including prompt and response").argument("<id>", "Generation ID").addHelpText(
12906
+ "after",
12907
+ `
12908
+ Examples:
12909
+ $ nexus tracing generation gen-123
12910
+ $ nexus tracing generation gen-123 --json`
12911
+ ).action(async (id) => {
12912
+ try {
12913
+ const client = createClient(program2.optsWithGlobals());
12914
+ const gen = await client.tracing.getGeneration(id);
12915
+ printRecord(gen, [
12916
+ { key: "id", label: "ID" },
12917
+ { key: "traceId", label: "Trace ID" },
12918
+ { key: "provider", label: "Provider" },
12919
+ { key: "modelName", label: "Model" },
12920
+ { key: "status", label: "Status" },
12921
+ { key: "inputTokens", label: "Input Tokens" },
12922
+ { key: "outputTokens", label: "Output Tokens" },
12923
+ {
12924
+ key: "costUsd",
12925
+ label: "Cost ($)",
12926
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
12927
+ },
12928
+ { key: "durationMs", label: "Duration (ms)" },
12929
+ { key: "temperature", label: "Temperature" },
12930
+ { key: "taskName", label: "Task" },
12931
+ { key: "startedAt", label: "Started" },
12932
+ { key: "completedAt", label: "Completed" },
12933
+ { key: "errorMessage", label: "Error" }
12934
+ ]);
12935
+ } catch (err) {
12936
+ process.exitCode = handleError(err);
12937
+ }
12938
+ });
12939
+ tracing.command("models").description("List distinct model names used in traces").addHelpText(
12940
+ "after",
12941
+ `
12942
+ Examples:
12943
+ $ nexus tracing models`
12944
+ ).action(async () => {
12945
+ try {
12946
+ const client = createClient(program2.optsWithGlobals());
12947
+ const models = await client.tracing.listModels();
12948
+ if (Array.isArray(models) && models.length > 0) {
12949
+ for (const m of models) console.log(m);
12950
+ } else {
12951
+ console.log("No models found.");
12952
+ }
12953
+ } catch (err) {
12954
+ process.exitCode = handleError(err);
12955
+ }
12956
+ });
12957
+ tracing.command("summary").description("Get tracing analytics summary").option("--start-date <iso>", "Period start (ISO 8601)").option("--end-date <iso>", "Period end (ISO 8601)").addHelpText(
12958
+ "after",
12959
+ `
12960
+ Examples:
12961
+ $ nexus tracing summary
12962
+ $ nexus tracing summary --start-date 2026-03-01 --end-date 2026-03-30
12963
+ $ nexus tracing summary --json`
12964
+ ).action(async (opts) => {
12965
+ try {
12966
+ const client = createClient(program2.optsWithGlobals());
12967
+ const summary = await client.tracing.getSummary({
12968
+ startDate: opts.startDate,
12969
+ endDate: opts.endDate
12970
+ });
12971
+ printRecord(summary, [
12972
+ { key: "totalTraces", label: "Total Traces" },
12973
+ { key: "completedTraces", label: "Completed" },
12974
+ { key: "failedTraces", label: "Failed" },
12975
+ { key: "inProgressTraces", label: "In Progress" },
12976
+ {
12977
+ key: "totalCostUsd",
12978
+ label: "Total Cost ($)",
12979
+ format: (v) => `$${Number(v).toFixed(4)}`
12980
+ },
12981
+ { key: "totalInputTokens", label: "Input Tokens" },
12982
+ { key: "totalOutputTokens", label: "Output Tokens" },
12983
+ {
12984
+ key: "avgDurationMs",
12985
+ label: "Avg Duration",
12986
+ format: (v) => v != null ? `${Number(v).toFixed(0)}ms` : "-"
12987
+ },
12988
+ { key: "distinctModelCount", label: "Distinct Models" }
12989
+ ]);
12990
+ } catch (err) {
12991
+ process.exitCode = handleError(err);
12992
+ }
12993
+ });
12994
+ tracing.command("cost-breakdown").description("Get cost breakdown by model, agent, or workflow").option("--group-by <key>", "Group by (model, agent, workflow)", "model").option("--start-date <iso>", "Period start").option("--end-date <iso>", "Period end").addHelpText(
12995
+ "after",
12996
+ `
12997
+ Examples:
12998
+ $ nexus tracing cost-breakdown
12999
+ $ nexus tracing cost-breakdown --group-by agent
13000
+ $ nexus tracing cost-breakdown --group-by workflow --json`
13001
+ ).action(async (opts) => {
13002
+ try {
13003
+ const client = createClient(program2.optsWithGlobals());
13004
+ const result = await client.tracing.getCostBreakdown({
13005
+ groupBy: opts.groupBy,
13006
+ startDate: opts.startDate,
13007
+ endDate: opts.endDate
13008
+ });
13009
+ const entries = result.entries ?? [];
13010
+ printList(entries, void 0, [
13011
+ { key: "groupKey", label: "KEY", width: 36 },
13012
+ { key: "groupLabel", label: "LABEL", width: 25 },
13013
+ {
13014
+ key: "totalCostUsd",
13015
+ label: "COST ($)",
13016
+ width: 12,
13017
+ format: (v) => `$${Number(v).toFixed(4)}`
13018
+ },
13019
+ { key: "traceCount", label: "TRACES", width: 8 },
13020
+ { key: "generationCount", label: "GENS", width: 8 },
13021
+ { key: "totalInputTokens", label: "IN TOKENS", width: 12 },
13022
+ { key: "totalOutputTokens", label: "OUT TOKENS", width: 12 }
13023
+ ]);
13024
+ } catch (err) {
13025
+ process.exitCode = handleError(err);
13026
+ }
13027
+ });
13028
+ tracing.command("timeline").description("Get tracing timeline data").option("--granularity <g>", "Granularity (hour, day, week)", "day").option("--start-date <iso>", "Period start").option("--end-date <iso>", "Period end").addHelpText(
13029
+ "after",
13030
+ `
13031
+ Examples:
13032
+ $ nexus tracing timeline
13033
+ $ nexus tracing timeline --granularity hour --start-date 2026-03-29 --json`
13034
+ ).action(async (opts) => {
13035
+ try {
13036
+ const client = createClient(program2.optsWithGlobals());
13037
+ const result = await client.tracing.getTimeline({
13038
+ granularity: opts.granularity,
13039
+ startDate: opts.startDate,
13040
+ endDate: opts.endDate
13041
+ });
13042
+ const points = result.points ?? [];
13043
+ printList(points, void 0, [
13044
+ { key: "date", label: "DATE", width: 22 },
13045
+ { key: "traceCount", label: "TRACES", width: 8 },
13046
+ { key: "generationCount", label: "GENS", width: 8 },
13047
+ {
13048
+ key: "totalCostUsd",
13049
+ label: "COST ($)",
13050
+ width: 12,
13051
+ format: (v) => `$${Number(v).toFixed(4)}`
13052
+ },
13053
+ {
13054
+ key: "avgDurationMs",
13055
+ label: "AVG DUR",
13056
+ width: 10,
13057
+ format: (v) => v != null ? `${Number(v).toFixed(0)}ms` : "-"
13058
+ }
13059
+ ]);
13060
+ } catch (err) {
13061
+ process.exitCode = handleError(err);
13062
+ }
13063
+ });
13064
+ tracing.command("export").description("Export a single trace").argument("<id>", "Trace ID").option("--format <fmt>", "Output format (json, csv)", "json").addHelpText(
13065
+ "after",
13066
+ `
13067
+ Examples:
13068
+ $ nexus tracing export abc-123
13069
+ $ nexus tracing export abc-123 --format csv > trace.csv`
13070
+ ).action(async (id, opts) => {
13071
+ try {
13072
+ const client = createClient(program2.optsWithGlobals());
13073
+ const result = await client.tracing.exportTrace(id, { format: opts.format });
13074
+ console.log(result.content);
13075
+ } catch (err) {
13076
+ process.exitCode = handleError(err);
13077
+ }
13078
+ });
13079
+ tracing.command("export-bulk").description("Bulk export traces (max 1000)").option("--format <fmt>", "Output format (json, csv)", "json").option("--status <status>", "Filter by status").option("--agent-id <id>", "Filter by agent ID").option("--workflow-id <id>", "Filter by workflow ID").option("--start-date <iso>", "Filter from date").option("--end-date <iso>", "Filter to date").option("--limit <n>", "Max traces to export", "100").addHelpText(
13080
+ "after",
13081
+ `
13082
+ Examples:
13083
+ $ nexus tracing export-bulk --format csv > traces.csv
13084
+ $ nexus tracing export-bulk --status FAILED --limit 50`
13085
+ ).action(async (opts) => {
13086
+ try {
13087
+ const client = createClient(program2.optsWithGlobals());
13088
+ const result = await client.tracing.bulkExport({
13089
+ format: opts.format,
13090
+ status: opts.status,
13091
+ agentId: opts.agentId,
13092
+ workflowId: opts.workflowId,
13093
+ startDate: opts.startDate,
13094
+ endDate: opts.endDate,
13095
+ limit: parseInt(opts.limit, 10)
13096
+ });
13097
+ console.log(result.content);
13098
+ } catch (err) {
13099
+ process.exitCode = handleError(err);
13100
+ }
13101
+ });
13102
+ }
13103
+ function formatStatus(v) {
13104
+ const s = String(v);
13105
+ if (s === "COMPLETED") return color.green(s);
13106
+ if (s === "FAILED") return color.red(s);
13107
+ if (s === "IN_PROGRESS" || s === "RUNNING") return color.yellow(s);
13108
+ if (s === "PENDING") return color.dim(s);
13109
+ return s;
13110
+ }
13111
+
12033
13112
  // src/commands/upgrade.ts
12034
13113
  var import_node_child_process3 = require("child_process");
12035
13114
  init_output();
12036
13115
 
12037
13116
  // src/util/package-manager.ts
12038
- var import_node_fs4 = __toESM(require("fs"));
13117
+ var import_node_fs5 = __toESM(require("fs"));
12039
13118
  function detectPackageManager() {
12040
13119
  try {
12041
- const resolved = import_node_fs4.default.realpathSync(process.argv[1]).replace(/\\/g, "/");
13120
+ const resolved = import_node_fs5.default.realpathSync(process.argv[1]).replace(/\\/g, "/");
12042
13121
  if (/[/]\.?pnpm[/]/.test(resolved)) return "pnpm";
12043
13122
  if (/[/]\.yarn[/]/.test(resolved)) return "yarn";
12044
13123
  } catch {
@@ -12069,7 +13148,7 @@ function getGlobalUpdateHint(pkg) {
12069
13148
  }
12070
13149
 
12071
13150
  // src/util/version-check.ts
12072
- var import_node_fs5 = __toESM(require("fs"));
13151
+ var import_node_fs6 = __toESM(require("fs"));
12073
13152
  var import_node_os2 = __toESM(require("os"));
12074
13153
  var import_node_path4 = __toESM(require("path"));
12075
13154
  var PACKAGE_NAME = "@agent-nexus/cli";
@@ -12078,7 +13157,7 @@ var FETCH_TIMEOUT_MS = 3e3;
12078
13157
  var CACHE_FILE = import_node_path4.default.join(import_node_os2.default.homedir(), ".nexus-mcp", "version-check.json");
12079
13158
  function loadCache() {
12080
13159
  try {
12081
- return JSON.parse(import_node_fs5.default.readFileSync(CACHE_FILE, "utf-8"));
13160
+ return JSON.parse(import_node_fs6.default.readFileSync(CACHE_FILE, "utf-8"));
12082
13161
  } catch {
12083
13162
  return null;
12084
13163
  }
@@ -12086,8 +13165,8 @@ function loadCache() {
12086
13165
  function saveCache(cache) {
12087
13166
  try {
12088
13167
  const dir = import_node_path4.default.dirname(CACHE_FILE);
12089
- import_node_fs5.default.mkdirSync(dir, { recursive: true, mode: 448 });
12090
- import_node_fs5.default.writeFileSync(CACHE_FILE, JSON.stringify(cache), { mode: 384 });
13168
+ import_node_fs6.default.mkdirSync(dir, { recursive: true, mode: 448 });
13169
+ import_node_fs6.default.writeFileSync(CACHE_FILE, JSON.stringify(cache), { mode: 384 });
12091
13170
  } catch {
12092
13171
  }
12093
13172
  }
@@ -12174,11 +13253,29 @@ async function autoUpdate(currentVersion) {
12174
13253
 
12175
13254
  // src/commands/upgrade.ts
12176
13255
  var PACKAGE_NAME2 = "@agent-nexus/cli";
13256
+ var UPGRADE_ALIASES = [
13257
+ "update",
13258
+ "latest",
13259
+ "up",
13260
+ "install",
13261
+ "reinstall",
13262
+ "refresh",
13263
+ "fetch",
13264
+ "pull",
13265
+ "sync",
13266
+ "get",
13267
+ "download",
13268
+ "self-update",
13269
+ "selfupdate",
13270
+ "self-upgrade",
13271
+ "selfupgrade",
13272
+ "new",
13273
+ "patch",
13274
+ "bump"
13275
+ ];
12177
13276
  function registerUpgradeCommand(program2) {
12178
13277
  const currentVersion = require_package().version;
12179
- program2.command("upgrade").description("Upgrade the Nexus CLI to the latest version").addHelpText("after", `
12180
- Examples:
12181
- $ nexus upgrade`).action(async () => {
13278
+ const upgradeAction = async () => {
12182
13279
  process.stderr.write(`Current version: ${color.cyan(currentVersion)}
12183
13280
  `);
12184
13281
  process.stderr.write("Checking for updates\u2026\n");
@@ -12208,7 +13305,13 @@ Upgrade failed. Try running manually:
12208
13305
  );
12209
13306
  process.exitCode = 1;
12210
13307
  }
12211
- });
13308
+ };
13309
+ program2.command("upgrade").description("Upgrade the Nexus CLI to the latest version").addHelpText("after", `
13310
+ Examples:
13311
+ $ nexus upgrade`).action(upgradeAction);
13312
+ for (const alias of UPGRADE_ALIASES) {
13313
+ program2.command(alias, { hidden: true }).action(upgradeAction);
13314
+ }
12212
13315
  }
12213
13316
 
12214
13317
  // src/commands/version.ts
@@ -12993,7 +14096,7 @@ Examples:
12993
14096
  // src/index.ts
12994
14097
  init_output();
12995
14098
  var { version: VERSION } = require_package();
12996
- var program = new import_commander.Command().name("nexus").description("Official CLI for the Nexus AI agent platform").version(VERSION, "-v, --version").option("--json", "Output as JSON").option("--api-key <key>", "Override API key for this invocation").option("--base-url <url>", "Override API base URL").option("--profile <name>", "Use a specific named profile").option("--no-auto-update", "Disable automatic updates when a new version is detected").hook("preAction", (thisCommand) => {
14099
+ var program = new import_commander.Command().name("nexus").description("Official CLI for the Nexus AI agent platform").version(VERSION, "-v, --version").option("--json", "Output as JSON").option("--api-key <key>", "Override API key for this invocation").option("--base-url <url>", "Override API base URL").option("--dashboard-url <url>", "Override dashboard URL (for browser links)").option("--profile <name>", "Use a specific named profile").option("--no-auto-update", "Disable automatic updates when a new version is detected").hook("preAction", (thisCommand) => {
12997
14100
  const opts = thisCommand.optsWithGlobals();
12998
14101
  if (opts.json) setJsonMode(true);
12999
14102
  const cmdName = thisCommand.name();
@@ -13009,7 +14112,8 @@ var program = new import_commander.Command().name("nexus").description("Official
13009
14112
  "pin",
13010
14113
  "unpin",
13011
14114
  "status",
13012
- "docs"
14115
+ "docs",
14116
+ ...UPGRADE_ALIASES
13013
14117
  ].includes(cmdName);
13014
14118
  if (!skipBanner && !isJsonMode()) {
13015
14119
  try {
@@ -13054,6 +14158,7 @@ registerModelCommands(program);
13054
14158
  registerCustomModelCommands(program);
13055
14159
  registerPhoneNumberCommands(program);
13056
14160
  registerChannelCommands(program);
14161
+ registerTracingCommands(program);
13057
14162
  registerUpgradeCommand(program);
13058
14163
  registerDocsCommand(program);
13059
14164
  if (process.argv.length <= 2) {
@@ -13062,7 +14167,7 @@ if (process.argv.length <= 2) {
13062
14167
  program.parseAsync(process.argv).then(async () => {
13063
14168
  if (isJsonMode()) return;
13064
14169
  const ranCommand = process.argv[2];
13065
- if (ranCommand === "upgrade") return;
14170
+ if (ranCommand === "upgrade" || UPGRADE_ALIASES.includes(ranCommand)) return;
13066
14171
  const opts = program.opts();
13067
14172
  if (opts.autoUpdate) {
13068
14173
  const msg = await autoUpdate(VERSION);