@carrierllc/mcp 0.13.30 → 0.13.31

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-EHJEAI4V.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,288 @@ 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 (found?.[0] === "reseller") {
1293
+ const accounts = projectResellerAccounts(rows2);
1294
+ lines.push(...facts(accounts.length ? accounts : rows2));
1295
+ } else if (found?.[0] === "template" || found?.[0] === "prepaidPackageTemplate") {
1296
+ lines.push(...facts(projectPackageTemplates(rows2) ?? 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
+ // Live listPrepaidPackageTemplate rows use these wire names, not templateId.
1348
+ prepaidpackagetemplateid: "scalar",
1349
+ prepaidpackagetemplatename: "string",
1350
+ resellerid: "scalar",
1351
+ locationzoneid: "scalar",
1352
+ // listResellerAccount account rows. packageOnly is a boolean on the live shape.
1353
+ packageOnly: "boolean"
1354
+ };
1355
+ function inline(value) {
1356
+ return sanitizeAskText(String(value)).replace(/[\r\n\t]+/g, " ");
1357
+ }
1358
+ function sanitizeAskText(text3) {
1359
+ 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, "");
1360
+ }
1361
+ function label(key) {
1362
+ const words = key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ");
1363
+ return words.charAt(0).toUpperCase() + words.slice(1);
1364
+ }
1365
+ function recordFacts(record2, fields = FIELD_TYPES, warnUnknown = true) {
1366
+ const lines = [];
1367
+ let unsupported = false;
1368
+ for (const [key, value] of Object.entries(record2)) {
1369
+ const expected = Object.hasOwn(fields, key) ? fields[key] : void 0;
1370
+ const valid = expected === "scalar" ? typeof value === "string" || typeof value === "number" && Number.isFinite(value) : expected === "money" ? typeof value === "number" && Number.isFinite(value) || typeof value === "string" && /^[+-]?\d+(?:\.\d+)?$/.test(value) && Number.isFinite(Number(value)) : typeof value === expected && (expected !== "number" || Number.isFinite(value));
1371
+ if (!expected || !valid) {
1372
+ if (warnUnknown || expected) unsupported = true;
1373
+ continue;
1374
+ }
1375
+ lines.push(`${label(key)}: ${typeof value === "boolean" ? String(value) : inline(value)}`);
1376
+ }
1377
+ if (unsupported) lines.push(UNSUPPORTED);
1378
+ return lines.length ? lines : ["No result fields were returned."];
1379
+ }
1380
+ var TEMPLATE_WIRE_FIELDS = ["prepaidpackagetemplateid", "prepaidpackagetemplatename", "resellerid", "locationzoneid"];
1381
+ var ACCOUNT_WIRE_FIELDS = ["id", "name", "balance", "packageOnly"];
1382
+ function projectResellerAccounts(rows2) {
1383
+ const accounts = [];
1384
+ for (const row of rows2) {
1385
+ const reseller = object(row);
1386
+ if (!reseller || !Array.isArray(reseller.account)) continue;
1387
+ for (const item of reseller.account) {
1388
+ const account = object(item);
1389
+ if (!account) continue;
1390
+ const projected = {};
1391
+ if (typeof reseller.id === "number" && Number.isFinite(reseller.id)) projected.resellerId = reseller.id;
1392
+ for (const key of ACCOUNT_WIRE_FIELDS) {
1393
+ if (Object.hasOwn(account, key)) projected[key] = account[key];
1394
+ }
1395
+ accounts.push(projected);
1396
+ }
1397
+ }
1398
+ return accounts;
1399
+ }
1400
+ function projectPackageTemplates(rows2) {
1401
+ const projected = [];
1402
+ for (const row of rows2) {
1403
+ const record2 = object(row);
1404
+ if (!record2) return void 0;
1405
+ const wire = Object.hasOwn(record2, "prepaidpackagetemplateid") || Object.hasOwn(record2, "prepaidpackagetemplatename");
1406
+ if (!wire) return void 0;
1407
+ const next = {};
1408
+ for (const key of TEMPLATE_WIRE_FIELDS) {
1409
+ if (Object.hasOwn(record2, key)) next[key] = record2[key];
1410
+ }
1411
+ projected.push(next);
1412
+ }
1413
+ return projected;
1414
+ }
1415
+ function facts(value) {
1416
+ if (Array.isArray(value)) {
1417
+ if (!value.length) return ["No items were returned."];
1418
+ const lines2 = value.slice(0, MAX_ROWS).flatMap((row, i) => {
1419
+ const record3 = object(row);
1420
+ return record3 ? [`Item ${i + 1}: ${recordFacts(record3).join("; ")}`] : [UNSUPPORTED];
1421
+ });
1422
+ if (value.length > MAX_ROWS) lines2.push(`Showing ${MAX_ROWS} of ${value.length} returned rows. ${RAW_HINT}`);
1423
+ return lines2;
1424
+ }
1425
+ const record2 = object(value);
1426
+ if (!record2) return [UNSUPPORTED];
1427
+ const outer = Object.fromEntries(Object.entries(record2).filter(([key]) => !["account", "result", "data"].includes(key)));
1428
+ const lines = Object.keys(outer).length ? recordFacts(outer) : [];
1429
+ for (const key of ["account", "result", "data"]) {
1430
+ if (!Object.hasOwn(record2, key)) continue;
1431
+ const inner = object(record2[key]);
1432
+ if (inner) lines.push(`${label(key)}:`, ...recordFacts(inner));
1433
+ else if (key === "account" && Array.isArray(record2[key])) {
1434
+ const accounts = record2[key];
1435
+ lines.push(...facts(accounts));
1436
+ } else lines.push(UNSUPPORTED);
1437
+ }
1438
+ return lines.length ? lines : ["No result fields were returned."];
1439
+ }
1440
+
1156
1441
  // src/cli/lib/ask.ts
1157
1442
  var DEFAULT_ERROR_HINTS = [
1158
1443
  "Re-check the arguments: carrier <domain> <command> --help",
@@ -1342,11 +1627,24 @@ function parseRouting(text3) {
1342
1627
  return void 0;
1343
1628
  }
1344
1629
  }
1345
- async function carrierAsk(intent) {
1630
+ function readableAskError(result) {
1631
+ try {
1632
+ const body = JSON.parse(result.text);
1633
+ const message = body.error ?? body.message;
1634
+ if (typeof message === "string") return { ...result, text: message };
1635
+ } catch {
1636
+ }
1637
+ return result;
1638
+ }
1639
+ async function carrierAsk(intent, opts = {}) {
1640
+ const result = await routeAsk(intent, opts);
1641
+ return opts.json ? result : { ...result, text: sanitizeAskText(result.text) };
1642
+ }
1643
+ async function routeAsk(intent, opts) {
1346
1644
  const routed = await callMcpTool("carrier_ask", { intent }, { errorHints: ASK_ERROR_HINTS });
1347
1645
  if (!routed.ok) return routed;
1348
1646
  const routing = parseRouting(routed.text);
1349
- if (!routing) return routed;
1647
+ if (!routing) return opts.json ? routed : { ...routed, text: formatAskAnswer(routed.text, "carrier_ask", routed.structured, intent) };
1350
1648
  if (!routing.resolved_tool) {
1351
1649
  const candidates = (routing.candidates ?? []).map((c) => c.tool ? `Candidate: ${c.tool}${c.reason ? ` \u2014 ${c.reason}` : ""}` : void 0).filter((s) => Boolean(s));
1352
1650
  return {
@@ -1372,13 +1670,16 @@ async function carrierAsk(intent) {
1372
1670
  return {
1373
1671
  ok: false,
1374
1672
  text: withNextStep(`"${intent}" resolves to ${tool}, which changes live service or billing.`, [
1375
- `Params: ${JSON.stringify(args)}`,
1673
+ `Params: ${JSON.stringify(args, (_k, v) => typeof v === "string" ? sanitizeAskText(v) : v)}`,
1376
1674
  "Run the matching discrete command so every flag is explicit and reviewable",
1377
1675
  "carrier --help lists every domain"
1378
1676
  ])
1379
1677
  };
1380
1678
  }
1381
- return callMcpTool(tool, args, { errorHints: ASK_ERROR_HINTS });
1679
+ const result = await callMcpTool(tool, args, { errorHints: ASK_ERROR_HINTS });
1680
+ if (opts.json) return result;
1681
+ if (!result.ok) return readableAskError(result);
1682
+ return { ...result, text: formatAskAnswer(result.text, tool, result.structured, intent) };
1382
1683
  }
1383
1684
 
1384
1685
  // src/cli/lib/rest.ts
@@ -1568,9 +1869,9 @@ var NAME_TO_CODE = /* @__PURE__ */ new Map();
1568
1869
  for (let first = 65; first <= 90; first += 1) {
1569
1870
  for (let second = 65; second <= 90; second += 1) {
1570
1871
  const code = String.fromCharCode(first, second);
1571
- const label = regionLabel(code);
1572
- if (!label) continue;
1573
- const key = label.toLowerCase();
1872
+ const label2 = regionLabel(code);
1873
+ if (!label2) continue;
1874
+ const key = label2.toLowerCase();
1574
1875
  const current = NAME_TO_CODE.get(key);
1575
1876
  if (!current || PREFERRED.has(code)) NAME_TO_CODE.set(key, code);
1576
1877
  }
@@ -1702,9 +2003,9 @@ function logoMime(bytes) {
1702
2003
  function regionLabel(code) {
1703
2004
  if (!/^[A-Z]{2}$/.test(code)) return void 0;
1704
2005
  try {
1705
- const label = regionNames.of(code);
1706
- if (!label || label.toUpperCase() === code) return void 0;
1707
- return label;
2006
+ const label2 = regionNames.of(code);
2007
+ if (!label2 || label2.toUpperCase() === code) return void 0;
2008
+ return label2;
1708
2009
  } catch {
1709
2010
  return void 0;
1710
2011
  }
@@ -1825,9 +2126,9 @@ function storefrontPlanBody(input) {
1825
2126
  function isStorefrontPlanError(value) {
1826
2127
  return value !== null && "error" in value;
1827
2128
  }
1828
- function parseWholeNumber(value, label) {
2129
+ function parseWholeNumber(value, label2) {
1829
2130
  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.` };
2131
+ if (!/^\d+$/.test(value)) return { ok: false, message: `${label2} must be a whole number.` };
1831
2132
  return { ok: true, value: Number(value) };
1832
2133
  }
1833
2134
  function overlay(base, input, fields) {
@@ -2738,12 +3039,12 @@ async function showHomeStatus() {
2738
3039
  printStatus(st);
2739
3040
  return st;
2740
3041
  }
2741
- async function openAndReport(url, label) {
3042
+ async function openAndReport(url, label2) {
2742
3043
  const r = await openUrl(url);
2743
3044
  if (r.ok) p.log.success(r.hint);
2744
3045
  else {
2745
3046
  p.log.warn(r.hint);
2746
- p.note(url, label);
3047
+ p.note(url, label2);
2747
3048
  }
2748
3049
  }
2749
3050
  async function runHome(opts) {
@@ -2848,7 +3149,7 @@ async function runHome(opts) {
2848
3149
  spin.start("Asking Carrier MCP\u2026");
2849
3150
  const result = await carrierAsk(String(intent).trim());
2850
3151
  spin.stop(result.ok ? "Answer" : "Could not complete ask");
2851
- if (result.ok) p.note(result.text.slice(0, 4e3), "carrier ask");
3152
+ if (result.ok) p.note(result.text, "carrier ask");
2852
3153
  else p.log.error(result.text);
2853
3154
  break;
2854
3155
  }
@@ -5736,7 +6037,7 @@ function formatJsonOutput(text3) {
5736
6037
  }
5737
6038
  }
5738
6039
  async function runCapability(domain, cmd, opts) {
5739
- const label = `${domain.name} ${cmd.name}`;
6040
+ const label2 = `${domain.name} ${cmd.name}`;
5740
6041
  const quiet = opts.json === true;
5741
6042
  const { args, errors, notes } = buildToolArgs(cmd, opts);
5742
6043
  if (cmd.write && opts.commit === true) delete args.dry_run;
@@ -5744,9 +6045,9 @@ async function runCapability(domain, cmd, opts) {
5744
6045
  errors.push(`${capabilityTarget(cmd)} has no preview mode. Pass --confirm to run it for real.`);
5745
6046
  }
5746
6047
  if (errors.length) {
5747
- const message = withNextStep(`Cannot run \`carrier ${label}\`.`, [
6048
+ const message = withNextStep(`Cannot run \`carrier ${label2}\`.`, [
5748
6049
  ...errors,
5749
- `See every flag: carrier ${label} --help`
6050
+ `See every flag: carrier ${label2} --help`
5750
6051
  ]);
5751
6052
  if (quiet) console.error(message);
5752
6053
  else p2.log.error(message);
@@ -5762,7 +6063,7 @@ async function runCapability(domain, cmd, opts) {
5762
6063
  ].join("\n");
5763
6064
  if (quiet) process.stdout.write(`${formatJsonOutput(preview)}
5764
6065
  `);
5765
- else p2.note(preview, `carrier ${label}`);
6066
+ else p2.note(preview, `carrier ${label2}`);
5766
6067
  return;
5767
6068
  }
5768
6069
  const dryRun = cmd.write === true && args.dry_run === true;
@@ -5773,7 +6074,7 @@ async function runCapability(domain, cmd, opts) {
5773
6074
  }
5774
6075
  const target = capabilityTarget(cmd);
5775
6076
  const errorHints = [
5776
- `Check the arguments: carrier ${label} --help`,
6077
+ `Check the arguments: carrier ${label2} --help`,
5777
6078
  'Or describe the task in words: carrier ask "\u2026"'
5778
6079
  ];
5779
6080
  const s = quiet ? null : p2.spinner();
@@ -6587,8 +6888,8 @@ program.command("examples").description("Print natural-language fleet prompts fo
6587
6888
  });
6588
6889
  program.command("ask").description(
6589
6890
  '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();
6891
+ ).argument("<intent...>", "Natural-language intent").option("--json", "Print the complete raw tool result (may contain sensitive data)").action(async (parts, opts) => {
6892
+ if (!opts.json) header();
6592
6893
  const intent = parts.join(" ").trim();
6593
6894
  if (!intent) {
6594
6895
  fail("Missing intent.", [
@@ -6598,12 +6899,27 @@ program.command("ask").description(
6598
6899
  process.exitCode = 1;
6599
6900
  return;
6600
6901
  }
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");
6902
+ const s = opts.json ? void 0 : p3.spinner();
6903
+ s?.start("Asking Carrier MCP\u2026");
6904
+ const result = await carrierAsk(intent, opts);
6905
+ s?.stop(result.ok ? "Done" : "Failed");
6906
+ if (opts.json) {
6907
+ if (result.ok) {
6908
+ let raw = result.text;
6909
+ try {
6910
+ JSON.parse(raw);
6911
+ } catch {
6912
+ raw = JSON.stringify(raw);
6913
+ }
6914
+ process.stdout.write(raw + "\n");
6915
+ } else {
6916
+ process.stderr.write(result.text + "\n");
6917
+ process.exitCode = 1;
6918
+ }
6919
+ return;
6920
+ }
6605
6921
  if (result.ok) {
6606
- p3.note(result.text.slice(0, 6e3), "Answer");
6922
+ p3.note(result.text, "Answer");
6607
6923
  p3.outro("");
6608
6924
  } else {
6609
6925
  p3.log.error(result.text);