@carrierllc/mcp 0.8.0 → 0.9.1
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/{chunk-JROD4YAU.js → chunk-V4CYEMLJ.js} +3 -2
- package/dist/chunk-V4CYEMLJ.js.map +1 -0
- package/dist/cli.js +38 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.js +496 -204
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-JROD4YAU.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
subscriberIdParams,
|
|
38
38
|
usageOverPeriodParams,
|
|
39
39
|
verifyStorefront
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-V4CYEMLJ.js";
|
|
41
41
|
import "./chunk-SHKKVIIA.js";
|
|
42
42
|
|
|
43
43
|
// src/index.ts
|
|
@@ -69,11 +69,11 @@ var OcsClient = class {
|
|
|
69
69
|
async call(method, params = {}) {
|
|
70
70
|
await acquireEndpointSlot(this.token, method, "interactive");
|
|
71
71
|
const url = `${this.baseUrl}/v1?token=${this.token}`;
|
|
72
|
-
const
|
|
72
|
+
const body2 = JSON.stringify({ [method]: params });
|
|
73
73
|
const res = await fetch(url, {
|
|
74
74
|
method: "POST",
|
|
75
75
|
headers: { "Content-Type": "application/json" },
|
|
76
|
-
body
|
|
76
|
+
body: body2
|
|
77
77
|
});
|
|
78
78
|
if (!res.ok) {
|
|
79
79
|
throw new OcsApiError(res.status, `HTTP ${res.status} ${res.statusText}`, method);
|
|
@@ -98,21 +98,221 @@ var OcsClient = class {
|
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
// ../../packages/carrier-ai/dist/index.js
|
|
102
|
+
import { AwsClient } from "aws4fetch";
|
|
103
|
+
var AI_TIER_POLICY = {
|
|
104
|
+
free: { limit: 5, window: "day" },
|
|
105
|
+
pro: { limit: 1e3, window: "month" },
|
|
106
|
+
enterprise: { limit: 1e4, window: "month" },
|
|
107
|
+
// Finite, deliberately. This was `Infinity` when the tier was introduced in
|
|
108
|
+
// #762, and Infinity breaks in two ways that both hide themselves:
|
|
109
|
+
//
|
|
110
|
+
// 1. JSON.stringify(Infinity) is "null". The quota endpoint returned
|
|
111
|
+
// {"limit":null,"remaining":null} and the console badge rendered
|
|
112
|
+
// "1 of null used". That is the whole reason this was investigated.
|
|
113
|
+
// 2. An infinite allowance is unmetered Bedrock. A staff account could
|
|
114
|
+
// spend without bound and nothing would refuse it or show a number.
|
|
115
|
+
//
|
|
116
|
+
// 100,000 is roughly $580 of Haiku at the measured $0.0058/call. Nobody on
|
|
117
|
+
// staff will reach it by using the product, and a runaway loop stops.
|
|
118
|
+
superadmin: { limit: 1e5, window: "month" }
|
|
119
|
+
};
|
|
120
|
+
for (const [tier, policy] of Object.entries(AI_TIER_POLICY)) {
|
|
121
|
+
if (!Number.isFinite(policy.limit) || policy.limit <= 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`AI_TIER_POLICY.${tier}.limit must be a positive finite number, got ${policy.limit}. A non-finite limit serialises to null in JSON and leaves spend unmetered.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var PRELUDE_BYTES = 12;
|
|
128
|
+
var MESSAGE_CRC_BYTES = 4;
|
|
129
|
+
var MIN_FRAME_BYTES = PRELUDE_BYTES + MESSAGE_CRC_BYTES;
|
|
130
|
+
var MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
131
|
+
var MAX_ENTRIES = 16;
|
|
132
|
+
var MAX_KEY_LEN = 256;
|
|
133
|
+
var MAX_VALUE_LEN = 256;
|
|
134
|
+
var ALLOWED = /[^a-zA-Z0-9\s:_@$#=/+,\-.]/g;
|
|
135
|
+
function clean(value, max) {
|
|
136
|
+
return value.replace(ALLOWED, "").slice(0, max);
|
|
137
|
+
}
|
|
138
|
+
var REQUEST_METADATA_HEADER = "X-Amzn-Bedrock-Request-Metadata";
|
|
139
|
+
function requestMetadata(attr2) {
|
|
140
|
+
const out = {};
|
|
141
|
+
const pairs = [
|
|
142
|
+
["product", "carrier"],
|
|
143
|
+
["org", attr2.orgId],
|
|
144
|
+
["subaccount", attr2.subAccountId],
|
|
145
|
+
["surface", attr2.surface],
|
|
146
|
+
["tier", attr2.tier]
|
|
147
|
+
];
|
|
148
|
+
for (const [k, v] of pairs) {
|
|
149
|
+
if (v === void 0 || v === "") continue;
|
|
150
|
+
const key = clean(k, MAX_KEY_LEN);
|
|
151
|
+
const value = clean(v, MAX_VALUE_LEN);
|
|
152
|
+
if (key && value && Object.keys(out).length < MAX_ENTRIES) out[key] = value;
|
|
153
|
+
}
|
|
154
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
155
|
+
}
|
|
156
|
+
var ZERO_USAGE = {
|
|
157
|
+
inputTokens: 0,
|
|
158
|
+
outputTokens: 0,
|
|
159
|
+
cacheReadTokens: 0,
|
|
160
|
+
cacheWriteTokens: 0
|
|
161
|
+
};
|
|
162
|
+
function extractUsage(payload) {
|
|
163
|
+
if (!payload || typeof payload !== "object") return ZERO_USAGE;
|
|
164
|
+
const root = payload;
|
|
165
|
+
const holder = root.usage ?? root.message?.usage;
|
|
166
|
+
if (!holder) return ZERO_USAGE;
|
|
167
|
+
const num = (v) => {
|
|
168
|
+
const n = Number(v);
|
|
169
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
inputTokens: num(holder.input_tokens),
|
|
173
|
+
outputTokens: num(holder.output_tokens),
|
|
174
|
+
cacheReadTokens: num(holder.cache_read_input_tokens),
|
|
175
|
+
cacheWriteTokens: num(holder.cache_creation_input_tokens)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
var HAIKU_RATES = {
|
|
179
|
+
inputPerMTok: 1,
|
|
180
|
+
outputPerMTok: 5,
|
|
181
|
+
cacheReadPerMTok: 0.1,
|
|
182
|
+
cacheWritePerMTok: 1.25,
|
|
183
|
+
asOf: "2026-08-22"
|
|
184
|
+
};
|
|
185
|
+
function estimateCostUsd(u, rates = HAIKU_RATES) {
|
|
186
|
+
const perTok = (mtok) => mtok / 1e6;
|
|
187
|
+
return u.inputTokens * perTok(rates.inputPerMTok) + u.outputTokens * perTok(rates.outputPerMTok) + u.cacheReadTokens * perTok(rates.cacheReadPerMTok) + u.cacheWriteTokens * perTok(rates.cacheWritePerMTok);
|
|
188
|
+
}
|
|
189
|
+
var DEFAULT_BEDROCK_REGION = "us-east-1";
|
|
190
|
+
var DEFAULT_MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0";
|
|
191
|
+
var BEDROCK_TIMEOUT_MS = 3e4;
|
|
192
|
+
var BedrockRateLimitError = class extends Error {
|
|
193
|
+
isRateLimit = true;
|
|
194
|
+
/**
|
|
195
|
+
* The raw `retry-after` header, when Bedrock sent one. Callers surface it as
|
|
196
|
+
* the delay they tell the user to wait; without it they have to guess.
|
|
197
|
+
*/
|
|
198
|
+
retryAfter;
|
|
199
|
+
constructor(message, retryAfter = null) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.name = "BedrockRateLimitError";
|
|
202
|
+
this.retryAfter = retryAfter;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var BedrockError = class extends Error {
|
|
206
|
+
constructor(message, status) {
|
|
207
|
+
super(message);
|
|
208
|
+
this.status = status;
|
|
209
|
+
this.name = "BedrockError";
|
|
210
|
+
}
|
|
211
|
+
status;
|
|
212
|
+
};
|
|
213
|
+
function client(creds) {
|
|
214
|
+
const region = creds.region ?? DEFAULT_BEDROCK_REGION;
|
|
215
|
+
return {
|
|
216
|
+
aws: new AwsClient({
|
|
217
|
+
accessKeyId: creds.accessKeyId,
|
|
218
|
+
secretAccessKey: creds.secretAccessKey,
|
|
219
|
+
...creds.sessionToken ? { sessionToken: creds.sessionToken } : {},
|
|
220
|
+
region,
|
|
221
|
+
service: "bedrock"
|
|
222
|
+
}),
|
|
223
|
+
region,
|
|
224
|
+
modelId: creds.modelId ?? DEFAULT_MODEL_ID
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function body(req) {
|
|
228
|
+
return {
|
|
229
|
+
anthropic_version: "bedrock-2023-05-31",
|
|
230
|
+
max_tokens: req.maxTokens,
|
|
231
|
+
...req.temperature !== void 0 ? { temperature: req.temperature } : {},
|
|
232
|
+
system: [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }],
|
|
233
|
+
messages: req.messages.map((m) => ({ role: m.role, content: m.content })),
|
|
234
|
+
...req.tools ? { tools: req.tools, tool_choice: { type: "any" } } : {}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function headersFor(req, accept) {
|
|
238
|
+
const headers = {
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
Accept: accept
|
|
241
|
+
};
|
|
242
|
+
const meta = req.attribution ? requestMetadata(req.attribution) : null;
|
|
243
|
+
if (meta) headers[REQUEST_METADATA_HEADER] = JSON.stringify(meta);
|
|
244
|
+
return headers;
|
|
245
|
+
}
|
|
246
|
+
function reportUsage(req, usage) {
|
|
247
|
+
if (!req.onUsage) return;
|
|
248
|
+
try {
|
|
249
|
+
req.onUsage(usage);
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function fail(resp) {
|
|
254
|
+
const text = await resp.text().catch(() => "");
|
|
255
|
+
if (resp.status === 429 || /throttl/i.test(text)) {
|
|
256
|
+
throw new BedrockRateLimitError(
|
|
257
|
+
text || "Bedrock throttled the request",
|
|
258
|
+
resp.headers.get("retry-after")
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
throw new BedrockError(text || `Bedrock returned HTTP ${resp.status}`, resp.status);
|
|
262
|
+
}
|
|
263
|
+
async function invokeTool(creds, req, signal) {
|
|
264
|
+
const { aws, region, modelId } = client(creds);
|
|
265
|
+
const controller = new AbortController();
|
|
266
|
+
const timer = setTimeout(() => controller.abort(), req.timeoutMs ?? BEDROCK_TIMEOUT_MS);
|
|
267
|
+
const onAbort = () => controller.abort();
|
|
268
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
269
|
+
try {
|
|
270
|
+
const headers = headersFor(req, "application/json");
|
|
271
|
+
const resp = await aws.fetch(
|
|
272
|
+
`https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`,
|
|
273
|
+
{
|
|
274
|
+
method: "POST",
|
|
275
|
+
headers,
|
|
276
|
+
body: JSON.stringify(body(req)),
|
|
277
|
+
signal: controller.signal
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
if (!resp.ok) await fail(resp);
|
|
281
|
+
const json = await resp.json();
|
|
282
|
+
reportUsage(req, extractUsage(json));
|
|
283
|
+
const block = json.content?.find((b) => b.type === "tool_use");
|
|
284
|
+
return block?.name ? { name: block.name, input: block.input ?? {} } : null;
|
|
285
|
+
} finally {
|
|
286
|
+
clearTimeout(timer);
|
|
287
|
+
signal?.removeEventListener("abort", onAbort);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
101
291
|
// src/billing.ts
|
|
102
292
|
var TIER_CALL_LIMITS = {
|
|
103
|
-
free:
|
|
104
|
-
pro:
|
|
105
|
-
enterprise: Infinity
|
|
293
|
+
free: 1e3,
|
|
294
|
+
pro: Infinity,
|
|
295
|
+
enterprise: Infinity,
|
|
296
|
+
superadmin: Infinity
|
|
106
297
|
};
|
|
298
|
+
var CARRIER_ASK_PRO_MONTHLY_LIMIT = AI_TIER_POLICY.pro.limit;
|
|
299
|
+
var CARRIER_ASK_FREE_DAILY_LIMIT = AI_TIER_POLICY.free.limit;
|
|
107
300
|
var UPGRADE_URL = "https://mcp.carrier.llc/upgrade";
|
|
108
|
-
function
|
|
109
|
-
const raw = env.
|
|
110
|
-
|
|
301
|
+
function platformOrgId(env) {
|
|
302
|
+
const raw = env.CARRIER_PLATFORM_ORG_ID;
|
|
303
|
+
if (typeof raw !== "string") return null;
|
|
304
|
+
const trimmed = raw.trim();
|
|
305
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
306
|
+
}
|
|
307
|
+
function isPlatformOperator(env, orgId) {
|
|
308
|
+
const platform = platformOrgId(env);
|
|
309
|
+
if (!platform) return false;
|
|
310
|
+
return Boolean(orgId) && orgId === platform;
|
|
111
311
|
}
|
|
112
312
|
async function checkCallQuota(env, sub, tier) {
|
|
113
313
|
const limit = TIER_CALL_LIMITS[tier];
|
|
114
314
|
const resetAt = firstDayNextMonth();
|
|
115
|
-
if (tier === "enterprise") {
|
|
315
|
+
if (tier === "pro" || tier === "enterprise" || tier === "superadmin") {
|
|
116
316
|
return { allowed: true, remaining: Infinity, resetAt, tier };
|
|
117
317
|
}
|
|
118
318
|
if (!env.CARRIER_USERS) {
|
|
@@ -133,6 +333,7 @@ async function checkCallQuota(env, sub, tier) {
|
|
|
133
333
|
function recordUsage(env, sub, tier) {
|
|
134
334
|
if (!env.CARRIER_USERS) return;
|
|
135
335
|
(async () => {
|
|
336
|
+
if (tier === "pro" || tier === "enterprise" || tier === "superadmin") return;
|
|
136
337
|
const month = currentMonth();
|
|
137
338
|
const usageKey = `usage:${sub}:${month}`;
|
|
138
339
|
const raw = await env.CARRIER_USERS.get(usageKey, "json").catch(() => null);
|
|
@@ -143,35 +344,8 @@ function recordUsage(env, sub, tier) {
|
|
|
143
344
|
JSON.stringify({ calls: next, updated_at: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
144
345
|
{ expirationTtl: 35 * 24 * 60 * 60 }
|
|
145
346
|
);
|
|
146
|
-
if (tier === "pro" && next % 100 === 0 && getBillingPrimary(env) === "stripe") {
|
|
147
|
-
await pushStripeUsageRecord(env, sub, 100).catch(() => {
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
347
|
})();
|
|
151
348
|
}
|
|
152
|
-
async function pushStripeUsageRecord(env, sub, quantity) {
|
|
153
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
154
|
-
if (!stripeKey) return;
|
|
155
|
-
if (!env.CARRIER_USERS) return;
|
|
156
|
-
const subItemId = await env.CARRIER_USERS.get(`stripe_sub_item_id:${sub}`);
|
|
157
|
-
if (!subItemId) return;
|
|
158
|
-
const body = new URLSearchParams({
|
|
159
|
-
quantity: String(quantity),
|
|
160
|
-
timestamp: String(Math.floor(Date.now() / 1e3)),
|
|
161
|
-
action: "increment"
|
|
162
|
-
});
|
|
163
|
-
await fetch(
|
|
164
|
-
`https://api.stripe.com/v1/subscription_items/${subItemId}/usage_records`,
|
|
165
|
-
{
|
|
166
|
-
method: "POST",
|
|
167
|
-
headers: {
|
|
168
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
169
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
170
|
-
},
|
|
171
|
-
body: body.toString()
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
349
|
function currentMonth() {
|
|
176
350
|
const now = /* @__PURE__ */ new Date();
|
|
177
351
|
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
@@ -520,8 +694,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
520
694
|
{
|
|
521
695
|
type: "text",
|
|
522
696
|
text: [
|
|
523
|
-
`Quota exceeded: your ${quota.tier} plan has reached the
|
|
524
|
-
`
|
|
697
|
+
`Quota exceeded: your ${quota.tier} plan has reached the monthly limit of ${TIER_CALL_LIMITS.free.toLocaleString()} regular tool calls.`,
|
|
698
|
+
`Resets ${quota.resetAt}.`,
|
|
525
699
|
`Upgrade to Pro for unlimited calls at ${UPGRADE_URL}`
|
|
526
700
|
].join(" ")
|
|
527
701
|
}
|
|
@@ -589,8 +763,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
589
763
|
};
|
|
590
764
|
}
|
|
591
765
|
async function ocsCall(env, token2, method, params = {}) {
|
|
592
|
-
const
|
|
593
|
-
const result2 = await
|
|
766
|
+
const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
767
|
+
const result2 = await client2.call(method, params);
|
|
594
768
|
return {
|
|
595
769
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
596
770
|
};
|
|
@@ -598,14 +772,14 @@ async function ocsCall(env, token2, method, params = {}) {
|
|
|
598
772
|
async function resolveSubscriberByIccid(env, token2, iccid, cache) {
|
|
599
773
|
const hit = cache.get(iccid);
|
|
600
774
|
if (hit) return hit;
|
|
601
|
-
const
|
|
602
|
-
const record = await
|
|
775
|
+
const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
776
|
+
const record = await client2.call("getSingleSubscriber", { iccid });
|
|
603
777
|
cache.set(iccid, record);
|
|
604
778
|
return record;
|
|
605
779
|
}
|
|
606
780
|
async function getDefaultResellerId(env, token2) {
|
|
607
|
-
const
|
|
608
|
-
const info = await
|
|
781
|
+
const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
782
|
+
const info = await client2.call("getResellerInfo", {});
|
|
609
783
|
const id = info?.id;
|
|
610
784
|
if (typeof id !== "number") {
|
|
611
785
|
throw new Error("Could not determine resellerId from getResellerInfo");
|
|
@@ -809,8 +983,8 @@ function registerAllTools(server2, ctx) {
|
|
|
809
983
|
ctx,
|
|
810
984
|
async (args, token2) => {
|
|
811
985
|
const params = buildListSubscriberParams(args);
|
|
812
|
-
const
|
|
813
|
-
const raw = await
|
|
986
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
987
|
+
const raw = await client2.call("listSubscriber", params);
|
|
814
988
|
const payload = applySubscriberFilters(raw, args);
|
|
815
989
|
return {
|
|
816
990
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
|
|
@@ -3286,8 +3460,8 @@ var ocsAppMethods = ocsSpec.v1_app_methods;
|
|
|
3286
3460
|
// src/intelligence.ts
|
|
3287
3461
|
async function safeCall(env, token2, method, params = {}) {
|
|
3288
3462
|
try {
|
|
3289
|
-
const
|
|
3290
|
-
const data = await
|
|
3463
|
+
const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
3464
|
+
const data = await client2.call(method, params);
|
|
3291
3465
|
return { data, error: null };
|
|
3292
3466
|
} catch (err7) {
|
|
3293
3467
|
return { data: null, error: err7 instanceof Error ? err7.message : String(err7) };
|
|
@@ -4304,8 +4478,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4304
4478
|
ctx,
|
|
4305
4479
|
async ({ iccid, phone_number, phone_type }, token2) => {
|
|
4306
4480
|
const ocsMethod = phone_type === "fake" ? "affectSubscriberFakePhoneNumber" : "affectSubscriberRealPhoneNumber";
|
|
4307
|
-
const
|
|
4308
|
-
const result2 = await
|
|
4481
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4482
|
+
const result2 = await client2.call(ocsMethod, { subscriber: iccid, phoneNumber: phone_number });
|
|
4309
4483
|
return {
|
|
4310
4484
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4311
4485
|
};
|
|
@@ -4342,11 +4516,11 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4342
4516
|
cell_id,
|
|
4343
4517
|
signal_strength
|
|
4344
4518
|
}, token2) => {
|
|
4345
|
-
const
|
|
4519
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4346
4520
|
const params = { radioType: radio_type, mcc, mnc, lac };
|
|
4347
4521
|
if (cell_id !== void 0) params.cellId = cell_id;
|
|
4348
4522
|
if (signal_strength !== void 0) params.signalStrength = signal_strength;
|
|
4349
|
-
const result2 = await
|
|
4523
|
+
const result2 = await client2.call("getSubscriberLocationByCellId", params);
|
|
4350
4524
|
return {
|
|
4351
4525
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4352
4526
|
};
|
|
@@ -4370,16 +4544,16 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4370
4544
|
ctx,
|
|
4371
4545
|
async ({ resellerId }, token2) => {
|
|
4372
4546
|
const id = resellerId ?? await (async () => {
|
|
4373
|
-
const
|
|
4374
|
-
const info = await
|
|
4547
|
+
const client3 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4548
|
+
const info = await client3.call("getResellerInfo", {});
|
|
4375
4549
|
const resolvedId = info?.id;
|
|
4376
4550
|
if (typeof resolvedId !== "number") {
|
|
4377
4551
|
throw new Error("Could not determine resellerId from getResellerInfo");
|
|
4378
4552
|
}
|
|
4379
4553
|
return resolvedId;
|
|
4380
4554
|
})();
|
|
4381
|
-
const
|
|
4382
|
-
const result2 = await
|
|
4555
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4556
|
+
const result2 = await client2.call("listDetailedDestinationList", id);
|
|
4383
4557
|
return {
|
|
4384
4558
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4385
4559
|
};
|
|
@@ -4404,8 +4578,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4404
4578
|
BACKLOG_TOOL_SCOPES["modify_subscriber_mobile_plan"],
|
|
4405
4579
|
ctx,
|
|
4406
4580
|
async ({ iccid, mobile_plan_id }, token2) => {
|
|
4407
|
-
const
|
|
4408
|
-
const result2 = await
|
|
4581
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4582
|
+
const result2 = await client2.call(
|
|
4409
4583
|
"modifySubscriberMobilePlan",
|
|
4410
4584
|
{ subscriber: iccid, mobilePlanId: mobile_plan_id }
|
|
4411
4585
|
);
|
|
@@ -4438,8 +4612,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4438
4612
|
const params = { subscriber: iccid, packageId: package_id };
|
|
4439
4613
|
if (start_date !== void 0) params.startDate = start_date;
|
|
4440
4614
|
if (end_date !== void 0) params.endDate = end_date;
|
|
4441
|
-
const
|
|
4442
|
-
const result2 = await
|
|
4615
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4616
|
+
const result2 = await client2.call(
|
|
4443
4617
|
"modifySubscriberPrepaidPackageActivePeriod",
|
|
4444
4618
|
params
|
|
4445
4619
|
);
|
|
@@ -4467,8 +4641,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4467
4641
|
BACKLOG_TOOL_SCOPES["modify_subscriber_voip_plan"],
|
|
4468
4642
|
ctx,
|
|
4469
4643
|
async ({ iccid, voip_plan_id }, token2) => {
|
|
4470
|
-
const
|
|
4471
|
-
const result2 = await
|
|
4644
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4645
|
+
const result2 = await client2.call(
|
|
4472
4646
|
"modifySubscriberVoipPlan",
|
|
4473
4647
|
{ subscriber: iccid, voipPlanId: voip_plan_id }
|
|
4474
4648
|
);
|
|
@@ -4498,8 +4672,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4498
4672
|
const cache = /* @__PURE__ */ new Map();
|
|
4499
4673
|
const sub = await resolveSubscriberByIccid(ctx.env, token2, iccid, cache);
|
|
4500
4674
|
const subscriberId = sub.id ?? sub.subscriberId ?? iccid;
|
|
4501
|
-
const
|
|
4502
|
-
const result2 = await
|
|
4675
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4676
|
+
const result2 = await client2.call(
|
|
4503
4677
|
"pushSteeringToSubs",
|
|
4504
4678
|
{ subscriber: subscriberId }
|
|
4505
4679
|
);
|
|
@@ -4526,8 +4700,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4526
4700
|
BACKLOG_TOOL_SCOPES["reset_subscriber_gz_counter"],
|
|
4527
4701
|
ctx,
|
|
4528
4702
|
async ({ iccid }, token2) => {
|
|
4529
|
-
const
|
|
4530
|
-
const result2 = await
|
|
4703
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4704
|
+
const result2 = await client2.call("resetSubsGzCounter", { subscriber: iccid });
|
|
4531
4705
|
return {
|
|
4532
4706
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4533
4707
|
};
|
|
@@ -4550,14 +4724,14 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4550
4724
|
BACKLOG_TOOL_SCOPES["carrier_webhook_config"],
|
|
4551
4725
|
ctx,
|
|
4552
4726
|
async ({ reseller_id }, token2) => {
|
|
4553
|
-
const
|
|
4727
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4554
4728
|
let resellerId = reseller_id;
|
|
4555
4729
|
if (resellerId === void 0) {
|
|
4556
4730
|
resellerId = await getDefaultResellerId(ctx.env, token2);
|
|
4557
4731
|
}
|
|
4558
4732
|
const params = {};
|
|
4559
4733
|
if (resellerId !== void 0) params.id = resellerId;
|
|
4560
|
-
const raw = await
|
|
4734
|
+
const raw = await client2.call("getResellerInfo", params);
|
|
4561
4735
|
const traffic = raw.trafficInfo ?? {};
|
|
4562
4736
|
const notification = raw.notificationInfo ?? {};
|
|
4563
4737
|
const result2 = {
|
|
@@ -4592,12 +4766,12 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4592
4766
|
BACKLOG_TOOL_SCOPES["list_subscriber_voip_tariff"],
|
|
4593
4767
|
ctx,
|
|
4594
4768
|
async ({ reseller_id }, token2) => {
|
|
4595
|
-
const
|
|
4769
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4596
4770
|
const params = {};
|
|
4597
4771
|
if (reseller_id !== void 0) {
|
|
4598
4772
|
params.resellerId = reseller_id;
|
|
4599
4773
|
}
|
|
4600
|
-
const result2 = await
|
|
4774
|
+
const result2 = await client2.call("listSubscriberVoipTariff", params);
|
|
4601
4775
|
return {
|
|
4602
4776
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4603
4777
|
};
|
|
@@ -4622,8 +4796,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4622
4796
|
BACKLOG_TOOL_SCOPES["list_voip_tariff_rule"],
|
|
4623
4797
|
ctx,
|
|
4624
4798
|
async ({ voip_plan_id }, token2) => {
|
|
4625
|
-
const
|
|
4626
|
-
const result2 = await
|
|
4799
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4800
|
+
const result2 = await client2.call("listVoipTariffRule", voip_plan_id);
|
|
4627
4801
|
return {
|
|
4628
4802
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
4629
4803
|
};
|
|
@@ -4651,8 +4825,8 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4651
4825
|
location_zone_id,
|
|
4652
4826
|
network_profile_id
|
|
4653
4827
|
}, token2) => {
|
|
4654
|
-
const
|
|
4655
|
-
const result2 = await
|
|
4828
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
4829
|
+
const result2 = await client2.call("changeNetworkProfileOfLocationZone", {
|
|
4656
4830
|
locationZoneId: location_zone_id,
|
|
4657
4831
|
networkProfileId: network_profile_id
|
|
4658
4832
|
});
|
|
@@ -4665,7 +4839,6 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4665
4839
|
}
|
|
4666
4840
|
|
|
4667
4841
|
// src/tools-carrier-ask.ts
|
|
4668
|
-
import { AwsClient } from "aws4fetch";
|
|
4669
4842
|
import { z as z11 } from "zod";
|
|
4670
4843
|
|
|
4671
4844
|
// src/tools-ui-agent.ts
|
|
@@ -4711,7 +4884,7 @@ var MANUS_API_BASE = "https://api.manus.ai/v2";
|
|
|
4711
4884
|
|
|
4712
4885
|
// src/tools-ui-agent.ts
|
|
4713
4886
|
async function createManusTask(apiKey, prompt, title, outputSchema) {
|
|
4714
|
-
const
|
|
4887
|
+
const body2 = {
|
|
4715
4888
|
message: { content: prompt },
|
|
4716
4889
|
agent_profile: "manus-1.6-lite",
|
|
4717
4890
|
hide_in_task_list: true,
|
|
@@ -4719,7 +4892,7 @@ async function createManusTask(apiKey, prompt, title, outputSchema) {
|
|
|
4719
4892
|
title
|
|
4720
4893
|
};
|
|
4721
4894
|
if (outputSchema) {
|
|
4722
|
-
|
|
4895
|
+
body2.structured_output_schema = outputSchema;
|
|
4723
4896
|
}
|
|
4724
4897
|
const res = await fetch(`${MANUS_API_BASE}/task.create`, {
|
|
4725
4898
|
method: "POST",
|
|
@@ -4727,7 +4900,7 @@ async function createManusTask(apiKey, prompt, title, outputSchema) {
|
|
|
4727
4900
|
"x-manus-api-key": apiKey,
|
|
4728
4901
|
"Content-Type": "application/json"
|
|
4729
4902
|
},
|
|
4730
|
-
body: JSON.stringify(
|
|
4903
|
+
body: JSON.stringify(body2)
|
|
4731
4904
|
});
|
|
4732
4905
|
return await res.json();
|
|
4733
4906
|
}
|
|
@@ -4932,6 +5105,7 @@ var UI_AGENT_TOOL_SCOPES = {
|
|
|
4932
5105
|
ui_create_steering_list: "write",
|
|
4933
5106
|
ui_build_steering_list: "write",
|
|
4934
5107
|
ui_set_account_steering_list: "write",
|
|
5108
|
+
ui_request_reseller_relay_change: "write",
|
|
4935
5109
|
ui_create_account: "admin",
|
|
4936
5110
|
ui_create_destination_list: "write",
|
|
4937
5111
|
ui_edit_destination_list: "write",
|
|
@@ -5017,6 +5191,66 @@ Open account ID ${args.account_id}.
|
|
|
5017
5191
|
`
|
|
5018
5192
|
)
|
|
5019
5193
|
);
|
|
5194
|
+
server2.registerTool(
|
|
5195
|
+
"ui_request_reseller_relay_change",
|
|
5196
|
+
{
|
|
5197
|
+
title: "Request Reseller Relay Change",
|
|
5198
|
+
description: "Drafts a parent-reseller/support request for Relay Gy, calls/SMS relay, or VoIP relay when those portal controls are read-only at the current reseller tier. Does not send the request or mutate the portal.",
|
|
5199
|
+
inputSchema: {
|
|
5200
|
+
reseller_id: z4.number().optional().describe("Reseller id; defaults to the authenticated reseller"),
|
|
5201
|
+
relay_gy: z4.boolean().optional().describe("Desired Relay Gy state"),
|
|
5202
|
+
relay_calls_sms: z4.boolean().optional().describe("Desired calls/SMS relay state"),
|
|
5203
|
+
relay_voip: z4.boolean().optional().describe("Desired VoIP relay state"),
|
|
5204
|
+
reason: z4.string().optional().describe("Operational reason for the requested change")
|
|
5205
|
+
}
|
|
5206
|
+
},
|
|
5207
|
+
async (args) => {
|
|
5208
|
+
if (!ctx.props.scope.includes("write")) {
|
|
5209
|
+
return {
|
|
5210
|
+
isError: true,
|
|
5211
|
+
content: [{ type: "text", text: "Scope denied: tool 'ui_request_reseller_relay_change' requires 'write' scope." }]
|
|
5212
|
+
};
|
|
5213
|
+
}
|
|
5214
|
+
const resellerId = args.reseller_id ?? ctx.props.reseller_id;
|
|
5215
|
+
const requested = [
|
|
5216
|
+
args.relay_gy === void 0 ? null : `Relay Gy: ${args.relay_gy ? "enable" : "disable"}`,
|
|
5217
|
+
args.relay_calls_sms === void 0 ? null : `Calls/SMS relay: ${args.relay_calls_sms ? "enable" : "disable"}`,
|
|
5218
|
+
args.relay_voip === void 0 ? null : `VoIP relay: ${args.relay_voip ? "enable" : "disable"}`
|
|
5219
|
+
].filter((value) => value !== null);
|
|
5220
|
+
if (requested.length === 0) {
|
|
5221
|
+
return {
|
|
5222
|
+
isError: true,
|
|
5223
|
+
content: [{ type: "text", text: JSON.stringify({ error: "relay_state_required", message: "Choose at least one relay flag and desired state." }) }]
|
|
5224
|
+
};
|
|
5225
|
+
}
|
|
5226
|
+
const draft = [
|
|
5227
|
+
`Please update the parent-controlled OCS relay settings for reseller ${resellerId}.`,
|
|
5228
|
+
"",
|
|
5229
|
+
...requested.map((line) => `- ${line}`),
|
|
5230
|
+
...args.reason ? ["", `Reason: ${args.reason}`] : [],
|
|
5231
|
+
"",
|
|
5232
|
+
"Please confirm once the change is active so we can verify the dependent OCS operation."
|
|
5233
|
+
].join("\n");
|
|
5234
|
+
ctx.audit({
|
|
5235
|
+
tool_name: "ui_request_reseller_relay_change",
|
|
5236
|
+
ocs_method: "[ui-agent:G-20]",
|
|
5237
|
+
status: "parent_request_drafted",
|
|
5238
|
+
dry_run: true,
|
|
5239
|
+
duration_ms: 0,
|
|
5240
|
+
event_type: "ui_agent_dispatch"
|
|
5241
|
+
});
|
|
5242
|
+
return {
|
|
5243
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
5244
|
+
status: "parent_request_drafted",
|
|
5245
|
+
capability: "parent_controlled",
|
|
5246
|
+
reseller_id: resellerId,
|
|
5247
|
+
parent_reseller: resellerId === 1170 ? "Bridge4IP" : "parent reseller/support",
|
|
5248
|
+
draft,
|
|
5249
|
+
sent: false
|
|
5250
|
+
}, null, 2) }]
|
|
5251
|
+
};
|
|
5252
|
+
}
|
|
5253
|
+
);
|
|
5020
5254
|
server2.registerTool(
|
|
5021
5255
|
"ui_create_account",
|
|
5022
5256
|
{
|
|
@@ -5252,14 +5486,14 @@ async function withFallback(fn, keys) {
|
|
|
5252
5486
|
true
|
|
5253
5487
|
);
|
|
5254
5488
|
}
|
|
5255
|
-
async function manusPost(path, apiKey,
|
|
5489
|
+
async function manusPost(path, apiKey, body2) {
|
|
5256
5490
|
const res = await fetch(`${MANUS_API_BASE2}/${path}`, {
|
|
5257
5491
|
method: "POST",
|
|
5258
5492
|
headers: {
|
|
5259
5493
|
"x-manus-api-key": apiKey,
|
|
5260
5494
|
"Content-Type": "application/json"
|
|
5261
5495
|
},
|
|
5262
|
-
body: JSON.stringify(
|
|
5496
|
+
body: JSON.stringify(body2)
|
|
5263
5497
|
});
|
|
5264
5498
|
const data = await res.json();
|
|
5265
5499
|
return { _httpStatus: res.status, data, key_used: "primary" };
|
|
@@ -5269,14 +5503,14 @@ async function sendMessage(keys, taskId, content, opts) {
|
|
|
5269
5503
|
if (opts?.connectors?.length) message.connectors = opts.connectors;
|
|
5270
5504
|
if (opts?.enableSkills?.length) message.enable_skills = opts.enableSkills;
|
|
5271
5505
|
if (opts?.forceSkills?.length) message.force_skills = opts.forceSkills;
|
|
5272
|
-
const
|
|
5506
|
+
const body2 = {
|
|
5273
5507
|
task_id: taskId,
|
|
5274
5508
|
message
|
|
5275
5509
|
};
|
|
5276
|
-
if (opts?.agentProfile)
|
|
5277
|
-
if (opts?.outputSchema)
|
|
5510
|
+
if (opts?.agentProfile) body2.agent_profile = opts.agentProfile;
|
|
5511
|
+
if (opts?.outputSchema) body2.structured_output_schema = opts.outputSchema;
|
|
5278
5512
|
return withFallback(
|
|
5279
|
-
(apiKey) => manusPost("task.sendMessage", apiKey,
|
|
5513
|
+
(apiKey) => manusPost("task.sendMessage", apiKey, body2),
|
|
5280
5514
|
keys
|
|
5281
5515
|
);
|
|
5282
5516
|
}
|
|
@@ -5601,7 +5835,7 @@ function validateCron(cron) {
|
|
|
5601
5835
|
return minuteFieldViolatesFiveMinuteRule(minuteField ?? "", cron);
|
|
5602
5836
|
}
|
|
5603
5837
|
async function createSchedule(apiKey, params) {
|
|
5604
|
-
const
|
|
5838
|
+
const body2 = {
|
|
5605
5839
|
name: params.name,
|
|
5606
5840
|
cron: params.cron,
|
|
5607
5841
|
prompt_template: params.prompt_template,
|
|
@@ -5615,7 +5849,7 @@ async function createSchedule(apiKey, params) {
|
|
|
5615
5849
|
"x-manus-api-key": apiKey,
|
|
5616
5850
|
"Content-Type": "application/json"
|
|
5617
5851
|
},
|
|
5618
|
-
body: JSON.stringify(
|
|
5852
|
+
body: JSON.stringify(body2)
|
|
5619
5853
|
});
|
|
5620
5854
|
if (!res.ok) {
|
|
5621
5855
|
throw new ManusScheduleError(
|
|
@@ -6245,19 +6479,33 @@ function registerScheduleAndUsageTools(server2, ctx) {
|
|
|
6245
6479
|
import { z as z7 } from "zod";
|
|
6246
6480
|
import * as Sentry2 from "@sentry/cloudflare";
|
|
6247
6481
|
|
|
6482
|
+
// src/audit-schema.ts
|
|
6483
|
+
var MARKER_INDEX = 11;
|
|
6484
|
+
var SCHEMA_GENERIC = "v1-generic";
|
|
6485
|
+
var SCHEMA_CARRIER_ASK = "v1-carrier-ask";
|
|
6486
|
+
function withSchemaMarker(blobs, marker) {
|
|
6487
|
+
const out = [...blobs];
|
|
6488
|
+
while (out.length < MARKER_INDEX) out.push("");
|
|
6489
|
+
out.push(marker);
|
|
6490
|
+
return out;
|
|
6491
|
+
}
|
|
6492
|
+
|
|
6248
6493
|
// src/audit.ts
|
|
6249
6494
|
function writeAudit(env, row) {
|
|
6250
6495
|
env.AUDIT_LOG.writeDataPoint({
|
|
6251
|
-
blobs:
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6496
|
+
blobs: withSchemaMarker(
|
|
6497
|
+
[
|
|
6498
|
+
row.tool_name,
|
|
6499
|
+
row.ocs_method,
|
|
6500
|
+
row.status,
|
|
6501
|
+
row.dry_run ? "1" : "0",
|
|
6502
|
+
row.sub,
|
|
6503
|
+
row.manus_task_id ?? "",
|
|
6504
|
+
row.manus_profile ?? "",
|
|
6505
|
+
row.manus_key_used ?? ""
|
|
6506
|
+
],
|
|
6507
|
+
SCHEMA_GENERIC
|
|
6508
|
+
),
|
|
6261
6509
|
doubles: [row.duration_ms, row.ocs_status_code ?? 0],
|
|
6262
6510
|
indexes: [String(row.reseller_id)]
|
|
6263
6511
|
});
|
|
@@ -6396,14 +6644,14 @@ function resolveConfig(env) {
|
|
|
6396
6644
|
async function atlasFetch(env, path, init) {
|
|
6397
6645
|
const { baseUrl: baseUrl2, key } = resolveConfig(env);
|
|
6398
6646
|
const headers = { "X-Carrier-Internal-Key": key };
|
|
6399
|
-
let
|
|
6647
|
+
let body2;
|
|
6400
6648
|
if (init.body !== void 0) {
|
|
6401
6649
|
headers["Content-Type"] = "application/json";
|
|
6402
|
-
|
|
6650
|
+
body2 = JSON.stringify(init.body);
|
|
6403
6651
|
}
|
|
6404
6652
|
let res;
|
|
6405
6653
|
try {
|
|
6406
|
-
res = await fetch(`${baseUrl2}${path}`, { method: init.method, headers, body });
|
|
6654
|
+
res = await fetch(`${baseUrl2}${path}`, { method: init.method, headers, body: body2 });
|
|
6407
6655
|
} catch (e) {
|
|
6408
6656
|
throw new WalletClientError(
|
|
6409
6657
|
`ATLAS wallet request failed: ${e instanceof Error ? e.message : "network error"}`,
|
|
@@ -6564,7 +6812,7 @@ async function runAutoTopup(env, args) {
|
|
|
6564
6812
|
return { charged: false, creditedCents: 0, reason: "no_payment_method" };
|
|
6565
6813
|
}
|
|
6566
6814
|
const bonusCents = bonusCentsFor(pack);
|
|
6567
|
-
const
|
|
6815
|
+
const body2 = new URLSearchParams({
|
|
6568
6816
|
amount: String(pack.eurCents),
|
|
6569
6817
|
currency: "eur",
|
|
6570
6818
|
customer: customerId,
|
|
@@ -6581,7 +6829,7 @@ async function runAutoTopup(env, args) {
|
|
|
6581
6829
|
const resp = await stripePostForm(
|
|
6582
6830
|
stripeKey,
|
|
6583
6831
|
"/v1/payment_intents",
|
|
6584
|
-
|
|
6832
|
+
body2
|
|
6585
6833
|
);
|
|
6586
6834
|
if (!resp.ok) {
|
|
6587
6835
|
return {
|
|
@@ -6855,6 +7103,13 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
6855
7103
|
annotations: annotationsFor("stripe_connect_status", "read")
|
|
6856
7104
|
},
|
|
6857
7105
|
async ({ operator_id }) => {
|
|
7106
|
+
if (operator_id !== void 0 && operator_id !== operatorId) {
|
|
7107
|
+
if (!isPlatformOperator(env, props2.org_id)) {
|
|
7108
|
+
return err2(
|
|
7109
|
+
"operator_id may only be overridden by Carrier's own organization. Omit it to read your own Stripe Connect status."
|
|
7110
|
+
);
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
6858
7113
|
const opId = operator_id ?? operatorId;
|
|
6859
7114
|
const opKvKey = operator_id ? `stripe_account:${operator_id}` : kvKey;
|
|
6860
7115
|
const start = Date.now();
|
|
@@ -7471,7 +7726,8 @@ function registerStorefrontDeployTools(server2) {
|
|
|
7471
7726
|
var TIER_CREDIT_ALLOTMENTS = {
|
|
7472
7727
|
free: 5e3,
|
|
7473
7728
|
pro: 1e5,
|
|
7474
|
-
enterprise: Infinity
|
|
7729
|
+
enterprise: Infinity,
|
|
7730
|
+
superadmin: Infinity
|
|
7475
7731
|
};
|
|
7476
7732
|
var DAILY_FREE_CREDITS = 50;
|
|
7477
7733
|
var SCOPE_CREDIT_COSTS = {
|
|
@@ -7494,7 +7750,7 @@ async function checkCredits(env, sub, tier, scope) {
|
|
|
7494
7750
|
const month = currentMonth2();
|
|
7495
7751
|
const today = currentDay();
|
|
7496
7752
|
const resetAt = firstDayNextMonth2();
|
|
7497
|
-
if (tier === "enterprise") {
|
|
7753
|
+
if (tier === "enterprise" || tier === "superadmin") {
|
|
7498
7754
|
return {
|
|
7499
7755
|
allowed: true,
|
|
7500
7756
|
credits_remaining: Infinity,
|
|
@@ -7534,7 +7790,7 @@ function deductCredits(env, sub, tier, scope) {
|
|
|
7534
7790
|
const cost = SCOPE_CREDIT_COSTS[scope];
|
|
7535
7791
|
const month = currentMonth2();
|
|
7536
7792
|
const today = currentDay();
|
|
7537
|
-
if (tier === "enterprise") return;
|
|
7793
|
+
if (tier === "enterprise" || tier === "superadmin") return;
|
|
7538
7794
|
const dailyFree = await getDailyFreeCredits(env, sub, today);
|
|
7539
7795
|
const dailyRemaining = dailyFree.granted - dailyFree.consumed;
|
|
7540
7796
|
if (dailyRemaining >= cost) {
|
|
@@ -7593,7 +7849,7 @@ async function triggerBillingThreshold(env, sub, amountCents) {
|
|
|
7593
7849
|
if (!stripeKey) return;
|
|
7594
7850
|
const customerId = await env.CARRIER_USERS.get(`stripe_customer_id:${sub}`);
|
|
7595
7851
|
if (!customerId) return;
|
|
7596
|
-
const
|
|
7852
|
+
const body2 = new URLSearchParams({
|
|
7597
7853
|
customer: customerId,
|
|
7598
7854
|
amount: String(Math.round(amountCents)),
|
|
7599
7855
|
currency: "usd",
|
|
@@ -7605,7 +7861,7 @@ async function triggerBillingThreshold(env, sub, amountCents) {
|
|
|
7605
7861
|
Authorization: `Bearer ${stripeKey}`,
|
|
7606
7862
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
7607
7863
|
},
|
|
7608
|
-
body:
|
|
7864
|
+
body: body2.toString()
|
|
7609
7865
|
});
|
|
7610
7866
|
if (!resp.ok) return;
|
|
7611
7867
|
const invoiceBody = new URLSearchParams({
|
|
@@ -7629,7 +7885,7 @@ async function pushOverageToStripe(env, sub, quantity, ratePerCredit) {
|
|
|
7629
7885
|
if (!stripeKey) return;
|
|
7630
7886
|
const subItemId = await env.CARRIER_USERS.get(`stripe_sub_item_id:${sub}`);
|
|
7631
7887
|
if (!subItemId) return;
|
|
7632
|
-
const
|
|
7888
|
+
const body2 = new URLSearchParams({
|
|
7633
7889
|
quantity: String(quantity),
|
|
7634
7890
|
timestamp: String(Math.floor(Date.now() / 1e3)),
|
|
7635
7891
|
action: "increment"
|
|
@@ -7642,7 +7898,7 @@ async function pushOverageToStripe(env, sub, quantity, ratePerCredit) {
|
|
|
7642
7898
|
Authorization: `Bearer ${stripeKey}`,
|
|
7643
7899
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
7644
7900
|
},
|
|
7645
|
-
body:
|
|
7901
|
+
body: body2.toString()
|
|
7646
7902
|
}
|
|
7647
7903
|
);
|
|
7648
7904
|
await env.CARRIER_USERS.put(
|
|
@@ -7740,7 +7996,7 @@ async function resolveUserTier(env, sub) {
|
|
|
7740
7996
|
const raw = await env.CARRIER_USERS.get(`user:${sub}`, "json").catch(() => null);
|
|
7741
7997
|
if (raw && typeof raw === "object" && "tier" in raw) {
|
|
7742
7998
|
const t = raw.tier;
|
|
7743
|
-
if (t === "pro" || t === "enterprise") return t;
|
|
7999
|
+
if (t === "pro" || t === "enterprise" || t === "superadmin") return t;
|
|
7744
8000
|
}
|
|
7745
8001
|
return "free";
|
|
7746
8002
|
}
|
|
@@ -7790,6 +8046,14 @@ var DEFAULT_THRESHOLD_CONFIGS = {
|
|
|
7790
8046
|
auto_pause_on_cap: false,
|
|
7791
8047
|
notification_email: null,
|
|
7792
8048
|
webhook_url: null
|
|
8049
|
+
},
|
|
8050
|
+
superadmin: {
|
|
8051
|
+
notification_thresholds: [],
|
|
8052
|
+
hard_cap_cents: 0,
|
|
8053
|
+
invoice_threshold_cents: 0,
|
|
8054
|
+
auto_pause_on_cap: false,
|
|
8055
|
+
notification_email: null,
|
|
8056
|
+
webhook_url: null
|
|
7793
8057
|
}
|
|
7794
8058
|
};
|
|
7795
8059
|
async function getThresholdConfig(env, sub, tier) {
|
|
@@ -7812,7 +8076,7 @@ async function resolveUserTier2(env, sub) {
|
|
|
7812
8076
|
const raw = await env.CARRIER_USERS.get(`user:${sub}`, "json").catch(() => null);
|
|
7813
8077
|
if (raw && typeof raw === "object" && "tier" in raw) {
|
|
7814
8078
|
const t = raw.tier;
|
|
7815
|
-
if (t === "pro" || t === "enterprise") return t;
|
|
8079
|
+
if (t === "pro" || t === "enterprise" || t === "superadmin") return t;
|
|
7816
8080
|
}
|
|
7817
8081
|
return "free";
|
|
7818
8082
|
}
|
|
@@ -9316,37 +9580,9 @@ function routerTools() {
|
|
|
9316
9580
|
var ROUTER_SYSTEM_PROMPT = `You are a carrier fleet operations router. Your job is to map a user's natural-language intent to exactly ONE tool from the Carrier MCP tool registry.
|
|
9317
9581
|
|
|
9318
9582
|
${ROUTER_RULES}`;
|
|
9319
|
-
var
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
const region = env.AWS_REGION ?? DEFAULT_REGION;
|
|
9323
|
-
const modelId = env.BEDROCK_MODEL_ID ?? DEFAULT_MODEL_ID;
|
|
9324
|
-
const aws = new AwsClient({
|
|
9325
|
-
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
|
9326
|
-
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
|
|
9327
|
-
region,
|
|
9328
|
-
service: "bedrock"
|
|
9329
|
-
});
|
|
9330
|
-
const url = `https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`;
|
|
9331
|
-
const resp = await aws.fetch(url, {
|
|
9332
|
-
method: "POST",
|
|
9333
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
9334
|
-
body: JSON.stringify(payload)
|
|
9335
|
-
});
|
|
9336
|
-
if (!resp.ok) {
|
|
9337
|
-
const body = await resp.text();
|
|
9338
|
-
if (resp.status === 429) {
|
|
9339
|
-
const retryAfter = resp.headers.get("retry-after");
|
|
9340
|
-
const err7 = new Error(`Bedrock rate limit: ${body}`);
|
|
9341
|
-
err7.isRateLimit = true;
|
|
9342
|
-
err7.retryAfter = retryAfter;
|
|
9343
|
-
throw err7;
|
|
9344
|
-
}
|
|
9345
|
-
throw new Error(`Bedrock invoke failed: ${resp.status} ${body}`);
|
|
9346
|
-
}
|
|
9347
|
-
return await resp.json();
|
|
9348
|
-
}
|
|
9349
|
-
async function _routeIntent(intent, context, env) {
|
|
9583
|
+
var ROUTER_TIMEOUT_MS = 12e3;
|
|
9584
|
+
async function _routeIntent(intent, context, env, opts = {}) {
|
|
9585
|
+
const { attribution, onUsage } = opts;
|
|
9350
9586
|
if (env.CARRIER_ASK_ENABLED !== "true" || !env.AWS_ACCESS_KEY_ID || !env.AWS_SECRET_ACCESS_KEY) {
|
|
9351
9587
|
return {
|
|
9352
9588
|
match: "routing_pending",
|
|
@@ -9362,26 +9598,32 @@ async function _routeIntent(intent, context, env) {
|
|
|
9362
9598
|
|
|
9363
9599
|
Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
9364
9600
|
const userMessage = `${intent}${contextNote}`;
|
|
9365
|
-
const
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
tools: routerTools(),
|
|
9371
|
-
tool_choice: { type: "any" }
|
|
9372
|
-
};
|
|
9373
|
-
let response;
|
|
9601
|
+
const routerToolList = routerTools();
|
|
9602
|
+
const tools = routerToolList.map(
|
|
9603
|
+
(tool, idx) => idx === routerToolList.length - 1 ? { ...tool, cache_control: { type: "ephemeral" } } : tool
|
|
9604
|
+
);
|
|
9605
|
+
let toolCall;
|
|
9374
9606
|
try {
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9607
|
+
toolCall = await invokeTool(
|
|
9608
|
+
{
|
|
9609
|
+
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
|
9610
|
+
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
|
|
9611
|
+
...env.AWS_REGION ? { region: env.AWS_REGION } : {},
|
|
9612
|
+
...env.BEDROCK_MODEL_ID ? { modelId: env.BEDROCK_MODEL_ID } : {}
|
|
9613
|
+
},
|
|
9614
|
+
{
|
|
9615
|
+
system: ROUTER_SYSTEM_PROMPT,
|
|
9616
|
+
messages: [{ role: "user", content: userMessage }],
|
|
9617
|
+
maxTokens: 512,
|
|
9618
|
+
tools,
|
|
9619
|
+
timeoutMs: ROUTER_TIMEOUT_MS,
|
|
9620
|
+
...attribution ? { attribution } : {},
|
|
9621
|
+
...onUsage ? { onUsage } : {}
|
|
9622
|
+
}
|
|
9623
|
+
);
|
|
9381
9624
|
} catch (err7) {
|
|
9382
|
-
if (err7 instanceof
|
|
9383
|
-
const
|
|
9384
|
-
const parsedSeconds = retryAfter ? parseInt(retryAfter, 10) : 60;
|
|
9625
|
+
if (err7 instanceof BedrockRateLimitError) {
|
|
9626
|
+
const parsedSeconds = err7.retryAfter ? parseInt(err7.retryAfter, 10) : 60;
|
|
9385
9627
|
return {
|
|
9386
9628
|
match: "rate_limited",
|
|
9387
9629
|
retry_after_seconds: Number.isFinite(parsedSeconds) ? parsedSeconds : 60,
|
|
@@ -9390,17 +9632,14 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
9390
9632
|
}
|
|
9391
9633
|
throw err7;
|
|
9392
9634
|
}
|
|
9393
|
-
|
|
9394
|
-
(block) => block.type === "tool_use"
|
|
9395
|
-
);
|
|
9396
|
-
if (!toolUseBlock) {
|
|
9635
|
+
if (!toolCall) {
|
|
9397
9636
|
return {
|
|
9398
9637
|
match: "none",
|
|
9399
9638
|
closest: [],
|
|
9400
9639
|
suggestion: "Could not determine the right tool for this intent. Try rephrasing or use carrier_ask_describe to explore available tools."
|
|
9401
9640
|
};
|
|
9402
9641
|
}
|
|
9403
|
-
const { name: resolvedTool, input } =
|
|
9642
|
+
const { name: resolvedTool, input } = toolCall;
|
|
9404
9643
|
const resolvedParams = input ?? {};
|
|
9405
9644
|
if (resolvedTool === "carrier_clarify") {
|
|
9406
9645
|
const candidates = resolvedParams.candidates ?? [];
|
|
@@ -9443,23 +9682,47 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
9443
9682
|
function writeCarrierAskAudit(env, row) {
|
|
9444
9683
|
try {
|
|
9445
9684
|
env.AUDIT_LOG.writeDataPoint({
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9685
|
+
// The marker at blob[11] is what separates these rows from writeAudit's,
|
|
9686
|
+
// which writes a different layout to this same dataset — its blob[4] is
|
|
9687
|
+
// `sub` where this one's is an intent hash. Filter on it before reading
|
|
9688
|
+
// any other position.
|
|
9689
|
+
blobs: withSchemaMarker(
|
|
9690
|
+
[
|
|
9691
|
+
"carrier_ask",
|
|
9692
|
+
// blob[0] tool_name
|
|
9693
|
+
row.resolved_tool,
|
|
9694
|
+
// blob[1] resolved tool (or "none")
|
|
9695
|
+
row.status,
|
|
9696
|
+
// blob[2] ok | error
|
|
9697
|
+
row.match,
|
|
9698
|
+
// blob[3] match type
|
|
9699
|
+
row.intent_hash,
|
|
9700
|
+
// blob[4] SHA-256 of intent
|
|
9701
|
+
row.confirm_token_state,
|
|
9702
|
+
// blob[5] confirm token state
|
|
9703
|
+
row.sub,
|
|
9704
|
+
// blob[6] user subject
|
|
9705
|
+
// Appended, never reordered: blob positions are the query contract and
|
|
9706
|
+
// an existing dashboard reading blob[3] must keep reading match.
|
|
9707
|
+
row.model_id,
|
|
9708
|
+
// blob[7] model id, or "none"
|
|
9709
|
+
row.tier
|
|
9710
|
+
// blob[8] tier at call time
|
|
9711
|
+
],
|
|
9712
|
+
SCHEMA_CARRIER_ASK
|
|
9713
|
+
),
|
|
9714
|
+
// Same rule for doubles. latency stays at [0].
|
|
9715
|
+
doubles: [
|
|
9716
|
+
row.latency_ms,
|
|
9717
|
+
row.usage.inputTokens,
|
|
9718
|
+
row.usage.outputTokens,
|
|
9719
|
+
row.usage.cacheReadTokens,
|
|
9720
|
+
row.usage.cacheWriteTokens,
|
|
9721
|
+
// An estimate from a rate card we maintain, not a billed figure. It is
|
|
9722
|
+
// here so a per-tenant cost query is one SQL statement rather than a
|
|
9723
|
+
// join against a rate table that lives in another package.
|
|
9724
|
+
estimateCostUsd(row.usage)
|
|
9461
9725
|
],
|
|
9462
|
-
doubles: [row.latency_ms],
|
|
9463
9726
|
indexes: [String(row.reseller_id)]
|
|
9464
9727
|
});
|
|
9465
9728
|
} catch {
|
|
@@ -9505,7 +9768,10 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9505
9768
|
status: "error",
|
|
9506
9769
|
latency_ms: Date.now() - start,
|
|
9507
9770
|
sub: ctx.props.sub,
|
|
9508
|
-
reseller_id: ctx.props.reseller_id
|
|
9771
|
+
reseller_id: ctx.props.reseller_id,
|
|
9772
|
+
model_id: "none",
|
|
9773
|
+
tier: ctx.props.tier,
|
|
9774
|
+
usage: ZERO_USAGE
|
|
9509
9775
|
});
|
|
9510
9776
|
return {
|
|
9511
9777
|
content: [
|
|
@@ -9543,7 +9809,10 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9543
9809
|
status: "ok",
|
|
9544
9810
|
latency_ms: Date.now() - start,
|
|
9545
9811
|
sub: ctx.props.sub,
|
|
9546
|
-
reseller_id: ctx.props.reseller_id
|
|
9812
|
+
reseller_id: ctx.props.reseller_id,
|
|
9813
|
+
model_id: "none",
|
|
9814
|
+
tier: ctx.props.tier,
|
|
9815
|
+
usage: ZERO_USAGE
|
|
9547
9816
|
});
|
|
9548
9817
|
return {
|
|
9549
9818
|
content: [
|
|
@@ -9560,8 +9829,21 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9560
9829
|
};
|
|
9561
9830
|
}
|
|
9562
9831
|
let route;
|
|
9832
|
+
let routeUsage = ZERO_USAGE;
|
|
9833
|
+
let modelRan = false;
|
|
9563
9834
|
try {
|
|
9564
|
-
route = await _routeIntent(intent, context, ctx.env
|
|
9835
|
+
route = await _routeIntent(intent, context, ctx.env, {
|
|
9836
|
+
attribution: {
|
|
9837
|
+
...ctx.props.org_id ? { orgId: ctx.props.org_id } : {},
|
|
9838
|
+
...ctx.props.reseller_id ? { subAccountId: String(ctx.props.reseller_id) } : {},
|
|
9839
|
+
surface: "carrier_ask",
|
|
9840
|
+
tier: ctx.props.tier
|
|
9841
|
+
},
|
|
9842
|
+
onUsage: (u) => {
|
|
9843
|
+
routeUsage = u;
|
|
9844
|
+
modelRan = true;
|
|
9845
|
+
}
|
|
9846
|
+
});
|
|
9565
9847
|
} catch (err7) {
|
|
9566
9848
|
writeCarrierAskAudit(ctx.env, {
|
|
9567
9849
|
intent_hash: intentHash,
|
|
@@ -9571,7 +9853,14 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9571
9853
|
status: "error",
|
|
9572
9854
|
latency_ms: Date.now() - start,
|
|
9573
9855
|
sub: ctx.props.sub,
|
|
9574
|
-
reseller_id: ctx.props.reseller_id
|
|
9856
|
+
reseller_id: ctx.props.reseller_id,
|
|
9857
|
+
model_id: modelRan ? ctx.env.BEDROCK_MODEL_ID ?? DEFAULT_MODEL_ID : "none",
|
|
9858
|
+
tier: ctx.props.tier,
|
|
9859
|
+
// Not ZERO_USAGE: a call can throw AFTER the model answered, and
|
|
9860
|
+
// those tokens were billed regardless. Recording zero here is how
|
|
9861
|
+
// measured spend drifts below real spend in exactly the rows used
|
|
9862
|
+
// to check it.
|
|
9863
|
+
usage: routeUsage
|
|
9575
9864
|
});
|
|
9576
9865
|
return {
|
|
9577
9866
|
content: [
|
|
@@ -9608,7 +9897,10 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9608
9897
|
status: "ok",
|
|
9609
9898
|
latency_ms: Date.now() - start,
|
|
9610
9899
|
sub: ctx.props.sub,
|
|
9611
|
-
reseller_id: ctx.props.reseller_id
|
|
9900
|
+
reseller_id: ctx.props.reseller_id,
|
|
9901
|
+
model_id: modelRan ? ctx.env.BEDROCK_MODEL_ID ?? DEFAULT_MODEL_ID : "none",
|
|
9902
|
+
tier: ctx.props.tier,
|
|
9903
|
+
usage: routeUsage
|
|
9612
9904
|
});
|
|
9613
9905
|
return {
|
|
9614
9906
|
content: [
|
|
@@ -10150,22 +10442,22 @@ function toFleetInput(raw) {
|
|
|
10150
10442
|
|
|
10151
10443
|
// src/apps/fleet-health-app.ts
|
|
10152
10444
|
var FLEET_APP_RESOURCE_URI = "ui://fleet-health-dashboard";
|
|
10153
|
-
async function safeCall2(
|
|
10445
|
+
async function safeCall2(client2, method, params) {
|
|
10154
10446
|
try {
|
|
10155
|
-
return { data: await
|
|
10447
|
+
return { data: await client2.call(method, params), error: null };
|
|
10156
10448
|
} catch (err7) {
|
|
10157
10449
|
return { data: null, error: err7 instanceof Error ? err7.message : String(err7) };
|
|
10158
10450
|
}
|
|
10159
10451
|
}
|
|
10160
10452
|
async function loadFleet(ctx, accountId) {
|
|
10161
10453
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
10162
|
-
const
|
|
10454
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
10163
10455
|
const resellerId = await getDefaultResellerId(ctx.env, token2).catch(() => void 0);
|
|
10164
10456
|
const statusParams = accountId !== void 0 ? { accountId } : resellerId !== void 0 ? { resellerId } : {};
|
|
10165
10457
|
const accountParams = resellerId !== void 0 ? { resellerId } : {};
|
|
10166
10458
|
const [status, accounts] = await Promise.all([
|
|
10167
|
-
safeCall2(
|
|
10168
|
-
safeCall2(
|
|
10459
|
+
safeCall2(client2, "esimStatusPerAccount", statusParams),
|
|
10460
|
+
safeCall2(client2, "listResellerAccount", accountParams)
|
|
10169
10461
|
]);
|
|
10170
10462
|
return toFleetInput({
|
|
10171
10463
|
status: status.data,
|
|
@@ -10311,9 +10603,9 @@ async function deleteWizardSession(env, sub, wizardId) {
|
|
|
10311
10603
|
}
|
|
10312
10604
|
|
|
10313
10605
|
// src/apps/provisioning-wizard.ts
|
|
10314
|
-
async function safeCallWithToken(
|
|
10606
|
+
async function safeCallWithToken(client2, _token, method, params = {}) {
|
|
10315
10607
|
try {
|
|
10316
|
-
return { data: await
|
|
10608
|
+
return { data: await client2.call(method, params), error: null };
|
|
10317
10609
|
} catch (err7) {
|
|
10318
10610
|
return {
|
|
10319
10611
|
data: null,
|
|
@@ -10380,11 +10672,11 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10380
10672
|
},
|
|
10381
10673
|
async ({ step, wizardId, subscriber_iccid, package_template_id }) => {
|
|
10382
10674
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
10383
|
-
const
|
|
10675
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
10384
10676
|
if (step === "init") {
|
|
10385
10677
|
const newWizardId = generateWizardId();
|
|
10386
10678
|
const subscribersResult = await safeCallWithToken(
|
|
10387
|
-
|
|
10679
|
+
client2,
|
|
10388
10680
|
token2,
|
|
10389
10681
|
"listResellerAccount",
|
|
10390
10682
|
{}
|
|
@@ -10462,7 +10754,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10462
10754
|
}
|
|
10463
10755
|
const resellerIdForTemplates = await getDefaultResellerId(ctx.env, token2);
|
|
10464
10756
|
const packagesResult = await safeCallWithToken(
|
|
10465
|
-
|
|
10757
|
+
client2,
|
|
10466
10758
|
token2,
|
|
10467
10759
|
"listPrepaidPackageTemplate",
|
|
10468
10760
|
{ resellerId: resellerIdForTemplates }
|
|
@@ -10529,7 +10821,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10529
10821
|
};
|
|
10530
10822
|
}
|
|
10531
10823
|
const previewResult = await safeCallWithToken(
|
|
10532
|
-
|
|
10824
|
+
client2,
|
|
10533
10825
|
token2,
|
|
10534
10826
|
"listPrepaidPackageTemplate",
|
|
10535
10827
|
{}
|
|
@@ -10601,7 +10893,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10601
10893
|
};
|
|
10602
10894
|
}
|
|
10603
10895
|
const subRecord = await safeCallWithToken(
|
|
10604
|
-
|
|
10896
|
+
client2,
|
|
10605
10897
|
token2,
|
|
10606
10898
|
"getSingleSubscriber",
|
|
10607
10899
|
{ iccid: session.subscriber_iccid }
|
|
@@ -10630,7 +10922,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10630
10922
|
};
|
|
10631
10923
|
}
|
|
10632
10924
|
const result2 = await safeCallWithToken(
|
|
10633
|
-
|
|
10925
|
+
client2,
|
|
10634
10926
|
token2,
|
|
10635
10927
|
"affectPackageToSubscriber",
|
|
10636
10928
|
{
|
|
@@ -10674,9 +10966,9 @@ import {
|
|
|
10674
10966
|
registerAppResource as registerAppResource3,
|
|
10675
10967
|
RESOURCE_MIME_TYPE as RESOURCE_MIME_TYPE3
|
|
10676
10968
|
} from "@modelcontextprotocol/ext-apps/server";
|
|
10677
|
-
async function safeCallWithToken2(
|
|
10969
|
+
async function safeCallWithToken2(client2, _token, method, params = {}) {
|
|
10678
10970
|
try {
|
|
10679
|
-
return { data: await
|
|
10971
|
+
return { data: await client2.call(method, params), error: null };
|
|
10680
10972
|
} catch (err7) {
|
|
10681
10973
|
return {
|
|
10682
10974
|
data: null,
|
|
@@ -10740,10 +11032,10 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
10740
11032
|
ctx,
|
|
10741
11033
|
async ({ iccid, delta, preview }) => {
|
|
10742
11034
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
10743
|
-
const
|
|
11035
|
+
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
10744
11036
|
if (preview === true) {
|
|
10745
11037
|
const result2 = await safeCallWithToken2(
|
|
10746
|
-
|
|
11038
|
+
client2,
|
|
10747
11039
|
token2,
|
|
10748
11040
|
"getSingleSubscriber",
|
|
10749
11041
|
{ iccid }
|
|
@@ -10768,7 +11060,7 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
10768
11060
|
};
|
|
10769
11061
|
}
|
|
10770
11062
|
const execResult = await safeCallWithToken2(
|
|
10771
|
-
|
|
11063
|
+
client2,
|
|
10772
11064
|
token2,
|
|
10773
11065
|
// CAR-78: subscriber top-up uses modifySubscriberBalance with { subscriber, amount }
|
|
10774
11066
|
// (delta). modifyAccountBalance is account-level and expects { accountId, amount, mode }.
|