@carrierllc/mcp 0.2.19 → 0.3.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 +22 -1
- package/dist/chunk-ZII4CD4U.js +461 -0
- package/dist/chunk-ZII4CD4U.js.map +1 -0
- package/dist/cli.js +1294 -105
- package/dist/cli.js.map +1 -1
- package/dist/index.js +144 -315
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/carrier/commands/storefront.md +24 -12
- package/plugin/carrier/skills/carrier-operations/SKILL.md +4 -1
- package/templates/storefront/{next.config.ts → next.config.mjs} +2 -6
- package/templates/storefront/public/brand/logo.svg +13 -0
- package/templates/storefront/src/app/activate/[orderId]/ActivateClient.tsx +12 -12
- package/templates/storefront/src/app/checkout/[templateId]/CheckoutClient.tsx +5 -5
- package/templates/storefront/src/app/checkout/success/CheckoutSuccessClient.tsx +29 -29
- package/templates/storefront/src/app/checkout/success/page.tsx +1 -1
- package/templates/storefront/src/app/contact/page.tsx +33 -25
- package/templates/storefront/src/app/dashboard/page.tsx +7 -4
- package/templates/storefront/src/app/globals.css +72 -82
- package/templates/storefront/src/app/help/page.tsx +22 -17
- package/templates/storefront/src/app/layout.tsx +15 -4
- package/templates/storefront/src/app/page.tsx +16 -7
- package/templates/storefront/src/app/shop/ShopClient.tsx +7 -3
- package/templates/storefront/src/app/sign-in/[[...sign-in]]/page.tsx +1 -1
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/StorefrontSignUpClient.tsx +16 -16
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/page.tsx +1 -1
- package/templates/storefront/src/brand.config.ts +3 -3
- package/templates/storefront/src/components/faq/FaqSection.tsx +23 -26
- package/templates/storefront/src/components/footer/StorefrontFooter.tsx +37 -22
- package/templates/storefront/src/components/landing/FinalCTA.tsx +28 -0
- package/templates/storefront/src/components/landing/HeroSection.tsx +57 -24
- package/templates/storefront/src/components/landing/HowItWorks.tsx +44 -0
- package/templates/storefront/src/components/landing/PlanCard.tsx +58 -28
- package/templates/storefront/src/components/nav/StorefrontNav.tsx +59 -24
- package/templates/storefront/src/components/theme/BrandStyles.tsx +8 -0
- package/templates/storefront/src/components/theme/ThemeToggle.tsx +27 -0
- package/templates/storefront/src/components/theme/clerk-appearance.ts +33 -35
- package/templates/storefront/src/components/theme/theme-provider.tsx +25 -32
- package/templates/storefront/src/components/theme/theme-script.tsx +4 -7
- package/templates/storefront/src/lib/complete-email-sign-up.ts +3 -3
- package/templates/storefront/src/vendor/ui/brand.ts +6 -6
package/dist/index.js
CHANGED
|
@@ -1,204 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CARRIER_VERSION,
|
|
4
|
+
acquireEndpointSlot,
|
|
5
|
+
extractEsimStatusCounts,
|
|
6
|
+
generateStorefrontLogo,
|
|
7
|
+
getLimitForEndpoint,
|
|
8
|
+
getRateLimitWindowCounts,
|
|
9
|
+
locationParams,
|
|
10
|
+
networkEventsOverPeriodParams,
|
|
11
|
+
subscriberIdParams,
|
|
12
|
+
usageOverPeriodParams
|
|
13
|
+
} from "./chunk-ZII4CD4U.js";
|
|
2
14
|
|
|
3
15
|
// src/index.ts
|
|
4
16
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
17
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
18
|
|
|
7
|
-
// ../../packages/ocs-client/dist/chunk-PMFMORPK.js
|
|
8
|
-
var ENDPOINT_LIMITS_PER_MIN = {
|
|
9
|
-
// Global sentinel (keyed as "__global__")
|
|
10
|
-
__global__: 600,
|
|
11
|
-
// 30/min — tightest endpoint
|
|
12
|
-
subscriberNetworkEventsOverPeriod: 30,
|
|
13
|
-
// 100/min
|
|
14
|
-
subscriberUsageOverPeriod: 100,
|
|
15
|
-
// 150/min
|
|
16
|
-
listLocationZoneElement: 150,
|
|
17
|
-
sendMtSms: 150,
|
|
18
|
-
// 300/min (explicit; also the fallback default)
|
|
19
|
-
modifySubscriberMobilePlan: 300,
|
|
20
|
-
affectRecurringPackageToSubscriber: 300,
|
|
21
|
-
affectSubscriberFakePhoneNumber: 300,
|
|
22
|
-
affectSubscriberRealPhoneNumber: 300,
|
|
23
|
-
esimStatusPerAccount: 300,
|
|
24
|
-
getSubscriberActivePeriod: 300,
|
|
25
|
-
hlrGetBitrate: 300,
|
|
26
|
-
hlrSetBitrate: 300,
|
|
27
|
-
listSubscriber: 300,
|
|
28
|
-
listSubscriberPrepaidPackages: 300,
|
|
29
|
-
moveSubscriberRangeToAccount: 300,
|
|
30
|
-
pushSteeringToSubs: 300,
|
|
31
|
-
setSubscriberTrafficRestrictions: 300,
|
|
32
|
-
getSingleSubscriber: 300,
|
|
33
|
-
getSubscriberLocationByCellId: 300
|
|
34
|
-
};
|
|
35
|
-
var DEFAULT_LIMIT_PER_MIN = 300;
|
|
36
|
-
var WINDOW_MS2 = 6e4;
|
|
37
|
-
var METRIC_EMIT_MIN_INTERVAL_MS = 1e4;
|
|
38
|
-
var BATCH_FRACTION = 0.2;
|
|
39
|
-
function resellerKeyHash(resellerKey) {
|
|
40
|
-
if (!resellerKey) return "00000000";
|
|
41
|
-
let h = 5381;
|
|
42
|
-
for (let i = 0; i < resellerKey.length; i++) {
|
|
43
|
-
h = ((h << 5) + h ^ resellerKey.charCodeAt(i)) >>> 0;
|
|
44
|
-
}
|
|
45
|
-
return h.toString(16).padStart(8, "0");
|
|
46
|
-
}
|
|
47
|
-
function emitMetric(endpoint, resellerKey, callsInWindow, limitPerMin) {
|
|
48
|
-
const pct = Math.round(callsInWindow / limitPerMin * 100);
|
|
49
|
-
const reseller_hash = resellerKeyHash(resellerKey);
|
|
50
|
-
console.log(
|
|
51
|
-
JSON.stringify({
|
|
52
|
-
metric: "carrier_ocs_calls_per_min",
|
|
53
|
-
endpoint,
|
|
54
|
-
reseller_hash,
|
|
55
|
-
calls_in_window: callsInWindow,
|
|
56
|
-
limit_per_min: limitPerMin,
|
|
57
|
-
utilisation_pct: pct,
|
|
58
|
-
alert: pct >= 80,
|
|
59
|
-
ts: Date.now()
|
|
60
|
-
})
|
|
61
|
-
);
|
|
62
|
-
if (pct >= 80) {
|
|
63
|
-
console.warn(
|
|
64
|
-
`[rate-governor] ALERT: ${endpoint} at ${pct}% capacity (${callsInWindow}/${limitPerMin} per min) reseller=${reseller_hash}`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
var _endpointWindows = /* @__PURE__ */ new Map();
|
|
69
|
-
function _getEndpointState(key) {
|
|
70
|
-
let s = _endpointWindows.get(key);
|
|
71
|
-
if (!s) {
|
|
72
|
-
s = {
|
|
73
|
-
log: [],
|
|
74
|
-
batchLog: [],
|
|
75
|
-
gate: Promise.resolve(),
|
|
76
|
-
lastMetricEmitAt: 0,
|
|
77
|
-
metricBelowHighUtil: true
|
|
78
|
-
};
|
|
79
|
-
_endpointWindows.set(key, s);
|
|
80
|
-
}
|
|
81
|
-
return s;
|
|
82
|
-
}
|
|
83
|
-
function _prune(log, windowStart) {
|
|
84
|
-
let i = 0;
|
|
85
|
-
while (i < log.length && log[i] <= windowStart) i++;
|
|
86
|
-
return i > 0 ? log.slice(i) : log;
|
|
87
|
-
}
|
|
88
|
-
async function acquireEndpointSlot(resellerId, endpoint, priority = "interactive") {
|
|
89
|
-
const proc = globalThis["process"];
|
|
90
|
-
if (proc?.env?.["RATE_FLOOR_DISABLED"] === "true" || globalThis["RATE_FLOOR_DISABLED"] === "true") {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
await _acquireOneSlot(resellerId, "__global__", priority);
|
|
94
|
-
await _acquireOneSlot(resellerId, endpoint, priority);
|
|
95
|
-
}
|
|
96
|
-
async function _acquireOneSlot(resellerId, endpoint, priority) {
|
|
97
|
-
const bucketKey = `${resellerId}:${endpoint}`;
|
|
98
|
-
const limitPerMin = ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;
|
|
99
|
-
const batchCap = Math.floor(limitPerMin * BATCH_FRACTION);
|
|
100
|
-
const state = _getEndpointState(bucketKey);
|
|
101
|
-
const ticket = state.gate.then(async () => {
|
|
102
|
-
while (true) {
|
|
103
|
-
const now = Date.now();
|
|
104
|
-
const windowStart = now - WINDOW_MS2;
|
|
105
|
-
state.log = _prune(state.log, windowStart);
|
|
106
|
-
state.batchLog = _prune(state.batchLog, windowStart);
|
|
107
|
-
const totalInWindow = state.log.length;
|
|
108
|
-
const batchInWindow = state.batchLog.length;
|
|
109
|
-
if (totalInWindow > 0 && endpoint !== "__global__") {
|
|
110
|
-
const utilPct = Math.round(totalInWindow / limitPerMin * 100);
|
|
111
|
-
const intervalOk = now - state.lastMetricEmitAt >= METRIC_EMIT_MIN_INTERVAL_MS;
|
|
112
|
-
const enteredHighUtil = utilPct >= 80 && state.metricBelowHighUtil;
|
|
113
|
-
if (intervalOk || enteredHighUtil) {
|
|
114
|
-
emitMetric(endpoint, resellerId, totalInWindow, limitPerMin);
|
|
115
|
-
state.lastMetricEmitAt = now;
|
|
116
|
-
if (utilPct >= 80) {
|
|
117
|
-
state.metricBelowHighUtil = false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (utilPct < 80) {
|
|
121
|
-
state.metricBelowHighUtil = true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const globalFull = totalInWindow >= limitPerMin;
|
|
125
|
-
const batchFull = priority === "batch" && batchInWindow >= batchCap;
|
|
126
|
-
if (!globalFull && !batchFull) {
|
|
127
|
-
state.log.push(now);
|
|
128
|
-
if (priority === "batch") {
|
|
129
|
-
state.batchLog.push(now);
|
|
130
|
-
}
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
let waitMs;
|
|
134
|
-
if (globalFull) {
|
|
135
|
-
const oldest = state.log[0];
|
|
136
|
-
waitMs = WINDOW_MS2 - (now - oldest) + 1;
|
|
137
|
-
} else {
|
|
138
|
-
const oldestBatch = state.batchLog[0];
|
|
139
|
-
waitMs = WINDOW_MS2 - (now - oldestBatch) + 1;
|
|
140
|
-
}
|
|
141
|
-
await new Promise((r) => setTimeout(r, waitMs));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
state.gate = ticket;
|
|
145
|
-
return ticket;
|
|
146
|
-
}
|
|
147
|
-
function getLimitForEndpoint(endpoint) {
|
|
148
|
-
return ENDPOINT_LIMITS_PER_MIN[endpoint] ?? DEFAULT_LIMIT_PER_MIN;
|
|
149
|
-
}
|
|
150
|
-
function getRateLimitWindowCounts(resellerKey, endpoint) {
|
|
151
|
-
const bucketKey = `${resellerKey}:${endpoint}`;
|
|
152
|
-
const state = _endpointWindows.get(bucketKey);
|
|
153
|
-
if (!state) {
|
|
154
|
-
return { calls_in_window: 0, batch_calls_in_window: 0 };
|
|
155
|
-
}
|
|
156
|
-
const windowStart = Date.now() - WINDOW_MS2;
|
|
157
|
-
let calls_in_window = 0;
|
|
158
|
-
for (const ts of state.log) {
|
|
159
|
-
if (ts > windowStart) calls_in_window++;
|
|
160
|
-
}
|
|
161
|
-
let batch_calls_in_window = 0;
|
|
162
|
-
for (const ts of state.batchLog) {
|
|
163
|
-
if (ts > windowStart) batch_calls_in_window++;
|
|
164
|
-
}
|
|
165
|
-
return { calls_in_window, batch_calls_in_window };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// ../../packages/ocs-client/dist/chunk-W6L6SMW5.js
|
|
169
|
-
var OCS_MAX_USAGE_WINDOW_DAYS = 7;
|
|
170
|
-
function clampUsagePeriod(start, end, maxDays = OCS_MAX_USAGE_WINDOW_DAYS) {
|
|
171
|
-
const endMs = Date.parse(`${end}T00:00:00Z`);
|
|
172
|
-
const startMs = Date.parse(`${start}T00:00:00Z`);
|
|
173
|
-
if (Number.isNaN(endMs) || Number.isNaN(startMs) || startMs > endMs) {
|
|
174
|
-
return { start, end };
|
|
175
|
-
}
|
|
176
|
-
const minStartMs = endMs - (maxDays - 1) * 864e5;
|
|
177
|
-
if (startMs < minStartMs) {
|
|
178
|
-
return { start: new Date(minStartMs).toISOString().slice(0, 10), end };
|
|
179
|
-
}
|
|
180
|
-
return { start, end };
|
|
181
|
-
}
|
|
182
|
-
function locationParams(iccid) {
|
|
183
|
-
return { iccid };
|
|
184
|
-
}
|
|
185
|
-
function subscriberIdParams(iccid) {
|
|
186
|
-
return { iccid };
|
|
187
|
-
}
|
|
188
|
-
function networkEventsOverPeriodParams(iccid, start, end) {
|
|
189
|
-
return usageOverPeriodParams(iccid, start, end);
|
|
190
|
-
}
|
|
191
|
-
function usageOverPeriodParams(iccid, start, end) {
|
|
192
|
-
const period = clampUsagePeriod(start, end);
|
|
193
|
-
return { subscriber: { iccid }, period };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
19
|
// src/tools.ts
|
|
197
20
|
import { z } from "zod";
|
|
198
21
|
import * as Sentry from "@sentry/cloudflare";
|
|
199
22
|
|
|
200
23
|
// src/client.ts
|
|
201
|
-
var
|
|
24
|
+
var OcsApiError = class extends Error {
|
|
202
25
|
constructor(code, message, method) {
|
|
203
26
|
super(`[${method}] OCS error ${code}: ${message}`);
|
|
204
27
|
this.code = code;
|
|
@@ -208,7 +31,7 @@ var OcsApiError2 = class extends Error {
|
|
|
208
31
|
code;
|
|
209
32
|
method;
|
|
210
33
|
};
|
|
211
|
-
var
|
|
34
|
+
var OcsClient = class {
|
|
212
35
|
baseUrl;
|
|
213
36
|
token;
|
|
214
37
|
constructor(baseUrl2, token2) {
|
|
@@ -225,11 +48,11 @@ var OcsClient2 = class {
|
|
|
225
48
|
body
|
|
226
49
|
});
|
|
227
50
|
if (!res.ok) {
|
|
228
|
-
throw new
|
|
51
|
+
throw new OcsApiError(res.status, `HTTP ${res.status} ${res.statusText}`, method);
|
|
229
52
|
}
|
|
230
53
|
const json = await res.json();
|
|
231
54
|
if (json.status?.code !== 0) {
|
|
232
|
-
throw new
|
|
55
|
+
throw new OcsApiError(json.status?.code ?? -1, json.status?.msg ?? "Unknown error", method);
|
|
233
56
|
}
|
|
234
57
|
if (method === "getCustomerTariff" && json["listTariffRule"] !== void 0) {
|
|
235
58
|
return json["listTariffRule"];
|
|
@@ -487,9 +310,9 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
487
310
|
try {
|
|
488
311
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
489
312
|
result2 = await handler(args, token2);
|
|
490
|
-
} catch (
|
|
313
|
+
} catch (err5) {
|
|
491
314
|
try {
|
|
492
|
-
Sentry.captureException(
|
|
315
|
+
Sentry.captureException(err5, {
|
|
493
316
|
tags: {
|
|
494
317
|
tool: toolName,
|
|
495
318
|
feature: "mcp",
|
|
@@ -498,8 +321,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
498
321
|
});
|
|
499
322
|
} catch {
|
|
500
323
|
}
|
|
501
|
-
const message =
|
|
502
|
-
const ocsCode =
|
|
324
|
+
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
325
|
+
const ocsCode = err5 instanceof OcsApiError ? err5.code : void 0;
|
|
503
326
|
ctx.audit({
|
|
504
327
|
tool_name: toolName,
|
|
505
328
|
ocs_method: ocsMethod,
|
|
@@ -527,7 +350,7 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
527
350
|
};
|
|
528
351
|
}
|
|
529
352
|
async function ocsCall(env, token2, method, params = {}) {
|
|
530
|
-
const client = new
|
|
353
|
+
const client = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
531
354
|
const result2 = await client.call(method, params);
|
|
532
355
|
return {
|
|
533
356
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
@@ -536,13 +359,13 @@ async function ocsCall(env, token2, method, params = {}) {
|
|
|
536
359
|
async function resolveSubscriberByIccid(env, token2, iccid, cache) {
|
|
537
360
|
const hit = cache.get(iccid);
|
|
538
361
|
if (hit) return hit;
|
|
539
|
-
const client = new
|
|
362
|
+
const client = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
540
363
|
const record = await client.call("getSingleSubscriber", { iccid });
|
|
541
364
|
cache.set(iccid, record);
|
|
542
365
|
return record;
|
|
543
366
|
}
|
|
544
367
|
async function getDefaultResellerId(env, token2) {
|
|
545
|
-
const client = new
|
|
368
|
+
const client = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
546
369
|
const info = await client.call("getResellerInfo", {});
|
|
547
370
|
const id = info?.id;
|
|
548
371
|
if (typeof id !== "number") {
|
|
@@ -757,7 +580,7 @@ function registerAllTools(server2, ctx) {
|
|
|
757
580
|
if (args.msisdn) params.msisdn = args.msisdn;
|
|
758
581
|
if (args.status) params.status = args.status;
|
|
759
582
|
if (args.offset !== void 0) params.offset = args.offset;
|
|
760
|
-
const client = new
|
|
583
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
761
584
|
const raw = await client.call("listSubscriber", params);
|
|
762
585
|
const limit = args.limit;
|
|
763
586
|
const payload = Array.isArray(raw) && typeof limit === "number" && limit >= 0 ? raw.slice(0, limit) : raw;
|
|
@@ -3216,11 +3039,11 @@ var ocsAppMethods = ocsSpec.v1_app_methods;
|
|
|
3216
3039
|
// src/intelligence.ts
|
|
3217
3040
|
async function safeCall(env, token2, method, params = {}) {
|
|
3218
3041
|
try {
|
|
3219
|
-
const client = new
|
|
3042
|
+
const client = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
3220
3043
|
const data = await client.call(method, params);
|
|
3221
3044
|
return { data, error: null };
|
|
3222
|
-
} catch (
|
|
3223
|
-
return { data: null, error:
|
|
3045
|
+
} catch (err5) {
|
|
3046
|
+
return { data: null, error: err5 instanceof Error ? err5.message : String(err5) };
|
|
3224
3047
|
}
|
|
3225
3048
|
}
|
|
3226
3049
|
async function fetchActiveSubscribers(env, token2, accountId, resellerId) {
|
|
@@ -3420,52 +3243,16 @@ function registerIntelligenceTools(server2, ctx) {
|
|
|
3420
3243
|
)
|
|
3421
3244
|
]);
|
|
3422
3245
|
const sections = ["# Fleet Health Dashboard\n"];
|
|
3246
|
+
const counts = extractEsimStatusCounts(statusResult.data);
|
|
3247
|
+
const statusOk = statusResult.data !== null && (counts.total > 0 || statusResult.error === null);
|
|
3423
3248
|
const statusRaw = statusResult.data;
|
|
3424
3249
|
const statusAccounts = Array.isArray(statusRaw) ? statusRaw : statusRaw?.account ?? [];
|
|
3425
|
-
const statusOk = statusResult.data !== null && (statusAccounts.length > 0 || statusResult.error === null);
|
|
3426
|
-
const countByState = (account) => {
|
|
3427
|
-
const sponsors = account.sponsor ?? [];
|
|
3428
|
-
let active = 0, suspended = 0, inventory = 0, other = 0;
|
|
3429
|
-
for (const sp of sponsors) {
|
|
3430
|
-
const esim = sp.esim ?? {};
|
|
3431
|
-
const statuses = esim.status ?? [];
|
|
3432
|
-
for (const s of statuses) {
|
|
3433
|
-
const count = Number(s.count ?? 0);
|
|
3434
|
-
const num = s.statusNum;
|
|
3435
|
-
if (typeof num === "number") {
|
|
3436
|
-
if (num === 1) active += count;
|
|
3437
|
-
else if (num === 2) suspended += count;
|
|
3438
|
-
else if (num === 0) inventory += count;
|
|
3439
|
-
else other += count;
|
|
3440
|
-
continue;
|
|
3441
|
-
}
|
|
3442
|
-
const str = String(s.statusStr ?? s.name ?? "").toUpperCase();
|
|
3443
|
-
if (str === "ACTIVATED" || str === "ACTIVE") active += count;
|
|
3444
|
-
else if (str === "SUSPENDED") suspended += count;
|
|
3445
|
-
else if (str === "FREE" || str === "INVENTORY" || str === "NOT_ACTIVATED" || str === "AVAILABLE") inventory += count;
|
|
3446
|
-
else other += count;
|
|
3447
|
-
}
|
|
3448
|
-
}
|
|
3449
|
-
return { active, suspended, inventory, other };
|
|
3450
|
-
};
|
|
3451
|
-
let totalActive = 0, totalSuspended = 0, totalInventory = 0, totalOther = 0;
|
|
3452
3250
|
if (statusOk) {
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
totalSuspended += c.suspended;
|
|
3459
|
-
totalInventory += c.inventory;
|
|
3460
|
-
totalOther += c.other;
|
|
3461
|
-
} else {
|
|
3462
|
-
totalActive += Number(account.active ?? 0);
|
|
3463
|
-
totalSuspended += Number(account.suspended ?? 0);
|
|
3464
|
-
totalInventory += Number(account.inventory ?? account.notActivated ?? 0);
|
|
3465
|
-
totalOther += Number(account.other ?? account.terminated ?? 0);
|
|
3466
|
-
}
|
|
3467
|
-
}
|
|
3468
|
-
const total = totalActive + totalSuspended + totalInventory + totalOther;
|
|
3251
|
+
const totalActive = counts.active;
|
|
3252
|
+
const totalSuspended = counts.suspended;
|
|
3253
|
+
const totalInventory = counts.inventory;
|
|
3254
|
+
const totalOther = counts.other;
|
|
3255
|
+
const total = counts.total;
|
|
3469
3256
|
const utilization = total > 0 ? (totalActive / total * 100).toFixed(1) : "0";
|
|
3470
3257
|
sections.push(`## eSIM Fleet Status`);
|
|
3471
3258
|
if (total === 0) {
|
|
@@ -4262,7 +4049,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4262
4049
|
ctx,
|
|
4263
4050
|
async ({ iccid, phone_number, phone_type }, token2) => {
|
|
4264
4051
|
const ocsMethod = phone_type === "fake" ? "affectSubscriberFakePhoneNumber" : "affectSubscriberRealPhoneNumber";
|
|
4265
|
-
const client = new
|
|
4052
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4266
4053
|
const result2 = await client.call(ocsMethod, { subscriber: iccid, phoneNumber: phone_number });
|
|
4267
4054
|
return {
|
|
4268
4055
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
@@ -4300,7 +4087,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4300
4087
|
cell_id,
|
|
4301
4088
|
signal_strength
|
|
4302
4089
|
}, token2) => {
|
|
4303
|
-
const client = new
|
|
4090
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4304
4091
|
const params = { radioType: radio_type, mcc, mnc, lac };
|
|
4305
4092
|
if (cell_id !== void 0) params.cellId = cell_id;
|
|
4306
4093
|
if (signal_strength !== void 0) params.signalStrength = signal_strength;
|
|
@@ -4328,7 +4115,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4328
4115
|
ctx,
|
|
4329
4116
|
async ({ resellerId }, token2) => {
|
|
4330
4117
|
const id = resellerId ?? await (async () => {
|
|
4331
|
-
const client2 = new
|
|
4118
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4332
4119
|
const info = await client2.call("getResellerInfo", {});
|
|
4333
4120
|
const resolvedId = info?.id;
|
|
4334
4121
|
if (typeof resolvedId !== "number") {
|
|
@@ -4336,7 +4123,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4336
4123
|
}
|
|
4337
4124
|
return resolvedId;
|
|
4338
4125
|
})();
|
|
4339
|
-
const client = new
|
|
4126
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4340
4127
|
const result2 = await client.call("listDetailedDestinationList", id);
|
|
4341
4128
|
return {
|
|
4342
4129
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
@@ -4362,7 +4149,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4362
4149
|
BACKLOG_TOOL_SCOPES["modify_subscriber_mobile_plan"],
|
|
4363
4150
|
ctx,
|
|
4364
4151
|
async ({ iccid, mobile_plan_id }, token2) => {
|
|
4365
|
-
const client = new
|
|
4152
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4366
4153
|
const result2 = await client.call(
|
|
4367
4154
|
"modifySubscriberMobilePlan",
|
|
4368
4155
|
{ subscriber: iccid, mobilePlanId: mobile_plan_id }
|
|
@@ -4396,7 +4183,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4396
4183
|
const params = { subscriber: iccid, packageId: package_id };
|
|
4397
4184
|
if (start_date !== void 0) params.startDate = start_date;
|
|
4398
4185
|
if (end_date !== void 0) params.endDate = end_date;
|
|
4399
|
-
const client = new
|
|
4186
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4400
4187
|
const result2 = await client.call(
|
|
4401
4188
|
"modifySubscriberPrepaidPackageActivePeriod",
|
|
4402
4189
|
params
|
|
@@ -4425,7 +4212,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4425
4212
|
BACKLOG_TOOL_SCOPES["modify_subscriber_voip_plan"],
|
|
4426
4213
|
ctx,
|
|
4427
4214
|
async ({ iccid, voip_plan_id }, token2) => {
|
|
4428
|
-
const client = new
|
|
4215
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4429
4216
|
const result2 = await client.call(
|
|
4430
4217
|
"modifySubscriberVoipPlan",
|
|
4431
4218
|
{ subscriber: iccid, voipPlanId: voip_plan_id }
|
|
@@ -4456,7 +4243,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4456
4243
|
const cache = /* @__PURE__ */ new Map();
|
|
4457
4244
|
const sub = await resolveSubscriberByIccid(ctx.env, token2, iccid, cache);
|
|
4458
4245
|
const subscriberId = sub.id ?? sub.subscriberId ?? iccid;
|
|
4459
|
-
const client = new
|
|
4246
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4460
4247
|
const result2 = await client.call(
|
|
4461
4248
|
"pushSteeringToSubs",
|
|
4462
4249
|
{ subscriber: subscriberId }
|
|
@@ -4484,7 +4271,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4484
4271
|
BACKLOG_TOOL_SCOPES["reset_subscriber_gz_counter"],
|
|
4485
4272
|
ctx,
|
|
4486
4273
|
async ({ iccid }, token2) => {
|
|
4487
|
-
const client = new
|
|
4274
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4488
4275
|
const result2 = await client.call("resetSubsGzCounter", { subscriber: iccid });
|
|
4489
4276
|
return {
|
|
4490
4277
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
@@ -4508,7 +4295,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4508
4295
|
BACKLOG_TOOL_SCOPES["carrier_webhook_config"],
|
|
4509
4296
|
ctx,
|
|
4510
4297
|
async ({ reseller_id }, token2) => {
|
|
4511
|
-
const client = new
|
|
4298
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4512
4299
|
let resellerId = reseller_id;
|
|
4513
4300
|
if (resellerId === void 0) {
|
|
4514
4301
|
resellerId = await getDefaultResellerId(ctx.env, token2);
|
|
@@ -4550,7 +4337,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4550
4337
|
BACKLOG_TOOL_SCOPES["list_subscriber_voip_tariff"],
|
|
4551
4338
|
ctx,
|
|
4552
4339
|
async ({ reseller_id }, token2) => {
|
|
4553
|
-
const client = new
|
|
4340
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4554
4341
|
const params = {};
|
|
4555
4342
|
if (reseller_id !== void 0) {
|
|
4556
4343
|
params.resellerId = reseller_id;
|
|
@@ -4580,7 +4367,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4580
4367
|
BACKLOG_TOOL_SCOPES["list_voip_tariff_rule"],
|
|
4581
4368
|
ctx,
|
|
4582
4369
|
async ({ voip_plan_id }, token2) => {
|
|
4583
|
-
const client = new
|
|
4370
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4584
4371
|
const result2 = await client.call("listVoipTariffRule", voip_plan_id);
|
|
4585
4372
|
return {
|
|
4586
4373
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
@@ -4609,7 +4396,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4609
4396
|
location_zone_id,
|
|
4610
4397
|
network_profile_id
|
|
4611
4398
|
}, token2) => {
|
|
4612
|
-
const client = new
|
|
4399
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4613
4400
|
const result2 = await client.call("changeNetworkProfileOfLocationZone", {
|
|
4614
4401
|
locationZoneId: location_zone_id,
|
|
4615
4402
|
networkProfileId: network_profile_id
|
|
@@ -4659,7 +4446,8 @@ var TOOL_REGISTRY = /* @__PURE__ */ new Set([
|
|
|
4659
4446
|
"greenzone_whitelist_list",
|
|
4660
4447
|
// router tools (self-reference)
|
|
4661
4448
|
"carrier_ask",
|
|
4662
|
-
"carrier_ask_describe"
|
|
4449
|
+
"carrier_ask_describe",
|
|
4450
|
+
"generate_storefront_logo"
|
|
4663
4451
|
]);
|
|
4664
4452
|
var HARD_BLOCK_TOOLS = /* @__PURE__ */ new Set([
|
|
4665
4453
|
"clean_all_packages",
|
|
@@ -5137,10 +4925,10 @@ async function callBedrock(env, payload) {
|
|
|
5137
4925
|
const body = await resp.text();
|
|
5138
4926
|
if (resp.status === 429) {
|
|
5139
4927
|
const retryAfter = resp.headers.get("retry-after");
|
|
5140
|
-
const
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
throw
|
|
4928
|
+
const err5 = new Error(`Bedrock rate limit: ${body}`);
|
|
4929
|
+
err5.isRateLimit = true;
|
|
4930
|
+
err5.retryAfter = retryAfter;
|
|
4931
|
+
throw err5;
|
|
5144
4932
|
}
|
|
5145
4933
|
throw new Error(`Bedrock invoke failed: ${resp.status} ${body}`);
|
|
5146
4934
|
}
|
|
@@ -5178,9 +4966,9 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
5178
4966
|
AWS_REGION: env.AWS_REGION,
|
|
5179
4967
|
BEDROCK_MODEL_ID: env.BEDROCK_MODEL_ID
|
|
5180
4968
|
}, payload);
|
|
5181
|
-
} catch (
|
|
5182
|
-
if (
|
|
5183
|
-
const retryAfter =
|
|
4969
|
+
} catch (err5) {
|
|
4970
|
+
if (err5 instanceof Error && err5.isRateLimit) {
|
|
4971
|
+
const retryAfter = err5.retryAfter;
|
|
5184
4972
|
const parsedSeconds = retryAfter ? parseInt(retryAfter, 10) : 60;
|
|
5185
4973
|
return {
|
|
5186
4974
|
match: "rate_limited",
|
|
@@ -5188,7 +4976,7 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
5188
4976
|
suggestion: "Bedrock rate limit reached. Please retry after the indicated delay."
|
|
5189
4977
|
};
|
|
5190
4978
|
}
|
|
5191
|
-
throw
|
|
4979
|
+
throw err5;
|
|
5192
4980
|
}
|
|
5193
4981
|
const toolUseBlock = response.content.find(
|
|
5194
4982
|
(block) => block.type === "tool_use"
|
|
@@ -5362,7 +5150,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
5362
5150
|
let route;
|
|
5363
5151
|
try {
|
|
5364
5152
|
route = await _routeIntent(intent, context, ctx.env);
|
|
5365
|
-
} catch (
|
|
5153
|
+
} catch (err5) {
|
|
5366
5154
|
writeCarrierAskAudit(ctx.env, {
|
|
5367
5155
|
intent_hash: intentHash,
|
|
5368
5156
|
match: "error",
|
|
@@ -5379,7 +5167,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
5379
5167
|
type: "text",
|
|
5380
5168
|
text: JSON.stringify({
|
|
5381
5169
|
error: "routing_error",
|
|
5382
|
-
message:
|
|
5170
|
+
message: err5 instanceof Error ? err5.message : "An unexpected error occurred during routing."
|
|
5383
5171
|
})
|
|
5384
5172
|
}
|
|
5385
5173
|
],
|
|
@@ -5631,8 +5419,8 @@ function registerListRecentOcsEventsTool(server2, env) {
|
|
|
5631
5419
|
return {
|
|
5632
5420
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
5633
5421
|
};
|
|
5634
|
-
} catch (
|
|
5635
|
-
const message =
|
|
5422
|
+
} catch (err5) {
|
|
5423
|
+
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
5636
5424
|
return {
|
|
5637
5425
|
isError: true,
|
|
5638
5426
|
content: [{ type: "text", text: `Error reading OCS event buffer: ${message}` }]
|
|
@@ -5915,10 +5703,10 @@ import {
|
|
|
5915
5703
|
async function safeCallWithToken(client, _token, method, params = {}) {
|
|
5916
5704
|
try {
|
|
5917
5705
|
return { data: await client.call(method, params), error: null };
|
|
5918
|
-
} catch (
|
|
5706
|
+
} catch (err5) {
|
|
5919
5707
|
return {
|
|
5920
5708
|
data: null,
|
|
5921
|
-
error:
|
|
5709
|
+
error: err5 instanceof Error ? err5.message : String(err5)
|
|
5922
5710
|
};
|
|
5923
5711
|
}
|
|
5924
5712
|
}
|
|
@@ -5977,7 +5765,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
5977
5765
|
},
|
|
5978
5766
|
async ({ accountId }) => {
|
|
5979
5767
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
5980
|
-
const client = new
|
|
5768
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
5981
5769
|
const [statusResult, accountsResult] = await Promise.all([
|
|
5982
5770
|
safeCallWithToken(
|
|
5983
5771
|
client,
|
|
@@ -6116,10 +5904,10 @@ async function deleteWizardSession(env, sub, wizardId) {
|
|
|
6116
5904
|
async function safeCallWithToken2(client, _token, method, params = {}) {
|
|
6117
5905
|
try {
|
|
6118
5906
|
return { data: await client.call(method, params), error: null };
|
|
6119
|
-
} catch (
|
|
5907
|
+
} catch (err5) {
|
|
6120
5908
|
return {
|
|
6121
5909
|
data: null,
|
|
6122
|
-
error:
|
|
5910
|
+
error: err5 instanceof Error ? err5.message : String(err5)
|
|
6123
5911
|
};
|
|
6124
5912
|
}
|
|
6125
5913
|
}
|
|
@@ -6182,7 +5970,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
6182
5970
|
},
|
|
6183
5971
|
async ({ step, wizardId, subscriber_iccid, package_template_id }) => {
|
|
6184
5972
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
6185
|
-
const client = new
|
|
5973
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
6186
5974
|
if (step === "init") {
|
|
6187
5975
|
const newWizardId = generateWizardId();
|
|
6188
5976
|
const subscribersResult = await safeCallWithToken2(
|
|
@@ -6479,10 +6267,10 @@ import {
|
|
|
6479
6267
|
async function safeCallWithToken3(client, _token, method, params = {}) {
|
|
6480
6268
|
try {
|
|
6481
6269
|
return { data: await client.call(method, params), error: null };
|
|
6482
|
-
} catch (
|
|
6270
|
+
} catch (err5) {
|
|
6483
6271
|
return {
|
|
6484
6272
|
data: null,
|
|
6485
|
-
error:
|
|
6273
|
+
error: err5 instanceof Error ? err5.message : String(err5)
|
|
6486
6274
|
};
|
|
6487
6275
|
}
|
|
6488
6276
|
}
|
|
@@ -6521,7 +6309,7 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
6521
6309
|
"balance_topup_form",
|
|
6522
6310
|
{
|
|
6523
6311
|
title: "Balance Top-up Form",
|
|
6524
|
-
description: "
|
|
6312
|
+
description: "Adjust the OCS reseller-account balance behind one subscriber, through a form panel the user can review before it commits. Defaults to preview: a call that omits `preview` returns a projection and writes nothing. Only `preview: false` performs the OCS write. Params: `iccid` (subscriber identifier used to find the account), `delta` (amount to add when positive, deduct when negative), `preview` (boolean, default true). Requires `admin` scope plus recent MFA, and a client that can render UI panels. Do NOT use this in a headless or scripted context \u2014 call `modify_account_balance` directly, which takes an `accountId` and needs no panel. Do NOT use this for a subscriber's own balance \u2014 the `iccid` here only locates the account; per-subscriber money is `modify_subscriber_balance`. Do NOT use this for the Carrier prepaid wallet or MCP credits \u2014 see `wallet_topup_checkout` and `credit_balance`.",
|
|
6525
6313
|
inputSchema: {
|
|
6526
6314
|
iccid: z11.string().describe("The subscriber ICCID for the account lookup"),
|
|
6527
6315
|
delta: z11.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
|
|
@@ -6542,7 +6330,7 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
6542
6330
|
ctx,
|
|
6543
6331
|
async ({ iccid, delta, preview }) => {
|
|
6544
6332
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
6545
|
-
const client = new
|
|
6333
|
+
const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
6546
6334
|
if (preview === true) {
|
|
6547
6335
|
const result2 = await safeCallWithToken3(
|
|
6548
6336
|
client,
|
|
@@ -8143,8 +7931,8 @@ function buildPricingHandler(tool, ctx) {
|
|
|
8143
7931
|
}
|
|
8144
7932
|
]
|
|
8145
7933
|
};
|
|
8146
|
-
} catch (
|
|
8147
|
-
const message =
|
|
7934
|
+
} catch (err5) {
|
|
7935
|
+
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
8148
7936
|
ctx.audit({
|
|
8149
7937
|
tool_name: tool.name,
|
|
8150
7938
|
ocs_method: "billing",
|
|
@@ -8464,8 +8252,8 @@ function writeUsageThresholdAudit(env, severity, remainingCredits, month, taskCo
|
|
|
8464
8252
|
doubles: [remainingCredits],
|
|
8465
8253
|
indexes: ["manus_usage"]
|
|
8466
8254
|
});
|
|
8467
|
-
} catch (
|
|
8468
|
-
console.error(`[manus-usage] threshold audit write failed: ${
|
|
8255
|
+
} catch (err5) {
|
|
8256
|
+
console.error(`[manus-usage] threshold audit write failed: ${err5.message}`);
|
|
8469
8257
|
}
|
|
8470
8258
|
}
|
|
8471
8259
|
|
|
@@ -8575,7 +8363,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8575
8363
|
}
|
|
8576
8364
|
]
|
|
8577
8365
|
};
|
|
8578
|
-
} catch (
|
|
8366
|
+
} catch (err5) {
|
|
8579
8367
|
ctx.audit({
|
|
8580
8368
|
tool_name: "ui_agent_schedule_create",
|
|
8581
8369
|
ocs_method: "[manus:schedule.create]",
|
|
@@ -8584,7 +8372,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8584
8372
|
duration_ms: Date.now() - start,
|
|
8585
8373
|
event_type: "ui_agent_dispatch"
|
|
8586
8374
|
});
|
|
8587
|
-
const msg =
|
|
8375
|
+
const msg = err5 instanceof ManusScheduleError ? `Manus API error (HTTP ${err5.statusCode}): ${err5.message}` : err5 instanceof Error ? err5.message : String(err5);
|
|
8588
8376
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8589
8377
|
}
|
|
8590
8378
|
}
|
|
@@ -8642,7 +8430,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8642
8430
|
}
|
|
8643
8431
|
]
|
|
8644
8432
|
};
|
|
8645
|
-
} catch (
|
|
8433
|
+
} catch (err5) {
|
|
8646
8434
|
ctx.audit({
|
|
8647
8435
|
tool_name: "ui_agent_schedule_list",
|
|
8648
8436
|
ocs_method: "[manus:schedule.list]",
|
|
@@ -8650,7 +8438,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8650
8438
|
dry_run: false,
|
|
8651
8439
|
duration_ms: Date.now() - start
|
|
8652
8440
|
});
|
|
8653
|
-
const msg =
|
|
8441
|
+
const msg = err5 instanceof ManusScheduleError ? `Manus API error (HTTP ${err5.statusCode}): ${err5.message}` : err5 instanceof Error ? err5.message : String(err5);
|
|
8654
8442
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8655
8443
|
}
|
|
8656
8444
|
}
|
|
@@ -8711,7 +8499,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8711
8499
|
}
|
|
8712
8500
|
]
|
|
8713
8501
|
};
|
|
8714
|
-
} catch (
|
|
8502
|
+
} catch (err5) {
|
|
8715
8503
|
ctx.audit({
|
|
8716
8504
|
tool_name: "ui_agent_schedule_delete",
|
|
8717
8505
|
ocs_method: "[manus:schedule.delete]",
|
|
@@ -8720,7 +8508,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8720
8508
|
duration_ms: Date.now() - start,
|
|
8721
8509
|
event_type: "ui_agent_dispatch"
|
|
8722
8510
|
});
|
|
8723
|
-
const msg =
|
|
8511
|
+
const msg = err5 instanceof ManusScheduleError ? `Manus API error (HTTP ${err5.statusCode}): ${err5.message}` : err5 instanceof Error ? err5.message : String(err5);
|
|
8724
8512
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8725
8513
|
}
|
|
8726
8514
|
}
|
|
@@ -8781,7 +8569,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8781
8569
|
}
|
|
8782
8570
|
]
|
|
8783
8571
|
};
|
|
8784
|
-
} catch (
|
|
8572
|
+
} catch (err5) {
|
|
8785
8573
|
ctx.audit({
|
|
8786
8574
|
tool_name: "ui_agent_schedule_pause",
|
|
8787
8575
|
ocs_method: "[manus:schedule.pause]",
|
|
@@ -8790,7 +8578,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8790
8578
|
duration_ms: Date.now() - start,
|
|
8791
8579
|
event_type: "ui_agent_dispatch"
|
|
8792
8580
|
});
|
|
8793
|
-
const msg =
|
|
8581
|
+
const msg = err5 instanceof ManusScheduleError ? `Manus API error (HTTP ${err5.statusCode}): ${err5.message}` : err5 instanceof Error ? err5.message : String(err5);
|
|
8794
8582
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8795
8583
|
}
|
|
8796
8584
|
}
|
|
@@ -8851,7 +8639,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8851
8639
|
}
|
|
8852
8640
|
]
|
|
8853
8641
|
};
|
|
8854
|
-
} catch (
|
|
8642
|
+
} catch (err5) {
|
|
8855
8643
|
ctx.audit({
|
|
8856
8644
|
tool_name: "ui_agent_schedule_resume",
|
|
8857
8645
|
ocs_method: "[manus:schedule.resume]",
|
|
@@ -8860,7 +8648,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8860
8648
|
duration_ms: Date.now() - start,
|
|
8861
8649
|
event_type: "ui_agent_dispatch"
|
|
8862
8650
|
});
|
|
8863
|
-
const msg =
|
|
8651
|
+
const msg = err5 instanceof ManusScheduleError ? `Manus API error (HTTP ${err5.statusCode}): ${err5.message}` : err5 instanceof Error ? err5.message : String(err5);
|
|
8864
8652
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8865
8653
|
}
|
|
8866
8654
|
}
|
|
@@ -8905,7 +8693,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8905
8693
|
}
|
|
8906
8694
|
]
|
|
8907
8695
|
};
|
|
8908
|
-
} catch (
|
|
8696
|
+
} catch (err5) {
|
|
8909
8697
|
ctx.audit({
|
|
8910
8698
|
tool_name: "ui_agent_usage",
|
|
8911
8699
|
ocs_method: "[manus:usage.get]",
|
|
@@ -8913,7 +8701,7 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
8913
8701
|
dry_run: false,
|
|
8914
8702
|
duration_ms: Date.now() - start
|
|
8915
8703
|
});
|
|
8916
|
-
const msg =
|
|
8704
|
+
const msg = err5 instanceof Error ? err5.message : String(err5);
|
|
8917
8705
|
return { isError: true, content: [{ type: "text", text: msg }] };
|
|
8918
8706
|
}
|
|
8919
8707
|
}
|
|
@@ -9156,7 +8944,7 @@ Report success or failure with a clear summary.`;
|
|
|
9156
8944
|
}
|
|
9157
8945
|
]
|
|
9158
8946
|
};
|
|
9159
|
-
} catch (
|
|
8947
|
+
} catch (err5) {
|
|
9160
8948
|
ctx.audit({
|
|
9161
8949
|
tool_name: toolName,
|
|
9162
8950
|
ocs_method: `[ui-agent:${gapId}]`,
|
|
@@ -9170,7 +8958,7 @@ Report success or failure with a clear summary.`;
|
|
|
9170
8958
|
content: [
|
|
9171
8959
|
{
|
|
9172
8960
|
type: "text",
|
|
9173
|
-
text: `Error dispatching UI agent: ${
|
|
8961
|
+
text: `Error dispatching UI agent: ${err5 instanceof Error ? err5.message : String(err5)}`
|
|
9174
8962
|
}
|
|
9175
8963
|
]
|
|
9176
8964
|
};
|
|
@@ -9594,7 +9382,7 @@ function registerUiAgentAskTools(server2, ctx) {
|
|
|
9594
9382
|
try {
|
|
9595
9383
|
const replyOutcome = await askReply(keys, task_id, reply);
|
|
9596
9384
|
result2 = replyOutcome.data;
|
|
9597
|
-
} catch (
|
|
9385
|
+
} catch (err5) {
|
|
9598
9386
|
ctx.audit({
|
|
9599
9387
|
tool_name: "ui_agent_reply",
|
|
9600
9388
|
ocs_method: "[ui-agent:ask-reply]",
|
|
@@ -9609,7 +9397,7 @@ function registerUiAgentAskTools(server2, ctx) {
|
|
|
9609
9397
|
content: [
|
|
9610
9398
|
{
|
|
9611
9399
|
type: "text",
|
|
9612
|
-
text: `Error calling Manus task.reply: ${
|
|
9400
|
+
text: `Error calling Manus task.reply: ${err5 instanceof Error ? err5.message : String(err5)}`
|
|
9613
9401
|
}
|
|
9614
9402
|
]
|
|
9615
9403
|
};
|
|
@@ -9687,13 +9475,13 @@ function registerUiAgentAskTools(server2, ctx) {
|
|
|
9687
9475
|
try {
|
|
9688
9476
|
const listing = await ctx.env.CARRIER_USERS.list({ prefix: PENDING_ASK_PREFIX });
|
|
9689
9477
|
keys = listing.keys;
|
|
9690
|
-
} catch (
|
|
9478
|
+
} catch (err5) {
|
|
9691
9479
|
return {
|
|
9692
9480
|
isError: true,
|
|
9693
9481
|
content: [
|
|
9694
9482
|
{
|
|
9695
9483
|
type: "text",
|
|
9696
|
-
text: `Error listing pending tasks: ${
|
|
9484
|
+
text: `Error listing pending tasks: ${err5 instanceof Error ? err5.message : String(err5)}`
|
|
9697
9485
|
}
|
|
9698
9486
|
]
|
|
9699
9487
|
};
|
|
@@ -9864,7 +9652,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
9864
9652
|
);
|
|
9865
9653
|
server2.tool(
|
|
9866
9654
|
"stripe_connect_payouts",
|
|
9867
|
-
"Read-only:
|
|
9655
|
+
"List money Stripe has already sent from the operator's connected account to their bank, newest first. Read-only. Params: `limit` (1-50, default 10), `status` (optional Stripe payout status, e.g. 'paid', 'pending', 'failed'). Returns: { payouts: [{ id, amount, currency, status, arrival_date, automatic, created }], has_more } \u2014 amount in the currency's smallest unit. Do NOT use this for money still sitting at Stripe and not yet paid out \u2014 use `stripe_connect_balance`.",
|
|
9868
9656
|
{
|
|
9869
9657
|
limit: z17.number().int().min(1).max(50).default(10).describe("Number of payouts to return."),
|
|
9870
9658
|
status: z17.enum(["pending", "paid", "failed", "canceled", "in_transit"]).optional().describe("Filter by payout status.")
|
|
@@ -9898,7 +9686,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
9898
9686
|
);
|
|
9899
9687
|
server2.tool(
|
|
9900
9688
|
"stripe_connect_balance",
|
|
9901
|
-
"Read
|
|
9689
|
+
"Read the money sitting in the operator's connected Stripe account: available and pending, split per currency. Params: none (the connected account is resolved from the session). Returns: { account_id, available: [{ amount, currency }], pending: [{ amount, currency }] } \u2014 amounts in the currency's smallest unit (cents for EUR/USD). Do NOT use this for the Carrier prepaid wallet \u2014 use `wallet_balance`. Do NOT use this for money already paid out to the bank \u2014 use `stripe_connect_payouts`.",
|
|
9902
9690
|
{},
|
|
9903
9691
|
async () => {
|
|
9904
9692
|
const start = Date.now();
|
|
@@ -9931,7 +9719,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
9931
9719
|
);
|
|
9932
9720
|
server2.tool(
|
|
9933
9721
|
"stripe_connect_refund",
|
|
9934
|
-
"
|
|
9722
|
+
"Refund money to a customer on one charge in the operator's connected Stripe account. This returns real funds and Stripe cannot reverse a refund once issued. Two-step by design: call without `confirm_token` to get a token and a summary of what would be refunded, then call again with that token to execute. The token expires in 5 minutes. Params: `charge_id` (Stripe charge id, `ch_...`), `amount_cents` (optional integer in the charge's smallest currency unit \u2014 omit for a full refund), `reason` ('duplicate' | 'fraudulent' | 'requested_by_customer', optional), `confirm_token`. Returns: the refund id and status. Requires `write` scope. Do NOT use this to stop a charge that has not settled yet \u2014 decline it with `radar_review_decline`. Do NOT use this to contest a chargeback \u2014 read the case with `stripe_connect_dispute_list` first, since refunding a disputed charge can cost the operator twice.",
|
|
9935
9723
|
{
|
|
9936
9724
|
charge_id: z17.string().min(1).describe("Stripe charge ID (ch_...)."),
|
|
9937
9725
|
amount_cents: z17.number().int().min(1).optional().describe("Partial refund amount in cents. Omit for full refund."),
|
|
@@ -9981,7 +9769,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
9981
9769
|
);
|
|
9982
9770
|
server2.tool(
|
|
9983
9771
|
"stripe_connect_dispute_list",
|
|
9984
|
-
"Read-only:
|
|
9772
|
+
"List chargebacks customers have raised with their bank against the operator's connected Stripe account. Read-only \u2014 responding to one is Dashboard work. Params: `limit` (1-50, default 10), `status` (optional Stripe dispute status, e.g. 'needs_response', 'under_review'). Returns: { disputes: [{ id, charge, amount, currency, status, reason, evidence_details.due_by }], has_more } \u2014 amount in the currency's smallest unit, `due_by` a Unix timestamp or null. Do NOT use this for charges Stripe is holding for fraud review before they settle \u2014 those are `radar_review_list`. Check this before refunding: refunding a disputed charge can cost the operator the amount twice.",
|
|
9985
9773
|
{
|
|
9986
9774
|
limit: z17.number().int().min(1).max(50).default(10),
|
|
9987
9775
|
status: z17.string().optional().describe("Filter by dispute status (e.g. needs_response, under_review).")
|
|
@@ -10015,7 +9803,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
10015
9803
|
);
|
|
10016
9804
|
server2.tool(
|
|
10017
9805
|
"radar_review_list",
|
|
10018
|
-
"
|
|
9806
|
+
"List the Stripe Radar fraud reviews waiting on a manual approve/decline decision, newest first. Reads only \u2014 deciding a review is a separate call. Params: `open_only` (boolean, default true \u2014 false also returns already-decided reviews), `limit` (1-50, default 10). Returns: { reviews: [{ id (`prv_...`), charge, reason, opened_reason, closed_reason, created, closed }], has_more }. Call this first to get the `review_id` that `radar_review_approve` and `radar_review_decline` require. Do NOT use this for chargebacks the customer has already raised with their bank \u2014 use `stripe_connect_dispute_list`.",
|
|
10019
9807
|
{
|
|
10020
9808
|
open_only: z17.boolean().default(true).describe("If true, only returns open (undecided) reviews."),
|
|
10021
9809
|
limit: z17.number().int().min(1).max(50).default(10)
|
|
@@ -10047,7 +9835,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
10047
9835
|
);
|
|
10048
9836
|
server2.tool(
|
|
10049
9837
|
"radar_review_approve",
|
|
10050
|
-
"
|
|
9838
|
+
"Clear a Stripe Radar fraud review so the held charge is captured and the money is taken. Approving overrides Radar's fraud signal, so a genuinely fraudulent charge approved here usually returns later as a chargeback plus a fee. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to block a suspicious charge \u2014 that is `radar_review_decline`, the opposite outcome. Do NOT call this to return money on a charge that already went through \u2014 that is `stripe_connect_refund`.",
|
|
10051
9839
|
{
|
|
10052
9840
|
review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
|
|
10053
9841
|
confirm_token: z17.string().optional()
|
|
@@ -10092,7 +9880,7 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
|
|
|
10092
9880
|
);
|
|
10093
9881
|
server2.tool(
|
|
10094
9882
|
"radar_review_decline",
|
|
10095
|
-
"
|
|
9883
|
+
"Close a Stripe Radar fraud review as fraudulent, so the held charge is blocked and the customer is never billed. The customer sees a failed payment; if they were legitimate, they have to pay again. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to let a charge through \u2014 that is `radar_review_approve`, the opposite outcome. Do NOT call this on a charge that already succeeded; a review only exists while the charge is held, and returning settled money is `stripe_connect_refund`.",
|
|
10096
9884
|
{
|
|
10097
9885
|
review_id: z17.string().min(1),
|
|
10098
9886
|
confirm_token: z17.string().optional()
|
|
@@ -10137,7 +9925,7 @@ Expires in 5 minutes.`
|
|
|
10137
9925
|
);
|
|
10138
9926
|
server2.tool(
|
|
10139
9927
|
"radar_value_list_add",
|
|
10140
|
-
"
|
|
9928
|
+
"Add one value to an existing Stripe Radar value list, changing how every future charge is scored. Whether the value ends up blocked or allowed depends entirely on which list you name in `value_list_id` \u2014 this tool does not choose, and putting a value on the allow list when you meant the block list silently lets fraud through. Confirm the list's purpose before calling. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `value_list_id` (`rsl_...`, an existing list), `value` (one email, IP address, card fingerprint, or ISO country code \u2014 one item per call), `confirm_token`. Requires `admin` scope. Do NOT use this to decide a single charge that is already under review \u2014 use `radar_review_approve` or `radar_review_decline`. Do NOT use this to create a list or a rule: only `value` is added, and rules cannot be edited through this API at all (see `radar_rule_toggle`).",
|
|
10141
9929
|
{
|
|
10142
9930
|
value_list_id: z17.string().min(1).describe("Stripe Radar value list ID (rsl_...)."),
|
|
10143
9931
|
value: z17.string().min(1).describe("The value to add (email, IP address, country code, etc.)."),
|
|
@@ -10183,7 +9971,7 @@ Expires in 5 minutes.`
|
|
|
10183
9971
|
);
|
|
10184
9972
|
server2.tool(
|
|
10185
9973
|
"radar_rule_toggle",
|
|
10186
|
-
"
|
|
9974
|
+
"Return the Stripe Dashboard steps for enabling or disabling a Radar rule. This tool changes nothing: Stripe has no public API for rule state, so the call always succeeds and the rule stays exactly as it was. Never report the rule as toggled. Params: `rule_id` (Stripe Radar rule id), `enabled` (true for the enable instructions, false for disable). Returns: the Dashboard URL and steps for a human to follow. Requires `admin` scope. Do NOT use this when a change must actually take effect from an agent \u2014 the only Radar state reachable through this API is value-list membership, via `radar_value_list_add`.",
|
|
10187
9975
|
{
|
|
10188
9976
|
rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
|
|
10189
9977
|
enabled: z17.boolean().describe("true = enable, false = disable.")
|
|
@@ -10708,6 +10496,46 @@ function registerUiAgentGenericTools(server2, _ctx) {
|
|
|
10708
10496
|
);
|
|
10709
10497
|
}
|
|
10710
10498
|
|
|
10499
|
+
// src/tools-storefront-logo.ts
|
|
10500
|
+
import { z as z20 } from "zod";
|
|
10501
|
+
var ok4 = (text) => ({ content: [{ type: "text", text }] });
|
|
10502
|
+
var err4 = (text) => ({ isError: true, content: [{ type: "text", text }] });
|
|
10503
|
+
function registerStorefrontLogoTools(server2, env = {}) {
|
|
10504
|
+
server2.tool(
|
|
10505
|
+
"generate_storefront_logo",
|
|
10506
|
+
"Generate a storefront logo (SVG always; PNG when an image key is configured on the Carrier worker). Works for any authenticated Carrier MCP user \u2014 no local image-API key required. Write the SVG to public/brand/logo.svg in the storefront.",
|
|
10507
|
+
{
|
|
10508
|
+
name: z20.string().min(1).describe("Brand / shop name, e.g. Bananas"),
|
|
10509
|
+
accent: z20.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Accent hex, e.g. #FF8A00"),
|
|
10510
|
+
tagline: z20.string().optional().describe("Optional tagline for AI-enhanced marks")
|
|
10511
|
+
},
|
|
10512
|
+
async ({ name, accent, tagline }) => {
|
|
10513
|
+
try {
|
|
10514
|
+
const hex = accent ? accent.startsWith("#") ? accent : `#${accent}` : void 0;
|
|
10515
|
+
const logo = await generateStorefrontLogo({
|
|
10516
|
+
name,
|
|
10517
|
+
accent: hex,
|
|
10518
|
+
tagline,
|
|
10519
|
+
env: { ...env, ...process.env }
|
|
10520
|
+
});
|
|
10521
|
+
return ok4(
|
|
10522
|
+
JSON.stringify({
|
|
10523
|
+
brand: name,
|
|
10524
|
+
source: logo.source,
|
|
10525
|
+
filename: "public/brand/logo.svg",
|
|
10526
|
+
png_filename: logo.pngBase64 ? "public/brand/logo.png" : null,
|
|
10527
|
+
svg: logo.svg,
|
|
10528
|
+
png_base64: logo.pngBase64 ?? null,
|
|
10529
|
+
next: "Write svg to the storefront public/brand/logo.svg (and png if present). Nav and icons read that path."
|
|
10530
|
+
})
|
|
10531
|
+
);
|
|
10532
|
+
} catch (e) {
|
|
10533
|
+
return err4(e instanceof Error ? e.message : "logo generation failed");
|
|
10534
|
+
}
|
|
10535
|
+
}
|
|
10536
|
+
);
|
|
10537
|
+
}
|
|
10538
|
+
|
|
10711
10539
|
// src/index.ts
|
|
10712
10540
|
var baseUrl = process.env.CARRIER_OCS_BASE_URL ?? process.env.ESIMVAULT_BASE_URL ?? "https://ocs.esimvault.cloud";
|
|
10713
10541
|
var token = process.env.CARRIER_OCS_API_TOKEN ?? process.env.ESIMVAULT_API_TOKEN;
|
|
@@ -10745,7 +10573,7 @@ var audit = (_row) => {
|
|
|
10745
10573
|
var getUserToken = async (_sub) => token;
|
|
10746
10574
|
var toolCtx = { env: stdioEnv, props, audit, getUserToken };
|
|
10747
10575
|
var server = new McpServer(
|
|
10748
|
-
{ name: "carrier-mcp", version:
|
|
10576
|
+
{ name: "carrier-mcp", version: CARRIER_VERSION },
|
|
10749
10577
|
{
|
|
10750
10578
|
instructions: "Carrier MCP \u2014 single-user stdio mode. Full tool registry (mirrors the deployed Worker)."
|
|
10751
10579
|
}
|
|
@@ -10766,6 +10594,7 @@ registerUiAgentAskTools(server, toolCtx);
|
|
|
10766
10594
|
registerStripeConnectTools(server, { env: stdioEnv, props });
|
|
10767
10595
|
registerGreenzoneTools(server, toolCtx);
|
|
10768
10596
|
registerUiAgentGenericTools(server, toolCtx);
|
|
10597
|
+
registerStorefrontLogoTools(server, stdioEnv);
|
|
10769
10598
|
registerAllPrompts(server);
|
|
10770
10599
|
var transport = new StdioServerTransport();
|
|
10771
10600
|
await server.connect(transport);
|