@agent-nexus/cli 0.1.12 → 0.1.14

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 (2) hide show
  1. package/dist/index.js +693 -15
  2. 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.12",
169
+ version: "0.1.14",
170
170
  description: "Official CLI for the Nexus AI agent platform.",
171
171
  license: "MIT",
172
172
  keywords: [
@@ -778,10 +778,7 @@ var ChannelsResource = class extends BaseResource {
778
778
  return this.http.request("POST", "/channels/whatsapp-templates", { body });
779
779
  }
780
780
  async getWhatsAppTemplate(templateId) {
781
- return this.http.request(
782
- "GET",
783
- `/channels/whatsapp-templates/${templateId}`
784
- );
781
+ return this.http.request("GET", `/channels/whatsapp-templates/${templateId}`);
785
782
  }
786
783
  async deleteWhatsAppTemplate(templateId) {
787
784
  return this.http.request(
@@ -790,19 +787,34 @@ var ChannelsResource = class extends BaseResource {
790
787
  );
791
788
  }
792
789
  async listTemplateApprovals(params) {
790
+ return this.http.request("GET", "/channels/whatsapp-templates/approvals", {
791
+ query: params
792
+ });
793
+ }
794
+ async submitTemplateApproval(body) {
793
795
  return this.http.request(
794
- "GET",
796
+ "POST",
795
797
  "/channels/whatsapp-templates/approvals",
796
- { query: params }
798
+ { body }
797
799
  );
798
800
  }
799
- async submitTemplateApproval(body) {
801
+ // ===========================================================================
802
+ // WhatsApp Template Test-Send
803
+ // ===========================================================================
804
+ async testSendWhatsAppTemplate(templateId, body) {
800
805
  return this.http.request(
801
806
  "POST",
802
- "/channels/whatsapp-templates/approvals",
807
+ `/channels/whatsapp-templates/${templateId}/test-send`,
803
808
  { body }
804
809
  );
805
810
  }
811
+ async getTestSendStatus(templateId, messageSid, params) {
812
+ return this.http.request(
813
+ "GET",
814
+ `/channels/whatsapp-templates/${templateId}/test-send/${messageSid}/status`,
815
+ { query: params }
816
+ );
817
+ }
806
818
  };
807
819
  var CloudImportsResource = class extends BaseResource {
808
820
  // Google Drive
@@ -2061,6 +2073,64 @@ var ToolDiscoveryResource = class extends BaseResource {
2061
2073
  );
2062
2074
  }
2063
2075
  };
2076
+ var TracingResource = class extends BaseResource {
2077
+ // ── Traces ──────────────────────────────────────────────────────────────
2078
+ async listTraces(params) {
2079
+ const { data, meta } = await this.http.requestWithMeta(
2080
+ "GET",
2081
+ "/tracing/traces",
2082
+ { query: params }
2083
+ );
2084
+ return { data, meta };
2085
+ }
2086
+ async getTrace(traceId) {
2087
+ return this.http.request("GET", `/tracing/traces/${traceId}`);
2088
+ }
2089
+ async deleteTrace(traceId) {
2090
+ return this.http.request("DELETE", `/tracing/traces/${traceId}`);
2091
+ }
2092
+ // ── Generations ─────────────────────────────────────────────────────────
2093
+ async listGenerations(params) {
2094
+ const { data, meta } = await this.http.requestWithMeta(
2095
+ "GET",
2096
+ "/tracing/generations",
2097
+ { query: params }
2098
+ );
2099
+ return { data, meta };
2100
+ }
2101
+ async getGeneration(generationId) {
2102
+ return this.http.request("GET", `/tracing/generations/${generationId}`);
2103
+ }
2104
+ // ── Models ──────────────────────────────────────────────────────────────
2105
+ async listModels() {
2106
+ return this.http.request("GET", "/tracing/models");
2107
+ }
2108
+ // ── Analytics ───────────────────────────────────────────────────────────
2109
+ async getSummary(params) {
2110
+ return this.http.request("GET", "/tracing/analytics/summary", {
2111
+ query: params
2112
+ });
2113
+ }
2114
+ async getCostBreakdown(params) {
2115
+ return this.http.request("GET", "/tracing/analytics/cost-breakdown", {
2116
+ query: params
2117
+ });
2118
+ }
2119
+ async getTimeline(params) {
2120
+ return this.http.request("GET", "/tracing/analytics/timeline", {
2121
+ query: params
2122
+ });
2123
+ }
2124
+ // ── Export ──────────────────────────────────────────────────────────────
2125
+ async exportTrace(traceId, params) {
2126
+ return this.http.request("POST", `/tracing/traces/${traceId}/export`, {
2127
+ body: params
2128
+ });
2129
+ }
2130
+ async bulkExport(params) {
2131
+ return this.http.request("POST", "/tracing/export", { body: params });
2132
+ }
2133
+ };
2064
2134
  var WorkflowExecutionsResource = class extends BaseResource {
2065
2135
  async list(params) {
2066
2136
  const { data, meta } = await this.http.requestWithMeta("GET", "/workflows/executions", {
@@ -2533,6 +2603,7 @@ var NexusClient = class {
2533
2603
  this.phoneNumbers = new PhoneNumbersResource(http);
2534
2604
  this.tickets = new TicketsResource(http);
2535
2605
  this.channels = new ChannelsResource(http);
2606
+ this.tracing = new TracingResource(http);
2536
2607
  }
2537
2608
  };
2538
2609
 
@@ -3663,6 +3734,41 @@ function openUrl(url) {
3663
3734
  var import_node_child_process2 = require("child_process");
3664
3735
  var import_node_fs2 = require("fs");
3665
3736
  init_output();
3737
+ var VARIABLE_PATTERN = /\{\{\d+\}\}/g;
3738
+ function warnIfHighVariableDensity(types) {
3739
+ let warned = false;
3740
+ function checkField(text, fieldLabel) {
3741
+ const matches = text.match(VARIABLE_PATTERN) ?? [];
3742
+ if (matches.length === 0) return;
3743
+ const variableCharsLength = matches.reduce((sum, m) => sum + m.length, 0);
3744
+ const staticLength = text.length - variableCharsLength;
3745
+ if (staticLength < matches.length * 3) {
3746
+ console.warn(
3747
+ 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."`
3748
+ );
3749
+ warned = true;
3750
+ }
3751
+ }
3752
+ for (const [typeKey, typeValue] of Object.entries(types)) {
3753
+ if (!typeValue || typeof typeValue !== "object") continue;
3754
+ const tv = typeValue;
3755
+ if (typeof tv.body === "string") checkField(tv.body, `${typeKey} body`);
3756
+ if (typeof tv.title === "string") checkField(tv.title, `${typeKey} title`);
3757
+ if (typeof tv.subtitle === "string") checkField(tv.subtitle, `${typeKey} subtitle`);
3758
+ if (Array.isArray(tv.cards)) {
3759
+ tv.cards.forEach((card, i) => {
3760
+ if (typeof card?.body === "string") checkField(card.body, `${typeKey} card[${i}] body`);
3761
+ if (typeof card?.title === "string") checkField(card.title, `${typeKey} card[${i}] title`);
3762
+ });
3763
+ }
3764
+ }
3765
+ if (warned) {
3766
+ console.warn(
3767
+ color.yellow(" Tip:") + " Add more descriptive static text around {{N}} placeholders to avoid Meta rejection.\n"
3768
+ );
3769
+ }
3770
+ return warned;
3771
+ }
3666
3772
  function openUrl2(url) {
3667
3773
  const platform = process.platform;
3668
3774
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
@@ -3914,6 +4020,7 @@ Examples:
3914
4020
  return;
3915
4021
  }
3916
4022
  }
4023
+ warnIfHighVariableDensity(types);
3917
4024
  const client = createClient(program2.optsWithGlobals());
3918
4025
  const result = await client.channels.createWhatsAppTemplate({
3919
4026
  connectionId: opts.connectionId,
@@ -3945,6 +4052,44 @@ Examples:
3945
4052
  { key: "status", label: "Status" }
3946
4053
  ]);
3947
4054
  printSuccess("Template submitted for Meta approval.");
4055
+ const pollMaxMs = 3e4;
4056
+ const pollIntervalMs = 5e3;
4057
+ const pollStart = Date.now();
4058
+ let resolved = false;
4059
+ console.log("Checking approval status...");
4060
+ while (Date.now() - pollStart < pollMaxMs) {
4061
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
4062
+ try {
4063
+ const approvals = await client.channels.listTemplateApprovals({
4064
+ connectionId: opts.connectionId
4065
+ });
4066
+ const approvalsArr = approvals?.data ?? approvals;
4067
+ const items = Array.isArray(approvalsArr) ? approvalsArr : [approvalsArr];
4068
+ const match = items.find((a) => a.sid === data.id);
4069
+ if (match?.approvalRequests?.status) {
4070
+ const status = match.approvalRequests.status;
4071
+ if (status === "rejected") {
4072
+ console.log(color.red(`\u2717 Template rejected by Meta: ${status}`));
4073
+ if (match.approvalRequests.rejection_reason) {
4074
+ console.log(` Reason: ${match.approvalRequests.rejection_reason}`);
4075
+ }
4076
+ resolved = true;
4077
+ process.exitCode = 1;
4078
+ break;
4079
+ } else if (status === "approved") {
4080
+ console.log(color.green(`\u2713 Template approved by Meta.`));
4081
+ resolved = true;
4082
+ break;
4083
+ }
4084
+ }
4085
+ } catch {
4086
+ }
4087
+ }
4088
+ if (!resolved) {
4089
+ console.log(
4090
+ `Status still pending. Check later: ${color.dim("nexus channel whatsapp-template approvals")}`
4091
+ );
4092
+ }
3948
4093
  }
3949
4094
  } catch (err) {
3950
4095
  process.exitCode = handleError(err);
@@ -4069,6 +4214,83 @@ Next: Attach to deployment: ${color.dim("nexus deployment template attach <depId
4069
4214
  process.exitCode = handleError(err);
4070
4215
  }
4071
4216
  });
4217
+ waTemplate.command("test-send").description(
4218
+ "Test-send a WhatsApp template to a phone number via the real Twilio/Meta pipeline"
4219
+ ).requiredOption("--connection-id <id>", "Messaging connection ID").requiredOption("--template-id <id>", "Template ID (Twilio content SID)").requiredOption("--to <phone>", "Recipient phone number in E.164 format (e.g., +1234567890)").option("--variables <json>", `Template variables as JSON (e.g., '{"1": "Hello"}')`).option("--wait", "Poll delivery status until resolved (up to 2 minutes)").addHelpText(
4220
+ "after",
4221
+ `
4222
+ Examples:
4223
+ $ nexus channel whatsapp-template test-send --connection-id abc --template-id HX123 --to +1234567890
4224
+ $ nexus channel whatsapp-template test-send --connection-id abc --template-id HX123 --to +1234567890 --variables '{"1": "Sneakers"}' --wait`
4225
+ ).action(async (opts) => {
4226
+ try {
4227
+ const client = createClient(program2.optsWithGlobals());
4228
+ let variables;
4229
+ if (opts.variables) {
4230
+ try {
4231
+ variables = JSON.parse(opts.variables);
4232
+ } catch {
4233
+ console.error(`Invalid JSON for --variables. Example: '{"1": "Hello"}'`);
4234
+ process.exitCode = 1;
4235
+ return;
4236
+ }
4237
+ }
4238
+ const result = await client.channels.testSendWhatsAppTemplate(opts.templateId, {
4239
+ connectionId: opts.connectionId,
4240
+ to: opts.to,
4241
+ variables
4242
+ });
4243
+ const data = result?.data ?? result;
4244
+ printRecord(data, [
4245
+ { key: "messageSid", label: "Message SID" },
4246
+ { key: "status", label: "Status" },
4247
+ { key: "to", label: "To" },
4248
+ { key: "from", label: "From" },
4249
+ { key: "sentAt", label: "Sent At" }
4250
+ ]);
4251
+ printSuccess("Template test-send initiated.");
4252
+ if (opts.wait) {
4253
+ const maxWaitMs = 12e4;
4254
+ const intervalMs = 5e3;
4255
+ const startTime = Date.now();
4256
+ let lastStatus = data.status;
4257
+ console.log("Polling delivery status...");
4258
+ while (Date.now() - startTime < maxWaitMs) {
4259
+ await new Promise((r) => setTimeout(r, intervalMs));
4260
+ try {
4261
+ const statusResult = await client.channels.getTestSendStatus(
4262
+ opts.templateId,
4263
+ data.messageSid,
4264
+ { connectionId: opts.connectionId }
4265
+ );
4266
+ const statusData = statusResult?.data ?? statusResult;
4267
+ lastStatus = statusData.status;
4268
+ if (["delivered", "read"].includes(lastStatus)) {
4269
+ console.log(color.green(`\u2713 Message ${lastStatus}.`));
4270
+ break;
4271
+ } else if (["failed", "undelivered"].includes(lastStatus)) {
4272
+ console.log(color.red(`\u2717 Message ${lastStatus}.`));
4273
+ if (statusData.errorCode) {
4274
+ console.log(
4275
+ ` Error ${statusData.errorCode}: ${statusData.errorMessage ?? "Unknown error"}`
4276
+ );
4277
+ }
4278
+ process.exitCode = 1;
4279
+ break;
4280
+ }
4281
+ } catch {
4282
+ }
4283
+ }
4284
+ if (!["delivered", "read", "failed", "undelivered"].includes(lastStatus)) {
4285
+ console.log(
4286
+ `Status still '${lastStatus}' after 2m. The message may still be in transit.`
4287
+ );
4288
+ }
4289
+ }
4290
+ } catch (err) {
4291
+ process.exitCode = handleError(err);
4292
+ }
4293
+ });
4072
4294
  }
4073
4295
 
4074
4296
  // src/commands/collection.ts
@@ -4799,12 +5021,25 @@ Examples:
4799
5021
  process.exitCode = handleError(err);
4800
5022
  }
4801
5023
  });
4802
- 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("--variables <json>", 'Variables JSON: {"1":{"description":"Name","isBodyVariable":true}}').option("--type <type>", "Template type: template, card, or carousel", "template").option("--enable-multi-language", "Enable multi-language support").addHelpText(
5024
+ 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(
5025
+ "--variables <json>",
5026
+ 'Variables JSON: {"1":{"description":"Name","isBodyVariable":true}}'
5027
+ ).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(
5028
+ "--carousel-template-group <json>",
5029
+ "Carousel template group JSON with size variants (carousel only)"
5030
+ ).option(
5031
+ "--single-item-card-template-id <id>",
5032
+ "Fallback card template ID for single-item carousels"
5033
+ ).option(
5034
+ "--single-item-card-template-group <json>",
5035
+ "Single-item card template group JSON for multi-language fallback"
5036
+ ).addHelpText(
4803
5037
  "after",
4804
5038
  `
4805
5039
  Examples:
4806
5040
  $ nexus deployment template attach dep-123 --template-id HX456 --name welcome --description "Welcome message"
4807
- $ nexus deployment template attach dep-123 --template-id HX456 --name order --description "Order confirmation" --variables '{"1":{"description":"Customer name","isBodyVariable":true}}'`
5041
+ $ nexus deployment template attach dep-123 --template-id HX456 --name order --description "Order confirmation" --variables '{"1":{"description":"Customer name","isBodyVariable":true}}'
5042
+ $ 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}'`
4808
5043
  ).action(async (deploymentId, opts) => {
4809
5044
  try {
4810
5045
  let variables;
@@ -4817,6 +5052,26 @@ Examples:
4817
5052
  return;
4818
5053
  }
4819
5054
  }
5055
+ let carouselTemplateGroup;
5056
+ if (opts.carouselTemplateGroup) {
5057
+ try {
5058
+ carouselTemplateGroup = JSON.parse(opts.carouselTemplateGroup);
5059
+ } catch {
5060
+ console.error("Error: --carousel-template-group must be valid JSON.");
5061
+ process.exitCode = 1;
5062
+ return;
5063
+ }
5064
+ }
5065
+ let singleItemCardTemplateGroup;
5066
+ if (opts.singleItemCardTemplateGroup) {
5067
+ try {
5068
+ singleItemCardTemplateGroup = JSON.parse(opts.singleItemCardTemplateGroup);
5069
+ } catch {
5070
+ console.error("Error: --single-item-card-template-group must be valid JSON.");
5071
+ process.exitCode = 1;
5072
+ return;
5073
+ }
5074
+ }
4820
5075
  const client = createClient(program2.optsWithGlobals());
4821
5076
  const result = await client.deployments.attachDeploymentTemplate(deploymentId, {
4822
5077
  templateId: opts.templateId,
@@ -4824,7 +5079,11 @@ Examples:
4824
5079
  description: opts.description,
4825
5080
  variables,
4826
5081
  type: opts.type,
4827
- enableMultiLanguage: opts.enableMultiLanguage
5082
+ enableMultiLanguage: opts.enableMultiLanguage,
5083
+ enableDynamicSize: opts.enableDynamicSize,
5084
+ carouselTemplateGroup,
5085
+ singleItemCardTemplateId: opts.singleItemCardTemplateId,
5086
+ singleItemCardTemplateGroup
4828
5087
  });
4829
5088
  const data = result?.data ?? result;
4830
5089
  printRecord(data, [
@@ -4838,12 +5097,22 @@ Examples:
4838
5097
  process.exitCode = handleError(err);
4839
5098
  }
4840
5099
  });
4841
- 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").addHelpText(
5100
+ 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(
5101
+ "--carousel-template-group <json>",
5102
+ "Carousel template group JSON with size variants (carousel only)"
5103
+ ).option(
5104
+ "--single-item-card-template-id <id>",
5105
+ "Fallback card template ID for single-item carousels"
5106
+ ).option(
5107
+ "--single-item-card-template-group <json>",
5108
+ "Single-item card template group JSON for multi-language fallback"
5109
+ ).addHelpText(
4842
5110
  "after",
4843
5111
  `
4844
5112
  Examples:
4845
5113
  $ nexus deployment template update dep-123 HX456 --name "Updated Welcome"
4846
- $ nexus deployment template update dep-123 HX456 --variables '{"1":{"description":"Full name"}}'`
5114
+ $ nexus deployment template update dep-123 HX456 --variables '{"1":{"description":"Full name"}}'
5115
+ $ nexus deployment template update dep-123 HX456 --enable-dynamic-size --carousel-template-group '{"baseName":"products","availableTemplates":[...]}'`
4847
5116
  ).action(async (deploymentId, templateId, opts) => {
4848
5117
  try {
4849
5118
  const body = {};
@@ -4851,6 +5120,9 @@ Examples:
4851
5120
  if (opts.description !== void 0) body.description = opts.description;
4852
5121
  if (opts.enableMultiLanguage !== void 0)
4853
5122
  body.enableMultiLanguage = opts.enableMultiLanguage;
5123
+ if (opts.enableDynamicSize !== void 0) body.enableDynamicSize = opts.enableDynamicSize;
5124
+ if (opts.singleItemCardTemplateId !== void 0)
5125
+ body.singleItemCardTemplateId = opts.singleItemCardTemplateId;
4854
5126
  if (opts.variables) {
4855
5127
  try {
4856
5128
  body.variables = JSON.parse(opts.variables);
@@ -4860,6 +5132,24 @@ Examples:
4860
5132
  return;
4861
5133
  }
4862
5134
  }
5135
+ if (opts.carouselTemplateGroup) {
5136
+ try {
5137
+ body.carouselTemplateGroup = JSON.parse(opts.carouselTemplateGroup);
5138
+ } catch {
5139
+ console.error("Error: --carousel-template-group must be valid JSON.");
5140
+ process.exitCode = 1;
5141
+ return;
5142
+ }
5143
+ }
5144
+ if (opts.singleItemCardTemplateGroup) {
5145
+ try {
5146
+ body.singleItemCardTemplateGroup = JSON.parse(opts.singleItemCardTemplateGroup);
5147
+ } catch {
5148
+ console.error("Error: --single-item-card-template-group must be valid JSON.");
5149
+ process.exitCode = 1;
5150
+ return;
5151
+ }
5152
+ }
4863
5153
  const client = createClient(program2.optsWithGlobals());
4864
5154
  const result = await client.deployments.updateDeploymentTemplate(
4865
5155
  deploymentId,
@@ -4886,7 +5176,10 @@ Examples:
4886
5176
  output: process.stdout
4887
5177
  });
4888
5178
  const answer = await new Promise((resolve) => {
4889
- rl.question(`Detach template ${templateId} from deployment ${deploymentId}? (y/N) `, resolve);
5179
+ rl.question(
5180
+ `Detach template ${templateId} from deployment ${deploymentId}? (y/N) `,
5181
+ resolve
5182
+ );
4890
5183
  });
4891
5184
  rl.close();
4892
5185
  if (answer.toLowerCase() !== "y") {
@@ -12537,6 +12830,390 @@ Examples:
12537
12830
  });
12538
12831
  }
12539
12832
 
12833
+ // src/commands/tracing.ts
12834
+ init_output();
12835
+ function registerTracingCommands(program2) {
12836
+ const tracing = program2.command("tracing").description("View LLM traces and analytics");
12837
+ addPaginationOptions(
12838
+ 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 (max 255 chars)").option("--start-date <iso>", "Filter from date (ISO 8601, e.g. 2026-03-01)").option("--end-date <iso>", "Filter to date (ISO 8601, e.g. 2026-03-01)").option(
12839
+ "--sort-by <field>",
12840
+ "Sort by field (startedAt, totalCostUsd, totalDurationMs)",
12841
+ "startedAt"
12842
+ ).option("--order <dir>", "Sort order (asc, desc)", "desc").addHelpText(
12843
+ "after",
12844
+ `
12845
+ Examples:
12846
+ $ nexus tracing traces
12847
+ $ nexus tracing traces --status FAILED --limit 10
12848
+ $ nexus tracing traces --agent-id abc --start-date 2026-03-01 --json`
12849
+ )
12850
+ ).action(async (opts) => {
12851
+ try {
12852
+ const client = createClient(program2.optsWithGlobals());
12853
+ const { data, meta } = await client.tracing.listTraces({
12854
+ ...getPaginationParams(opts),
12855
+ status: opts.status,
12856
+ agentId: opts.agentId,
12857
+ workflowId: opts.workflowId,
12858
+ model: opts.model,
12859
+ startDate: opts.startDate,
12860
+ endDate: opts.endDate,
12861
+ sortBy: opts.sortBy,
12862
+ order: opts.order
12863
+ });
12864
+ printList(
12865
+ data,
12866
+ meta,
12867
+ [
12868
+ { key: "id", label: "ID", width: 36 },
12869
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12870
+ { key: "agentName", label: "AGENT", width: 20 },
12871
+ { key: "workflowName", label: "WORKFLOW", width: 20 },
12872
+ {
12873
+ key: "totalCostUsd",
12874
+ label: "COST ($)",
12875
+ width: 10,
12876
+ format: (v) => v != null ? `$${Number(v).toFixed(4)}` : "-"
12877
+ },
12878
+ {
12879
+ key: "totalDurationMs",
12880
+ label: "DURATION",
12881
+ width: 10,
12882
+ format: (v) => v != null ? `${Number(v)}ms` : "-"
12883
+ },
12884
+ { key: "generationCount", label: "GENS", width: 5 },
12885
+ { key: "startedAt", label: "STARTED", width: 20 }
12886
+ ]
12887
+ );
12888
+ } catch (err) {
12889
+ process.exitCode = handleError(err);
12890
+ }
12891
+ });
12892
+ tracing.command("trace").description("Get trace details with generations").argument("<id>", "Trace ID").addHelpText(
12893
+ "after",
12894
+ `
12895
+ Examples:
12896
+ $ nexus tracing trace abc-123
12897
+ $ nexus tracing trace abc-123 --json`
12898
+ ).action(async (id) => {
12899
+ try {
12900
+ const client = createClient(program2.optsWithGlobals());
12901
+ const trace = await client.tracing.getTrace(id);
12902
+ printRecord(trace, [
12903
+ { key: "id", label: "ID" },
12904
+ { key: "status", label: "Status" },
12905
+ { key: "agentName", label: "Agent" },
12906
+ { key: "workflowName", label: "Workflow" },
12907
+ {
12908
+ key: "totalCostUsd",
12909
+ label: "Cost ($)",
12910
+ format: (v) => v != null ? `$${Number(v).toFixed(4)}` : "-"
12911
+ },
12912
+ { key: "totalInputTokens", label: "Input Tokens" },
12913
+ { key: "totalOutputTokens", label: "Output Tokens" },
12914
+ { key: "totalDurationMs", label: "Duration (ms)" },
12915
+ { key: "startedAt", label: "Started" },
12916
+ { key: "completedAt", label: "Completed" }
12917
+ ]);
12918
+ const gens = trace.generations;
12919
+ if (gens && gens.length > 0) {
12920
+ console.log(`
12921
+ ${color.bold("Generations")} (${gens.length}):
12922
+ `);
12923
+ printList(gens, void 0, [
12924
+ { key: "id", label: "ID", width: 36 },
12925
+ { key: "modelName", label: "MODEL", width: 25 },
12926
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12927
+ {
12928
+ key: "costUsd",
12929
+ label: "COST ($)",
12930
+ width: 10,
12931
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
12932
+ },
12933
+ {
12934
+ key: "durationMs",
12935
+ label: "DURATION",
12936
+ width: 10,
12937
+ format: (v) => v != null ? `${v}ms` : "-"
12938
+ }
12939
+ ]);
12940
+ }
12941
+ } catch (err) {
12942
+ process.exitCode = handleError(err);
12943
+ }
12944
+ });
12945
+ tracing.command("delete").description("Delete a trace and its generations").argument("<id>", "Trace ID").addHelpText(
12946
+ "after",
12947
+ `
12948
+ Examples:
12949
+ $ nexus tracing delete abc-123`
12950
+ ).action(async (id) => {
12951
+ try {
12952
+ const client = createClient(program2.optsWithGlobals());
12953
+ await client.tracing.deleteTrace(id);
12954
+ printSuccess("Trace deleted.", { id });
12955
+ } catch (err) {
12956
+ process.exitCode = handleError(err);
12957
+ }
12958
+ });
12959
+ addPaginationOptions(
12960
+ 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 (max 255 chars)").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 (ISO 8601, e.g. 2026-03-01)").option("--end-date <iso>", "Filter to date (ISO 8601, e.g. 2026-03-01)").option("--min-cost <usd>", "Minimum cost in USD").option("--max-cost <usd>", "Maximum cost in USD").addHelpText(
12961
+ "after",
12962
+ `
12963
+ Examples:
12964
+ $ nexus tracing generations
12965
+ $ nexus tracing generations --provider ANTHROPIC --status FAILED
12966
+ $ nexus tracing generations --trace-id abc-123 --json`
12967
+ )
12968
+ ).action(async (opts) => {
12969
+ try {
12970
+ const client = createClient(program2.optsWithGlobals());
12971
+ const { data, meta } = await client.tracing.listGenerations({
12972
+ ...getPaginationParams(opts),
12973
+ traceId: opts.traceId,
12974
+ provider: opts.provider,
12975
+ modelName: opts.model,
12976
+ status: opts.status,
12977
+ agentId: opts.agentId,
12978
+ taskId: opts.taskId,
12979
+ startDate: opts.startDate,
12980
+ endDate: opts.endDate,
12981
+ minCostUsd: opts.minCost ? parseFloat(opts.minCost) : void 0,
12982
+ maxCostUsd: opts.maxCost ? parseFloat(opts.maxCost) : void 0
12983
+ });
12984
+ printList(
12985
+ data,
12986
+ meta,
12987
+ [
12988
+ { key: "id", label: "ID", width: 36 },
12989
+ { key: "traceId", label: "TRACE", width: 36 },
12990
+ { key: "modelName", label: "MODEL", width: 25 },
12991
+ { key: "status", label: "STATUS", width: 12, format: formatStatus },
12992
+ {
12993
+ key: "costUsd",
12994
+ label: "COST ($)",
12995
+ width: 10,
12996
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
12997
+ },
12998
+ {
12999
+ key: "durationMs",
13000
+ label: "DURATION",
13001
+ width: 10,
13002
+ format: (v) => v != null ? `${v}ms` : "-"
13003
+ }
13004
+ ]
13005
+ );
13006
+ } catch (err) {
13007
+ process.exitCode = handleError(err);
13008
+ }
13009
+ });
13010
+ tracing.command("generation").description("Get generation details including prompt and response").argument("<id>", "Generation ID").addHelpText(
13011
+ "after",
13012
+ `
13013
+ Examples:
13014
+ $ nexus tracing generation gen-123
13015
+ $ nexus tracing generation gen-123 --json`
13016
+ ).action(async (id) => {
13017
+ try {
13018
+ const client = createClient(program2.optsWithGlobals());
13019
+ const gen = await client.tracing.getGeneration(id);
13020
+ printRecord(gen, [
13021
+ { key: "id", label: "ID" },
13022
+ { key: "traceId", label: "Trace ID" },
13023
+ { key: "provider", label: "Provider" },
13024
+ { key: "modelName", label: "Model" },
13025
+ { key: "status", label: "Status" },
13026
+ { key: "inputTokens", label: "Input Tokens" },
13027
+ { key: "outputTokens", label: "Output Tokens" },
13028
+ {
13029
+ key: "costUsd",
13030
+ label: "Cost ($)",
13031
+ format: (v) => v != null ? `$${Number(v).toFixed(6)}` : "-"
13032
+ },
13033
+ { key: "durationMs", label: "Duration (ms)" },
13034
+ { key: "temperature", label: "Temperature" },
13035
+ { key: "taskName", label: "Task" },
13036
+ { key: "startedAt", label: "Started" },
13037
+ { key: "completedAt", label: "Completed" },
13038
+ { key: "errorMessage", label: "Error" }
13039
+ ]);
13040
+ } catch (err) {
13041
+ process.exitCode = handleError(err);
13042
+ }
13043
+ });
13044
+ tracing.command("models").description("List distinct model names used in traces").addHelpText(
13045
+ "after",
13046
+ `
13047
+ Examples:
13048
+ $ nexus tracing models`
13049
+ ).action(async () => {
13050
+ try {
13051
+ const client = createClient(program2.optsWithGlobals());
13052
+ const models = await client.tracing.listModels();
13053
+ if (Array.isArray(models) && models.length > 0) {
13054
+ for (const m of models) console.log(m);
13055
+ } else {
13056
+ console.log("No models found.");
13057
+ }
13058
+ } catch (err) {
13059
+ process.exitCode = handleError(err);
13060
+ }
13061
+ });
13062
+ 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(
13063
+ "after",
13064
+ `
13065
+ Examples:
13066
+ $ nexus tracing summary
13067
+ $ nexus tracing summary --start-date 2026-03-01 --end-date 2026-03-30
13068
+ $ nexus tracing summary --json`
13069
+ ).action(async (opts) => {
13070
+ try {
13071
+ const client = createClient(program2.optsWithGlobals());
13072
+ const summary = await client.tracing.getSummary({
13073
+ startDate: opts.startDate,
13074
+ endDate: opts.endDate
13075
+ });
13076
+ printRecord(summary, [
13077
+ { key: "totalTraces", label: "Total Traces" },
13078
+ { key: "completedTraces", label: "Completed" },
13079
+ { key: "failedTraces", label: "Failed" },
13080
+ { key: "inProgressTraces", label: "In Progress" },
13081
+ {
13082
+ key: "totalCostUsd",
13083
+ label: "Total Cost ($)",
13084
+ format: (v) => `$${Number(v).toFixed(4)}`
13085
+ },
13086
+ { key: "totalInputTokens", label: "Input Tokens" },
13087
+ { key: "totalOutputTokens", label: "Output Tokens" },
13088
+ {
13089
+ key: "avgDurationMs",
13090
+ label: "Avg Duration",
13091
+ format: (v) => v != null ? `${Number(v).toFixed(0)}ms` : "-"
13092
+ },
13093
+ { key: "distinctModelCount", label: "Distinct Models" }
13094
+ ]);
13095
+ } catch (err) {
13096
+ process.exitCode = handleError(err);
13097
+ }
13098
+ });
13099
+ 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(
13100
+ "after",
13101
+ `
13102
+ Examples:
13103
+ $ nexus tracing cost-breakdown
13104
+ $ nexus tracing cost-breakdown --group-by agent
13105
+ $ nexus tracing cost-breakdown --group-by workflow --json`
13106
+ ).action(async (opts) => {
13107
+ try {
13108
+ const client = createClient(program2.optsWithGlobals());
13109
+ const result = await client.tracing.getCostBreakdown({
13110
+ groupBy: opts.groupBy,
13111
+ startDate: opts.startDate,
13112
+ endDate: opts.endDate
13113
+ });
13114
+ const entries = result.entries ?? [];
13115
+ printList(entries, void 0, [
13116
+ { key: "groupKey", label: "KEY", width: 36 },
13117
+ { key: "groupLabel", label: "LABEL", width: 25 },
13118
+ {
13119
+ key: "totalCostUsd",
13120
+ label: "COST ($)",
13121
+ width: 12,
13122
+ format: (v) => `$${Number(v).toFixed(4)}`
13123
+ },
13124
+ { key: "traceCount", label: "TRACES", width: 8 },
13125
+ { key: "generationCount", label: "GENS", width: 8 },
13126
+ { key: "totalInputTokens", label: "IN TOKENS", width: 12 },
13127
+ { key: "totalOutputTokens", label: "OUT TOKENS", width: 12 }
13128
+ ]);
13129
+ } catch (err) {
13130
+ process.exitCode = handleError(err);
13131
+ }
13132
+ });
13133
+ 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(
13134
+ "after",
13135
+ `
13136
+ Examples:
13137
+ $ nexus tracing timeline
13138
+ $ nexus tracing timeline --granularity hour --start-date 2026-03-29 --json`
13139
+ ).action(async (opts) => {
13140
+ try {
13141
+ const client = createClient(program2.optsWithGlobals());
13142
+ const result = await client.tracing.getTimeline({
13143
+ granularity: opts.granularity,
13144
+ startDate: opts.startDate,
13145
+ endDate: opts.endDate
13146
+ });
13147
+ const points = result.points ?? [];
13148
+ printList(points, void 0, [
13149
+ { key: "date", label: "DATE", width: 22 },
13150
+ { key: "traceCount", label: "TRACES", width: 8 },
13151
+ { key: "generationCount", label: "GENS", width: 8 },
13152
+ {
13153
+ key: "totalCostUsd",
13154
+ label: "COST ($)",
13155
+ width: 12,
13156
+ format: (v) => `$${Number(v).toFixed(4)}`
13157
+ },
13158
+ {
13159
+ key: "avgDurationMs",
13160
+ label: "AVG DUR",
13161
+ width: 10,
13162
+ format: (v) => v != null ? `${Number(v).toFixed(0)}ms` : "-"
13163
+ }
13164
+ ]);
13165
+ } catch (err) {
13166
+ process.exitCode = handleError(err);
13167
+ }
13168
+ });
13169
+ tracing.command("export").description("Export a single trace").argument("<id>", "Trace ID").option("--format <fmt>", "Output format (json, csv)", "json").addHelpText(
13170
+ "after",
13171
+ `
13172
+ Examples:
13173
+ $ nexus tracing export abc-123
13174
+ $ nexus tracing export abc-123 --format csv > trace.csv`
13175
+ ).action(async (id, opts) => {
13176
+ try {
13177
+ const client = createClient(program2.optsWithGlobals());
13178
+ const result = await client.tracing.exportTrace(id, { format: opts.format });
13179
+ console.log(result.content);
13180
+ } catch (err) {
13181
+ process.exitCode = handleError(err);
13182
+ }
13183
+ });
13184
+ 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(
13185
+ "after",
13186
+ `
13187
+ Examples:
13188
+ $ nexus tracing export-bulk --format csv > traces.csv
13189
+ $ nexus tracing export-bulk --status FAILED --limit 50`
13190
+ ).action(async (opts) => {
13191
+ try {
13192
+ const client = createClient(program2.optsWithGlobals());
13193
+ const result = await client.tracing.bulkExport({
13194
+ format: opts.format,
13195
+ status: opts.status,
13196
+ agentId: opts.agentId,
13197
+ workflowId: opts.workflowId,
13198
+ startDate: opts.startDate,
13199
+ endDate: opts.endDate,
13200
+ limit: parseInt(opts.limit, 10)
13201
+ });
13202
+ console.log(result.content);
13203
+ } catch (err) {
13204
+ process.exitCode = handleError(err);
13205
+ }
13206
+ });
13207
+ }
13208
+ function formatStatus(v) {
13209
+ const s = String(v);
13210
+ if (s === "COMPLETED") return color.green(s);
13211
+ if (s === "FAILED") return color.red(s);
13212
+ if (s === "IN_PROGRESS" || s === "RUNNING") return color.yellow(s);
13213
+ if (s === "PENDING") return color.dim(s);
13214
+ return s;
13215
+ }
13216
+
12540
13217
  // src/commands/upgrade.ts
12541
13218
  var import_node_child_process3 = require("child_process");
12542
13219
  init_output();
@@ -13586,6 +14263,7 @@ registerModelCommands(program);
13586
14263
  registerCustomModelCommands(program);
13587
14264
  registerPhoneNumberCommands(program);
13588
14265
  registerChannelCommands(program);
14266
+ registerTracingCommands(program);
13589
14267
  registerUpgradeCommand(program);
13590
14268
  registerDocsCommand(program);
13591
14269
  if (process.argv.length <= 2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-nexus/cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Official CLI for the Nexus AI agent platform.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  "@types/node": "24.6.2",
36
36
  "tsup": "^8.5.0",
37
37
  "typescript": "^5.8.3",
38
- "@agent-nexus/sdk": "0.1.13"
38
+ "@agent-nexus/sdk": "0.1.16"
39
39
  },
40
40
  "scripts": {
41
41
  "gen:docs": "tsx scripts/bundle-docs.ts",