@carrierllc/mcp 0.13.17 → 0.13.19
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-SAZFMURF.js → chunk-NUVSW46O.js} +2 -2
- package/dist/{chunk-SAZFMURF.js.map → chunk-NUVSW46O.js.map} +1 -1
- package/dist/cli.js +32 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.js +377 -172
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- 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-NUVSW46O.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 err9 = new Error("Bedrock call timed out before a usable response");
|
|
295
|
+
err9.name = "TimeoutError";
|
|
296
|
+
return err9;
|
|
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 (err9) {
|
|
321
321
|
if (signal.aborted && throttled) return throttled;
|
|
322
|
-
throw
|
|
322
|
+
throw err9;
|
|
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 (err9) {
|
|
672
|
+
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
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 err9;
|
|
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 (err9) {
|
|
699
|
+
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
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 (err9) {
|
|
841
|
+
console.error(`[browser-use-agent] audit write failed: ${err9.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, z27) => n + (z27.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((z27) => {
|
|
1081
|
+
const ops = z27.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 { ...z27, operators: sliced, operatorCount: ops.length };
|
|
1088
1088
|
}
|
|
1089
1089
|
return {
|
|
1090
|
-
zoneId:
|
|
1091
|
-
zoneName:
|
|
1090
|
+
zoneId: z27.zoneId,
|
|
1091
|
+
zoneName: z27.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 (err9) {
|
|
1282
1282
|
try {
|
|
1283
|
-
Sentry.captureException(
|
|
1283
|
+
Sentry.captureException(err9, {
|
|
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 = err9 instanceof Error ? err9.message : String(err9);
|
|
1293
|
+
const ocsCode = err9 instanceof OcsApiError ? err9.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 (err9) {
|
|
3672
|
+
return { data: null, error: err9 instanceof Error ? err9.message : String(err9) };
|
|
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 z15 } from "zod";
|
|
5087
5087
|
|
|
5088
5088
|
// src/tools-ui-agent.ts
|
|
5089
5089
|
import { z as z4 } from "zod";
|
|
@@ -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 (err9) {
|
|
5360
|
+
if (err9 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: err9.message },
|
|
5373
5373
|
true
|
|
5374
5374
|
);
|
|
5375
5375
|
}
|
|
5376
|
-
throw
|
|
5376
|
+
throw err9;
|
|
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 (err9) {
|
|
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: ${err9 instanceof Error ? err9.message : String(err9)}`
|
|
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 (err9) {
|
|
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: ${err9 instanceof Error ? err9.message : String(err9)}`
|
|
5954
5954
|
}
|
|
5955
5955
|
]
|
|
5956
5956
|
};
|
|
@@ -6827,6 +6827,36 @@ 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
|
+
var ORG_KEY_NEEDS_PLATFORM_ACCOUNT = "STRIPE_ORG_SECRET_KEY is set but STRIPE_PLATFORM_ACCOUNT_ID is not. Organization keys reach a connected account only through Stripe-Context <platform>/<connected>, so set STRIPE_PLATFORM_ACCOUNT_ID to your platform's acct_ id (or unset STRIPE_ORG_SECRET_KEY to use STRIPE_SECRET_KEY).";
|
|
6836
|
+
function stripeAccountHeaders(stripeKey, connectedAccountId, platformAccountId) {
|
|
6837
|
+
if (isStripeOrgKey(stripeKey)) {
|
|
6838
|
+
if (connectedAccountId) {
|
|
6839
|
+
if (!platformAccountId) throw new Error(ORG_KEY_NEEDS_PLATFORM_ACCOUNT);
|
|
6840
|
+
return { "Stripe-Context": `${platformAccountId}/${connectedAccountId}` };
|
|
6841
|
+
}
|
|
6842
|
+
return platformAccountId ? { "Stripe-Context": platformAccountId } : {};
|
|
6843
|
+
}
|
|
6844
|
+
if (connectedAccountId) return { "Stripe-Account": connectedAccountId };
|
|
6845
|
+
return {};
|
|
6846
|
+
}
|
|
6847
|
+
function buildStripeHeaders(stripeKey, opts = {}) {
|
|
6848
|
+
if (opts.requirePlatformScope && isStripeOrgKey(stripeKey) && !opts.platformAccountId) {
|
|
6849
|
+
throw new Error(ORG_KEY_NEEDS_PLATFORM_ACCOUNT);
|
|
6850
|
+
}
|
|
6851
|
+
const headers = {
|
|
6852
|
+
Authorization: `Bearer ${stripeKey}`,
|
|
6853
|
+
"Stripe-Version": STRIPE_API_VERSION,
|
|
6854
|
+
...stripeAccountHeaders(stripeKey, opts.connectedAccountId, opts.platformAccountId)
|
|
6855
|
+
};
|
|
6856
|
+
if (opts.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
6857
|
+
return headers;
|
|
6858
|
+
}
|
|
6859
|
+
|
|
6830
6860
|
// src/stripe-connect-tools.ts
|
|
6831
6861
|
async function verifyConfirmToken(env, sub, toolName, token2) {
|
|
6832
6862
|
requireBinding(
|
|
@@ -6861,23 +6891,22 @@ function err2(text) {
|
|
|
6861
6891
|
return { isError: true, content: [{ type: "text", text }] };
|
|
6862
6892
|
}
|
|
6863
6893
|
async function stripeGet(stripeKey, path, connectedAccountId) {
|
|
6864
|
-
const headers = {
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6894
|
+
const headers = buildStripeHeaders(stripeKey, {
|
|
6895
|
+
connectedAccountId,
|
|
6896
|
+
platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
|
|
6897
|
+
requirePlatformScope: path.startsWith("/v1/accounts/")
|
|
6898
|
+
});
|
|
6869
6899
|
const res = await fetch(`https://api.stripe.com${path}`, { headers });
|
|
6870
6900
|
const data = await res.json();
|
|
6871
6901
|
return { ok: res.ok, status: res.status, data };
|
|
6872
6902
|
}
|
|
6873
6903
|
async function stripePost(stripeKey, path, params, connectedAccountId, idempotencyKey) {
|
|
6874
|
-
const headers = {
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
};
|
|
6879
|
-
|
|
6880
|
-
if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
|
|
6904
|
+
const headers = buildStripeHeaders(stripeKey, {
|
|
6905
|
+
connectedAccountId,
|
|
6906
|
+
platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
|
|
6907
|
+
idempotencyKey
|
|
6908
|
+
});
|
|
6909
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
6881
6910
|
const res = await fetch(`https://api.stripe.com${path}`, {
|
|
6882
6911
|
method: "POST",
|
|
6883
6912
|
headers,
|
|
@@ -6932,7 +6961,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
6932
6961
|
const opKvKey = operator_id ? `stripe_account:${operator_id}` : kvKey;
|
|
6933
6962
|
const start = Date.now();
|
|
6934
6963
|
try {
|
|
6935
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
6964
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
6936
6965
|
if (!stripeKey) return err2("Stripe not configured");
|
|
6937
6966
|
const accountId = await env.CARRIER_USERS.get(opKvKey);
|
|
6938
6967
|
if (!accountId) {
|
|
@@ -6985,7 +7014,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
6985
7014
|
if (denied) return denied;
|
|
6986
7015
|
const start = Date.now();
|
|
6987
7016
|
try {
|
|
6988
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7017
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
6989
7018
|
if (!stripeKey) return err2("Stripe not configured");
|
|
6990
7019
|
const accountId = await getAccountId();
|
|
6991
7020
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7022,7 +7051,7 @@ function registerStripeConnectTools(server2, ctx) {
|
|
|
7022
7051
|
if (denied) return denied;
|
|
7023
7052
|
const start = Date.now();
|
|
7024
7053
|
try {
|
|
7025
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7054
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7026
7055
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7027
7056
|
const accountId = await getAccountId();
|
|
7028
7057
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7079,7 +7108,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7079
7108
|
return err2("Invalid or expired confirm_token. Call without token to get a new one.");
|
|
7080
7109
|
}
|
|
7081
7110
|
try {
|
|
7082
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7111
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7083
7112
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7084
7113
|
const accountId = await getAccountId();
|
|
7085
7114
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7126,7 +7155,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7126
7155
|
if (denied) return denied;
|
|
7127
7156
|
const start = Date.now();
|
|
7128
7157
|
try {
|
|
7129
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7158
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7130
7159
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7131
7160
|
const accountId = await getAccountId();
|
|
7132
7161
|
if (!accountId) return err2("No connected Stripe account found.");
|
|
@@ -7171,7 +7200,7 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
|
|
|
7171
7200
|
}
|
|
7172
7201
|
const start = Date.now();
|
|
7173
7202
|
try {
|
|
7174
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7203
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7175
7204
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7176
7205
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
7177
7206
|
if (open_only) params.set("open", "true");
|
|
@@ -7225,7 +7254,7 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
|
|
|
7225
7254
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7226
7255
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7227
7256
|
try {
|
|
7228
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7257
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7229
7258
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7230
7259
|
const result2 = await stripePost(
|
|
7231
7260
|
stripeKey,
|
|
@@ -7281,7 +7310,7 @@ Expires in 5 minutes.`
|
|
|
7281
7310
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7282
7311
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7283
7312
|
try {
|
|
7284
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7313
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7285
7314
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7286
7315
|
const result2 = await stripePost(
|
|
7287
7316
|
stripeKey,
|
|
@@ -7338,7 +7367,7 @@ Expires in 5 minutes.`
|
|
|
7338
7367
|
const valid = await verifyConfirmToken(env, props2.sub, toolName, confirm_token);
|
|
7339
7368
|
if (!valid) return err2("Invalid or expired confirm_token.");
|
|
7340
7369
|
try {
|
|
7341
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
7370
|
+
const stripeKey = env.STRIPE_ORG_SECRET_KEY ?? env.STRIPE_SECRET_KEY;
|
|
7342
7371
|
if (!stripeKey) return err2("Stripe not configured");
|
|
7343
7372
|
const result2 = await stripePost(
|
|
7344
7373
|
stripeKey,
|
|
@@ -7728,8 +7757,8 @@ async function atlasPublicGet(baseUrl2, path) {
|
|
|
7728
7757
|
headers: { Accept: "application/json" },
|
|
7729
7758
|
signal: AbortSignal.timeout(8e3)
|
|
7730
7759
|
});
|
|
7731
|
-
} catch (
|
|
7732
|
-
const message =
|
|
7760
|
+
} catch (err9) {
|
|
7761
|
+
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
7733
7762
|
throw new Error(`Atlas request failed: ${message}`);
|
|
7734
7763
|
}
|
|
7735
7764
|
const text = await response.text();
|
|
@@ -7926,6 +7955,176 @@ function registerNetworkDefaultTools(server2, ctx) {
|
|
|
7926
7955
|
);
|
|
7927
7956
|
}
|
|
7928
7957
|
|
|
7958
|
+
// src/affiliate-tools.ts
|
|
7959
|
+
import { z as z14 } from "zod";
|
|
7960
|
+
var AFFILIATE_TOOL_SCOPES = {
|
|
7961
|
+
affiliate_status: "read",
|
|
7962
|
+
affiliate_commissions: "read",
|
|
7963
|
+
affiliate_join: "write"
|
|
7964
|
+
};
|
|
7965
|
+
var REF_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
7966
|
+
var LINK_BASE = "https://app.carrier.llc/sign-up";
|
|
7967
|
+
function newRefCode() {
|
|
7968
|
+
const bytes = crypto.getRandomValues(new Uint8Array(8));
|
|
7969
|
+
return Array.from(bytes, (b) => REF_ALPHABET[b % REF_ALPHABET.length]).join("");
|
|
7970
|
+
}
|
|
7971
|
+
var linkFor = (code) => `${LINK_BASE}?ref=${code}`;
|
|
7972
|
+
function ok5(text) {
|
|
7973
|
+
return { content: [{ type: "text", text }] };
|
|
7974
|
+
}
|
|
7975
|
+
function err6(text) {
|
|
7976
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
7977
|
+
}
|
|
7978
|
+
function registerAffiliateTools(server2, ctx) {
|
|
7979
|
+
const { env, props: props2 } = ctx;
|
|
7980
|
+
const operatorId = props2.org_id ?? props2.sub;
|
|
7981
|
+
server2.registerTool(
|
|
7982
|
+
"affiliate_status",
|
|
7983
|
+
{
|
|
7984
|
+
title: "Affiliate Status",
|
|
7985
|
+
description: "Read-only: returns the affiliate profile, referral code, link, counts, money owed and paid, and Connect payouts status for the authenticated operator.",
|
|
7986
|
+
inputSchema: {},
|
|
7987
|
+
annotations: annotationsFor("affiliate_status", "read")
|
|
7988
|
+
},
|
|
7989
|
+
async () => {
|
|
7990
|
+
const denied = denyUnlessScoped(props2, "affiliate_status", AFFILIATE_TOOL_SCOPES);
|
|
7991
|
+
if (denied) return denied;
|
|
7992
|
+
try {
|
|
7993
|
+
requireBinding(env.BILLING_DB, "affiliate_status", "BILLING_DB", "affiliate data");
|
|
7994
|
+
const me = await env.BILLING_DB.prepare("SELECT ref_code, status, created_at FROM affiliates WHERE org_id = ?").bind(operatorId).first();
|
|
7995
|
+
if (!me) {
|
|
7996
|
+
return err6("Not an affiliate yet. Call affiliate_join to join.");
|
|
7997
|
+
}
|
|
7998
|
+
const stats = await env.BILLING_DB.prepare(
|
|
7999
|
+
`SELECT (SELECT COUNT(*) FROM affiliate_referrals WHERE affiliate_org_id = ?) AS referrals,
|
|
8000
|
+
COALESCE((SELECT SUM(commission_cents) FROM affiliate_commissions WHERE affiliate_org_id = ? AND status <> 'paid'), 0) AS pending_cents,
|
|
8001
|
+
COALESCE((SELECT SUM(commission_cents) FROM affiliate_commissions WHERE affiliate_org_id = ? AND status = 'paid'), 0) AS paid_cents,
|
|
8002
|
+
COALESCE((SELECT payouts_enabled FROM operator_stripe_accounts WHERE operator_id = ?), 0) AS payouts_enabled`
|
|
8003
|
+
).bind(operatorId, operatorId, operatorId, operatorId).first();
|
|
8004
|
+
return ok5(
|
|
8005
|
+
JSON.stringify(
|
|
8006
|
+
{
|
|
8007
|
+
ref_code: me.ref_code,
|
|
8008
|
+
link: linkFor(me.ref_code),
|
|
8009
|
+
status: me.status,
|
|
8010
|
+
referrals: stats?.referrals ?? 0,
|
|
8011
|
+
pending_cents: stats?.pending_cents ?? 0,
|
|
8012
|
+
paid_cents: stats?.paid_cents ?? 0,
|
|
8013
|
+
payouts_enabled: Boolean(stats?.payouts_enabled)
|
|
8014
|
+
},
|
|
8015
|
+
null,
|
|
8016
|
+
2
|
|
8017
|
+
)
|
|
8018
|
+
);
|
|
8019
|
+
} catch (e) {
|
|
8020
|
+
return err6(e instanceof Error ? e.message : "unknown");
|
|
8021
|
+
}
|
|
8022
|
+
}
|
|
8023
|
+
);
|
|
8024
|
+
server2.registerTool(
|
|
8025
|
+
"affiliate_commissions",
|
|
8026
|
+
{
|
|
8027
|
+
title: "Affiliate Commissions",
|
|
8028
|
+
description: "Read-only: returns the list of commission records for the authenticated operator.",
|
|
8029
|
+
inputSchema: {
|
|
8030
|
+
limit: z14.number().int().min(1).max(200).optional().default(50).describe("Maximum number of commission records to return (1-200, default: 50).")
|
|
8031
|
+
},
|
|
8032
|
+
annotations: annotationsFor("affiliate_commissions", "read")
|
|
8033
|
+
},
|
|
8034
|
+
async ({ limit }) => {
|
|
8035
|
+
const denied = denyUnlessScoped(props2, "affiliate_commissions", AFFILIATE_TOOL_SCOPES);
|
|
8036
|
+
if (denied) return denied;
|
|
8037
|
+
try {
|
|
8038
|
+
requireBinding(env.BILLING_DB, "affiliate_commissions", "BILLING_DB", "affiliate data");
|
|
8039
|
+
const rawLimit = limit ?? 50;
|
|
8040
|
+
const clampedLimit = Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, rawLimit)) : 50;
|
|
8041
|
+
const rows = await env.BILLING_DB.prepare(
|
|
8042
|
+
`SELECT period, referred_org_id, base_cents, commission_bps, commission_cents, status, unmeasured_rows
|
|
8043
|
+
FROM affiliate_commissions
|
|
8044
|
+
WHERE affiliate_org_id = ?
|
|
8045
|
+
ORDER BY period DESC, id DESC
|
|
8046
|
+
LIMIT ?`
|
|
8047
|
+
).bind(operatorId, clampedLimit).all();
|
|
8048
|
+
return ok5(
|
|
8049
|
+
JSON.stringify(
|
|
8050
|
+
{
|
|
8051
|
+
commissions: rows.results ?? []
|
|
8052
|
+
},
|
|
8053
|
+
null,
|
|
8054
|
+
2
|
|
8055
|
+
)
|
|
8056
|
+
);
|
|
8057
|
+
} catch (e) {
|
|
8058
|
+
return err6(e instanceof Error ? e.message : "unknown");
|
|
8059
|
+
}
|
|
8060
|
+
}
|
|
8061
|
+
);
|
|
8062
|
+
server2.registerTool(
|
|
8063
|
+
"affiliate_join",
|
|
8064
|
+
{
|
|
8065
|
+
title: "Join Affiliate Program",
|
|
8066
|
+
description: "Join the Carrier affiliate program and generate a personal referral code and link.",
|
|
8067
|
+
inputSchema: {},
|
|
8068
|
+
annotations: annotationsFor("affiliate_join", "write")
|
|
8069
|
+
},
|
|
8070
|
+
async () => {
|
|
8071
|
+
const denied = denyUnlessScoped(props2, "affiliate_join", AFFILIATE_TOOL_SCOPES);
|
|
8072
|
+
if (denied) return denied;
|
|
8073
|
+
try {
|
|
8074
|
+
requireBinding(env.BILLING_DB, "affiliate_join", "BILLING_DB", "affiliate data");
|
|
8075
|
+
const existing = await env.BILLING_DB.prepare("SELECT ref_code FROM affiliates WHERE org_id = ?").bind(operatorId).first();
|
|
8076
|
+
if (existing) {
|
|
8077
|
+
return ok5(
|
|
8078
|
+
JSON.stringify(
|
|
8079
|
+
{
|
|
8080
|
+
ref_code: existing.ref_code,
|
|
8081
|
+
link: linkFor(existing.ref_code)
|
|
8082
|
+
},
|
|
8083
|
+
null,
|
|
8084
|
+
2
|
|
8085
|
+
)
|
|
8086
|
+
);
|
|
8087
|
+
}
|
|
8088
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
8089
|
+
const code = newRefCode();
|
|
8090
|
+
try {
|
|
8091
|
+
await env.BILLING_DB.prepare("INSERT INTO affiliates (org_id, ref_code, created_at) VALUES (?, ?, ?)").bind(operatorId, code, Date.now()).run();
|
|
8092
|
+
return ok5(
|
|
8093
|
+
JSON.stringify(
|
|
8094
|
+
{
|
|
8095
|
+
ref_code: code,
|
|
8096
|
+
link: linkFor(code)
|
|
8097
|
+
},
|
|
8098
|
+
null,
|
|
8099
|
+
2
|
|
8100
|
+
)
|
|
8101
|
+
);
|
|
8102
|
+
} catch (errCatch) {
|
|
8103
|
+
const msg = errCatch instanceof Error ? errCatch.message : "";
|
|
8104
|
+
if (!/UNIQUE|constraint/i.test(msg)) throw errCatch;
|
|
8105
|
+
const raced = await env.BILLING_DB.prepare("SELECT ref_code FROM affiliates WHERE org_id = ?").bind(operatorId).first();
|
|
8106
|
+
if (raced) {
|
|
8107
|
+
return ok5(
|
|
8108
|
+
JSON.stringify(
|
|
8109
|
+
{
|
|
8110
|
+
ref_code: raced.ref_code,
|
|
8111
|
+
link: linkFor(raced.ref_code)
|
|
8112
|
+
},
|
|
8113
|
+
null,
|
|
8114
|
+
2
|
|
8115
|
+
)
|
|
8116
|
+
);
|
|
8117
|
+
}
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
return err6("Could not allocate a referral code");
|
|
8121
|
+
} catch (e) {
|
|
8122
|
+
return err6(e instanceof Error ? e.message : "unknown");
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
);
|
|
8126
|
+
}
|
|
8127
|
+
|
|
7929
8128
|
// src/credits.ts
|
|
7930
8129
|
var TIER_CREDIT_ALLOTMENTS = {
|
|
7931
8130
|
free: 5e3,
|
|
@@ -9302,7 +9501,8 @@ var ALL_TOOL_SCOPES = Object.freeze({
|
|
|
9302
9501
|
...STOREFRONT_DEPLOY_TOOL_SCOPES,
|
|
9303
9502
|
...STOREFRONT_PLAN_TOOL_SCOPES,
|
|
9304
9503
|
...ATLAS_TOOL_SCOPES,
|
|
9305
|
-
...NETWORK_DEFAULT_TOOL_SCOPES
|
|
9504
|
+
...NETWORK_DEFAULT_TOOL_SCOPES,
|
|
9505
|
+
...AFFILIATE_TOOL_SCOPES
|
|
9306
9506
|
});
|
|
9307
9507
|
var TOOL_REGISTRY = new Set(Object.keys(ALL_TOOL_SCOPES));
|
|
9308
9508
|
var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
|
|
@@ -9863,16 +10063,16 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
|
|
|
9863
10063
|
...onUsage ? { onUsage } : {}
|
|
9864
10064
|
}
|
|
9865
10065
|
);
|
|
9866
|
-
} catch (
|
|
9867
|
-
if (
|
|
9868
|
-
const parsedSeconds =
|
|
10066
|
+
} catch (err9) {
|
|
10067
|
+
if (err9 instanceof BedrockRateLimitError) {
|
|
10068
|
+
const parsedSeconds = err9.retryAfter ? parseInt(err9.retryAfter, 10) : 60;
|
|
9869
10069
|
return {
|
|
9870
10070
|
match: "rate_limited",
|
|
9871
10071
|
retry_after_seconds: Number.isFinite(parsedSeconds) ? parsedSeconds : 60,
|
|
9872
10072
|
suggestion: "Bedrock rate limit reached. Please retry after the indicated delay."
|
|
9873
10073
|
};
|
|
9874
10074
|
}
|
|
9875
|
-
throw
|
|
10075
|
+
throw err9;
|
|
9876
10076
|
}
|
|
9877
10077
|
if (!toolCall) {
|
|
9878
10078
|
return {
|
|
@@ -9977,15 +10177,15 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9977
10177
|
title: "Natural Language Carrier Tool Router",
|
|
9978
10178
|
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.",
|
|
9979
10179
|
inputSchema: {
|
|
9980
|
-
intent:
|
|
10180
|
+
intent: z15.string().min(3).describe(
|
|
9981
10181
|
"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...')"
|
|
9982
10182
|
),
|
|
9983
|
-
context:
|
|
9984
|
-
iccid:
|
|
9985
|
-
account_id:
|
|
9986
|
-
reseller_id:
|
|
10183
|
+
context: z15.object({
|
|
10184
|
+
iccid: z15.string().optional().describe("Subscriber ICCID if already known"),
|
|
10185
|
+
account_id: z15.number().optional().describe("Account ID if already known"),
|
|
10186
|
+
reseller_id: z15.number().optional().describe("Reseller ID if already known")
|
|
9987
10187
|
}).optional().describe("Optional pre-resolved context to improve routing accuracy"),
|
|
9988
|
-
confirm_token:
|
|
10188
|
+
confirm_token: z15.string().optional().describe(
|
|
9989
10189
|
"One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
|
|
9990
10190
|
)
|
|
9991
10191
|
}
|
|
@@ -10086,8 +10286,8 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10086
10286
|
modelRan = true;
|
|
10087
10287
|
}
|
|
10088
10288
|
});
|
|
10089
|
-
} catch (
|
|
10090
|
-
if (
|
|
10289
|
+
} catch (err9) {
|
|
10290
|
+
if (err9 instanceof AskRouterNotConfiguredError) {
|
|
10091
10291
|
writeCarrierAskAudit(ctx.env, {
|
|
10092
10292
|
intent_hash: intentHash,
|
|
10093
10293
|
match: "error",
|
|
@@ -10107,8 +10307,8 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10107
10307
|
type: "text",
|
|
10108
10308
|
text: JSON.stringify({
|
|
10109
10309
|
error: "router_not_configured",
|
|
10110
|
-
missing:
|
|
10111
|
-
message:
|
|
10310
|
+
missing: err9.missing,
|
|
10311
|
+
message: err9.message
|
|
10112
10312
|
})
|
|
10113
10313
|
}
|
|
10114
10314
|
],
|
|
@@ -10138,7 +10338,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10138
10338
|
type: "text",
|
|
10139
10339
|
text: JSON.stringify({
|
|
10140
10340
|
error: "routing_error",
|
|
10141
|
-
message:
|
|
10341
|
+
message: err9 instanceof Error ? err9.message : "An unexpected error occurred during routing."
|
|
10142
10342
|
})
|
|
10143
10343
|
}
|
|
10144
10344
|
],
|
|
@@ -10189,7 +10389,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
10189
10389
|
title: "Describe a Carrier Tool",
|
|
10190
10390
|
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.",
|
|
10191
10391
|
inputSchema: {
|
|
10192
|
-
tool_name:
|
|
10392
|
+
tool_name: z15.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
|
|
10193
10393
|
}
|
|
10194
10394
|
},
|
|
10195
10395
|
wrapHandler(
|
|
@@ -10282,7 +10482,7 @@ function buildExamples(toolName) {
|
|
|
10282
10482
|
}
|
|
10283
10483
|
|
|
10284
10484
|
// src/list-recent-ocs-events.ts
|
|
10285
|
-
import { z as
|
|
10485
|
+
import { z as z16 } from "zod";
|
|
10286
10486
|
|
|
10287
10487
|
// src/ocs-scoping.ts
|
|
10288
10488
|
function managedOwnAccountId(auth) {
|
|
@@ -10315,10 +10515,10 @@ async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
|
|
|
10315
10515
|
return { ok: false, error: "Subscriber not found" };
|
|
10316
10516
|
}
|
|
10317
10517
|
return { ok: true };
|
|
10318
|
-
} catch (
|
|
10518
|
+
} catch (err9) {
|
|
10319
10519
|
return {
|
|
10320
10520
|
ok: false,
|
|
10321
|
-
error:
|
|
10521
|
+
error: err9 instanceof Error ? err9.message : String(err9)
|
|
10322
10522
|
};
|
|
10323
10523
|
}
|
|
10324
10524
|
}
|
|
@@ -10450,10 +10650,10 @@ var ALLOWED_EVENT_TYPES = [
|
|
|
10450
10650
|
"usage.anomaly"
|
|
10451
10651
|
];
|
|
10452
10652
|
var listRecentOcsEventsSchema = {
|
|
10453
|
-
iccid:
|
|
10454
|
-
limit:
|
|
10455
|
-
event_types:
|
|
10456
|
-
since:
|
|
10653
|
+
iccid: z16.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
|
|
10654
|
+
limit: z16.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
|
|
10655
|
+
event_types: z16.array(z16.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
|
|
10656
|
+
since: z16.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
|
|
10457
10657
|
};
|
|
10458
10658
|
async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
|
|
10459
10659
|
return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
|
|
@@ -10504,8 +10704,8 @@ function registerListRecentOcsEventsTool(server2, ctx) {
|
|
|
10504
10704
|
return {
|
|
10505
10705
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
|
|
10506
10706
|
};
|
|
10507
|
-
} catch (
|
|
10508
|
-
const message =
|
|
10707
|
+
} catch (err9) {
|
|
10708
|
+
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
10509
10709
|
return {
|
|
10510
10710
|
isError: true,
|
|
10511
10711
|
content: [{ type: "text", text: `Error reading OCS event buffer: ${message}` }]
|
|
@@ -10516,7 +10716,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
|
|
|
10516
10716
|
}
|
|
10517
10717
|
|
|
10518
10718
|
// src/tools-rate-governor.ts
|
|
10519
|
-
import { z as
|
|
10719
|
+
import { z as z17 } from "zod";
|
|
10520
10720
|
var MONITORED_ENDPOINTS = [
|
|
10521
10721
|
"subscriberNetworkEventsOverPeriod",
|
|
10522
10722
|
// 30/min
|
|
@@ -10548,7 +10748,7 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10548
10748
|
title: "API Rate-Limit Governor Status",
|
|
10549
10749
|
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.",
|
|
10550
10750
|
inputSchema: {
|
|
10551
|
-
endpoint:
|
|
10751
|
+
endpoint: z17.string().optional().describe(
|
|
10552
10752
|
"OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
|
|
10553
10753
|
)
|
|
10554
10754
|
},
|
|
@@ -10604,11 +10804,11 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10604
10804
|
}
|
|
10605
10805
|
|
|
10606
10806
|
// src/tools-depletion-events.ts
|
|
10607
|
-
import { z as
|
|
10608
|
-
function
|
|
10807
|
+
import { z as z18 } from "zod";
|
|
10808
|
+
function ok6(payload) {
|
|
10609
10809
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
10610
10810
|
}
|
|
10611
|
-
function
|
|
10811
|
+
function err7(message) {
|
|
10612
10812
|
return { isError: true, content: [{ type: "text", text: message }] };
|
|
10613
10813
|
}
|
|
10614
10814
|
async function ownershipToken(ctx) {
|
|
@@ -10632,10 +10832,10 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10632
10832
|
title: "Subscriber Bundle Depletion Events",
|
|
10633
10833
|
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.",
|
|
10634
10834
|
inputSchema: {
|
|
10635
|
-
subscriberId:
|
|
10835
|
+
subscriberId: z18.string().optional().describe(
|
|
10636
10836
|
"OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
|
|
10637
10837
|
),
|
|
10638
|
-
since:
|
|
10838
|
+
since: z18.string().optional().describe(
|
|
10639
10839
|
"ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
|
|
10640
10840
|
)
|
|
10641
10841
|
},
|
|
@@ -10643,13 +10843,13 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10643
10843
|
},
|
|
10644
10844
|
async (args) => {
|
|
10645
10845
|
if (!ctx.props.scope.includes("read")) {
|
|
10646
|
-
return
|
|
10846
|
+
return err7(
|
|
10647
10847
|
`Scope denied: subscriber_depletion_events requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
10648
10848
|
);
|
|
10649
10849
|
}
|
|
10650
10850
|
const kv = ctx.env.RATE_LIMIT_KV;
|
|
10651
10851
|
if (!kv) {
|
|
10652
|
-
return
|
|
10852
|
+
return err7(
|
|
10653
10853
|
"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."
|
|
10654
10854
|
);
|
|
10655
10855
|
}
|
|
@@ -10658,14 +10858,14 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10658
10858
|
try {
|
|
10659
10859
|
scopeToken = await ownershipToken(ctx);
|
|
10660
10860
|
} catch (e) {
|
|
10661
|
-
return
|
|
10861
|
+
return err7(
|
|
10662
10862
|
`Could not resolve the OCS credential needed to scope this read: ${e instanceof Error ? e.message : String(e)}`
|
|
10663
10863
|
);
|
|
10664
10864
|
}
|
|
10665
10865
|
if (subscriberId) {
|
|
10666
10866
|
const raw = await kv.get(`bundle-depleted:${subscriberId}`, "text");
|
|
10667
10867
|
if (!raw) {
|
|
10668
|
-
return
|
|
10868
|
+
return ok6({
|
|
10669
10869
|
subscriber_id: subscriberId,
|
|
10670
10870
|
depleted: false,
|
|
10671
10871
|
event: null,
|
|
@@ -10676,7 +10876,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10676
10876
|
try {
|
|
10677
10877
|
event = JSON.parse(raw);
|
|
10678
10878
|
} catch {
|
|
10679
|
-
return
|
|
10879
|
+
return ok6({
|
|
10680
10880
|
subscriber_id: subscriberId,
|
|
10681
10881
|
depleted: false,
|
|
10682
10882
|
event: null,
|
|
@@ -10684,7 +10884,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10684
10884
|
});
|
|
10685
10885
|
}
|
|
10686
10886
|
if (!await ownsEvent(ctx, scopeToken, event.iccid)) {
|
|
10687
|
-
return
|
|
10887
|
+
return ok6({
|
|
10688
10888
|
subscriber_id: subscriberId,
|
|
10689
10889
|
depleted: false,
|
|
10690
10890
|
event: null,
|
|
@@ -10692,14 +10892,14 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10692
10892
|
});
|
|
10693
10893
|
}
|
|
10694
10894
|
if (since && event.depletedAt < since) {
|
|
10695
|
-
return
|
|
10895
|
+
return ok6({
|
|
10696
10896
|
subscriber_id: subscriberId,
|
|
10697
10897
|
depleted: false,
|
|
10698
10898
|
event: null,
|
|
10699
10899
|
note: `No bundle depletion after ${since}.`
|
|
10700
10900
|
});
|
|
10701
10901
|
}
|
|
10702
|
-
return
|
|
10902
|
+
return ok6({
|
|
10703
10903
|
subscriber_id: subscriberId,
|
|
10704
10904
|
depleted: true,
|
|
10705
10905
|
event: {
|
|
@@ -10713,7 +10913,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10713
10913
|
}
|
|
10714
10914
|
const listResult = await kv.list({ prefix: "bundle-depleted:", limit: 20 });
|
|
10715
10915
|
if (listResult.keys.length === 0) {
|
|
10716
|
-
return
|
|
10916
|
+
return ok6({
|
|
10717
10917
|
depletions: [],
|
|
10718
10918
|
total: 0,
|
|
10719
10919
|
note: "No bundle depletion events in the last 7 days."
|
|
@@ -10740,7 +10940,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10740
10940
|
})
|
|
10741
10941
|
);
|
|
10742
10942
|
events.sort((a, b) => b.depleted_at.localeCompare(a.depleted_at));
|
|
10743
|
-
return
|
|
10943
|
+
return ok6({
|
|
10744
10944
|
depletions: events,
|
|
10745
10945
|
total: events.length,
|
|
10746
10946
|
list_truncated: !listResult.list_complete
|
|
@@ -10750,7 +10950,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10750
10950
|
}
|
|
10751
10951
|
|
|
10752
10952
|
// src/tools-country-history.ts
|
|
10753
|
-
import { z as
|
|
10953
|
+
import { z as z19 } from "zod";
|
|
10754
10954
|
function isoDate(now, daysAgo = 0) {
|
|
10755
10955
|
const d = new Date(now.getTime() - daysAgo * 864e5);
|
|
10756
10956
|
return d.toISOString().slice(0, 10);
|
|
@@ -10773,10 +10973,10 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10773
10973
|
title: "Subscriber Country History",
|
|
10774
10974
|
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.",
|
|
10775
10975
|
inputSchema: {
|
|
10776
|
-
subscriberId:
|
|
10777
|
-
startDate:
|
|
10778
|
-
endDate:
|
|
10779
|
-
limit:
|
|
10976
|
+
subscriberId: z19.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
|
|
10977
|
+
startDate: z19.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)"),
|
|
10978
|
+
endDate: z19.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)"),
|
|
10979
|
+
limit: z19.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
|
|
10780
10980
|
},
|
|
10781
10981
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
10782
10982
|
},
|
|
@@ -10807,8 +11007,8 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10807
11007
|
networkEventsOverPeriodParams(subscriberId, start, end)
|
|
10808
11008
|
);
|
|
10809
11009
|
gy = gyCountryHistoryFromResponse(events);
|
|
10810
|
-
} catch (
|
|
10811
|
-
gyError =
|
|
11010
|
+
} catch (err9) {
|
|
11011
|
+
gyError = err9 instanceof Error ? err9.message : String(err9);
|
|
10812
11012
|
}
|
|
10813
11013
|
let syntheticRingIccid = null;
|
|
10814
11014
|
const msisdnRaw = msisdnFieldFromSubscriberPayload(subscriberPayload);
|
|
@@ -10884,7 +11084,7 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10884
11084
|
}
|
|
10885
11085
|
|
|
10886
11086
|
// src/apps/fleet-health-app.ts
|
|
10887
|
-
import { z as
|
|
11087
|
+
import { z as z20 } from "zod";
|
|
10888
11088
|
import {
|
|
10889
11089
|
registerAppTool,
|
|
10890
11090
|
registerAppResource,
|
|
@@ -10935,8 +11135,8 @@ var FLEET_APP_RESOURCE_URI = "ui://fleet-health-dashboard";
|
|
|
10935
11135
|
async function safeCall2(client2, method, params) {
|
|
10936
11136
|
try {
|
|
10937
11137
|
return { data: await client2.call(method, params), error: null };
|
|
10938
|
-
} catch (
|
|
10939
|
-
return { data: null, error:
|
|
11138
|
+
} catch (err9) {
|
|
11139
|
+
return { data: null, error: err9 instanceof Error ? err9.message : String(err9) };
|
|
10940
11140
|
}
|
|
10941
11141
|
}
|
|
10942
11142
|
async function loadFleet(ctx, accountId) {
|
|
@@ -10968,7 +11168,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10968
11168
|
let html;
|
|
10969
11169
|
try {
|
|
10970
11170
|
html = renderHtml(fleetScreen(await loadFleet(ctx)));
|
|
10971
|
-
} catch (
|
|
11171
|
+
} catch (err9) {
|
|
10972
11172
|
html = renderHtml({
|
|
10973
11173
|
id: "fleet-health",
|
|
10974
11174
|
title: "Fleet health",
|
|
@@ -10976,7 +11176,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10976
11176
|
{
|
|
10977
11177
|
kind: "note",
|
|
10978
11178
|
tone: "critical",
|
|
10979
|
-
text: `Could not load fleet data: ${
|
|
11179
|
+
text: `Could not load fleet data: ${err9 instanceof Error ? err9.message : String(err9)}`
|
|
10980
11180
|
}
|
|
10981
11181
|
]
|
|
10982
11182
|
});
|
|
@@ -11000,7 +11200,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
11000
11200
|
title: "Fleet Health Dashboard",
|
|
11001
11201
|
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.",
|
|
11002
11202
|
inputSchema: {
|
|
11003
|
-
accountId:
|
|
11203
|
+
accountId: z20.number().optional().describe("Filter to a specific account (omit for all)")
|
|
11004
11204
|
},
|
|
11005
11205
|
annotations: { readOnlyHint: true },
|
|
11006
11206
|
_meta: {
|
|
@@ -11043,7 +11243,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
11043
11243
|
}
|
|
11044
11244
|
|
|
11045
11245
|
// src/apps/provisioning-wizard.ts
|
|
11046
|
-
import { z as
|
|
11246
|
+
import { z as z21 } from "zod";
|
|
11047
11247
|
import {
|
|
11048
11248
|
registerAppTool as registerAppTool2,
|
|
11049
11249
|
registerAppResource as registerAppResource2,
|
|
@@ -11096,10 +11296,10 @@ async function deleteWizardSession(env, sub, wizardId) {
|
|
|
11096
11296
|
async function safeCallWithToken(client2, _token, method, params = {}) {
|
|
11097
11297
|
try {
|
|
11098
11298
|
return { data: await client2.call(method, params), error: null };
|
|
11099
|
-
} catch (
|
|
11299
|
+
} catch (err9) {
|
|
11100
11300
|
return {
|
|
11101
11301
|
data: null,
|
|
11102
|
-
error:
|
|
11302
|
+
error: err9 instanceof Error ? err9.message : String(err9)
|
|
11103
11303
|
};
|
|
11104
11304
|
}
|
|
11105
11305
|
}
|
|
@@ -11148,10 +11348,10 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
11148
11348
|
title: "eSIM Provisioning Wizard",
|
|
11149
11349
|
description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
|
|
11150
11350
|
inputSchema: {
|
|
11151
|
-
step:
|
|
11152
|
-
wizardId:
|
|
11153
|
-
subscriber_iccid:
|
|
11154
|
-
package_template_id:
|
|
11351
|
+
step: z21.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
|
|
11352
|
+
wizardId: z21.string().optional().describe("Wizard session ID (absent on init)"),
|
|
11353
|
+
subscriber_iccid: z21.string().optional().describe("Subscriber ICCID (required for select-package)"),
|
|
11354
|
+
package_template_id: z21.number().optional().describe("Package template ID (required for preview)")
|
|
11155
11355
|
},
|
|
11156
11356
|
_meta: {
|
|
11157
11357
|
ui: {
|
|
@@ -11421,7 +11621,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
11421
11621
|
}
|
|
11422
11622
|
|
|
11423
11623
|
// src/apps/balance-topup.ts
|
|
11424
|
-
import { z as
|
|
11624
|
+
import { z as z22 } from "zod";
|
|
11425
11625
|
import {
|
|
11426
11626
|
registerAppTool as registerAppTool3,
|
|
11427
11627
|
registerAppResource as registerAppResource3,
|
|
@@ -11430,10 +11630,10 @@ import {
|
|
|
11430
11630
|
async function safeCallWithToken2(client2, _token, method, params = {}) {
|
|
11431
11631
|
try {
|
|
11432
11632
|
return { data: await client2.call(method, params), error: null };
|
|
11433
|
-
} catch (
|
|
11633
|
+
} catch (err9) {
|
|
11434
11634
|
return {
|
|
11435
11635
|
data: null,
|
|
11436
|
-
error:
|
|
11636
|
+
error: err9 instanceof Error ? err9.message : String(err9)
|
|
11437
11637
|
};
|
|
11438
11638
|
}
|
|
11439
11639
|
}
|
|
@@ -11474,9 +11674,9 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
11474
11674
|
title: "Balance Top-up Form",
|
|
11475
11675
|
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`.",
|
|
11476
11676
|
inputSchema: {
|
|
11477
|
-
iccid:
|
|
11478
|
-
delta:
|
|
11479
|
-
preview:
|
|
11677
|
+
iccid: z22.string().describe("The subscriber ICCID for the account lookup"),
|
|
11678
|
+
delta: z22.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
|
|
11679
|
+
preview: z22.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
|
|
11480
11680
|
},
|
|
11481
11681
|
annotations: { destructiveHint: true },
|
|
11482
11682
|
_meta: {
|
|
@@ -11559,7 +11759,7 @@ function registerAllApps(server2, ctx) {
|
|
|
11559
11759
|
}
|
|
11560
11760
|
|
|
11561
11761
|
// src/prompts.ts
|
|
11562
|
-
import { z as
|
|
11762
|
+
import { z as z23 } from "zod";
|
|
11563
11763
|
function registerAllPrompts(server2) {
|
|
11564
11764
|
server2.registerPrompt(
|
|
11565
11765
|
"fleet_health_report",
|
|
@@ -11595,7 +11795,7 @@ Format as a structured report with sections, tables, and actionable recommendati
|
|
|
11595
11795
|
{
|
|
11596
11796
|
title: "Subscriber Deep Dive",
|
|
11597
11797
|
description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
|
|
11598
|
-
argsSchema: { iccid:
|
|
11798
|
+
argsSchema: { iccid: z23.string().describe("The subscriber ICCID to analyse") }
|
|
11599
11799
|
},
|
|
11600
11800
|
async ({ iccid }) => ({
|
|
11601
11801
|
messages: [
|
|
@@ -11694,7 +11894,7 @@ Analyze and present:
|
|
|
11694
11894
|
{
|
|
11695
11895
|
title: "Bulk Operations Planner",
|
|
11696
11896
|
description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
|
|
11697
|
-
argsSchema: { operation:
|
|
11897
|
+
argsSchema: { operation: z23.string().describe("Describe the bulk operation you want to perform") }
|
|
11698
11898
|
},
|
|
11699
11899
|
async ({ operation }) => ({
|
|
11700
11900
|
messages: [
|
|
@@ -11727,7 +11927,7 @@ NEVER execute destructive operations without confirmation.`
|
|
|
11727
11927
|
}
|
|
11728
11928
|
|
|
11729
11929
|
// src/tools-pricing.ts
|
|
11730
|
-
import { z as
|
|
11930
|
+
import { z as z24 } from "zod";
|
|
11731
11931
|
function registerAllPricingTools(server2, ctx) {
|
|
11732
11932
|
const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
|
|
11733
11933
|
for (const tool of allTools) {
|
|
@@ -11809,8 +12009,8 @@ function buildPricingHandler(tool, ctx) {
|
|
|
11809
12009
|
}
|
|
11810
12010
|
]
|
|
11811
12011
|
};
|
|
11812
|
-
} catch (
|
|
11813
|
-
const message =
|
|
12012
|
+
} catch (err9) {
|
|
12013
|
+
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
11814
12014
|
ctx.audit({
|
|
11815
12015
|
tool_name: tool.name,
|
|
11816
12016
|
ocs_method: "billing",
|
|
@@ -11834,19 +12034,19 @@ function buildZodSchema(schema) {
|
|
|
11834
12034
|
for (const [key, prop] of Object.entries(properties)) {
|
|
11835
12035
|
let field;
|
|
11836
12036
|
if (prop.enum) {
|
|
11837
|
-
field =
|
|
12037
|
+
field = z24.enum(prop.enum);
|
|
11838
12038
|
} else if (prop.type === "number") {
|
|
11839
|
-
field =
|
|
12039
|
+
field = z24.number();
|
|
11840
12040
|
} else if (prop.type === "boolean") {
|
|
11841
|
-
field =
|
|
12041
|
+
field = z24.boolean();
|
|
11842
12042
|
} else if (prop.type === "array") {
|
|
11843
12043
|
if (prop.items?.type === "number") {
|
|
11844
|
-
field =
|
|
12044
|
+
field = z24.array(z24.number());
|
|
11845
12045
|
} else {
|
|
11846
|
-
field =
|
|
12046
|
+
field = z24.array(z24.string());
|
|
11847
12047
|
}
|
|
11848
12048
|
} else {
|
|
11849
|
-
field =
|
|
12049
|
+
field = z24.string();
|
|
11850
12050
|
}
|
|
11851
12051
|
const required = schema.required ?? [];
|
|
11852
12052
|
if (!required.includes(key)) {
|
|
@@ -11861,7 +12061,7 @@ function buildZodSchema(schema) {
|
|
|
11861
12061
|
}
|
|
11862
12062
|
|
|
11863
12063
|
// src/tools-greenzone.ts
|
|
11864
|
-
import { z as
|
|
12064
|
+
import { z as z25 } from "zod";
|
|
11865
12065
|
|
|
11866
12066
|
// src/greenzone-whitelist.ts
|
|
11867
12067
|
var STATE_KEY = "greenzone:state";
|
|
@@ -12138,10 +12338,10 @@ function resolvePortalBaseUrl(env) {
|
|
|
12138
12338
|
function getGreenzoneKv(env) {
|
|
12139
12339
|
return env.GREENZONE_STATE_KV ?? null;
|
|
12140
12340
|
}
|
|
12141
|
-
function
|
|
12341
|
+
function ok7(payload) {
|
|
12142
12342
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
12143
12343
|
}
|
|
12144
|
-
function
|
|
12344
|
+
function err8(message) {
|
|
12145
12345
|
return { isError: true, content: [{ type: "text", text: message }] };
|
|
12146
12346
|
}
|
|
12147
12347
|
var KAPTURE_UNAVAILABLE_MSG = "Execute each Kapture step using mcp__kapture__* tools in sequence. Ensure the Kapture extension is active in Chrome before invoking.";
|
|
@@ -12152,21 +12352,21 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12152
12352
|
title: "Greenzone Whitelist \u2014 Add",
|
|
12153
12353
|
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.",
|
|
12154
12354
|
inputSchema: {
|
|
12155
|
-
host:
|
|
12156
|
-
ip:
|
|
12157
|
-
dry_run:
|
|
12355
|
+
host: z25.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
|
|
12356
|
+
ip: z25.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
|
|
12357
|
+
dry_run: z25.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
12158
12358
|
},
|
|
12159
12359
|
annotations: { destructiveHint: true }
|
|
12160
12360
|
},
|
|
12161
12361
|
async (args) => {
|
|
12162
12362
|
if (!ctx.props.scope.includes("write")) {
|
|
12163
|
-
return
|
|
12363
|
+
return err8(
|
|
12164
12364
|
`Scope denied: greenzone_whitelist_add requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12165
12365
|
);
|
|
12166
12366
|
}
|
|
12167
12367
|
const { host, ip, dry_run } = args;
|
|
12168
12368
|
if (!host && !ip) {
|
|
12169
|
-
return
|
|
12369
|
+
return err8("At least one of 'host' or 'ip' must be provided.");
|
|
12170
12370
|
}
|
|
12171
12371
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12172
12372
|
const steps = [
|
|
@@ -12174,7 +12374,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12174
12374
|
...ip ? buildAddIpSteps(portalBaseUrl, ip) : []
|
|
12175
12375
|
];
|
|
12176
12376
|
if (dry_run) {
|
|
12177
|
-
return
|
|
12377
|
+
return ok7({
|
|
12178
12378
|
dry_run: true,
|
|
12179
12379
|
message: "Would execute the following Kapture steps against the OCS portal:",
|
|
12180
12380
|
kapture_steps: steps,
|
|
@@ -12197,7 +12397,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12197
12397
|
} else {
|
|
12198
12398
|
kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
|
|
12199
12399
|
}
|
|
12200
|
-
return
|
|
12400
|
+
return ok7({
|
|
12201
12401
|
requires_kapture: true,
|
|
12202
12402
|
message: "Execute the Kapture steps below to apply the change in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
|
|
12203
12403
|
kapture_steps: steps,
|
|
@@ -12213,21 +12413,21 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12213
12413
|
title: "Greenzone Whitelist \u2014 Remove",
|
|
12214
12414
|
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.",
|
|
12215
12415
|
inputSchema: {
|
|
12216
|
-
host:
|
|
12217
|
-
ip:
|
|
12218
|
-
dry_run:
|
|
12416
|
+
host: z25.string().optional().describe("Hostname to remove from whitelist"),
|
|
12417
|
+
ip: z25.string().optional().describe("IP address or CIDR to remove from whitelist"),
|
|
12418
|
+
dry_run: z25.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
12219
12419
|
},
|
|
12220
12420
|
annotations: { destructiveHint: true }
|
|
12221
12421
|
},
|
|
12222
12422
|
async (args) => {
|
|
12223
12423
|
if (!ctx.props.scope.includes("write")) {
|
|
12224
|
-
return
|
|
12424
|
+
return err8(
|
|
12225
12425
|
`Scope denied: greenzone_whitelist_remove requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12226
12426
|
);
|
|
12227
12427
|
}
|
|
12228
12428
|
const { host, ip, dry_run } = args;
|
|
12229
12429
|
if (!host && !ip) {
|
|
12230
|
-
return
|
|
12430
|
+
return err8("At least one of 'host' or 'ip' must be provided.");
|
|
12231
12431
|
}
|
|
12232
12432
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12233
12433
|
const steps = [
|
|
@@ -12235,7 +12435,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12235
12435
|
...ip ? buildRemoveIpSteps(portalBaseUrl, ip) : []
|
|
12236
12436
|
];
|
|
12237
12437
|
if (dry_run) {
|
|
12238
|
-
return
|
|
12438
|
+
return ok7({
|
|
12239
12439
|
dry_run: true,
|
|
12240
12440
|
message: "Would execute the following Kapture steps to remove from OCS portal:",
|
|
12241
12441
|
kapture_steps: steps,
|
|
@@ -12258,7 +12458,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12258
12458
|
} else {
|
|
12259
12459
|
kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
|
|
12260
12460
|
}
|
|
12261
|
-
return
|
|
12461
|
+
return ok7({
|
|
12262
12462
|
requires_kapture: true,
|
|
12263
12463
|
message: "Execute the Kapture steps below to apply the removal in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
|
|
12264
12464
|
kapture_steps: steps,
|
|
@@ -12274,7 +12474,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12274
12474
|
title: "Greenzone Whitelist \u2014 List",
|
|
12275
12475
|
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.",
|
|
12276
12476
|
inputSchema: {
|
|
12277
|
-
from_portal:
|
|
12477
|
+
from_portal: z25.boolean().optional().describe(
|
|
12278
12478
|
"When true, return Kapture steps to read live portal state instead of KV cache"
|
|
12279
12479
|
)
|
|
12280
12480
|
},
|
|
@@ -12282,14 +12482,14 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12282
12482
|
},
|
|
12283
12483
|
async (args) => {
|
|
12284
12484
|
if (!ctx.props.scope.includes("read")) {
|
|
12285
|
-
return
|
|
12485
|
+
return err8(
|
|
12286
12486
|
`Scope denied: greenzone_whitelist_list requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
|
|
12287
12487
|
);
|
|
12288
12488
|
}
|
|
12289
12489
|
const { from_portal } = args;
|
|
12290
12490
|
if (from_portal) {
|
|
12291
12491
|
const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
|
|
12292
|
-
return
|
|
12492
|
+
return ok7({
|
|
12293
12493
|
requires_kapture: true,
|
|
12294
12494
|
message: "Execute the Kapture steps below to read live Greenzone whitelist state from the OCS portal.",
|
|
12295
12495
|
kapture_steps: buildListSteps(portalBaseUrl),
|
|
@@ -12298,7 +12498,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12298
12498
|
}
|
|
12299
12499
|
const kv = getGreenzoneKv(ctx.env);
|
|
12300
12500
|
if (!kv) {
|
|
12301
|
-
return
|
|
12501
|
+
return ok7({
|
|
12302
12502
|
source: "kv_cache",
|
|
12303
12503
|
hosts: [],
|
|
12304
12504
|
ips: [],
|
|
@@ -12310,7 +12510,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12310
12510
|
}
|
|
12311
12511
|
try {
|
|
12312
12512
|
const state = await readState(kv);
|
|
12313
|
-
return
|
|
12513
|
+
return ok7({
|
|
12314
12514
|
source: "kv_cache",
|
|
12315
12515
|
hosts: state.hosts,
|
|
12316
12516
|
ips: state.ips,
|
|
@@ -12319,14 +12519,14 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12319
12519
|
reconcile_drift: state.reconcile_drift ?? false
|
|
12320
12520
|
});
|
|
12321
12521
|
} catch (e) {
|
|
12322
|
-
return
|
|
12522
|
+
return err8(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
|
|
12323
12523
|
}
|
|
12324
12524
|
}
|
|
12325
12525
|
);
|
|
12326
12526
|
}
|
|
12327
12527
|
|
|
12328
12528
|
// src/tools-ui-agent-generic.ts
|
|
12329
|
-
import { z as
|
|
12529
|
+
import { z as z26 } from "zod";
|
|
12330
12530
|
var COST_PER_STEP_USD = 0.014;
|
|
12331
12531
|
var SESSION_OVERHEAD_USD = 5e-3;
|
|
12332
12532
|
function estimateCostUsd2(steps) {
|
|
@@ -12368,13 +12568,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12368
12568
|
title: "Dispatch Generic Browsing Agent",
|
|
12369
12569
|
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.",
|
|
12370
12570
|
inputSchema: {
|
|
12371
|
-
prompt:
|
|
12571
|
+
prompt: z26.string().min(1).max(4e3).describe(
|
|
12372
12572
|
"Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
|
|
12373
12573
|
),
|
|
12374
|
-
max_steps:
|
|
12574
|
+
max_steps: z26.number().int().min(1).max(30).optional().describe(
|
|
12375
12575
|
"Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
|
|
12376
12576
|
),
|
|
12377
|
-
dry_run:
|
|
12577
|
+
dry_run: z26.boolean().optional().describe(
|
|
12378
12578
|
"Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
|
|
12379
12579
|
)
|
|
12380
12580
|
}
|
|
@@ -12417,8 +12617,8 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12417
12617
|
}
|
|
12418
12618
|
try {
|
|
12419
12619
|
runtime.budget.reserve();
|
|
12420
|
-
} catch (
|
|
12421
|
-
if (
|
|
12620
|
+
} catch (err9) {
|
|
12621
|
+
if (err9 instanceof SpendLimitError) {
|
|
12422
12622
|
ctx.audit({
|
|
12423
12623
|
tool_name: toolName,
|
|
12424
12624
|
ocs_method: "[ui-agent:generic-ask]",
|
|
@@ -12437,13 +12637,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12437
12637
|
type: "text",
|
|
12438
12638
|
text: JSON.stringify({
|
|
12439
12639
|
error: "run_limit_reached",
|
|
12440
|
-
message:
|
|
12640
|
+
message: err9.message
|
|
12441
12641
|
})
|
|
12442
12642
|
}
|
|
12443
12643
|
]
|
|
12444
12644
|
};
|
|
12445
12645
|
}
|
|
12446
|
-
throw
|
|
12646
|
+
throw err9;
|
|
12447
12647
|
}
|
|
12448
12648
|
try {
|
|
12449
12649
|
const result2 = await runUiAgent(
|
|
@@ -12481,7 +12681,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12481
12681
|
}
|
|
12482
12682
|
]
|
|
12483
12683
|
};
|
|
12484
|
-
} catch (
|
|
12684
|
+
} catch (err9) {
|
|
12485
12685
|
ctx.audit({
|
|
12486
12686
|
tool_name: toolName,
|
|
12487
12687
|
ocs_method: "[ui-agent:generic-ask]",
|
|
@@ -12497,7 +12697,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12497
12697
|
type: "text",
|
|
12498
12698
|
text: JSON.stringify({
|
|
12499
12699
|
error: "ui_agent_failed",
|
|
12500
|
-
message:
|
|
12700
|
+
message: err9 instanceof Error ? err9.message : String(err9)
|
|
12501
12701
|
})
|
|
12502
12702
|
}
|
|
12503
12703
|
]
|
|
@@ -12511,7 +12711,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12511
12711
|
title: "Poll Browsing Agent Task Status",
|
|
12512
12712
|
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.",
|
|
12513
12713
|
inputSchema: {
|
|
12514
|
-
task_id:
|
|
12714
|
+
task_id: z26.string().describe("Task ID returned by ui_agent_ask")
|
|
12515
12715
|
}
|
|
12516
12716
|
},
|
|
12517
12717
|
async (args) => {
|
|
@@ -12573,6 +12773,10 @@ var stdioEnv = {
|
|
|
12573
12773
|
CARRIER_USERS: null,
|
|
12574
12774
|
DOWNLOADS: null,
|
|
12575
12775
|
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY ?? "",
|
|
12776
|
+
// Undefined (not "") when absent so `ORG ?? SECRET` falls back in the
|
|
12777
|
+
// stripe-connect tools; an empty string would shadow the legacy key.
|
|
12778
|
+
STRIPE_ORG_SECRET_KEY: process.env.STRIPE_ORG_SECRET_KEY,
|
|
12779
|
+
STRIPE_PLATFORM_ACCOUNT_ID: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
|
|
12576
12780
|
// Browser Use Cloud. Read from the environment with a real fallback, the way
|
|
12577
12781
|
// STRIPE_SECRET_KEY above and the wallet values below are — unlike the
|
|
12578
12782
|
// MANUS_API_KEY this replaces, which was hardcoded to "" with no fallback at
|
|
@@ -12650,6 +12854,7 @@ registerScheduleAndUsageTools(server, toolCtx, uiAgentRuntime);
|
|
|
12650
12854
|
registerAllUiAgentTools(server, toolCtx, uiAgentRuntime, portalOptIn);
|
|
12651
12855
|
registerUiAgentAskTools(server, toolCtx, uiAgentRuntime);
|
|
12652
12856
|
registerStripeConnectTools(server, { env: stdioEnv, props });
|
|
12857
|
+
registerAffiliateTools(server, { env: stdioEnv, props });
|
|
12653
12858
|
registerWalletTools(server, { env: stdioEnv, props });
|
|
12654
12859
|
registerGreenzoneTools(server, toolCtx);
|
|
12655
12860
|
registerUiAgentGenericTools(server, toolCtx, uiAgentRuntime);
|