@carrierllc/mcp 0.9.2 → 0.10.0
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/README.md +63 -5
- package/dist/{chunk-4XHSOF62.js → chunk-DUAENMJE.js} +325 -4
- package/dist/chunk-DUAENMJE.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1575 -1401
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/plugin/carrier/README.md +1 -1
- package/dist/chunk-4XHSOF62.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Natural language control of your MVNO/eSIM fleet. 43 OCS tools + 8 intelligence
|
|
|
12
12
|
### Interactive CLI (non-developers)
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
npx -y @carrierllc/mcp
|
|
15
|
+
npx -y -p @carrierllc/mcp carrier-mcp
|
|
16
16
|
# opens the Carrier TUI: status, sign-up/sign-in, install MCP, fleet NL examples
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -51,7 +51,7 @@ flags. Two rules apply everywhere:
|
|
|
51
51
|
### Stdio server (token env)
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
ESIMVAULT_API_TOKEN=your_key npx -y @carrierllc/mcp
|
|
54
|
+
ESIMVAULT_API_TOKEN=your_key npx -y -p @carrierllc/mcp carrier-mcp
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Get a key at [mcp.carrier.llc](https://mcp.carrier.llc) or an org API key from the console.
|
|
@@ -67,7 +67,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
67
67
|
"mcpServers": {
|
|
68
68
|
"carrier": {
|
|
69
69
|
"command": "npx",
|
|
70
|
-
"args": ["-y", "@carrierllc/mcp"],
|
|
70
|
+
"args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
|
|
71
71
|
"env": {
|
|
72
72
|
"ESIMVAULT_API_TOKEN": "YOUR_KEY"
|
|
73
73
|
}
|
|
@@ -85,7 +85,7 @@ Add to `~/.cursor/mcp.json`:
|
|
|
85
85
|
"mcpServers": {
|
|
86
86
|
"carrier": {
|
|
87
87
|
"command": "npx",
|
|
88
|
-
"args": ["-y", "@carrierllc/mcp"],
|
|
88
|
+
"args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
|
|
89
89
|
"env": {
|
|
90
90
|
"ESIMVAULT_API_TOKEN": "YOUR_KEY"
|
|
91
91
|
}
|
|
@@ -103,7 +103,7 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
|
103
103
|
"mcpServers": {
|
|
104
104
|
"carrier": {
|
|
105
105
|
"command": "npx",
|
|
106
|
-
"args": ["-y", "@carrierllc/mcp"],
|
|
106
|
+
"args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
|
|
107
107
|
"env": {
|
|
108
108
|
"ESIMVAULT_API_TOKEN": "YOUR_KEY"
|
|
109
109
|
}
|
|
@@ -136,6 +136,64 @@ For multi-user deployments with OAuth 2.1 + PKCE, use the remote endpoint:
|
|
|
136
136
|
| `ESIMVAULT_API_TOKEN` | Yes | — | Your eSIMVault OCS API token |
|
|
137
137
|
| `CARRIER_OCS_API_TOKEN` | Alternative | — | Alias for `ESIMVAULT_API_TOKEN` |
|
|
138
138
|
| `CARRIER_OCS_BASE_URL` | No | `https://ocs.esimvault.cloud` | OCS base URL (no trailing slash) |
|
|
139
|
+
| `BROWSER_USE_API_KEY` | No | — | Browser Use Cloud key. Enables the browsing agent (`ui_agent_ask`). Runs are billed to this key at roughly **$0.20–1.00 each** |
|
|
140
|
+
| `CARRIER_BROWSER_USE_MAX_RUNS` | No | `10` | Browser-agent runs allowed per CLI process. `0` disables the agent. See the caveats below |
|
|
141
|
+
| `CARRIER_ALLOW_PORTAL_PASSWORD` | No | unset | Set to `1` to allow the ten OCS-dashboard tools to use a portal login from this machine. See below |
|
|
142
|
+
| `CARRIER_OCS_PORTAL_USERNAME` | No | — | OCS portal username. Only used when the flag above is set |
|
|
143
|
+
| `CARRIER_OCS_PORTAL_PASSWORD` | No | — | OCS portal password. Only used when the flag above is set |
|
|
144
|
+
| `CARRIER_OCS_DASHBOARD_URL` | No | `https://ocs.esimvault.cloud` | Dashboard the portal agent logs into |
|
|
145
|
+
|
|
146
|
+
### The browser agent costs real money
|
|
147
|
+
|
|
148
|
+
`ui_agent_ask` and the OCS-dashboard tools dispatch a hosted browser agent.
|
|
149
|
+
Each run costs roughly **$0.20–1.00**, billed to your own `BROWSER_USE_API_KEY`.
|
|
150
|
+
|
|
151
|
+
`CARRIER_BROWSER_USE_MAX_RUNS` caps how many runs one CLI process may start.
|
|
152
|
+
Read what it does not do:
|
|
153
|
+
|
|
154
|
+
- It caps **runs, not dollars**. The vendor prices a run after it finishes.
|
|
155
|
+
- It does **not survive a restart**. Each new CLI process starts a fresh count,
|
|
156
|
+
so a caller that respawns the CLI between runs is not bounded by it.
|
|
157
|
+
|
|
158
|
+
The only hard financial ceiling is a spend limit set in your Browser Use
|
|
159
|
+
account. Set one.
|
|
160
|
+
|
|
161
|
+
### Opting in to the OCS portal password
|
|
162
|
+
|
|
163
|
+
Ten `ui_*` tools — steering lists, sub-account creation, destination lists,
|
|
164
|
+
package-template deletion, location-zone edits — have no OCS REST equivalent.
|
|
165
|
+
They work by driving the OCS web dashboard, which means logging into it. By
|
|
166
|
+
default the CLI has no portal login and these tools refuse.
|
|
167
|
+
|
|
168
|
+
You can supply one. Understand what you are supplying first:
|
|
169
|
+
|
|
170
|
+
- **This is a portal password, not a scoped API token.** It grants full
|
|
171
|
+
dashboard access to anyone who holds it. `CARRIER_OCS_API_TOKEN` is a
|
|
172
|
+
different kind of secret and is not a precedent for this.
|
|
173
|
+
- **An environment variable is a poor place for one.** It is readable from shell
|
|
174
|
+
history, from `ps` output by any process on the machine, and from CI logs.
|
|
175
|
+
- **It is sent to Browser Use Cloud**, which types it into the OCS login form.
|
|
176
|
+
|
|
177
|
+
Carrier never logs it, never returns it in tool output, and never writes it to
|
|
178
|
+
an audit row — but the exposure above is yours to accept.
|
|
179
|
+
|
|
180
|
+
Two independent things must both be true, and neither alone does anything:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
export CARRIER_ALLOW_PORTAL_PASSWORD=1
|
|
184
|
+
export CARRIER_OCS_PORTAL_USERNAME='...'
|
|
185
|
+
export CARRIER_OCS_PORTAL_PASSWORD='...'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
With the flag set, the CLI prints the warning to stderr at startup. Without both
|
|
189
|
+
halves, the ten tools return the same "requires the remote Worker" refusal they
|
|
190
|
+
returned before this option existed.
|
|
191
|
+
|
|
192
|
+
Every one of the ten accepts `dry_run: true`, which returns the agent prompt
|
|
193
|
+
with the credentials redacted and dispatches nothing. Use it first.
|
|
194
|
+
|
|
195
|
+
`ui_request_reseller_relay_change` is not one of the ten and is never gated: it
|
|
196
|
+
drafts text and touches no portal.
|
|
139
197
|
|
|
140
198
|
---
|
|
141
199
|
|
|
@@ -1792,7 +1792,7 @@ function storefrontScreen(input) {
|
|
|
1792
1792
|
// package.json
|
|
1793
1793
|
var package_default = {
|
|
1794
1794
|
name: "@carrierllc/mcp",
|
|
1795
|
-
version: "0.
|
|
1795
|
+
version: "0.10.0",
|
|
1796
1796
|
description: "Carrier MCP \u2014 natural-language control of MVNO/eSIM fleets via eSIMVault OCS. Stdio mode for direct integration with Claude Desktop, Cursor, Windsurf, and MCP-compatible clients. Ships the `carrier` CLI (plugin install + white-label eSIM storefront scaffold).",
|
|
1797
1797
|
license: "MIT",
|
|
1798
1798
|
author: "Carrier (Lifecycle Innovations Limited)",
|
|
@@ -1840,7 +1840,7 @@ var package_default = {
|
|
|
1840
1840
|
zod: "^4.4.3"
|
|
1841
1841
|
},
|
|
1842
1842
|
devDependencies: {
|
|
1843
|
-
"@cloudflare/workers-types": "^
|
|
1843
|
+
"@cloudflare/workers-types": "^5.20260819.1",
|
|
1844
1844
|
"@types/node": "^26.1.2",
|
|
1845
1845
|
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
|
1846
1846
|
"@typescript-eslint/parser": "^8.67.0",
|
|
@@ -1876,7 +1876,113 @@ var package_default = {
|
|
|
1876
1876
|
// src/version.ts
|
|
1877
1877
|
var CARRIER_VERSION = package_default.version;
|
|
1878
1878
|
|
|
1879
|
-
// ../../packages/ocs-client/dist/chunk-
|
|
1879
|
+
// ../../packages/ocs-client/dist/chunk-CGMK4FTD.js
|
|
1880
|
+
var DEFAULT_OCS_BUDGET = {
|
|
1881
|
+
attemptMs: 3e4,
|
|
1882
|
+
maxAttempts: 3,
|
|
1883
|
+
totalMs: 75e3
|
|
1884
|
+
};
|
|
1885
|
+
var OCS_METHOD_BUDGETS = {
|
|
1886
|
+
// 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One
|
|
1887
|
+
// attempt: payload size does not vary, so a retry only doubles the wait.
|
|
1888
|
+
getCustomerTariff: { attemptMs: 18e4, maxAttempts: 1, totalMs: 18e4 }
|
|
1889
|
+
};
|
|
1890
|
+
var MIN_ATTEMPT_BUDGET_MS = 1e3;
|
|
1891
|
+
var BACKOFF_BASE_MS = 500;
|
|
1892
|
+
var OcsTimeoutError = class extends Error {
|
|
1893
|
+
constructor(method, timeoutMs, budgetMs = timeoutMs, attempts = 1) {
|
|
1894
|
+
super(
|
|
1895
|
+
`[${method}] OCS request timed out after ${timeoutMs}ms` + (attempts > 1 ? ` on each of ${attempts} attempts` : "") + (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : "") + `. The upstream did not deliver a complete response in time \u2014 usually an oversized payload or a wedged upstream. Narrow the request, or raise OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`
|
|
1896
|
+
);
|
|
1897
|
+
this.method = method;
|
|
1898
|
+
this.timeoutMs = timeoutMs;
|
|
1899
|
+
this.budgetMs = budgetMs;
|
|
1900
|
+
this.attempts = attempts;
|
|
1901
|
+
this.name = "OcsTimeoutError";
|
|
1902
|
+
}
|
|
1903
|
+
method;
|
|
1904
|
+
timeoutMs;
|
|
1905
|
+
budgetMs;
|
|
1906
|
+
attempts;
|
|
1907
|
+
};
|
|
1908
|
+
function envInt(name) {
|
|
1909
|
+
const proc = globalThis["process"];
|
|
1910
|
+
const parsed = Number(proc?.env?.[name]);
|
|
1911
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
1912
|
+
}
|
|
1913
|
+
function ocsBudgetFor(method) {
|
|
1914
|
+
const base = OCS_METHOD_BUDGETS[method] ?? DEFAULT_OCS_BUDGET;
|
|
1915
|
+
const attemptMs = envInt("OCS_TIMEOUT_MS") ?? base.attemptMs;
|
|
1916
|
+
const totalMs = Math.max(envInt("OCS_TOTAL_TIMEOUT_MS") ?? base.totalMs, attemptMs);
|
|
1917
|
+
return { attemptMs, maxAttempts: base.maxAttempts, totalMs };
|
|
1918
|
+
}
|
|
1919
|
+
function sleep2(ms, signal) {
|
|
1920
|
+
return new Promise((resolve) => {
|
|
1921
|
+
const timer = setTimeout(finish, ms);
|
|
1922
|
+
function finish() {
|
|
1923
|
+
clearTimeout(timer);
|
|
1924
|
+
signal.removeEventListener("abort", finish);
|
|
1925
|
+
resolve();
|
|
1926
|
+
}
|
|
1927
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
async function withDeadline(work, deadline, ms) {
|
|
1931
|
+
let timer;
|
|
1932
|
+
try {
|
|
1933
|
+
return await Promise.race([
|
|
1934
|
+
work,
|
|
1935
|
+
new Promise((_resolve, reject) => {
|
|
1936
|
+
timer = setTimeout(() => reject(deadline), ms);
|
|
1937
|
+
})
|
|
1938
|
+
]);
|
|
1939
|
+
} finally {
|
|
1940
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
async function runWithBudget(method, attempt, budget = ocsBudgetFor(method)) {
|
|
1944
|
+
const deadlineAt = Date.now() + budget.totalMs;
|
|
1945
|
+
const budgetController = new AbortController();
|
|
1946
|
+
const budgetTimer = setTimeout(() => budgetController.abort(), budget.totalMs);
|
|
1947
|
+
let attempts = 0;
|
|
1948
|
+
let lastTimeout;
|
|
1949
|
+
try {
|
|
1950
|
+
for (let i = 0; i < budget.maxAttempts; i++) {
|
|
1951
|
+
const remaining = deadlineAt - Date.now();
|
|
1952
|
+
if (remaining < MIN_ATTEMPT_BUDGET_MS) break;
|
|
1953
|
+
const attemptMs = Math.min(budget.attemptMs, remaining);
|
|
1954
|
+
attempts++;
|
|
1955
|
+
const attemptController = new AbortController();
|
|
1956
|
+
const onBudgetAbort = () => attemptController.abort();
|
|
1957
|
+
budgetController.signal.addEventListener("abort", onBudgetAbort, { once: true });
|
|
1958
|
+
const attemptTimer = setTimeout(() => attemptController.abort(), attemptMs);
|
|
1959
|
+
const breach = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
|
|
1960
|
+
try {
|
|
1961
|
+
return await withDeadline(
|
|
1962
|
+
attempt(attemptController.signal, attemptMs),
|
|
1963
|
+
breach,
|
|
1964
|
+
attemptMs
|
|
1965
|
+
);
|
|
1966
|
+
} catch (err) {
|
|
1967
|
+
if (!(err instanceof OcsTimeoutError)) throw err;
|
|
1968
|
+
lastTimeout = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
|
|
1969
|
+
} finally {
|
|
1970
|
+
clearTimeout(attemptTimer);
|
|
1971
|
+
budgetController.signal.removeEventListener("abort", onBudgetAbort);
|
|
1972
|
+
attemptController.abort();
|
|
1973
|
+
}
|
|
1974
|
+
if (i === budget.maxAttempts - 1) break;
|
|
1975
|
+
const backoff = Math.random() * BACKOFF_BASE_MS * 2 ** i;
|
|
1976
|
+
if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;
|
|
1977
|
+
await sleep2(backoff, budgetController.signal);
|
|
1978
|
+
}
|
|
1979
|
+
} finally {
|
|
1980
|
+
clearTimeout(budgetTimer);
|
|
1981
|
+
}
|
|
1982
|
+
throw lastTimeout ?? new OcsTimeoutError(method, budget.attemptMs, budget.totalMs, Math.max(attempts, 1));
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
// ../../packages/ocs-client/dist/chunk-OPUTHPZF.js
|
|
1880
1986
|
var ENDPOINT_LIMITS_PER_MIN = {
|
|
1881
1987
|
// Global sentinel (keyed as "__global__")
|
|
1882
1988
|
__global__: 600,
|
|
@@ -2036,6 +2142,24 @@ function getRateLimitWindowCounts(resellerKey, endpoint) {
|
|
|
2036
2142
|
}
|
|
2037
2143
|
return { calls_in_window, batch_calls_in_window };
|
|
2038
2144
|
}
|
|
2145
|
+
var OCS_PARAM_RENAMES = {
|
|
2146
|
+
getResellerInfo: { resellerId: "id" }
|
|
2147
|
+
};
|
|
2148
|
+
function applyParamRenames(method, params) {
|
|
2149
|
+
const renames = OCS_PARAM_RENAMES[method];
|
|
2150
|
+
if (!renames || !params || typeof params !== "object" || Array.isArray(params)) {
|
|
2151
|
+
return params;
|
|
2152
|
+
}
|
|
2153
|
+
const record = params;
|
|
2154
|
+
let out;
|
|
2155
|
+
for (const [from, to] of Object.entries(renames)) {
|
|
2156
|
+
if (!Object.prototype.hasOwnProperty.call(record, from)) continue;
|
|
2157
|
+
out ??= { ...record };
|
|
2158
|
+
if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];
|
|
2159
|
+
delete out[from];
|
|
2160
|
+
}
|
|
2161
|
+
return out ?? params;
|
|
2162
|
+
}
|
|
2039
2163
|
|
|
2040
2164
|
// ../../packages/ocs-client/dist/chunk-JWSVJVAF.js
|
|
2041
2165
|
var OCS_MAX_USAGE_WINDOW_DAYS = 7;
|
|
@@ -2388,6 +2512,195 @@ var ROUTER_RULES = `Rules:
|
|
|
2388
2512
|
7. A tool answers the question only if its description says it does. Do not infer capability from the tool's NAME: several names share words with unrelated questions, and picking on the name alone has produced confidently wrong answers.
|
|
2389
2513
|
8. Read the "Do NOT use this to \u2026" steers in a description as hard exclusions. They exist because that tool is the common wrong answer for a neighbouring question, and they name the tool to use instead.
|
|
2390
2514
|
9. Prefer carrier_clarify over a tool that would return real data about a different question. Answering "which subscribers erode margin" when the user asked "what do my users' megabytes cost in total" is worse than admitting the gap, because the output looks like an answer.`;
|
|
2515
|
+
var OCS_LIST_KEYS = {
|
|
2516
|
+
subscribers: ["subscriberList"],
|
|
2517
|
+
templates: ["template", "prepaidPackageTemplate"],
|
|
2518
|
+
zones: ["listDetailedLocationZone", "locationZone", "zone", "zones"],
|
|
2519
|
+
resellers: ["reseller"],
|
|
2520
|
+
packages: ["prepaidPackage", "package"]
|
|
2521
|
+
};
|
|
2522
|
+
function ocsArray(data, keys) {
|
|
2523
|
+
if (Array.isArray(data)) return data;
|
|
2524
|
+
if (data === null || typeof data !== "object") return [];
|
|
2525
|
+
const candidates = typeof keys === "string" ? OCS_LIST_KEYS[keys] : keys;
|
|
2526
|
+
const obj = data;
|
|
2527
|
+
for (const key of candidates) {
|
|
2528
|
+
const v = obj[key];
|
|
2529
|
+
if (Array.isArray(v)) return v;
|
|
2530
|
+
}
|
|
2531
|
+
return [];
|
|
2532
|
+
}
|
|
2533
|
+
var FACET_TALLY_MAX = 25;
|
|
2534
|
+
var OCS_LIST_FACETS = {
|
|
2535
|
+
templates: [
|
|
2536
|
+
{ label: "recurring", paths: ["recurring"] },
|
|
2537
|
+
{
|
|
2538
|
+
label: "locationZone",
|
|
2539
|
+
paths: [
|
|
2540
|
+
"rdbLocationZones.locationzonename",
|
|
2541
|
+
"locationzonename",
|
|
2542
|
+
"locationzoneid"
|
|
2543
|
+
]
|
|
2544
|
+
}
|
|
2545
|
+
],
|
|
2546
|
+
subscribers: [{ label: "status", paths: ["status"] }],
|
|
2547
|
+
packages: [
|
|
2548
|
+
{ label: "status", paths: ["status"] },
|
|
2549
|
+
{ label: "recurring", paths: ["recurring"] }
|
|
2550
|
+
],
|
|
2551
|
+
zones: [],
|
|
2552
|
+
resellers: []
|
|
2553
|
+
};
|
|
2554
|
+
var OCS_LIST_NESTED = {
|
|
2555
|
+
resellers: { label: "accounts", path: "account" }
|
|
2556
|
+
};
|
|
2557
|
+
function pagingHints(data) {
|
|
2558
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
2559
|
+
return { hasMore: false, nbFound: void 0 };
|
|
2560
|
+
}
|
|
2561
|
+
const obj = data;
|
|
2562
|
+
return {
|
|
2563
|
+
hasMore: obj.hasMore === true,
|
|
2564
|
+
nbFound: typeof obj.nbFound === "number" ? obj.nbFound : void 0
|
|
2565
|
+
};
|
|
2566
|
+
}
|
|
2567
|
+
function readPath(row, path) {
|
|
2568
|
+
let cur = row;
|
|
2569
|
+
for (const seg of path.split(".")) {
|
|
2570
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
2571
|
+
cur = cur[seg];
|
|
2572
|
+
}
|
|
2573
|
+
return cur;
|
|
2574
|
+
}
|
|
2575
|
+
function facetKey(v) {
|
|
2576
|
+
if (v === null || v === void 0) return null;
|
|
2577
|
+
if (typeof v === "boolean" || typeof v === "number") return String(v);
|
|
2578
|
+
if (typeof v === "string") return v;
|
|
2579
|
+
return null;
|
|
2580
|
+
}
|
|
2581
|
+
function ocsListSummary(data, kind, facets = OCS_LIST_FACETS[kind]) {
|
|
2582
|
+
const rows = ocsArray(data, kind);
|
|
2583
|
+
const breakdown = {};
|
|
2584
|
+
for (const facet of facets) {
|
|
2585
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2586
|
+
let missing = 0;
|
|
2587
|
+
for (const row of rows) {
|
|
2588
|
+
if (row === null || typeof row !== "object") {
|
|
2589
|
+
missing += 1;
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
let key = null;
|
|
2593
|
+
for (const path of facet.paths) {
|
|
2594
|
+
key = facetKey(readPath(row, path));
|
|
2595
|
+
if (key !== null) break;
|
|
2596
|
+
}
|
|
2597
|
+
if (key === null) {
|
|
2598
|
+
missing += 1;
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
2602
|
+
}
|
|
2603
|
+
if (counts.size === 0 && missing === 0) continue;
|
|
2604
|
+
const entry = { distinct: counts.size };
|
|
2605
|
+
if (counts.size > 0 && counts.size <= FACET_TALLY_MAX) {
|
|
2606
|
+
entry.counts = Object.fromEntries(
|
|
2607
|
+
[...counts].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
2608
|
+
);
|
|
2609
|
+
}
|
|
2610
|
+
if (missing > 0) entry.missing = missing;
|
|
2611
|
+
breakdown[facet.label] = entry;
|
|
2612
|
+
}
|
|
2613
|
+
const { hasMore, nbFound } = pagingHints(data);
|
|
2614
|
+
const more = hasMore || nbFound !== void 0 && nbFound > rows.length;
|
|
2615
|
+
const summary = {
|
|
2616
|
+
total: rows.length,
|
|
2617
|
+
note: `Counted server-side: ${rows.length} row${rows.length === 1 ? "" : "s"} in this result. Use these figures directly rather than recounting the rows below. ` + (more ? "This is ONE PAGE \u2014 OCS holds more rows than are returned here" + (nbFound !== void 0 ? ` (${nbFound} match upstream)` : "") + ", so do not report `total` as a fleet-wide figure; narrow the filters or page for the rest." : "Nothing was truncated: these are all the rows OCS returned.")
|
|
2618
|
+
};
|
|
2619
|
+
if (more) {
|
|
2620
|
+
summary.moreAvailable = true;
|
|
2621
|
+
if (nbFound !== void 0) summary.upstreamTotal = nbFound;
|
|
2622
|
+
}
|
|
2623
|
+
const nest = OCS_LIST_NESTED[kind];
|
|
2624
|
+
if (nest) {
|
|
2625
|
+
const n = rows.reduce((acc, row) => {
|
|
2626
|
+
const child = row && typeof row === "object" ? readPath(row, nest.path) : void 0;
|
|
2627
|
+
return acc + (Array.isArray(child) ? child.length : 0);
|
|
2628
|
+
}, 0);
|
|
2629
|
+
summary.nested = { [nest.label]: n };
|
|
2630
|
+
summary.note += ` This envelope NESTS: \`total\` counts the outer rows, while there are ${n} ${nest.label} inside them under \`${nest.path}\`. A question about ${nest.label} is answered by that figure, not by \`total\`.`;
|
|
2631
|
+
}
|
|
2632
|
+
if (Object.keys(breakdown).length > 0) {
|
|
2633
|
+
summary.breakdown = breakdown;
|
|
2634
|
+
summary.note += " `breakdown` gives, per field, how many distinct values occur and how many rows carry each (`missing` counts rows where the field is absent).";
|
|
2635
|
+
}
|
|
2636
|
+
return summary;
|
|
2637
|
+
}
|
|
2638
|
+
function withOcsListSummary(data, kind, facets) {
|
|
2639
|
+
const summary = ocsListSummary(data, kind, facets);
|
|
2640
|
+
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
2641
|
+
return { summary, ...data };
|
|
2642
|
+
}
|
|
2643
|
+
return { summary, [OCS_LIST_KEYS[kind][0]]: data };
|
|
2644
|
+
}
|
|
2645
|
+
var OCS_DATA_QUANTITY_TYPE = "33";
|
|
2646
|
+
function readBytes(entry) {
|
|
2647
|
+
const total = entry.total;
|
|
2648
|
+
if (total !== null && typeof total === "object") {
|
|
2649
|
+
const perType = total.quantityPerType;
|
|
2650
|
+
if (perType !== null && typeof perType === "object") {
|
|
2651
|
+
const v = perType[OCS_DATA_QUANTITY_TYPE];
|
|
2652
|
+
if (v !== void 0) return Number(v) || 0;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
return Number(entry.dataBytes ?? entry.dataVolume ?? entry.totalData ?? 0) || 0;
|
|
2656
|
+
}
|
|
2657
|
+
function extractDailyUsage(data) {
|
|
2658
|
+
const out = [];
|
|
2659
|
+
if (data === null || typeof data !== "object") return out;
|
|
2660
|
+
if (Array.isArray(data)) {
|
|
2661
|
+
for (const entry of data) {
|
|
2662
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
2663
|
+
const e = entry;
|
|
2664
|
+
out.push({ date: String(e.date ?? e.day ?? "?"), bytes: readBytes(e) });
|
|
2665
|
+
}
|
|
2666
|
+
return out;
|
|
2667
|
+
}
|
|
2668
|
+
const usages = data.usages;
|
|
2669
|
+
if (!Array.isArray(usages)) return out;
|
|
2670
|
+
for (const usage of usages) {
|
|
2671
|
+
if (usage === null || typeof usage !== "object") continue;
|
|
2672
|
+
const periods = usage.subsPeriodUsages;
|
|
2673
|
+
if (!Array.isArray(periods)) continue;
|
|
2674
|
+
for (const period of periods) {
|
|
2675
|
+
if (period === null || typeof period !== "object") continue;
|
|
2676
|
+
const p = period;
|
|
2677
|
+
out.push({ date: String(p.day ?? p.date ?? "?"), bytes: readBytes(p) });
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
return out;
|
|
2681
|
+
}
|
|
2682
|
+
function daysUntil(dateStr) {
|
|
2683
|
+
const now = /* @__PURE__ */ new Date();
|
|
2684
|
+
const target = new Date(dateStr);
|
|
2685
|
+
return Math.ceil((target.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
|
|
2686
|
+
}
|
|
2687
|
+
function formatBytes(bytes) {
|
|
2688
|
+
if (bytes === 0) return "0 B";
|
|
2689
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
2690
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
2691
|
+
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`;
|
|
2692
|
+
}
|
|
2693
|
+
function isActiveSubscriber(row) {
|
|
2694
|
+
return (extractSubscriberStatus(row) ?? "").toUpperCase() === "ACTIVE";
|
|
2695
|
+
}
|
|
2696
|
+
function filterActiveSubscribers(rows) {
|
|
2697
|
+
return rows.filter((row) => isActiveSubscriber(row));
|
|
2698
|
+
}
|
|
2699
|
+
function subscriberRows(raw) {
|
|
2700
|
+
if (Array.isArray(raw)) return raw;
|
|
2701
|
+
const list = raw?.subscriberList;
|
|
2702
|
+
return Array.isArray(list) ? list : [];
|
|
2703
|
+
}
|
|
2391
2704
|
|
|
2392
2705
|
// src/lib/storefront-logo.ts
|
|
2393
2706
|
var OPENAI_KEY_NAMES = [
|
|
@@ -2510,9 +2823,11 @@ async function generateStorefrontLogo(input) {
|
|
|
2510
2823
|
}
|
|
2511
2824
|
|
|
2512
2825
|
export {
|
|
2826
|
+
runWithBudget,
|
|
2513
2827
|
acquireEndpointSlot,
|
|
2514
2828
|
getLimitForEndpoint,
|
|
2515
2829
|
getRateLimitWindowCounts,
|
|
2830
|
+
applyParamRenames,
|
|
2516
2831
|
OCS_MAX_USAGE_WINDOW_DAYS,
|
|
2517
2832
|
clampUsagePeriod,
|
|
2518
2833
|
lastNDaysPeriod,
|
|
@@ -2531,6 +2846,12 @@ export {
|
|
|
2531
2846
|
recordToolDescription,
|
|
2532
2847
|
buildRouterCatalog,
|
|
2533
2848
|
ROUTER_RULES,
|
|
2849
|
+
withOcsListSummary,
|
|
2850
|
+
extractDailyUsage,
|
|
2851
|
+
daysUntil,
|
|
2852
|
+
formatBytes,
|
|
2853
|
+
filterActiveSubscribers,
|
|
2854
|
+
subscriberRows,
|
|
2534
2855
|
generateStorefrontLogo,
|
|
2535
2856
|
run,
|
|
2536
2857
|
which,
|
|
@@ -2569,4 +2890,4 @@ export {
|
|
|
2569
2890
|
storefrontScreen,
|
|
2570
2891
|
CARRIER_VERSION
|
|
2571
2892
|
};
|
|
2572
|
-
//# sourceMappingURL=chunk-
|
|
2893
|
+
//# sourceMappingURL=chunk-DUAENMJE.js.map
|