@carrierllc/mcp 0.13.16 → 0.13.18
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-D6VGTHND.js → chunk-VXROP2SU.js} +2 -2
- package/dist/{chunk-D6VGTHND.js.map → chunk-VXROP2SU.js.map} +1 -1
- package/dist/cli.js +14 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.js +314 -189
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/carrier/skills/publish-storefront/SKILL.md +10 -0
- package/templates/storefront/package-lock.json +46 -46
- package/templates/storefront/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
verifyStorefront,
|
|
81
81
|
withOcsListSummary,
|
|
82
82
|
withTemplateResellerId
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-VXROP2SU.js";
|
|
84
84
|
import "./chunk-SHKKVIIA.js";
|
|
85
85
|
|
|
86
86
|
// src/index.ts
|
|
@@ -291,9 +291,9 @@ async function fail(resp) {
|
|
|
291
291
|
throw new BedrockError(text || `Bedrock returned HTTP ${resp.status}`, resp.status);
|
|
292
292
|
}
|
|
293
293
|
function timedOut() {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
return
|
|
294
|
+
const err8 = new Error("Bedrock call timed out before a usable response");
|
|
295
|
+
err8.name = "TimeoutError";
|
|
296
|
+
return err8;
|
|
297
297
|
}
|
|
298
298
|
function sleep(ms, signal) {
|
|
299
299
|
if (signal.aborted) return Promise.resolve();
|
|
@@ -317,9 +317,9 @@ async function sendWithRetry(aws, url, init, signal, deadlineAt) {
|
|
|
317
317
|
let resp;
|
|
318
318
|
try {
|
|
319
319
|
resp = await aws.fetch(url, init);
|
|
320
|
-
} catch (
|
|
320
|
+
} catch (err8) {
|
|
321
321
|
if (signal.aborted && throttled) return throttled;
|
|
322
|
-
throw
|
|
322
|
+
throw err8;
|
|
323
323
|
}
|
|
324
324
|
if (!retryable(resp.status)) return resp;
|
|
325
325
|
throttled = resp;
|
|
@@ -668,8 +668,8 @@ async function beginUiAgentRun(prompt, title, env, opts) {
|
|
|
668
668
|
...opts?.model ? { model: opts.model } : {},
|
|
669
669
|
...opts?.outputSchema ? { outputSchema: opts.outputSchema } : {}
|
|
670
670
|
});
|
|
671
|
-
} catch (
|
|
672
|
-
const message =
|
|
671
|
+
} catch (err8) {
|
|
672
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
673
673
|
await putTask(env, {
|
|
674
674
|
...base,
|
|
675
675
|
status: "error",
|
|
@@ -681,7 +681,7 @@ async function beginUiAgentRun(prompt, title, env, opts) {
|
|
|
681
681
|
screenshots_taken: 0
|
|
682
682
|
}
|
|
683
683
|
});
|
|
684
|
-
throw
|
|
684
|
+
throw err8;
|
|
685
685
|
}
|
|
686
686
|
const dispatched = {
|
|
687
687
|
...base,
|
|
@@ -695,8 +695,8 @@ async function beginUiAgentRun(prompt, title, env, opts) {
|
|
|
695
695
|
let run;
|
|
696
696
|
try {
|
|
697
697
|
run = await waitForRun(env, created.runId);
|
|
698
|
-
} catch (
|
|
699
|
-
const message =
|
|
698
|
+
} catch (err8) {
|
|
699
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
700
700
|
const failed = {
|
|
701
701
|
success: false,
|
|
702
702
|
summary: `Browser Use run failed: ${message}`,
|
|
@@ -837,8 +837,8 @@ function writeAgentAudit(env, entry) {
|
|
|
837
837
|
],
|
|
838
838
|
indexes: [entry.task_id]
|
|
839
839
|
});
|
|
840
|
-
} catch (
|
|
841
|
-
console.error(`[browser-use-agent] audit write failed: ${
|
|
840
|
+
} catch (err8) {
|
|
841
|
+
console.error(`[browser-use-agent] audit write failed: ${err8.message}`);
|
|
842
842
|
}
|
|
843
843
|
}
|
|
844
844
|
|
|
@@ -1073,22 +1073,22 @@ function boundZonesResult(raw, opts = {}) {
|
|
|
1073
1073
|
const { verbose = false, resellerId } = opts;
|
|
1074
1074
|
const maxZones = opts.maxZones ?? ZONES_MAX_ZONES;
|
|
1075
1075
|
const maxOperators = opts.maxOperators ?? (verbose ? ZONES_VERBOSE_MAX_OPERATORS : ZONES_MAX_OPERATORS);
|
|
1076
|
-
const totalOperators = all.reduce((n,
|
|
1076
|
+
const totalOperators = all.reduce((n, z26) => n + (z26.operators?.length ?? 0), 0);
|
|
1077
1077
|
const keptZones = all.slice(0, maxZones);
|
|
1078
1078
|
let budget = maxOperators;
|
|
1079
1079
|
let returnedOperators = 0;
|
|
1080
|
-
const zones = keptZones.map((
|
|
1081
|
-
const ops =
|
|
1080
|
+
const zones = keptZones.map((z26) => {
|
|
1081
|
+
const ops = z26.operators ?? [];
|
|
1082
1082
|
const take = Math.max(0, Math.min(ops.length, budget));
|
|
1083
1083
|
budget -= take;
|
|
1084
1084
|
returnedOperators += take;
|
|
1085
1085
|
const sliced = ops.slice(0, take);
|
|
1086
1086
|
if (verbose) {
|
|
1087
|
-
return { ...
|
|
1087
|
+
return { ...z26, operators: sliced, operatorCount: ops.length };
|
|
1088
1088
|
}
|
|
1089
1089
|
return {
|
|
1090
|
-
zoneId:
|
|
1091
|
-
zoneName:
|
|
1090
|
+
zoneId: z26.zoneId,
|
|
1091
|
+
zoneName: z26.zoneName,
|
|
1092
1092
|
operatorCount: ops.length,
|
|
1093
1093
|
operators: sliced.map((o) => ({
|
|
1094
1094
|
net: o.networkId,
|
|
@@ -1278,9 +1278,9 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
|
|
|
1278
1278
|
try {
|
|
1279
1279
|
const token2 = await ctx.getUserToken(ctx.props.sub);
|
|
1280
1280
|
result2 = await handler(args, token2);
|
|
1281
|
-
} catch (
|
|
1281
|
+
} catch (err8) {
|
|
1282
1282
|
try {
|
|
1283
|
-
Sentry.captureException(
|
|
1283
|
+
Sentry.captureException(err8, {
|
|
1284
1284
|
tags: {
|
|
1285
1285
|
tool: toolName,
|
|
1286
1286
|
feature: "mcp",
|
|
@@ -1289,8 +1289,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
|
|
|
1289
1289
|
});
|
|
1290
1290
|
} catch {
|
|
1291
1291
|
}
|
|
1292
|
-
const message =
|
|
1293
|
-
const ocsCode =
|
|
1292
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
1293
|
+
const ocsCode = err8 instanceof OcsApiError ? err8.code : void 0;
|
|
1294
1294
|
ctx.audit({
|
|
1295
1295
|
tool_name: toolName,
|
|
1296
1296
|
ocs_method: ocsMethod,
|
|
@@ -3668,8 +3668,8 @@ async function safeCall(env, token2, method, params = {}) {
|
|
|
3668
3668
|
const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
|
|
3669
3669
|
const data = await client2.call(method, params);
|
|
3670
3670
|
return { data, error: null };
|
|
3671
|
-
} catch (
|
|
3672
|
-
return { data: null, error:
|
|
3671
|
+
} catch (err8) {
|
|
3672
|
+
return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
3673
3673
|
}
|
|
3674
3674
|
}
|
|
3675
3675
|
async function fetchActiveSubscribers(env, token2, accountId, resellerId) {
|
|
@@ -5083,7 +5083,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
5083
5083
|
}
|
|
5084
5084
|
|
|
5085
5085
|
// src/tools-carrier-ask.ts
|
|
5086
|
-
import { z as
|
|
5086
|
+
import { z as z14 } from "zod";
|
|
5087
5087
|
|
|
5088
5088
|
// src/tools-ui-agent.ts
|
|
5089
5089
|
import { z as z4 } from "zod";
|
|
@@ -5244,7 +5244,7 @@ var RunBudget = class {
|
|
|
5244
5244
|
}
|
|
5245
5245
|
};
|
|
5246
5246
|
function createUiAgentRuntime(processEnv = process.env) {
|
|
5247
|
-
const
|
|
5247
|
+
const apiKey3 = processEnv.BROWSER_USE_API_KEY;
|
|
5248
5248
|
const spend = { runs: 0, costUsd: 0 };
|
|
5249
5249
|
const audit2 = (row) => {
|
|
5250
5250
|
const cost = row.doubles?.[1];
|
|
@@ -5255,13 +5255,13 @@ function createUiAgentRuntime(processEnv = process.env) {
|
|
|
5255
5255
|
};
|
|
5256
5256
|
return {
|
|
5257
5257
|
env: {
|
|
5258
|
-
...
|
|
5258
|
+
...apiKey3 ? { apiKey: apiKey3 } : {},
|
|
5259
5259
|
store: createMemoryStore(),
|
|
5260
5260
|
audit: audit2
|
|
5261
5261
|
},
|
|
5262
5262
|
budget: new RunBudget(maxRunsPerProcess(processEnv)),
|
|
5263
5263
|
spend,
|
|
5264
|
-
configured: Boolean(
|
|
5264
|
+
configured: Boolean(apiKey3),
|
|
5265
5265
|
dispatchedTaskIds: /* @__PURE__ */ new Set()
|
|
5266
5266
|
};
|
|
5267
5267
|
}
|
|
@@ -5356,8 +5356,8 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
|
|
|
5356
5356
|
}
|
|
5357
5357
|
try {
|
|
5358
5358
|
runtime.budget.reserve();
|
|
5359
|
-
} catch (
|
|
5360
|
-
if (
|
|
5359
|
+
} catch (err8) {
|
|
5360
|
+
if (err8 instanceof SpendLimitError) {
|
|
5361
5361
|
ctx.audit({
|
|
5362
5362
|
tool_name: toolName,
|
|
5363
5363
|
ocs_method: ocsMethod,
|
|
@@ -5369,11 +5369,11 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
|
|
|
5369
5369
|
event_type: "ui_agent_dispatch"
|
|
5370
5370
|
});
|
|
5371
5371
|
return jsonResult(
|
|
5372
|
-
{ error: "run_limit_reached", message:
|
|
5372
|
+
{ error: "run_limit_reached", message: err8.message },
|
|
5373
5373
|
true
|
|
5374
5374
|
);
|
|
5375
5375
|
}
|
|
5376
|
-
throw
|
|
5376
|
+
throw err8;
|
|
5377
5377
|
}
|
|
5378
5378
|
try {
|
|
5379
5379
|
const result2 = await runUiAgent(
|
|
@@ -5436,7 +5436,7 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
|
|
|
5436
5436
|
note: "The run completed in-process \u2014 a local CLI has no background runtime, so this call blocked for its duration.",
|
|
5437
5437
|
portal_password_note: PORTAL_PASSWORD_RUN_NOTE
|
|
5438
5438
|
});
|
|
5439
|
-
} catch (
|
|
5439
|
+
} catch (err8) {
|
|
5440
5440
|
ctx.audit({
|
|
5441
5441
|
tool_name: toolName,
|
|
5442
5442
|
ocs_method: ocsMethod,
|
|
@@ -5451,7 +5451,7 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
|
|
|
5451
5451
|
{
|
|
5452
5452
|
type: "text",
|
|
5453
5453
|
text: redact(
|
|
5454
|
-
`Error dispatching the UI agent: ${
|
|
5454
|
+
`Error dispatching the UI agent: ${err8 instanceof Error ? err8.message : String(err8)}`
|
|
5455
5455
|
)
|
|
5456
5456
|
}
|
|
5457
5457
|
]
|
|
@@ -5944,13 +5944,13 @@ function registerUiAgentAskTools(server2, ctx, runtime) {
|
|
|
5944
5944
|
taskIds = listing.keys.map(
|
|
5945
5945
|
(k) => k.name.slice(PENDING_ASK_PREFIX.length)
|
|
5946
5946
|
);
|
|
5947
|
-
} catch (
|
|
5947
|
+
} catch (err8) {
|
|
5948
5948
|
return {
|
|
5949
5949
|
isError: true,
|
|
5950
5950
|
content: [
|
|
5951
5951
|
{
|
|
5952
5952
|
type: "text",
|
|
5953
|
-
text: `Error listing pending tasks: ${
|
|
5953
|
+
text: `Error listing pending tasks: ${err8 instanceof Error ? err8.message : String(err8)}`
|
|
5954
5954
|
}
|
|
5955
5955
|
]
|
|
5956
5956
|
};
|
|
@@ -6827,6 +6827,33 @@ function requireBinding(binding, tool, name, subject) {
|
|
|
6827
6827
|
);
|
|
6828
6828
|
}
|
|
6829
6829
|
|
|
6830
|
+
// src/stripe-headers.ts
|
|
6831
|
+
var STRIPE_API_VERSION = "2025-08-27.basil";
|
|
6832
|
+
function isStripeOrgKey(stripeKey) {
|
|
6833
|
+
return stripeKey.startsWith("sk_org_");
|
|
6834
|
+
}
|
|
6835
|
+
function stripeAccountHeaders(stripeKey, connectedAccountId, platformAccountId) {
|
|
6836
|
+
if (isStripeOrgKey(stripeKey)) {
|
|
6837
|
+
if (connectedAccountId) {
|
|
6838
|
+
return {
|
|
6839
|
+
"Stripe-Context": platformAccountId ? `${platformAccountId}/${connectedAccountId}` : connectedAccountId
|
|
6840
|
+
};
|
|
6841
|
+
}
|
|
6842
|
+
return platformAccountId ? { "Stripe-Context": platformAccountId } : {};
|
|
6843
|
+
}
|
|
6844
|
+
if (connectedAccountId) return { "Stripe-Account": connectedAccountId };
|
|
6845
|
+
return {};
|
|
6846
|
+
}
|
|
6847
|
+
function buildStripeHeaders(stripeKey, opts = {}) {
|
|
6848
|
+
const headers = {
|
|
6849
|
+
Authorization: `Bearer ${stripeKey}`,
|
|
6850
|
+
"Stripe-Version": STRIPE_API_VERSION,
|
|
6851
|
+
...stripeAccountHeaders(stripeKey, opts.connectedAccountId, opts.platformAccountId)
|
|
6852
|
+
};
|
|
6853
|
+
if (opts.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
6854
|
+
return headers;
|
|
6855
|
+
}
|
|
6856
|
+
|
|
6830
6857
|
// src/stripe-connect-tools.ts
|
|
6831
6858
|
async function verifyConfirmToken(env, sub, toolName, token2) {
|
|
6832
6859
|
requireBinding(
|
|
@@ -6861,23 +6888,21 @@ function err2(text) {
|
|
|
6861
6888
|
return { isError: true, content: [{ type: "text", text }] };
|
|
6862
6889
|
}
|
|
6863
6890
|
async function stripeGet(stripeKey, path, connectedAccountId) {
|
|
6864
|
-
const headers = {
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
};
|
|
6868
|
-
if (connectedAccountId) headers["Stripe-Account"] = connectedAccountId;
|
|
6891
|
+
const headers = buildStripeHeaders(stripeKey, {
|
|
6892
|
+
connectedAccountId,
|
|
6893
|
+
platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID
|
|
6894
|
+
});
|
|
6869
6895
|
const res = await fetch(`https://api.stripe.com${path}`, { headers });
|
|
6870
6896
|
const data = await res.json();
|
|
6871
6897
|
return { ok: res.ok, status: res.status, data };
|
|
6872
6898
|
}
|
|
6873
6899
|
async function stripePost(stripeKey, path, params, connectedAccountId, idempotencyKey) {
|
|
6874
|
-
const headers = {
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
};
|
|
6879
|
-
|
|
6880
|
-
if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
|
|
6900
|
+
const headers = buildStripeHeaders(stripeKey, {
|
|
6901
|
+
connectedAccountId,
|
|
6902
|
+
platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
|
|
6903
|
+
idempotencyKey
|
|
6904
|
+
});
|
|
6905
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
6881
6906
|
const res = await fetch(`https://api.stripe.com${path}`, {
|
|
6882
6907
|
method: "POST",
|
|
6883
6908
|
headers,
|
|
@@ -6932,7 +6957,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
6932
6957
|
const opKvKey = operator_id ? `stripe_account:${operator_id}` : kvKey;
|
|
6933
6958
|
const start = Date.now();
|
|
6934
6959
|
try {
|
|
6935
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
6960
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
6936
6961
|
if (!stripeKey) return err2("Stripe not configured");
|
|
6937
6962
|
const accountId = await env.CARRIER_USERS.get(opKvKey);
|
|
6938
6963
|
if (!accountId) {
|
|
@@ -6985,7 +7010,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
6985
7010
|
if (denied) return denied;
|
|
6986
7011
|
const start = Date.now();
|
|
6987
7012
|
try {
|
|
6988
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7013
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
6989
7014
|
if (!stripeKey) return err2("Stripe not configured");
|
|
6990
7015
|
const accountId = await getAccountId();
|
|
6991
7016
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7022,7 +7047,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
7022
7047
|
if (denied) return denied;
|
|
7023
7048
|
const start = Date.now();
|
|
7024
7049
|
try {
|
|
7025
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7050
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7026
7051
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7027
7052
|
const accountId = await getAccountId();
|
|
7028
7053
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7079,7 +7104,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7079
7104
|
return err2("Invalid or expired confirm_token. Call without token to get a new one.");
|
|
7080
7105
|
}
|
|
7081
7106
|
try {
|
|
7082
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7107
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7083
7108
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7084
7109
|
const accountId = await getAccountId();
|
|
7085
7110
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7126,7 +7151,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7126
7151
|
if (denied) return denied;
|
|
7127
7152
|
const start = Date.now();
|
|
7128
7153
|
try {
|
|
7129
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7154
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7130
7155
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7131
7156
|
const accountId = await getAccountId();
|
|
7132
7157
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7171,7 +7196,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7171
7196
|
}
|
|
7172
7197
|
const start = Date.now();
|
|
7173
7198
|
try {
|
|
7174
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7199
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7175
7200
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7176
7201
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
7177
7202
|
if (open_only) params.set("open", "true");
|
|
@@ -7225,7 +7250,7 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
|
|
|
7225
7250
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7226
7251
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7227
7252
|
try {
|
|
7228
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7253
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7229
7254
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7230
7255
|
const result2 = await stripePost(
|
|
7231
7256
|
stripeKey,
|
|
@@ -7281,7 +7306,7 @@ Expires in 5 minutes.`
|
|
|
7281
7306
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7282
7307
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7283
7308
|
try {
|
|
7284
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7309
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7285
7310
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7286
7311
|
const result2 = await stripePost(
|
|
7287
7312
|
stripeKey,
|
|
@@ -7338,7 +7363,7 @@ Expires in 5 minutes.`
|
|
|
7338
7363
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7339
7364
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7340
7365
|
try {
|
|
7341
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7366
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7342
7367
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7343
7368
|
const result2 = await stripePost(
|
|
7344
7369
|
stripeKey,
|
|
@@ -7601,9 +7626,103 @@ function registerStorefrontDeployTools(server2, props2) {
|
|
|
7601
7626
|
);
|
|
7602
7627
|
}
|
|
7603
7628
|
|
|
7604
|
-
// src/tools-
|
|
7629
|
+
// src/tools-storefront-plan.ts
|
|
7605
7630
|
import { z as z11 } from "zod";
|
|
7606
7631
|
|
|
7632
|
+
// src/lib/storefront-plan-update.ts
|
|
7633
|
+
function storefrontPlanUpdate(input) {
|
|
7634
|
+
if (input.priceCents === void 0 && input.active === void 0 && input.sortOrder === void 0) {
|
|
7635
|
+
return { ok: false, message: "Send a price, whether the plan is for sale, or a sort order." };
|
|
7636
|
+
}
|
|
7637
|
+
if (input.priceCents !== void 0 && (!Number.isSafeInteger(input.priceCents) || input.priceCents <= 0)) {
|
|
7638
|
+
return { ok: false, message: "Price must be a positive number of minor units." };
|
|
7639
|
+
}
|
|
7640
|
+
if (input.sortOrder !== void 0 && (!Number.isSafeInteger(input.sortOrder) || input.sortOrder < 0)) {
|
|
7641
|
+
return { ok: false, message: "sort_order must be a non-negative integer." };
|
|
7642
|
+
}
|
|
7643
|
+
const body2 = {};
|
|
7644
|
+
if (input.priceCents !== void 0) body2.price_cents = input.priceCents;
|
|
7645
|
+
if (input.active !== void 0) body2.active = input.active;
|
|
7646
|
+
if (input.sortOrder !== void 0) body2.sort_order = input.sortOrder;
|
|
7647
|
+
return { ok: true, body: body2 };
|
|
7648
|
+
}
|
|
7649
|
+
|
|
7650
|
+
// src/tools-storefront-plan.ts
|
|
7651
|
+
var STOREFRONT_PLAN_TOOL_SCOPES = {
|
|
7652
|
+
update_storefront_plan: "write"
|
|
7653
|
+
};
|
|
7654
|
+
var ok4 = (text) => ({ content: [{ type: "text", text }] });
|
|
7655
|
+
var err5 = (text) => ({ isError: true, content: [{ type: "text", text }] });
|
|
7656
|
+
var DEFAULT_API = "https://api.carrier.llc";
|
|
7657
|
+
function apiOrigin() {
|
|
7658
|
+
return (process.env.CARRIER_API_URL ?? DEFAULT_API).replace(/\/$/, "");
|
|
7659
|
+
}
|
|
7660
|
+
function apiKey2() {
|
|
7661
|
+
return process.env.CARRIER_API_KEY?.trim() || process.env.CARRIER_ORG_API_KEY?.trim() || null;
|
|
7662
|
+
}
|
|
7663
|
+
async function patchStorefrontPlan(storefrontId, slug, body2) {
|
|
7664
|
+
const key = apiKey2();
|
|
7665
|
+
if (!key) {
|
|
7666
|
+
return { ok: false, status: 401, body: { error: "not_signed_in", message: "Not signed in." } };
|
|
7667
|
+
}
|
|
7668
|
+
const res = await fetch(
|
|
7669
|
+
`${apiOrigin()}/v1/storefronts/${encodeURIComponent(storefrontId)}/plans/${encodeURIComponent(slug)}`,
|
|
7670
|
+
{
|
|
7671
|
+
method: "PATCH",
|
|
7672
|
+
headers: {
|
|
7673
|
+
Authorization: `Bearer ${key}`,
|
|
7674
|
+
Accept: "application/json",
|
|
7675
|
+
"Content-Type": "application/json"
|
|
7676
|
+
},
|
|
7677
|
+
body: JSON.stringify(body2)
|
|
7678
|
+
}
|
|
7679
|
+
);
|
|
7680
|
+
const raw = await res.text();
|
|
7681
|
+
let parsed = raw;
|
|
7682
|
+
try {
|
|
7683
|
+
parsed = raw.trim() === "" ? {} : JSON.parse(raw);
|
|
7684
|
+
} catch {
|
|
7685
|
+
parsed = { message: raw };
|
|
7686
|
+
}
|
|
7687
|
+
return { ok: res.ok, status: res.status, body: parsed };
|
|
7688
|
+
}
|
|
7689
|
+
function registerStorefrontPlanTools(server2, props2, call = (path, body2) => {
|
|
7690
|
+
const [storefrontId, slug] = path.split("/");
|
|
7691
|
+
return patchStorefrontPlan(storefrontId ?? "", slug ?? "", body2);
|
|
7692
|
+
}) {
|
|
7693
|
+
server2.registerTool(
|
|
7694
|
+
"update_storefront_plan",
|
|
7695
|
+
{
|
|
7696
|
+
title: "Update Storefront Plan",
|
|
7697
|
+
description: "Change the retail price, whether a plan is for sale, or its display order on the caller's own hosted storefront. The plan must already exist. A price of zero or less is refused. A plan on another storefront is not found and is left unchanged. This writes the shop's own price. It does not create or change a Stripe price.",
|
|
7698
|
+
inputSchema: {
|
|
7699
|
+
storefront_id: z11.string().min(1).describe("Id of the hosted storefront"),
|
|
7700
|
+
slug: z11.string().min(1).describe("Plan slug, such as eu-1gb"),
|
|
7701
|
+
price_cents: z11.number().int().positive().optional().describe("Retail price in minor units. Must be greater than zero. Omit to leave the price."),
|
|
7702
|
+
active: z11.boolean().optional().describe("Whether the plan is for sale. Omit to leave it."),
|
|
7703
|
+
sort_order: z11.number().int().nonnegative().optional().describe("Display order, lowest first. Omit to leave it.")
|
|
7704
|
+
},
|
|
7705
|
+
annotations: annotationsFor("update_storefront_plan", "write")
|
|
7706
|
+
},
|
|
7707
|
+
async ({ storefront_id, slug, price_cents, active, sort_order }) => {
|
|
7708
|
+
const denied = denyUnlessScoped(props2, "update_storefront_plan", STOREFRONT_PLAN_TOOL_SCOPES);
|
|
7709
|
+
if (denied) return denied;
|
|
7710
|
+
const update = storefrontPlanUpdate({ priceCents: price_cents, active, sortOrder: sort_order });
|
|
7711
|
+
if (!update.ok) return err5(update.message);
|
|
7712
|
+
try {
|
|
7713
|
+
const result2 = await call(`${storefront_id}/${slug}`, update.body);
|
|
7714
|
+
const text = JSON.stringify(result2.body);
|
|
7715
|
+
return result2.ok ? ok4(text) : err5(text);
|
|
7716
|
+
} catch (e) {
|
|
7717
|
+
return err5(e instanceof Error ? e.message : "plan update failed");
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
);
|
|
7721
|
+
}
|
|
7722
|
+
|
|
7723
|
+
// src/tools-atlas.ts
|
|
7724
|
+
import { z as z12 } from "zod";
|
|
7725
|
+
|
|
7607
7726
|
// src/atlas-public.ts
|
|
7608
7727
|
var ATLAS_PUBLIC_BASE_URL = "https://atlas.carrier.llc";
|
|
7609
7728
|
function atlasBaseUrl(env) {
|
|
@@ -7634,8 +7753,8 @@ async function atlasPublicGet(baseUrl2, path) {
|
|
|
7634
7753
|
headers: { Accept: "application/json" },
|
|
7635
7754
|
signal: AbortSignal.timeout(8e3)
|
|
7636
7755
|
});
|
|
7637
|
-
} catch (
|
|
7638
|
-
const message =
|
|
7756
|
+
} catch (err8) {
|
|
7757
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7639
7758
|
throw new Error(`Atlas request failed: ${message}`);
|
|
7640
7759
|
}
|
|
7641
7760
|
const text = await response.text();
|
|
@@ -7660,13 +7779,13 @@ var ATLAS_TOOL_SCOPES = {
|
|
|
7660
7779
|
get_atlas_network_status: "read"
|
|
7661
7780
|
};
|
|
7662
7781
|
var coverageSchema = {
|
|
7663
|
-
country:
|
|
7664
|
-
status:
|
|
7665
|
-
technology:
|
|
7782
|
+
country: z12.string().regex(/^[A-Za-z]{2}$/, "country must be a 2-letter ISO code").describe("ISO 3166-1 alpha-2 country code, for example JP or NL"),
|
|
7783
|
+
status: z12.enum(["up", "degraded", "down", "unknown"]).optional().describe("Keep only networks whose latest probe has this status"),
|
|
7784
|
+
technology: z12.enum(["2G", "3G", "4G", "5G"]).optional().describe("Keep only networks of this radio generation")
|
|
7666
7785
|
};
|
|
7667
7786
|
var networkSchema = {
|
|
7668
|
-
mcc:
|
|
7669
|
-
mnc:
|
|
7787
|
+
mcc: z12.string().regex(/^\d{3}$/, "mcc must be 3 digits").describe("Mobile Country Code"),
|
|
7788
|
+
mnc: z12.string().regex(/^\d{2,3}$/, "mnc must be 2 or 3 digits").describe("Mobile Network Code")
|
|
7670
7789
|
};
|
|
7671
7790
|
function jsonResult2(body2) {
|
|
7672
7791
|
return {
|
|
@@ -7721,15 +7840,15 @@ function registerAtlasTools(server2, ctx) {
|
|
|
7721
7840
|
}
|
|
7722
7841
|
|
|
7723
7842
|
// src/tools-network-default.ts
|
|
7724
|
-
import { z as
|
|
7843
|
+
import { z as z13 } from "zod";
|
|
7725
7844
|
var NETWORK_DEFAULT_TOOL_SCOPES = {
|
|
7726
7845
|
get_network_default: "read",
|
|
7727
7846
|
set_network_default: "write",
|
|
7728
7847
|
plan_network_default: "read"
|
|
7729
7848
|
};
|
|
7730
|
-
var choiceShape =
|
|
7731
|
-
mode:
|
|
7732
|
-
steeringListId:
|
|
7849
|
+
var choiceShape = z13.object({
|
|
7850
|
+
mode: z13.enum(NETWORK_DEFAULT_MODES),
|
|
7851
|
+
steeringListId: z13.number().int().positive().optional()
|
|
7733
7852
|
}).describe("`cheapest_available`, `all`, or `custom` with a steeringListId from list_steering_lists");
|
|
7734
7853
|
function ioFor(ctx, token2) {
|
|
7735
7854
|
const kv = ctx.env.CARRIER_USERS;
|
|
@@ -7784,9 +7903,9 @@ function registerNetworkDefaultTools(server2, ctx) {
|
|
|
7784
7903
|
title: "Set Network Default",
|
|
7785
7904
|
description: "Use this to set the organisation's network default to `cheapest_available`, `all`, or `custom` (a steering list id from `list_steering_lists`, which must exist). This only stores the setting. It changes no SIM, writes nothing to OCS and does not migrate existing customers: it applies to NEW assignments. A single assignment can still override it with `plan_network_default`'s `simOverride`. Params: `mode` (required), `steeringListId` (required when mode is `custom`), `dry_run` (preview only). Do NOT use this to assign a list to one SIM \u2014 use `modify_subscriber_steering_list`.",
|
|
7786
7905
|
inputSchema: {
|
|
7787
|
-
mode:
|
|
7788
|
-
steeringListId:
|
|
7789
|
-
dry_run:
|
|
7906
|
+
mode: z13.enum(NETWORK_DEFAULT_MODES).describe("cheapest_available | all | custom"),
|
|
7907
|
+
steeringListId: z13.number().int().positive().optional().describe("Required when mode is custom"),
|
|
7908
|
+
dry_run: z13.boolean().optional().describe("If true, validate and return what would be stored; store nothing")
|
|
7790
7909
|
},
|
|
7791
7910
|
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
|
|
7792
7911
|
},
|
|
@@ -7815,10 +7934,10 @@ function registerNetworkDefaultTools(server2, ctx) {
|
|
|
7815
7934
|
title: "Plan Network Default",
|
|
7816
7935
|
description: "Use this to see which networks a SIM would be given in one or more countries, before anything is assigned. By default that is the cheapest available network per country (lowest data rate above zero, among rules really in force). Precedence: `simOverride`, then `packageOverride`, then the org default, then `cheapest_available`. Read only: `applied` is always false. If the tariff is missing or cannot be compared it falls back to ALL networks and says so in `warnings` and `summary` (`fellBackToAll: true`); never present that as a cheapest choice. The price basis is the wholesale data rate per MB, not the end price. Creating a steering list has no OCS REST method, so when no existing list matches the result is `needs_list`, not a created list. Params: `countries` (ISO-3166 alpha-2 codes, required), `simOverride`, `packageOverride`, `resellerId` (optional).",
|
|
7817
7936
|
inputSchema: {
|
|
7818
|
-
countries:
|
|
7937
|
+
countries: z13.array(z13.string().length(2)).min(1).describe('ISO-3166 alpha-2 codes, e.g. ["CA"]'),
|
|
7819
7938
|
simOverride: choiceShape.optional(),
|
|
7820
7939
|
packageOverride: choiceShape.optional(),
|
|
7821
|
-
resellerId:
|
|
7940
|
+
resellerId: z13.number().int().positive().optional().describe("Omit to use the token owner's reseller")
|
|
7822
7941
|
},
|
|
7823
7942
|
annotations: { readOnlyHint: true }
|
|
7824
7943
|
},
|
|
@@ -9206,6 +9325,7 @@ var ALL_TOOL_SCOPES = Object.freeze({
|
|
|
9206
9325
|
...STOREFRONT_LOGO_TOOL_SCOPES,
|
|
9207
9326
|
// Storefront deploy tools exist only in this package, not in the Worker.
|
|
9208
9327
|
...STOREFRONT_DEPLOY_TOOL_SCOPES,
|
|
9328
|
+
...STOREFRONT_PLAN_TOOL_SCOPES,
|
|
9209
9329
|
...ATLAS_TOOL_SCOPES,
|
|
9210
9330
|
...NETWORK_DEFAULT_TOOL_SCOPES
|
|
9211
9331
|
});
|
|
@@ -9768,16 +9888,16 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
9768
9888
|
...onUsage ? { onUsage } : {}
|
|
9769
9889
|
}
|
|
9770
9890
|
);
|
|
9771
|
-
} catch (
|
|
9772
|
-
if (
|
|
9773
|
-
const parsedSeconds =
|
|
9891
|
+
} catch (err8) {
|
|
9892
|
+
if (err8 instanceof BedrockRateLimitError) {
|
|
9893
|
+
const parsedSeconds = err8.retryAfter ? parseInt(err8.retryAfter, 10) : 60;
|
|
9774
9894
|
return {
|
|
9775
9895
|
match: "rate_limited",
|
|
9776
9896
|
retry_after_seconds: Number.isFinite(parsedSeconds) ? parsedSeconds : 60,
|
|
9777
9897
|
suggestion: "Bedrock rate limit reached. Please retry after the indicated delay."
|
|
9778
9898
|
};
|
|
9779
9899
|
}
|
|
9780
|
-
throw
|
|
9900
|
+
throw err8;
|
|
9781
9901
|
}
|
|
9782
9902
|
if (!toolCall) {
|
|
9783
9903
|
return {
|
|
@@ -9882,15 +10002,15 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9882
10002
|
title: "Natural Language Carrier Tool Router",
|
|
9883
10003
|
description: "Use this when you want to perform a carrier operation but don't know which specific tool to call. Describe your intent in plain language and carrier_ask will identify the correct tool(s) and suggest the required parameters. For simple read intents, it executes directly and returns results. For destructive operations it returns a confirm_token that you must pass in a second call. Params: `intent` (string \u2014 natural language description of what you want to do), `context` (optional: iccid, account_id, reseller_id if already known). Returns: RouteResult \u2014 confirmed | pending_confirm | ambiguous | none. Do NOT use this when you know the right tool \u2014 direct calls are faster and cheaper.",
|
|
9884
10004
|
inputSchema: {
|
|
9885
|
-
intent:
|
|
10005
|
+
intent: z14.string().min(3).describe(
|
|
9886
10006
|
"Natural-language description of the operation to perform (e.g. 'suspend ICCID 89316 until next month', 'show fleet health', 'assign Europe 5GB package to subscriber 89316...')"
|
|
9887
10007
|
),
|
|
9888
|
-
context:
|
|
9889
|
-
iccid:
|
|
9890
|
-
account_id:
|
|
9891
|
-
reseller_id:
|
|
10008
|
+
context: z14.object({
|
|
10009
|
+
iccid: z14.string().optional().describe("Subscriber ICCID if already known"),
|
|
10010
|
+
account_id: z14.number().optional().describe("Account ID if already known"),
|
|
10011
|
+
reseller_id: z14.number().optional().describe("Reseller ID if already known")
|
|
9892
10012
|
}).optional().describe("Optional pre-resolved context to improve routing accuracy"),
|
|
9893
|
-
confirm_token:
|
|
10013
|
+
confirm_token: z14.string().optional().describe(
|
|
9894
10014
|
"One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
|
|
9895
10015
|
)
|
|
9896
10016
|
}
|
|
@@ -9991,8 +10111,8 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9991
10111
|
modelRan = true;
|
|
9992
10112
|
}
|
|
9993
10113
|
});
|
|
9994
|
-
} catch (
|
|
9995
|
-
if (
|
|
10114
|
+
} catch (err8) {
|
|
10115
|
+
if (err8 instanceof AskRouterNotConfiguredError) {
|
|
9996
10116
|
writeCarrierAskAudit(ctx.env, {
|
|
9997
10117
|
intent_hash: intentHash,
|
|
9998
10118
|
match: "error",
|
|
@@ -10012,8 +10132,8 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10012
10132
|
type: "text",
|
|
10013
10133
|
text: JSON.stringify({
|
|
10014
10134
|
error: "router_not_configured",
|
|
10015
|
-
missing:
|
|
10016
|
-
message:
|
|
10135
|
+
missing: err8.missing,
|
|
10136
|
+
message: err8.message
|
|
10017
10137
|
})
|
|
10018
10138
|
}
|
|
10019
10139
|
],
|
|
@@ -10043,7 +10163,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10043
10163
|
type: "text",
|
|
10044
10164
|
text: JSON.stringify({
|
|
10045
10165
|
error: "routing_error",
|
|
10046
|
-
message:
|
|
10166
|
+
message: err8 instanceof Error ? err8.message : "An unexpected error occurred during routing."
|
|
10047
10167
|
})
|
|
10048
10168
|
}
|
|
10049
10169
|
],
|
|
@@ -10094,7 +10214,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10094
10214
|
title: "Describe a Carrier Tool",
|
|
10095
10215
|
description: "Get full documentation for any registered Carrier MCP tool: description, parameters, 2-3 example invocations, required scope, destructive flag, and guidance. Params: `tool_name` (exact MCP tool name, e.g. 'assign_package'). Returns: structured tool metadata. Does NOT execute the tool.",
|
|
10096
10216
|
inputSchema: {
|
|
10097
|
-
tool_name:
|
|
10217
|
+
tool_name: z14.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
|
|
10098
10218
|
}
|
|
10099
10219
|
},
|
|
10100
10220
|
wrapHandler(
|
|
@@ -10187,7 +10307,7 @@ function buildExamples(toolName) {
|
|
|
10187
10307
|
}
|
|
10188
10308
|
|
|
10189
10309
|
// src/list-recent-ocs-events.ts
|
|
10190
|
-
import { z as
|
|
10310
|
+
import { z as z15 } from "zod";
|
|
10191
10311
|
|
|
10192
10312
|
// src/ocs-scoping.ts
|
|
10193
10313
|
function managedOwnAccountId(auth) {
|
|
@@ -10220,10 +10340,10 @@ async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
|
|
|
10220
10340
|
return { ok: false, error: "Subscriber not found" };
|
|
10221
10341
|
}
|
|
10222
10342
|
return { ok: true };
|
|
10223
|
-
} catch (
|
|
10343
|
+
} catch (err8) {
|
|
10224
10344
|
return {
|
|
10225
10345
|
ok: false,
|
|
10226
|
-
error:
|
|
10346
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
10227
10347
|
};
|
|
10228
10348
|
}
|
|
10229
10349
|
}
|
|
@@ -10355,10 +10475,10 @@ var ALLOWED_EVENT_TYPES = [
|
|
|
10355
10475
|
"usage.anomaly"
|
|
10356
10476
|
];
|
|
10357
10477
|
var listRecentOcsEventsSchema = {
|
|
10358
|
-
iccid:
|
|
10359
|
-
limit:
|
|
10360
|
-
event_types:
|
|
10361
|
-
since:
|
|
10478
|
+
iccid: z15.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
|
|
10479
|
+
limit: z15.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
|
|
10480
|
+
event_types: z15.array(z15.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
|
|
10481
|
+
since: z15.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
|
|
10362
10482
|
};
|
|
10363
10483
|
async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
|
|
10364
10484
|
return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
|
|
@@ -10409,8 +10529,8 @@ function registerListRecentOcsEventsTool(server2, ctx) {
|
|
|
10409
10529
|
return {
|
|
10410
10530
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
10411
10531
|
};
|
|
10412
|
-
} catch (
|
|
10413
|
-
const message =
|
|
10532
|
+
} catch (err8) {
|
|
10533
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
10414
10534
|
return {
|
|
10415
10535
|
isError: true,
|
|
10416
10536
|
content: [{ type: "text", text: `Error reading OCS event buffer: ${message}` }]
|
|
@@ -10421,7 +10541,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
|
|
|
10421
10541
|
}
|
|
10422
10542
|
|
|
10423
10543
|
// src/tools-rate-governor.ts
|
|
10424
|
-
import { z as
|
|
10544
|
+
import { z as z16 } from "zod";
|
|
10425
10545
|
var MONITORED_ENDPOINTS = [
|
|
10426
10546
|
"subscriberNetworkEventsOverPeriod",
|
|
10427
10547
|
// 30/min
|
|
@@ -10453,7 +10573,7 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10453
10573
|
title: "API Rate-Limit Governor Status",
|
|
10454
10574
|
description: "Inspect Bridge4IP OCS rate-limit governor state \u2014 bucket fill levels, per-endpoint limits, and 80% alert thresholds. Use this to answer: 'are we rate limited', 'check ocs quota', 'how full is the rate limit bucket', 'rate limit status'. Params: `endpoint` (optional \u2014 OCS camelCase method name to inspect a single bucket, e.g. 'subscriberUsageOverPeriod'; omit for all monitored endpoints). Returns: per-endpoint { limit_per_min, batch_cap_per_min, calls_in_window, batch_calls_in_window, utilisation_pct, alert (true when \u226580% of limit) }. Note: counts come from the in-process sliding-window log (same key as OCS calls: API token + endpoint) \u2014 they reset when the Worker isolate recycles. Use as a relative signal, not an exact counter. Do NOT use this to adjust limits \u2014 limits are set by Bridge4IP contract and cannot be changed via MCP.",
|
|
10455
10575
|
inputSchema: {
|
|
10456
|
-
endpoint:
|
|
10576
|
+
endpoint: z16.string().optional().describe(
|
|
10457
10577
|
"OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
|
|
10458
10578
|
)
|
|
10459
10579
|
},
|
|
@@ -10509,11 +10629,11 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10509
10629
|
}
|
|
10510
10630
|
|
|
10511
10631
|
// src/tools-depletion-events.ts
|
|
10512
|
-
import { z as
|
|
10513
|
-
function
|
|
10632
|
+
import { z as z17 } from "zod";
|
|
10633
|
+
function ok5(payload) {
|
|
10514
10634
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
10515
10635
|
}
|
|
10516
|
-
function
|
|
10636
|
+
function err6(message) {
|
|
10517
10637
|
return { isError: true, content: [{ type: "text", text: message }] };
|
|
10518
10638
|
}
|
|
10519
10639
|
async function ownershipToken(ctx) {
|
|
@@ -10537,10 +10657,10 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10537
10657
|
title: "Subscriber Bundle Depletion Events",
|
|
10538
10658
|
description: "Look up bundle depletion events for a subscriber (or list recent fleet-wide depletions). Returns the most recent bundle.depleted event stored when the prepaid usage webhook received a last_slice notification. Events are retained for 7 days. Use subscriberId to check a specific subscriber, or omit to list the 20 most recent fleet-wide depletions. Requires 'read' scope.",
|
|
10539
10659
|
inputSchema: {
|
|
10540
|
-
subscriberId:
|
|
10660
|
+
subscriberId: z17.string().optional().describe(
|
|
10541
10661
|
"OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
|
|
10542
10662
|
),
|
|
10543
|
-
since:
|
|
10663
|
+
since: z17.string().optional().describe(
|
|
10544
10664
|
"ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
|
|
10545
10665
|
)
|
|
10546
10666
|
},
|
|
@@ -10548,13 +10668,13 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10548
10668
|
},
|
|
10549
10669
|
async (args) => {
|
|
10550
10670
|
if (!ctx.props.scope.includes("read")) {
|
|
10551
|
-
return
|
|
10671
|
+
return err6(
|
|
10552
10672
|
`Scope denied: subscriber_depletion_events requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
10553
10673
|
);
|
|
10554
10674
|
}
|
|
10555
10675
|
const kv = ctx.env.RATE_LIMIT_KV;
|
|
10556
10676
|
if (!kv) {
|
|
10557
|
-
return
|
|
10677
|
+
return err6(
|
|
10558
10678
|
"subscriber_depletion_events cannot answer here. Depletion events are written by the Carrier webhook receiver as they arrive from the network and kept for 7 days in hosted storage; a local stdio server receives no webhooks and has nowhere to keep them, so there is no history on this machine to read. There is nothing to set in your environment. Use the hosted MCP at https://mcp.carrier.llc/mcp for this tool. To see a subscriber's current bundle state instead of its depletion history, `get_subscriber` and `list_subscriber_packages` query OCS directly and work here."
|
|
10559
10679
|
);
|
|
10560
10680
|
}
|
|
@@ -10563,14 +10683,14 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10563
10683
|
try {
|
|
10564
10684
|
scopeToken = await ownershipToken(ctx);
|
|
10565
10685
|
} catch (e) {
|
|
10566
|
-
return
|
|
10686
|
+
return err6(
|
|
10567
10687
|
`Could not resolve the OCS credential needed to scope this read: ${e instanceof Error ? e.message : String(e)}`
|
|
10568
10688
|
);
|
|
10569
10689
|
}
|
|
10570
10690
|
if (subscriberId) {
|
|
10571
10691
|
const raw = await kv.get(`bundle-depleted:${subscriberId}`, "text");
|
|
10572
10692
|
if (!raw) {
|
|
10573
|
-
return
|
|
10693
|
+
return ok5({
|
|
10574
10694
|
subscriber_id: subscriberId,
|
|
10575
10695
|
depleted: false,
|
|
10576
10696
|
event: null,
|
|
@@ -10581,7 +10701,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10581
10701
|
try {
|
|
10582
10702
|
event = JSON.parse(raw);
|
|
10583
10703
|
} catch {
|
|
10584
|
-
return
|
|
10704
|
+
return ok5({
|
|
10585
10705
|
subscriber_id: subscriberId,
|
|
10586
10706
|
depleted: false,
|
|
10587
10707
|
event: null,
|
|
@@ -10589,7 +10709,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10589
10709
|
});
|
|
10590
10710
|
}
|
|
10591
10711
|
if (!await ownsEvent(ctx, scopeToken, event.iccid)) {
|
|
10592
|
-
return
|
|
10712
|
+
return ok5({
|
|
10593
10713
|
subscriber_id: subscriberId,
|
|
10594
10714
|
depleted: false,
|
|
10595
10715
|
event: null,
|
|
@@ -10597,14 +10717,14 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10597
10717
|
});
|
|
10598
10718
|
}
|
|
10599
10719
|
if (since && event.depletedAt < since) {
|
|
10600
|
-
return
|
|
10720
|
+
return ok5({
|
|
10601
10721
|
subscriber_id: subscriberId,
|
|
10602
10722
|
depleted: false,
|
|
10603
10723
|
event: null,
|
|
10604
10724
|
note: `No bundle depletion after ${since}.`
|
|
10605
10725
|
});
|
|
10606
10726
|
}
|
|
10607
|
-
return
|
|
10727
|
+
return ok5({
|
|
10608
10728
|
subscriber_id: subscriberId,
|
|
10609
10729
|
depleted: true,
|
|
10610
10730
|
event: {
|
|
@@ -10618,7 +10738,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10618
10738
|
}
|
|
10619
10739
|
const listResult = await kv.list({ prefix: "bundle-depleted:", limit: 20 });
|
|
10620
10740
|
if (listResult.keys.length === 0) {
|
|
10621
|
-
return
|
|
10741
|
+
return ok5({
|
|
10622
10742
|
depletions: [],
|
|
10623
10743
|
total: 0,
|
|
10624
10744
|
note: "No bundle depletion events in the last 7 days."
|
|
@@ -10645,7 +10765,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10645
10765
|
})
|
|
10646
10766
|
);
|
|
10647
10767
|
events.sort((a, b) => b.depleted_at.localeCompare(a.depleted_at));
|
|
10648
|
-
return
|
|
10768
|
+
return ok5({
|
|
10649
10769
|
depletions: events,
|
|
10650
10770
|
total: events.length,
|
|
10651
10771
|
list_truncated: !listResult.list_complete
|
|
@@ -10655,7 +10775,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10655
10775
|
}
|
|
10656
10776
|
|
|
10657
10777
|
// src/tools-country-history.ts
|
|
10658
|
-
import { z as
|
|
10778
|
+
import { z as z18 } from "zod";
|
|
10659
10779
|
function isoDate(now, daysAgo = 0) {
|
|
10660
10780
|
const d = new Date(now.getTime() - daysAgo * 864e5);
|
|
10661
10781
|
return d.toISOString().slice(0, 10);
|
|
@@ -10678,10 +10798,10 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10678
10798
|
title: "Subscriber Country History",
|
|
10679
10799
|
description: "Return cross-border movement history for a subscriber, reconstructed from OCS Gy charging events. Reads `gyAnswer.events` from `subscriberNetworkEventsOverPeriod` \u2014 every Gy record carries its own country, operator and MCC \u2014 and collapses consecutive same-country records into stays with first-seen and last-seen timestamps. This needs no webhook: it works with Relay LU and Relay Gy off. Relay `country.entered` events from the 24h ring-buffer are merged in as a supplement when present. Use this to answer: 'where has subscriber X traveled', 'show country history for ICCID', 'border crossings for this SIM'. Params: `subscriberId` (ICCID, 19\u201320 digits), `startDate`/`endDate` (YYYY-MM-DD, max 7 days apart, default the last 7 days), `limit` (1\u201350, default 20, newest first). Returns: `stays` ({ country, country_name, first_seen, last_seen, operators, mccs, event_count, used_bytes }), `transitions` ({ from, to, at }), and `country_events` in the legacy flat shape. `used_bytes` already handles the string-typed `usedUnits` and its 'N/A' values. Do NOT use this for raw per-session records \u2014 use `subscriber_network_events` for those.",
|
|
10680
10800
|
inputSchema: {
|
|
10681
|
-
subscriberId:
|
|
10682
|
-
startDate:
|
|
10683
|
-
endDate:
|
|
10684
|
-
limit:
|
|
10801
|
+
subscriberId: z18.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
|
|
10802
|
+
startDate: z18.string().regex(/^\d{4}-\d{2}-\d{2}$/, "startDate must be YYYY-MM-DD").optional().describe("Start of the period, YYYY-MM-DD inclusive (default: 7 days ago)"),
|
|
10803
|
+
endDate: z18.string().regex(/^\d{4}-\d{2}-\d{2}$/, "endDate must be YYYY-MM-DD").optional().describe("End of the period, YYYY-MM-DD inclusive, max 7 days from start (default: today)"),
|
|
10804
|
+
limit: z18.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
|
|
10685
10805
|
},
|
|
10686
10806
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
10687
10807
|
},
|
|
@@ -10712,8 +10832,8 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10712
10832
|
networkEventsOverPeriodParams(subscriberId, start, end)
|
|
10713
10833
|
);
|
|
10714
10834
|
gy = gyCountryHistoryFromResponse(events);
|
|
10715
|
-
} catch (
|
|
10716
|
-
gyError =
|
|
10835
|
+
} catch (err8) {
|
|
10836
|
+
gyError = err8 instanceof Error ? err8.message : String(err8);
|
|
10717
10837
|
}
|
|
10718
10838
|
let syntheticRingIccid = null;
|
|
10719
10839
|
const msisdnRaw = msisdnFieldFromSubscriberPayload(subscriberPayload);
|
|
@@ -10789,7 +10909,7 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10789
10909
|
}
|
|
10790
10910
|
|
|
10791
10911
|
// src/apps/fleet-health-app.ts
|
|
10792
|
-
import { z as
|
|
10912
|
+
import { z as z19 } from "zod";
|
|
10793
10913
|
import {
|
|
10794
10914
|
registerAppTool,
|
|
10795
10915
|
registerAppResource,
|
|
@@ -10840,8 +10960,8 @@ var FLEET_APP_RESOURCE_URI = "ui://fleet-health-dashboard";
|
|
|
10840
10960
|
async function safeCall2(client2, method, params) {
|
|
10841
10961
|
try {
|
|
10842
10962
|
return { data: await client2.call(method, params), error: null };
|
|
10843
|
-
} catch (
|
|
10844
|
-
return { data: null, error:
|
|
10963
|
+
} catch (err8) {
|
|
10964
|
+
return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
10845
10965
|
}
|
|
10846
10966
|
}
|
|
10847
10967
|
async function loadFleet(ctx, accountId) {
|
|
@@ -10873,7 +10993,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10873
10993
|
let html;
|
|
10874
10994
|
try {
|
|
10875
10995
|
html = renderHtml(fleetScreen(await loadFleet(ctx)));
|
|
10876
|
-
} catch (
|
|
10996
|
+
} catch (err8) {
|
|
10877
10997
|
html = renderHtml({
|
|
10878
10998
|
id: "fleet-health",
|
|
10879
10999
|
title: "Fleet health",
|
|
@@ -10881,7 +11001,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10881
11001
|
{
|
|
10882
11002
|
kind: "note",
|
|
10883
11003
|
tone: "critical",
|
|
10884
|
-
text: `Could not load fleet data: ${
|
|
11004
|
+
text: `Could not load fleet data: ${err8 instanceof Error ? err8.message : String(err8)}`
|
|
10885
11005
|
}
|
|
10886
11006
|
]
|
|
10887
11007
|
});
|
|
@@ -10905,7 +11025,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10905
11025
|
title: "Fleet Health Dashboard",
|
|
10906
11026
|
description: "Draw the fleet overview as an interactive panel: eSIM status, per-account breakdown and low-balance alerts. Returns structuredContent for the panel, so it only helps a client that can render MCP UI resources. Do NOT use this when the answer has to be readable text, or in any headless or scripted context \u2014 `fleet_health` returns the same data as prose.",
|
|
10907
11027
|
inputSchema: {
|
|
10908
|
-
accountId:
|
|
11028
|
+
accountId: z19.number().optional().describe("Filter to a specific account (omit for all)")
|
|
10909
11029
|
},
|
|
10910
11030
|
annotations: { readOnlyHint: true },
|
|
10911
11031
|
_meta: {
|
|
@@ -10948,7 +11068,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10948
11068
|
}
|
|
10949
11069
|
|
|
10950
11070
|
// src/apps/provisioning-wizard.ts
|
|
10951
|
-
import { z as
|
|
11071
|
+
import { z as z20 } from "zod";
|
|
10952
11072
|
import {
|
|
10953
11073
|
registerAppTool as registerAppTool2,
|
|
10954
11074
|
registerAppResource as registerAppResource2,
|
|
@@ -11001,10 +11121,10 @@ async function deleteWizardSession(env, sub, wizardId) {
|
|
|
11001
11121
|
async function safeCallWithToken(client2, _token, method, params = {}) {
|
|
11002
11122
|
try {
|
|
11003
11123
|
return { data: await client2.call(method, params), error: null };
|
|
11004
|
-
} catch (
|
|
11124
|
+
} catch (err8) {
|
|
11005
11125
|
return {
|
|
11006
11126
|
data: null,
|
|
11007
|
-
error:
|
|
11127
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
11008
11128
|
};
|
|
11009
11129
|
}
|
|
11010
11130
|
}
|
|
@@ -11053,10 +11173,10 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
11053
11173
|
title: "eSIM Provisioning Wizard",
|
|
11054
11174
|
description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
|
|
11055
11175
|
inputSchema: {
|
|
11056
|
-
step:
|
|
11057
|
-
wizardId:
|
|
11058
|
-
subscriber_iccid:
|
|
11059
|
-
package_template_id:
|
|
11176
|
+
step: z20.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
|
|
11177
|
+
wizardId: z20.string().optional().describe("Wizard session ID (absent on init)"),
|
|
11178
|
+
subscriber_iccid: z20.string().optional().describe("Subscriber ICCID (required for select-package)"),
|
|
11179
|
+
package_template_id: z20.number().optional().describe("Package template ID (required for preview)")
|
|
11060
11180
|
},
|
|
11061
11181
|
_meta: {
|
|
11062
11182
|
ui: {
|
|
@@ -11326,7 +11446,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
11326
11446
|
}
|
|
11327
11447
|
|
|
11328
11448
|
// src/apps/balance-topup.ts
|
|
11329
|
-
import { z as
|
|
11449
|
+
import { z as z21 } from "zod";
|
|
11330
11450
|
import {
|
|
11331
11451
|
registerAppTool as registerAppTool3,
|
|
11332
11452
|
registerAppResource as registerAppResource3,
|
|
@@ -11335,10 +11455,10 @@ import {
|
|
|
11335
11455
|
async function safeCallWithToken2(client2, _token, method, params = {}) {
|
|
11336
11456
|
try {
|
|
11337
11457
|
return { data: await client2.call(method, params), error: null };
|
|
11338
|
-
} catch (
|
|
11458
|
+
} catch (err8) {
|
|
11339
11459
|
return {
|
|
11340
11460
|
data: null,
|
|
11341
|
-
error:
|
|
11461
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
11342
11462
|
};
|
|
11343
11463
|
}
|
|
11344
11464
|
}
|
|
@@ -11379,9 +11499,9 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
11379
11499
|
title: "Balance Top-up Form",
|
|
11380
11500
|
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`.",
|
|
11381
11501
|
inputSchema: {
|
|
11382
|
-
iccid:
|
|
11383
|
-
delta:
|
|
11384
|
-
preview:
|
|
11502
|
+
iccid: z21.string().describe("The subscriber ICCID for the account lookup"),
|
|
11503
|
+
delta: z21.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
|
|
11504
|
+
preview: z21.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
|
|
11385
11505
|
},
|
|
11386
11506
|
annotations: { destructiveHint: true },
|
|
11387
11507
|
_meta: {
|
|
@@ -11464,7 +11584,7 @@ function registerAllApps(server2, ctx) {
|
|
|
11464
11584
|
}
|
|
11465
11585
|
|
|
11466
11586
|
// src/prompts.ts
|
|
11467
|
-
import { z as
|
|
11587
|
+
import { z as z22 } from "zod";
|
|
11468
11588
|
function registerAllPrompts(server2) {
|
|
11469
11589
|
server2.registerPrompt(
|
|
11470
11590
|
"fleet_health_report",
|
|
@@ -11500,7 +11620,7 @@ Format as a structured report with sections, tables, and actionable recommendati
|
|
|
11500
11620
|
{
|
|
11501
11621
|
title: "Subscriber Deep Dive",
|
|
11502
11622
|
description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
|
|
11503
|
-
argsSchema: { iccid:
|
|
11623
|
+
argsSchema: { iccid: z22.string().describe("The subscriber ICCID to analyse") }
|
|
11504
11624
|
},
|
|
11505
11625
|
async ({ iccid }) => ({
|
|
11506
11626
|
messages: [
|
|
@@ -11599,7 +11719,7 @@ Analyze and present:
|
|
|
11599
11719
|
{
|
|
11600
11720
|
title: "Bulk Operations Planner",
|
|
11601
11721
|
description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
|
|
11602
|
-
argsSchema: { operation:
|
|
11722
|
+
argsSchema: { operation: z22.string().describe("Describe the bulk operation you want to perform") }
|
|
11603
11723
|
},
|
|
11604
11724
|
async ({ operation }) => ({
|
|
11605
11725
|
messages: [
|
|
@@ -11632,7 +11752,7 @@ NEVER execute destructive operations without confirmation.`
|
|
|
11632
11752
|
}
|
|
11633
11753
|
|
|
11634
11754
|
// src/tools-pricing.ts
|
|
11635
|
-
import { z as
|
|
11755
|
+
import { z as z23 } from "zod";
|
|
11636
11756
|
function registerAllPricingTools(server2, ctx) {
|
|
11637
11757
|
const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
|
|
11638
11758
|
for (const tool of allTools) {
|
|
@@ -11714,8 +11834,8 @@ function buildPricingHandler(tool, ctx) {
|
|
|
11714
11834
|
}
|
|
11715
11835
|
]
|
|
11716
11836
|
};
|
|
11717
|
-
} catch (
|
|
11718
|
-
const message =
|
|
11837
|
+
} catch (err8) {
|
|
11838
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
11719
11839
|
ctx.audit({
|
|
11720
11840
|
tool_name: tool.name,
|
|
11721
11841
|
ocs_method: "billing",
|
|
@@ -11739,19 +11859,19 @@ function buildZodSchema(schema) {
|
|
|
11739
11859
|
for (const [key, prop] of Object.entries(properties)) {
|
|
11740
11860
|
let field;
|
|
11741
11861
|
if (prop.enum) {
|
|
11742
|
-
field =
|
|
11862
|
+
field = z23.enum(prop.enum);
|
|
11743
11863
|
} else if (prop.type === "number") {
|
|
11744
|
-
field =
|
|
11864
|
+
field = z23.number();
|
|
11745
11865
|
} else if (prop.type === "boolean") {
|
|
11746
|
-
field =
|
|
11866
|
+
field = z23.boolean();
|
|
11747
11867
|
} else if (prop.type === "array") {
|
|
11748
11868
|
if (prop.items?.type === "number") {
|
|
11749
|
-
field =
|
|
11869
|
+
field = z23.array(z23.number());
|
|
11750
11870
|
} else {
|
|
11751
|
-
field =
|
|
11871
|
+
field = z23.array(z23.string());
|
|
11752
11872
|
}
|
|
11753
11873
|
} else {
|
|
11754
|
-
field =
|
|
11874
|
+
field = z23.string();
|
|
11755
11875
|
}
|
|
11756
11876
|
const required = schema.required ?? [];
|
|
11757
11877
|
if (!required.includes(key)) {
|
|
@@ -11766,7 +11886,7 @@ function buildZodSchema(schema) {
|
|
|
11766
11886
|
}
|
|
11767
11887
|
|
|
11768
11888
|
// src/tools-greenzone.ts
|
|
11769
|
-
import { z as
|
|
11889
|
+
import { z as z24 } from "zod";
|
|
11770
11890
|
|
|
11771
11891
|
// src/greenzone-whitelist.ts
|
|
11772
11892
|
var STATE_KEY = "greenzone:state";
|
|
@@ -12043,10 +12163,10 @@ function resolvePortalBaseUrl(env) {
|
|
|
12043
12163
|
function getGreenzoneKv(env) {
|
|
12044
12164
|
return env.GREENZONE_STATE_KV ?? null;
|
|
12045
12165
|
}
|
|
12046
|
-
function
|
|
12166
|
+
function ok6(payload) {
|
|
12047
12167
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
12048
12168
|
}
|
|
12049
|
-
function
|
|
12169
|
+
function err7(message) {
|
|
12050
12170
|
return { isError: true, content: [{ type: "text", text: message }] };
|
|
12051
12171
|
}
|
|
12052
12172
|
var KAPTURE_UNAVAILABLE_MSG = "Execute each Kapture step using mcp__kapture__* tools in sequence. Ensure the Kapture extension is active in Chrome before invoking.";
|
|
@@ -12057,21 +12177,21 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12057
12177
|
title: "Greenzone Whitelist \u2014 Add",
|
|
12058
12178
|
description: "Add a hostname and/or IP address to the Bridge4IP Greenzone whitelist. The Greenzone whitelist controls which hosts/IPs are exempt from data charging after a subscriber's bundle is depleted. This operates via browser automation (Kapture) against the OCS portal \u2014 no REST API exists for this surface. Provide `host` (e.g. 'example.com') and/or `ip` (e.g. '203.0.113.0/24'). At least one of host or ip must be provided. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
|
|
12059
12179
|
inputSchema: {
|
|
12060
|
-
host:
|
|
12061
|
-
ip:
|
|
12062
|
-
dry_run:
|
|
12180
|
+
host: z24.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
|
|
12181
|
+
ip: z24.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
|
|
12182
|
+
dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
12063
12183
|
},
|
|
12064
12184
|
annotations: { destructiveHint: true }
|
|
12065
12185
|
},
|
|
12066
12186
|
async (args) => {
|
|
12067
12187
|
if (!ctx.props.scope.includes("write")) {
|
|
12068
|
-
return
|
|
12188
|
+
return err7(
|
|
12069
12189
|
`Scope denied: greenzone_whitelist_add requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12070
12190
|
);
|
|
12071
12191
|
}
|
|
12072
12192
|
const { host, ip, dry_run } = args;
|
|
12073
12193
|
if (!host && !ip) {
|
|
12074
|
-
return
|
|
12194
|
+
return err7("At least one of 'host' or 'ip' must be provided.");
|
|
12075
12195
|
}
|
|
12076
12196
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12077
12197
|
const steps = [
|
|
@@ -12079,7 +12199,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12079
12199
|
...ip ? buildAddIpSteps(portalBaseUrl, ip) : []
|
|
12080
12200
|
];
|
|
12081
12201
|
if (dry_run) {
|
|
12082
|
-
return
|
|
12202
|
+
return ok6({
|
|
12083
12203
|
dry_run: true,
|
|
12084
12204
|
message: "Would execute the following Kapture steps against the OCS portal:",
|
|
12085
12205
|
kapture_steps: steps,
|
|
@@ -12102,7 +12222,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12102
12222
|
} else {
|
|
12103
12223
|
kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
|
|
12104
12224
|
}
|
|
12105
|
-
return
|
|
12225
|
+
return ok6({
|
|
12106
12226
|
requires_kapture: true,
|
|
12107
12227
|
message: "Execute the Kapture steps below to apply the change in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
|
|
12108
12228
|
kapture_steps: steps,
|
|
@@ -12118,21 +12238,21 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12118
12238
|
title: "Greenzone Whitelist \u2014 Remove",
|
|
12119
12239
|
description: "Remove a hostname and/or IP address from the Bridge4IP Greenzone whitelist. This operates via browser automation (Kapture) against the OCS portal. Provide `host` and/or `ip` to remove \u2014 at least one required. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
|
|
12120
12240
|
inputSchema: {
|
|
12121
|
-
host:
|
|
12122
|
-
ip:
|
|
12123
|
-
dry_run:
|
|
12241
|
+
host: z24.string().optional().describe("Hostname to remove from whitelist"),
|
|
12242
|
+
ip: z24.string().optional().describe("IP address or CIDR to remove from whitelist"),
|
|
12243
|
+
dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
12124
12244
|
},
|
|
12125
12245
|
annotations: { destructiveHint: true }
|
|
12126
12246
|
},
|
|
12127
12247
|
async (args) => {
|
|
12128
12248
|
if (!ctx.props.scope.includes("write")) {
|
|
12129
|
-
return
|
|
12249
|
+
return err7(
|
|
12130
12250
|
`Scope denied: greenzone_whitelist_remove requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12131
12251
|
);
|
|
12132
12252
|
}
|
|
12133
12253
|
const { host, ip, dry_run } = args;
|
|
12134
12254
|
if (!host && !ip) {
|
|
12135
|
-
return
|
|
12255
|
+
return err7("At least one of 'host' or 'ip' must be provided.");
|
|
12136
12256
|
}
|
|
12137
12257
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12138
12258
|
const steps = [
|
|
@@ -12140,7 +12260,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12140
12260
|
...ip ? buildRemoveIpSteps(portalBaseUrl, ip) : []
|
|
12141
12261
|
];
|
|
12142
12262
|
if (dry_run) {
|
|
12143
|
-
return
|
|
12263
|
+
return ok6({
|
|
12144
12264
|
dry_run: true,
|
|
12145
12265
|
message: "Would execute the following Kapture steps to remove from OCS portal:",
|
|
12146
12266
|
kapture_steps: steps,
|
|
@@ -12163,7 +12283,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12163
12283
|
} else {
|
|
12164
12284
|
kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
|
|
12165
12285
|
}
|
|
12166
|
-
return
|
|
12286
|
+
return ok6({
|
|
12167
12287
|
requires_kapture: true,
|
|
12168
12288
|
message: "Execute the Kapture steps below to apply the removal in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
|
|
12169
12289
|
kapture_steps: steps,
|
|
@@ -12179,7 +12299,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12179
12299
|
title: "Greenzone Whitelist \u2014 List",
|
|
12180
12300
|
description: "List current Greenzone whitelist entries from the KV cache. Returns all whitelisted hosts and IPs, when the state was last synced, and whether the last hourly reconciliation detected drift from the portal. This is a fast read from KV \u2014 no browser automation required. Use `from_portal: true` to trigger a live portal read via Kapture instead of KV. Requires 'read' scope.",
|
|
12181
12301
|
inputSchema: {
|
|
12182
|
-
from_portal:
|
|
12302
|
+
from_portal: z24.boolean().optional().describe(
|
|
12183
12303
|
"When true, return Kapture steps to read live portal state instead of KV cache"
|
|
12184
12304
|
)
|
|
12185
12305
|
},
|
|
@@ -12187,14 +12307,14 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12187
12307
|
},
|
|
12188
12308
|
async (args) => {
|
|
12189
12309
|
if (!ctx.props.scope.includes("read")) {
|
|
12190
|
-
return
|
|
12310
|
+
return err7(
|
|
12191
12311
|
`Scope denied: greenzone_whitelist_list requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12192
12312
|
);
|
|
12193
12313
|
}
|
|
12194
12314
|
const { from_portal } = args;
|
|
12195
12315
|
if (from_portal) {
|
|
12196
12316
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12197
|
-
return
|
|
12317
|
+
return ok6({
|
|
12198
12318
|
requires_kapture: true,
|
|
12199
12319
|
message: "Execute the Kapture steps below to read live Greenzone whitelist state from the OCS portal.",
|
|
12200
12320
|
kapture_steps: buildListSteps(portalBaseUrl),
|
|
@@ -12203,7 +12323,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12203
12323
|
}
|
|
12204
12324
|
const kv = getGreenzoneKv(ctx.env);
|
|
12205
12325
|
if (!kv) {
|
|
12206
|
-
return
|
|
12326
|
+
return ok6({
|
|
12207
12327
|
source: "kv_cache",
|
|
12208
12328
|
hosts: [],
|
|
12209
12329
|
ips: [],
|
|
@@ -12215,7 +12335,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12215
12335
|
}
|
|
12216
12336
|
try {
|
|
12217
12337
|
const state = await readState(kv);
|
|
12218
|
-
return
|
|
12338
|
+
return ok6({
|
|
12219
12339
|
source: "kv_cache",
|
|
12220
12340
|
hosts: state.hosts,
|
|
12221
12341
|
ips: state.ips,
|
|
@@ -12224,14 +12344,14 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12224
12344
|
reconcile_drift: state.reconcile_drift ?? false
|
|
12225
12345
|
});
|
|
12226
12346
|
} catch (e) {
|
|
12227
|
-
return
|
|
12347
|
+
return err7(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
|
|
12228
12348
|
}
|
|
12229
12349
|
}
|
|
12230
12350
|
);
|
|
12231
12351
|
}
|
|
12232
12352
|
|
|
12233
12353
|
// src/tools-ui-agent-generic.ts
|
|
12234
|
-
import { z as
|
|
12354
|
+
import { z as z25 } from "zod";
|
|
12235
12355
|
var COST_PER_STEP_USD = 0.014;
|
|
12236
12356
|
var SESSION_OVERHEAD_USD = 5e-3;
|
|
12237
12357
|
function estimateCostUsd2(steps) {
|
|
@@ -12273,13 +12393,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12273
12393
|
title: "Dispatch Generic Browsing Agent",
|
|
12274
12394
|
description: "Send a plain-language task to a hosted browsing agent, which drives a real browser on the open web \u2014 navigating pages, searching, extracting content, filling forms outside the OCS portal. Each run costs roughly $0.20-1.00 in model spend against your own BROWSER_USE_API_KEY, so treat it as the expensive last resort, not a lookup. Limited to CARRIER_BROWSER_USE_MAX_RUNS runs per CLI session (default 10). Params: `prompt` (1-4000 chars \u2014 name the target URL and the exact data or action), `max_steps` (1-30, default 15; prices the dry-run estimate only), `dry_run` (true returns only `estimated_cost_usd` and creates no session). Returns the finished result: unlike the hosted Worker, a local CLI has no background runtime, so this call blocks for the length of the run. Requires `write` scope. Do NOT use this to work out which Carrier tool answers a request \u2014 that is `carrier_ask`, which costs nothing. Do NOT use this for anything the OCS API already covers; every other tool here is faster, free, and audited.",
|
|
12275
12395
|
inputSchema: {
|
|
12276
|
-
prompt:
|
|
12396
|
+
prompt: z25.string().min(1).max(4e3).describe(
|
|
12277
12397
|
"Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
|
|
12278
12398
|
),
|
|
12279
|
-
max_steps:
|
|
12399
|
+
max_steps: z25.number().int().min(1).max(30).optional().describe(
|
|
12280
12400
|
"Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
|
|
12281
12401
|
),
|
|
12282
|
-
dry_run:
|
|
12402
|
+
dry_run: z25.boolean().optional().describe(
|
|
12283
12403
|
"Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
|
|
12284
12404
|
)
|
|
12285
12405
|
}
|
|
@@ -12322,8 +12442,8 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12322
12442
|
}
|
|
12323
12443
|
try {
|
|
12324
12444
|
runtime.budget.reserve();
|
|
12325
|
-
} catch (
|
|
12326
|
-
if (
|
|
12445
|
+
} catch (err8) {
|
|
12446
|
+
if (err8 instanceof SpendLimitError) {
|
|
12327
12447
|
ctx.audit({
|
|
12328
12448
|
tool_name: toolName,
|
|
12329
12449
|
ocs_method: "[ui-agent:generic-ask]",
|
|
@@ -12342,13 +12462,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12342
12462
|
type: "text",
|
|
12343
12463
|
text: JSON.stringify({
|
|
12344
12464
|
error: "run_limit_reached",
|
|
12345
|
-
message:
|
|
12465
|
+
message: err8.message
|
|
12346
12466
|
})
|
|
12347
12467
|
}
|
|
12348
12468
|
]
|
|
12349
12469
|
};
|
|
12350
12470
|
}
|
|
12351
|
-
throw
|
|
12471
|
+
throw err8;
|
|
12352
12472
|
}
|
|
12353
12473
|
try {
|
|
12354
12474
|
const result2 = await runUiAgent(
|
|
@@ -12386,7 +12506,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12386
12506
|
}
|
|
12387
12507
|
]
|
|
12388
12508
|
};
|
|
12389
|
-
} catch (
|
|
12509
|
+
} catch (err8) {
|
|
12390
12510
|
ctx.audit({
|
|
12391
12511
|
tool_name: toolName,
|
|
12392
12512
|
ocs_method: "[ui-agent:generic-ask]",
|
|
@@ -12402,7 +12522,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12402
12522
|
type: "text",
|
|
12403
12523
|
text: JSON.stringify({
|
|
12404
12524
|
error: "ui_agent_failed",
|
|
12405
|
-
message:
|
|
12525
|
+
message: err8 instanceof Error ? err8.message : String(err8)
|
|
12406
12526
|
})
|
|
12407
12527
|
}
|
|
12408
12528
|
]
|
|
@@ -12416,7 +12536,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12416
12536
|
title: "Poll Browsing Agent Task Status",
|
|
12417
12537
|
description: "Returns the current state of a browsing-agent task by `task_id`, including its result once finished. In the local CLI, ui_agent_ask already returns the finished result, so this is mainly for re-reading a task within the same session. Task records do not survive the CLI process exiting. Requires `read` scope.",
|
|
12418
12538
|
inputSchema: {
|
|
12419
|
-
task_id:
|
|
12539
|
+
task_id: z25.string().describe("Task ID returned by ui_agent_ask")
|
|
12420
12540
|
}
|
|
12421
12541
|
},
|
|
12422
12542
|
async (args) => {
|
|
@@ -12478,6 +12598,10 @@ var stdioEnv = {
|
|
|
12478
12598
|
CARRIER_USERS: null,
|
|
12479
12599
|
DOWNLOADS: null,
|
|
12480
12600
|
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY ?? "",
|
|
12601
|
+
// Undefined (not "") when absent so `ORG ?? SECRET` falls back in the
|
|
12602
|
+
// stripe-connect tools; an empty string would shadow the legacy key.
|
|
12603
|
+
STRIPE_ORG_SECRET_KEY: process.env.STRIPE_ORG_SECRET_KEY,
|
|
12604
|
+
STRIPE_PLATFORM_ACCOUNT_ID: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
|
|
12481
12605
|
// Browser Use Cloud. Read from the environment with a real fallback, the way
|
|
12482
12606
|
// STRIPE_SECRET_KEY above and the wallet values below are — unlike the
|
|
12483
12607
|
// MANUS_API_KEY this replaces, which was hardcoded to "" with no fallback at
|
|
@@ -12564,6 +12688,7 @@ registerStorefrontLogoTools(
|
|
|
12564
12688
|
props
|
|
12565
12689
|
);
|
|
12566
12690
|
registerStorefrontDeployTools(server, props);
|
|
12691
|
+
registerStorefrontPlanTools(server, props);
|
|
12567
12692
|
registerAtlasTools(server, toolCtx);
|
|
12568
12693
|
registerAllPrompts(server);
|
|
12569
12694
|
var transport = new StdioServerTransport();
|