@carrierllc/mcp 0.13.30 → 0.13.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  CARRIER_BRAND,
4
4
  CARRIER_VERSION,
5
+ OCS_LIST_KEYS,
5
6
  OCS_MAX_USAGE_WINDOW_DAYS,
6
7
  TARGET_IDS,
7
8
  billingScreen,
@@ -13,6 +14,7 @@ import {
13
14
  deriveAccentDark,
14
15
  deriveAccentLight,
15
16
  deriveAccentPaletteSubs,
17
+ extractSubscriberStatus,
16
18
  fleetScreen,
17
19
  formatProbes,
18
20
  generateStorefrontLogo,
@@ -36,12 +38,13 @@ import {
36
38
  slug,
37
39
  storefrontClerkUrls,
38
40
  storefrontScreen,
41
+ subscriberIccid,
39
42
  subscribersScreen,
40
43
  usageScreen,
41
44
  verifyStorefront,
42
45
  walletScreen,
43
46
  which
44
- } from "./chunk-ZJY7H2QW.js";
47
+ } from "./chunk-2P76KIOS.js";
45
48
  import {
46
49
  copyTree,
47
50
  exists,
@@ -552,14 +555,14 @@ async function claudeResolvedConfig(server) {
552
555
  }
553
556
  const json = await privateJson(file);
554
557
  if (!json) return null;
555
- const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
558
+ const object2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
556
559
  const readOptional = async (path) => {
557
560
  try {
558
561
  await lstat(path);
559
562
  const text3 = await privateText(path, false);
560
563
  if (text3 === null) return null;
561
564
  const value = JSON.parse(text3);
562
- return object(value) ? value : null;
565
+ return object2(value) ? value : null;
563
566
  } catch (e) {
564
567
  return e.code === "ENOENT" ? void 0 : null;
565
568
  }
@@ -600,15 +603,15 @@ async function claudeResolvedConfig(server) {
600
603
  if (settings === null || settings && policyFields.some((k) => Object.hasOwn(settings, k))) return null;
601
604
  }
602
605
  const projects = json.projects;
603
- if (projects !== void 0 && !object(projects)) return null;
604
- const local = object(projects) ? projects[workspace] : void 0;
605
- if (local !== void 0 && !object(local)) return null;
606
+ if (projects !== void 0 && !object2(projects)) return null;
607
+ const local = object2(projects) ? projects[workspace] : void 0;
608
+ if (local !== void 0 && !object2(local)) return null;
606
609
  const userServers = json.mcpServers;
607
- const localServers = object(local) ? local.mcpServers : void 0;
608
- if (userServers !== void 0 && !object(userServers) || localServers !== void 0 && !object(localServers)) return null;
609
- let config = object(userServers) ? userServers[server] : void 0;
610
+ const localServers = object2(local) ? local.mcpServers : void 0;
611
+ if (userServers !== void 0 && !object2(userServers) || localServers !== void 0 && !object2(localServers)) return null;
612
+ let config = object2(userServers) ? userServers[server] : void 0;
610
613
  let scope = "user", selectedFile = file;
611
- const hasLocal = object(localServers) && Object.hasOwn(localServers, server);
614
+ const hasLocal = object2(localServers) && Object.hasOwn(localServers, server);
612
615
  let project, projectFile;
613
616
  const dirs = [];
614
617
  for (let dir = cwd; ; ) {
@@ -621,7 +624,7 @@ async function claudeResolvedConfig(server) {
621
624
  const path = join6(dir, ".mcp.json"), value = await readOptional(path);
622
625
  if (value === null) return null;
623
626
  if (!value) continue;
624
- if (!object(value.mcpServers)) return null;
627
+ if (!object2(value.mcpServers)) return null;
625
628
  if (Object.hasOwn(value.mcpServers, server)) {
626
629
  project = value.mcpServers[server];
627
630
  projectFile = path;
@@ -629,7 +632,7 @@ async function claudeResolvedConfig(server) {
629
632
  }
630
633
  if (projectFile && !hasLocal) {
631
634
  const settings = await readOptional(join6(configDir, "settings.json"));
632
- if (settings === null || !object(local) || local.hasTrustDialogAccepted !== true) return null;
635
+ if (settings === null || !object2(local) || local.hasTrustDialogAccepted !== true) return null;
633
636
  const disabled = settings?.disabledMcpjsonServers;
634
637
  const enabled = settings?.enabledMcpjsonServers;
635
638
  if (disabled !== void 0 && (!Array.isArray(disabled) || disabled.some((v) => typeof v !== "string"))) return null;
@@ -648,8 +651,8 @@ async function claudeResolvedConfig(server) {
648
651
  config = localServers[server];
649
652
  scope = "local";
650
653
  }
651
- if (!object(config) || config.url !== MCP_URL || config.command || config.type !== void 0 && config.type !== "http" && config.type !== "sse") return null;
652
- if (config.headers !== void 0 && !object(config.headers)) return null;
654
+ if (!object2(config) || config.url !== MCP_URL || config.command || config.type !== void 0 && config.type !== "http" && config.type !== "sse") return null;
655
+ if (config.headers !== void 0 && !object2(config.headers)) return null;
653
656
  for (const [key, value] of Object.entries(config.headers ?? {})) {
654
657
  if (typeof value !== "string" || /authorization|api[-_]?key|token|cookie/i.test(key) || /\$\{|[\r\n\0]/.test(value)) return null;
655
658
  }
@@ -877,7 +880,7 @@ async function startCallbackServer() {
877
880
  res.end(page("You're signed in", "Close this tab and return to your terminal."));
878
881
  resolveCode?.(code);
879
882
  });
880
- let bound = false;
883
+ let bound2 = false;
881
884
  let lastErr;
882
885
  for (const port2 of CALLBACK_PORTS) {
883
886
  try {
@@ -888,13 +891,13 @@ async function startCallbackServer() {
888
891
  resolve5();
889
892
  });
890
893
  });
891
- bound = true;
894
+ bound2 = true;
892
895
  break;
893
896
  } catch (e) {
894
897
  lastErr = e;
895
898
  }
896
899
  }
897
- if (!bound) {
900
+ if (!bound2) {
898
901
  throw new Error(
899
902
  `Could not bind a local callback port (tried ${CALLBACK_PORTS.filter(Boolean).join(", ")}): ${lastErr instanceof Error ? lastErr.message : String(lastErr)}`
900
903
  );
@@ -1153,6 +1156,360 @@ function withNextStep(errorMsg, next) {
1153
1156
  return [errorMsg, "", "What to do next:", ...next.map((s) => ` \u2192 ${s}`)].join("\n");
1154
1157
  }
1155
1158
 
1159
+ // src/cli/lib/ask-answer.ts
1160
+ var MAX_CHARS = 3800;
1161
+ var MAX_ROWS = 10;
1162
+ var RAW_HINT = 'Use carrier ask "your question" --json for the complete raw result (may contain sensitive data).';
1163
+ function object(value) {
1164
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1165
+ }
1166
+ function measured(value) {
1167
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
1168
+ }
1169
+ function bound(text3, prose = false) {
1170
+ text3 = sanitizeAskText(text3);
1171
+ if (text3.length <= MAX_CHARS) return text3;
1172
+ const notice = `Answer shortened; complete rows or sentences omitted. ${RAW_HINT}`;
1173
+ const budget = MAX_CHARS - notice.length - 1;
1174
+ const units = prose ? text3.match(/[^\n]+?(?:[.!?](?=\s|$)|(?=\n|$))/g) ?? [] : text3.split("\n");
1175
+ const kept = [];
1176
+ let size = 0;
1177
+ for (const unit of units) {
1178
+ const clean = unit.trim();
1179
+ if (size + clean.length + 1 > budget) break;
1180
+ kept.push(clean);
1181
+ size += clean.length + 1;
1182
+ }
1183
+ return [...kept, notice].join("\n");
1184
+ }
1185
+ function safeFacet(key, value) {
1186
+ if (key === "recurring") return value === "true" || value === "false";
1187
+ if (key === "status") return ["active", "inactive", "inventory", "manually suspended", "end of life", "free", "expired", "terminated"].includes(value.toLowerCase());
1188
+ return false;
1189
+ }
1190
+ function unsupportedJudgement(intent) {
1191
+ return /\b(predict\w*|most likely|least likely|likelihood|probability|forecast\w*|voorspel\w*|waarschijnlijk)\b/i.test(intent) || /\b(who|which|what|wie|welke)\b.*\b(best|worst|beste|slechtste)\b(?=\s*(?:[?!.]|$))/i.test(intent) || /\b(who|which|what|wie|welke)\s+(?:(?:is|are|has|the|subscriber|package|action)\s+)*(best|worst|beste|slechtste)\b/i.test(intent) || /\b(?:recommend\w*|advise)\b/i.test(intent) || /^\s*should\b/i.test(intent) || /\b(will|going to)\b.*\b(cancel|churn|leave|fail)\b/i.test(intent);
1192
+ }
1193
+ function subscriberLine(value) {
1194
+ const row = object(value);
1195
+ if (!row) return void 0;
1196
+ const status = extractSubscriberStatus(row);
1197
+ const sim = object(row.sim);
1198
+ const id = typeof row.subscriberId === "string" || typeof row.subscriberId === "number" ? row.subscriberId : "(identifier not reported)";
1199
+ return `Subscriber ${inline(id)}. Subscriber status: ${inline(status || "not reported")}. SIM inventory status: ${typeof sim?.status === "string" ? inline(sim.status) : "not reported"}.`;
1200
+ }
1201
+ function subscriberStatusAnswer(rows2, summary, intent) {
1202
+ const identities = [...intent.matchAll(/\b(subscriberId|subscriber\s+id|subscriber|ICCID)(?:\s*[:=]\s*|\s+)(?:"([^"\r\n]+)"|'([^'\r\n]+)'|([^\s"'?!,;]+))/gi)].map((m) => {
1203
+ if (m[4]) m[4] = m[4].replace(/[.:)]+$/, "");
1204
+ return m;
1205
+ }).filter((m) => !/^(?:status|statuses|current|is|are|the|list|count|in|for|of|from|their|my|our|all|each|every|now|today|currently|and|or)$/i.test(m[4] ?? ""));
1206
+ if (identities.length) {
1207
+ const tail = intent.slice((identities[0].index ?? 0) + identities[0][0].length);
1208
+ const extraIdentity = /\b(?:and|or)\b/i.test(tail) && !/^\s*[.:)]*\s+and\s+(?:(?:its|their|the)\s+)?(?:ICCID|subscriberId|subscriber\s+id|(?:current\s+)?status)\s*[.?!]*$/i.test(tail);
1209
+ if (identities.length !== 1 || /\b(named|with|whose|and|or)\b/i.test(identities[0][4] ?? "") || extraIdentity) {
1210
+ return "Carrier cannot identify a single subscriber from this unclear request. Specify one exact subscriberId or ICCID; no subscriber was selected.";
1211
+ }
1212
+ const match = identities[0];
1213
+ const id = match[2] ?? match[3] ?? match[4];
1214
+ if (!rows2) return "Subscriber row details were not returned; Carrier cannot determine the requested current status.";
1215
+ const selected = rows2.filter((value) => {
1216
+ const row = object(value);
1217
+ if (!row) return false;
1218
+ return match[1].toLowerCase() === "iccid" ? subscriberIccid(row) === id : (typeof row.subscriberId === "string" || typeof row.subscriberId === "number" && Number.isFinite(row.subscriberId)) && String(row.subscriberId) === id;
1219
+ });
1220
+ if (!selected.length) return "The requested identifier is not present in the returned rows. This does not establish fleet-wide absence.";
1221
+ if (selected.length !== 1) return "Carrier cannot select a single subscriber: that identifier is ambiguous in the returned rows.";
1222
+ return bound(`Current status from the returned row:
1223
+ ${subscriberLine(selected[0])}`);
1224
+ }
1225
+ const fieldList = /^\s*(?:list|show)\b/i.test(intent) && /\b(?:all|each|every)\s+subscribers\b/i.test(intent);
1226
+ if (!fieldList && /\b(subscriberId|subscriber\s+id|ICCID)\b|\b(?:of|for)\s+(?:the\s+)?subscriber\b/i.test(intent)) return "Carrier cannot identify a single subscriber from this unclear request. Specify one exact subscriberId or ICCID; no subscriber was selected.";
1227
+ if (!rows2) return "Subscriber row details were not returned; current statuses are unavailable.";
1228
+ const lines = ["Current subscriber statuses from the returned rows only:"];
1229
+ if (/\b(how many|count|number|total|hoeveel)\b/i.test(intent)) {
1230
+ lines.push(`Carrier reports ${summary.total} subscribers in this result.`);
1231
+ if (rows2.length < summary.total) lines.push(`Only ${rows2.length} of the reported ${summary.total} rows are present in the response; the remaining details are unavailable.`);
1232
+ }
1233
+ if (rows2.length === 0) lines.push("No subscriber rows were returned; no current statuses can be determined.");
1234
+ for (const row of rows2.slice(0, MAX_ROWS)) {
1235
+ const line = subscriberLine(row);
1236
+ if (line) lines.push(line);
1237
+ else lines.push("A returned row has an unsupported shape; its current status is unavailable.");
1238
+ }
1239
+ if (rows2.length < summary.total || summary.moreAvailable === true) lines.push("Current statuses for remaining rows are unavailable; these returned statuses are not a fleet-wide status breakdown.");
1240
+ if (rows2.length > MAX_ROWS) lines.push(`Showing ${MAX_ROWS} of ${rows2.length} returned rows. ${RAW_HINT}`);
1241
+ return bound(lines.join("\n"));
1242
+ }
1243
+ function formatAskAnswer(text3, tool, structured, intent = "") {
1244
+ const countOnly = /\b(how many|count|number of|hoeveel|aantal)\b/i.test(intent) && !/\b(status|list|details?|which|their|lijst)\b/i.test(intent);
1245
+ if (unsupportedJudgement(intent) && !INTELLIGENCE_TOOLS.has(tool)) return `Carrier cannot determine or predict that judgement from this tool result. No such conclusion is supported by the returned records. ${RAW_HINT}`;
1246
+ let value = structured;
1247
+ try {
1248
+ const parsed = JSON.parse(text3);
1249
+ if (structured === void 0) value = parsed;
1250
+ } catch {
1251
+ if (text3.trim() && !/^[\[{]/.test(text3.trim())) return bound(text3, true);
1252
+ if (structured === void 0) return `The tool result could not be read as complete JSON. ${RAW_HINT}`;
1253
+ }
1254
+ const fallback = object(value);
1255
+ if (structured === void 0 && Array.isArray(fallback?.content) && fallback.content.length === 0) {
1256
+ return "No result data was returned.";
1257
+ }
1258
+ const body = object(value);
1259
+ if (tool === "get_subscriber" && /\b(status|statuses)\b/i.test(intent)) {
1260
+ const row = object(body?.subscriber) ?? object(object(body?.data)?.subscriber) ?? object(body?.data) ?? body;
1261
+ if (row && (Object.hasOwn(row, "subscriberId") || Object.hasOwn(row, "status") || Object.hasOwn(row, "sim"))) {
1262
+ return subscriberStatusAnswer([row], { total: 1 }, intent);
1263
+ }
1264
+ }
1265
+ const summary = object(body?.summary);
1266
+ if (measured(summary?.total)) {
1267
+ const data = object(body?.data) ?? body;
1268
+ const collections = [["subscriberList", "subscribers"], ["template", "package templates"], ["prepaidPackageTemplate", "package templates"], ["reseller", "resellers"], ...OCS_LIST_KEYS.packages.map((key) => [key, "packages"]), ["zone", "zones"]];
1269
+ const found = collections.find(([key]) => Array.isArray(data?.[key]));
1270
+ const noun = found?.[1] ?? (tool === "list_subscribers" ? "subscribers" : tool === "list_package_templates" ? "package templates" : tool === "list_subscriber_packages" ? "packages" : "items");
1271
+ const rows2 = found ? data?.[found[0]] : void 0;
1272
+ if (noun === "subscribers" && !countOnly && /\b(status|statuses)\b/i.test(intent)) return subscriberStatusAnswer(rows2, summary, intent);
1273
+ const lines = [`Carrier reports ${summary.total} ${noun} in this result.`];
1274
+ for (const [key, count] of Object.entries(object(summary.nested) ?? {})) {
1275
+ if (key === "accounts" && measured(count)) lines.push(`${count} accounts reported inside the result.`);
1276
+ }
1277
+ if (summary.moreAvailable === true) lines.push("More results are available; this is not a complete fleet total.");
1278
+ if (measured(summary.upstreamTotal)) lines.push(`Upstream reports ${summary.upstreamTotal} matches before paging.`);
1279
+ for (const [key, value2] of Object.entries(object(summary.breakdown) ?? {})) {
1280
+ const counts = object(object(value2)?.counts);
1281
+ const entries = Object.entries(counts ?? {}).filter(([name, n]) => measured(n) && safeFacet(key, name));
1282
+ if (entries.length) lines.push(`${label(key)} (server-reported): ${entries.map(([k, n]) => `${k}: ${n}`).join(", ")}.`);
1283
+ }
1284
+ if (rows2) {
1285
+ if (rows2.length < summary.total) lines.push(`Only ${rows2.length} of the reported ${summary.total} rows are present in the response; the remaining details are unavailable.`);
1286
+ if (countOnly) return bound(lines.join("\n"));
1287
+ if (noun === "subscribers") {
1288
+ for (const value2 of rows2.slice(0, MAX_ROWS)) {
1289
+ const line = subscriberLine(value2);
1290
+ if (line) lines.push(line);
1291
+ }
1292
+ } else if (tool === "list_reseller_accounts" && found?.[0] === "reseller" && rows2.length) {
1293
+ const nested = object(summary.nested)?.accounts;
1294
+ lines.push(...resellerAccountLines(rows2, measured(nested) ? nested : void 0));
1295
+ } else if (tool === "list_package_templates" && (found?.[0] === "template" || found?.[0] === "prepaidPackageTemplate") && rows2.length) {
1296
+ lines.push(...templateLines(rows2));
1297
+ } else lines.push(...facts(rows2));
1298
+ if (rows2.length > MAX_ROWS && noun === "subscribers") lines.push(`Showing ${MAX_ROWS} of ${rows2.length} returned rows. ${RAW_HINT}`);
1299
+ } else lines.push("Row details were not returned.");
1300
+ return bound(lines.join("\n"));
1301
+ }
1302
+ if (value === null) return "No result data was returned.";
1303
+ if (typeof value !== "object") return bound(`Carrier returned: ${String(value)}`);
1304
+ if (tool === "fleet_health_app" && body) {
1305
+ return bound(["Carrier returned these fleet facts:", ...recordFacts(body, FLEET_FIELDS, false)].join("\n"));
1306
+ }
1307
+ return bound(["Carrier returned these facts (not an inferred answer):", ...facts(value)].join("\n"));
1308
+ }
1309
+ var UNSUPPORTED = `Carrier cannot safely interpret unsupported result fields or shapes. ${RAW_HINT}`;
1310
+ var INTELLIGENCE_TOOLS = /* @__PURE__ */ new Set(["optimize_package", "churn_risk", "fleet_health", "fleet_health_app"]);
1311
+ var FLEET_FIELDS = {
1312
+ utilization: "number",
1313
+ totalActive: "number",
1314
+ totalSuspended: "number",
1315
+ totalInventory: "number",
1316
+ totalOther: "number",
1317
+ totalAccounts: "number",
1318
+ lowBalanceCount: "number"
1319
+ };
1320
+ var FIELD_TYPES = {
1321
+ name: "string",
1322
+ description: "string",
1323
+ detail: "string",
1324
+ currency: "string",
1325
+ status: "string",
1326
+ balance: "money",
1327
+ amount: "money",
1328
+ price: "money",
1329
+ dataLimit: "number",
1330
+ dataUsed: "number",
1331
+ expirationDate: "string",
1332
+ startDate: "string",
1333
+ endDate: "string",
1334
+ useddatabyte: "number",
1335
+ pckdatabyte: "number",
1336
+ tsexpirationutc: "string",
1337
+ recurring: "boolean",
1338
+ active: "boolean",
1339
+ enabled: "boolean",
1340
+ id: "scalar",
1341
+ accountId: "scalar",
1342
+ resellerId: "scalar",
1343
+ templateId: "scalar",
1344
+ packageId: "scalar",
1345
+ zoneId: "scalar",
1346
+ subscriberId: "scalar",
1347
+ // packageOnly is a boolean on the live listResellerAccount account rows.
1348
+ packageOnly: "boolean"
1349
+ };
1350
+ var TEMPLATE_FIELD_TYPES = {
1351
+ prepaidpackagetemplateid: "scalar",
1352
+ prepaidpackagetemplatename: "string",
1353
+ resellerid: "scalar",
1354
+ locationzoneid: "scalar"
1355
+ };
1356
+ var ACCOUNT_FIELD_TYPES = {
1357
+ id: "scalar",
1358
+ name: "string",
1359
+ balance: "money",
1360
+ packageOnly: "boolean"
1361
+ };
1362
+ function inline(value) {
1363
+ return sanitizeAskText(String(value)).replace(/[\r\n\t]+/g, " ");
1364
+ }
1365
+ function sanitizeAskText(text3) {
1366
+ return text3.replace(/(?:\x1b\]|\x9d)[\s\S]*?(?:\x07|\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b[P^_X]|[\x90\x98\x9e\x9f])[\s\S]*?(?:\x1b\\|\x9c|$)/g, "").replace(/(?:\x1b\[|\x9b)[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[ -/]*[@-~]/g, "").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
1367
+ }
1368
+ function label(key) {
1369
+ const words = key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ");
1370
+ return words.charAt(0).toUpperCase() + words.slice(1);
1371
+ }
1372
+ function validField(expected, value) {
1373
+ if (expected === "scalar") return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
1374
+ if (expected === "money") return typeof value === "number" && Number.isFinite(value) || typeof value === "string" && /^[+-]?\d+(?:\.\d+)?$/.test(value) && Number.isFinite(Number(value));
1375
+ if (!expected) return false;
1376
+ return typeof value === expected && (expected !== "number" || Number.isFinite(value));
1377
+ }
1378
+ function recordFacts(record2, fields = FIELD_TYPES, warnUnknown = true) {
1379
+ const lines = [];
1380
+ let unsupported = false;
1381
+ for (const [key, value] of Object.entries(record2)) {
1382
+ const expected = Object.hasOwn(fields, key) ? fields[key] : void 0;
1383
+ if (!expected || !validField(expected, value)) {
1384
+ if (warnUnknown || expected) unsupported = true;
1385
+ continue;
1386
+ }
1387
+ lines.push(`${label(key)}: ${typeof value === "boolean" ? String(value) : inline(value)}`);
1388
+ }
1389
+ if (unsupported) lines.push(UNSUPPORTED);
1390
+ return lines.length ? lines : ["No result fields were returned."];
1391
+ }
1392
+ var TEMPLATE_WIRE_FIELDS = ["prepaidpackagetemplateid", "prepaidpackagetemplatename", "resellerid", "locationzoneid"];
1393
+ var ACCOUNT_FIELD_ORDER = ["name", "balance", "packageOnly"];
1394
+ function own(record2, key) {
1395
+ return Object.hasOwn(record2, key) ? record2[key] : void 0;
1396
+ }
1397
+ function identifier(value, state) {
1398
+ if (value === void 0) return void 0;
1399
+ if (!validField("scalar", value)) {
1400
+ state.unsupported++;
1401
+ return void 0;
1402
+ }
1403
+ return inline(value) || void 0;
1404
+ }
1405
+ function resellerAccountLines(rows2, nestedAccounts) {
1406
+ const state = { unsupported: 0 };
1407
+ const accountLines = [];
1408
+ let listed = 0;
1409
+ let empty = 0;
1410
+ for (const entry of rows2) {
1411
+ const reseller = object(entry);
1412
+ if (!reseller) {
1413
+ state.unsupported++;
1414
+ continue;
1415
+ }
1416
+ const accounts = own(reseller, "account");
1417
+ if (!Array.isArray(accounts)) {
1418
+ state.unsupported++;
1419
+ continue;
1420
+ }
1421
+ if (accounts.length === 0) {
1422
+ empty++;
1423
+ continue;
1424
+ }
1425
+ const resellerId = identifier(own(reseller, "id"), state);
1426
+ const resellerName = own(reseller, "name");
1427
+ if (resellerName !== void 0 && typeof resellerName !== "string") state.unsupported++;
1428
+ const association = `reseller ${resellerId ?? "(identifier not reported)"}${typeof resellerName === "string" && inline(resellerName) ? ` (${inline(resellerName)})` : ""}`;
1429
+ for (const item of accounts) {
1430
+ const account = object(item);
1431
+ if (!account) {
1432
+ state.unsupported++;
1433
+ continue;
1434
+ }
1435
+ listed++;
1436
+ if (accountLines.length >= MAX_ROWS) continue;
1437
+ const parts = [`Account ${identifier(own(account, "id"), state) ?? "(identifier not reported)"}`, association];
1438
+ for (const key of ACCOUNT_FIELD_ORDER) {
1439
+ const value = own(account, key);
1440
+ if (value === void 0) continue;
1441
+ if (!validField(ACCOUNT_FIELD_TYPES[key], value)) {
1442
+ state.unsupported++;
1443
+ continue;
1444
+ }
1445
+ parts.push(`${label(key)}: ${typeof value === "boolean" ? String(value) : inline(value)}`);
1446
+ }
1447
+ accountLines.push(parts.join("; "));
1448
+ }
1449
+ }
1450
+ const lines = [];
1451
+ if (nestedAccounts !== void 0 && listed < nestedAccounts) {
1452
+ lines.push(`Only ${listed} of the reported ${nestedAccounts} accounts are present in the response; the remaining account details are unavailable.`);
1453
+ }
1454
+ if (state.unsupported) lines.push(UNSUPPORTED);
1455
+ if (empty) lines.push(`No accounts are listed under ${empty} ${empty === 1 ? "reseller" : "resellers"} in the returned rows.`);
1456
+ if (listed > MAX_ROWS) lines.push(`Showing ${accountLines.length} of ${listed} returned accounts. ${RAW_HINT}`);
1457
+ return [...lines, ...accountLines];
1458
+ }
1459
+ function templateLines(rows2) {
1460
+ const state = { unsupported: 0 };
1461
+ const itemLines = [];
1462
+ rows2.forEach((row, i) => {
1463
+ const record2 = object(row);
1464
+ if (!record2) {
1465
+ state.unsupported++;
1466
+ return;
1467
+ }
1468
+ const wire = Object.hasOwn(record2, "prepaidpackagetemplateid") || Object.hasOwn(record2, "prepaidpackagetemplatename");
1469
+ const types = wire ? TEMPLATE_FIELD_TYPES : FIELD_TYPES;
1470
+ const entries = wire ? TEMPLATE_WIRE_FIELDS.filter((key) => Object.hasOwn(record2, key)).map((key) => [key, record2[key]]) : Object.entries(record2);
1471
+ const parts = [];
1472
+ for (const [key, value] of entries) {
1473
+ const expected = Object.hasOwn(types, key) ? types[key] : void 0;
1474
+ if (!expected || !validField(expected, value)) {
1475
+ state.unsupported++;
1476
+ continue;
1477
+ }
1478
+ parts.push(`${label(key)}: ${typeof value === "boolean" ? String(value) : inline(value)}`);
1479
+ }
1480
+ if (itemLines.length < MAX_ROWS) itemLines.push(`Item ${i + 1}: ${parts.length ? parts.join("; ") : "No result fields were returned."}`);
1481
+ });
1482
+ const lines = [];
1483
+ if (state.unsupported) lines.push(UNSUPPORTED);
1484
+ if (rows2.length > MAX_ROWS) lines.push(`Showing ${MAX_ROWS} of ${rows2.length} returned rows. ${RAW_HINT}`);
1485
+ return [...lines, ...itemLines];
1486
+ }
1487
+ function facts(value) {
1488
+ if (Array.isArray(value)) {
1489
+ if (!value.length) return ["No items were returned."];
1490
+ const lines2 = value.slice(0, MAX_ROWS).flatMap((row, i) => {
1491
+ const record3 = object(row);
1492
+ return record3 ? [`Item ${i + 1}: ${recordFacts(record3).join("; ")}`] : [UNSUPPORTED];
1493
+ });
1494
+ if (value.length > MAX_ROWS) lines2.push(`Showing ${MAX_ROWS} of ${value.length} returned rows. ${RAW_HINT}`);
1495
+ return lines2;
1496
+ }
1497
+ const record2 = object(value);
1498
+ if (!record2) return [UNSUPPORTED];
1499
+ const outer = Object.fromEntries(Object.entries(record2).filter(([key]) => !["account", "result", "data"].includes(key)));
1500
+ const lines = Object.keys(outer).length ? recordFacts(outer) : [];
1501
+ for (const key of ["account", "result", "data"]) {
1502
+ if (!Object.hasOwn(record2, key)) continue;
1503
+ const inner = object(record2[key]);
1504
+ if (inner) lines.push(`${label(key)}:`, ...recordFacts(inner));
1505
+ else if (key === "account" && Array.isArray(record2[key])) {
1506
+ const accounts = record2[key];
1507
+ lines.push(...facts(accounts));
1508
+ } else lines.push(UNSUPPORTED);
1509
+ }
1510
+ return lines.length ? lines : ["No result fields were returned."];
1511
+ }
1512
+
1156
1513
  // src/cli/lib/ask.ts
1157
1514
  var DEFAULT_ERROR_HINTS = [
1158
1515
  "Re-check the arguments: carrier <domain> <command> --help",
@@ -1342,11 +1699,24 @@ function parseRouting(text3) {
1342
1699
  return void 0;
1343
1700
  }
1344
1701
  }
1345
- async function carrierAsk(intent) {
1702
+ function readableAskError(result) {
1703
+ try {
1704
+ const body = JSON.parse(result.text);
1705
+ const message = body.error ?? body.message;
1706
+ if (typeof message === "string") return { ...result, text: message };
1707
+ } catch {
1708
+ }
1709
+ return result;
1710
+ }
1711
+ async function carrierAsk(intent, opts = {}) {
1712
+ const result = await routeAsk(intent, opts);
1713
+ return opts.json ? result : { ...result, text: sanitizeAskText(result.text) };
1714
+ }
1715
+ async function routeAsk(intent, opts) {
1346
1716
  const routed = await callMcpTool("carrier_ask", { intent }, { errorHints: ASK_ERROR_HINTS });
1347
1717
  if (!routed.ok) return routed;
1348
1718
  const routing = parseRouting(routed.text);
1349
- if (!routing) return routed;
1719
+ if (!routing) return opts.json ? routed : { ...routed, text: formatAskAnswer(routed.text, "carrier_ask", routed.structured, intent) };
1350
1720
  if (!routing.resolved_tool) {
1351
1721
  const candidates = (routing.candidates ?? []).map((c) => c.tool ? `Candidate: ${c.tool}${c.reason ? ` \u2014 ${c.reason}` : ""}` : void 0).filter((s) => Boolean(s));
1352
1722
  return {
@@ -1372,13 +1742,16 @@ async function carrierAsk(intent) {
1372
1742
  return {
1373
1743
  ok: false,
1374
1744
  text: withNextStep(`"${intent}" resolves to ${tool}, which changes live service or billing.`, [
1375
- `Params: ${JSON.stringify(args)}`,
1745
+ `Params: ${JSON.stringify(args, (_k, v) => typeof v === "string" ? sanitizeAskText(v) : v)}`,
1376
1746
  "Run the matching discrete command so every flag is explicit and reviewable",
1377
1747
  "carrier --help lists every domain"
1378
1748
  ])
1379
1749
  };
1380
1750
  }
1381
- return callMcpTool(tool, args, { errorHints: ASK_ERROR_HINTS });
1751
+ const result = await callMcpTool(tool, args, { errorHints: ASK_ERROR_HINTS });
1752
+ if (opts.json) return result;
1753
+ if (!result.ok) return readableAskError(result);
1754
+ return { ...result, text: formatAskAnswer(result.text, tool, result.structured, intent) };
1382
1755
  }
1383
1756
 
1384
1757
  // src/cli/lib/rest.ts
@@ -1568,9 +1941,9 @@ var NAME_TO_CODE = /* @__PURE__ */ new Map();
1568
1941
  for (let first = 65; first <= 90; first += 1) {
1569
1942
  for (let second = 65; second <= 90; second += 1) {
1570
1943
  const code = String.fromCharCode(first, second);
1571
- const label = regionLabel(code);
1572
- if (!label) continue;
1573
- const key = label.toLowerCase();
1944
+ const label2 = regionLabel(code);
1945
+ if (!label2) continue;
1946
+ const key = label2.toLowerCase();
1574
1947
  const current = NAME_TO_CODE.get(key);
1575
1948
  if (!current || PREFERRED.has(code)) NAME_TO_CODE.set(key, code);
1576
1949
  }
@@ -1702,9 +2075,9 @@ function logoMime(bytes) {
1702
2075
  function regionLabel(code) {
1703
2076
  if (!/^[A-Z]{2}$/.test(code)) return void 0;
1704
2077
  try {
1705
- const label = regionNames.of(code);
1706
- if (!label || label.toUpperCase() === code) return void 0;
1707
- return label;
2078
+ const label2 = regionNames.of(code);
2079
+ if (!label2 || label2.toUpperCase() === code) return void 0;
2080
+ return label2;
1708
2081
  } catch {
1709
2082
  return void 0;
1710
2083
  }
@@ -1825,9 +2198,9 @@ function storefrontPlanBody(input) {
1825
2198
  function isStorefrontPlanError(value) {
1826
2199
  return value !== null && "error" in value;
1827
2200
  }
1828
- function parseWholeNumber(value, label) {
2201
+ function parseWholeNumber(value, label2) {
1829
2202
  if (value === void 0) return { ok: true, value: void 0 };
1830
- if (!/^\d+$/.test(value)) return { ok: false, message: `${label} must be a whole number.` };
2203
+ if (!/^\d+$/.test(value)) return { ok: false, message: `${label2} must be a whole number.` };
1831
2204
  return { ok: true, value: Number(value) };
1832
2205
  }
1833
2206
  function overlay(base, input, fields) {
@@ -2738,12 +3111,12 @@ async function showHomeStatus() {
2738
3111
  printStatus(st);
2739
3112
  return st;
2740
3113
  }
2741
- async function openAndReport(url, label) {
3114
+ async function openAndReport(url, label2) {
2742
3115
  const r = await openUrl(url);
2743
3116
  if (r.ok) p.log.success(r.hint);
2744
3117
  else {
2745
3118
  p.log.warn(r.hint);
2746
- p.note(url, label);
3119
+ p.note(url, label2);
2747
3120
  }
2748
3121
  }
2749
3122
  async function runHome(opts) {
@@ -2848,7 +3221,7 @@ async function runHome(opts) {
2848
3221
  spin.start("Asking Carrier MCP\u2026");
2849
3222
  const result = await carrierAsk(String(intent).trim());
2850
3223
  spin.stop(result.ok ? "Answer" : "Could not complete ask");
2851
- if (result.ok) p.note(result.text.slice(0, 4e3), "carrier ask");
3224
+ if (result.ok) p.note(result.text, "carrier ask");
2852
3225
  else p.log.error(result.text);
2853
3226
  break;
2854
3227
  }
@@ -5736,7 +6109,7 @@ function formatJsonOutput(text3) {
5736
6109
  }
5737
6110
  }
5738
6111
  async function runCapability(domain, cmd, opts) {
5739
- const label = `${domain.name} ${cmd.name}`;
6112
+ const label2 = `${domain.name} ${cmd.name}`;
5740
6113
  const quiet = opts.json === true;
5741
6114
  const { args, errors, notes } = buildToolArgs(cmd, opts);
5742
6115
  if (cmd.write && opts.commit === true) delete args.dry_run;
@@ -5744,9 +6117,9 @@ async function runCapability(domain, cmd, opts) {
5744
6117
  errors.push(`${capabilityTarget(cmd)} has no preview mode. Pass --confirm to run it for real.`);
5745
6118
  }
5746
6119
  if (errors.length) {
5747
- const message = withNextStep(`Cannot run \`carrier ${label}\`.`, [
6120
+ const message = withNextStep(`Cannot run \`carrier ${label2}\`.`, [
5748
6121
  ...errors,
5749
- `See every flag: carrier ${label} --help`
6122
+ `See every flag: carrier ${label2} --help`
5750
6123
  ]);
5751
6124
  if (quiet) console.error(message);
5752
6125
  else p2.log.error(message);
@@ -5762,7 +6135,7 @@ async function runCapability(domain, cmd, opts) {
5762
6135
  ].join("\n");
5763
6136
  if (quiet) process.stdout.write(`${formatJsonOutput(preview)}
5764
6137
  `);
5765
- else p2.note(preview, `carrier ${label}`);
6138
+ else p2.note(preview, `carrier ${label2}`);
5766
6139
  return;
5767
6140
  }
5768
6141
  const dryRun = cmd.write === true && args.dry_run === true;
@@ -5773,7 +6146,7 @@ async function runCapability(domain, cmd, opts) {
5773
6146
  }
5774
6147
  const target = capabilityTarget(cmd);
5775
6148
  const errorHints = [
5776
- `Check the arguments: carrier ${label} --help`,
6149
+ `Check the arguments: carrier ${label2} --help`,
5777
6150
  'Or describe the task in words: carrier ask "\u2026"'
5778
6151
  ];
5779
6152
  const s = quiet ? null : p2.spinner();
@@ -6587,8 +6960,8 @@ program.command("examples").description("Print natural-language fleet prompts fo
6587
6960
  });
6588
6961
  program.command("ask").description(
6589
6962
  'Natural-language fallback for whatever the domain commands miss: carrier ask "show fleet health" (needs CARRIER_API_KEY or OCS token).'
6590
- ).argument("<intent...>", "Natural-language intent").action(async (parts) => {
6591
- header();
6963
+ ).argument("<intent...>", "Natural-language intent").option("--json", "Print the complete raw tool result (may contain sensitive data)").action(async (parts, opts) => {
6964
+ if (!opts.json) header();
6592
6965
  const intent = parts.join(" ").trim();
6593
6966
  if (!intent) {
6594
6967
  fail("Missing intent.", [
@@ -6598,12 +6971,27 @@ program.command("ask").description(
6598
6971
  process.exitCode = 1;
6599
6972
  return;
6600
6973
  }
6601
- const s = p3.spinner();
6602
- s.start("Asking Carrier MCP\u2026");
6603
- const result = await carrierAsk(intent);
6604
- s.stop(result.ok ? "Done" : "Failed");
6974
+ const s = opts.json ? void 0 : p3.spinner();
6975
+ s?.start("Asking Carrier MCP\u2026");
6976
+ const result = await carrierAsk(intent, opts);
6977
+ s?.stop(result.ok ? "Done" : "Failed");
6978
+ if (opts.json) {
6979
+ if (result.ok) {
6980
+ let raw = result.text;
6981
+ try {
6982
+ JSON.parse(raw);
6983
+ } catch {
6984
+ raw = JSON.stringify(raw);
6985
+ }
6986
+ process.stdout.write(raw + "\n");
6987
+ } else {
6988
+ process.stderr.write(result.text + "\n");
6989
+ process.exitCode = 1;
6990
+ }
6991
+ return;
6992
+ }
6605
6993
  if (result.ok) {
6606
- p3.note(result.text.slice(0, 6e3), "Answer");
6994
+ p3.note(result.text, "Answer");
6607
6995
  p3.outro("");
6608
6996
  } else {
6609
6997
  p3.log.error(result.text);