@carrierllc/mcp 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
subscriberIdParams,
|
|
38
38
|
usageOverPeriodParams,
|
|
39
39
|
verifyStorefront
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-V4CYEMLJ.js";
|
|
41
41
|
import "./chunk-SHKKVIIA.js";
|
|
42
42
|
|
|
43
43
|
// src/index.ts
|
|
@@ -98,17 +98,206 @@ var OcsClient = class {
|
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
// ../../packages/carrier-ai/dist/index.js
|
|
102
|
+
import { AwsClient } from "aws4fetch";
|
|
103
|
+
var AI_TIER_POLICY = {
|
|
104
|
+
free: { limit: 5, window: "day" },
|
|
105
|
+
pro: { limit: 1e3, window: "month" },
|
|
106
|
+
enterprise: { limit: 1e4, window: "month" },
|
|
107
|
+
// Finite, deliberately. This was `Infinity` when the tier was introduced in
|
|
108
|
+
// #762, and Infinity breaks in two ways that both hide themselves:
|
|
109
|
+
//
|
|
110
|
+
// 1. JSON.stringify(Infinity) is "null". The quota endpoint returned
|
|
111
|
+
// {"limit":null,"remaining":null} and the console badge rendered
|
|
112
|
+
// "1 of null used". That is the whole reason this was investigated.
|
|
113
|
+
// 2. An infinite allowance is unmetered Bedrock. A staff account could
|
|
114
|
+
// spend without bound and nothing would refuse it or show a number.
|
|
115
|
+
//
|
|
116
|
+
// 100,000 is roughly $580 of Haiku at the measured $0.0058/call. Nobody on
|
|
117
|
+
// staff will reach it by using the product, and a runaway loop stops.
|
|
118
|
+
superadmin: { limit: 1e5, window: "month" }
|
|
119
|
+
};
|
|
120
|
+
for (const [tier, policy] of Object.entries(AI_TIER_POLICY)) {
|
|
121
|
+
if (!Number.isFinite(policy.limit) || policy.limit <= 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`AI_TIER_POLICY.${tier}.limit must be a positive finite number, got ${policy.limit}. A non-finite limit serialises to null in JSON and leaves spend unmetered.`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var PRELUDE_BYTES = 12;
|
|
128
|
+
var MESSAGE_CRC_BYTES = 4;
|
|
129
|
+
var MIN_FRAME_BYTES = PRELUDE_BYTES + MESSAGE_CRC_BYTES;
|
|
130
|
+
var MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
131
|
+
var MAX_ENTRIES = 16;
|
|
132
|
+
var MAX_KEY_LEN = 256;
|
|
133
|
+
var MAX_VALUE_LEN = 256;
|
|
134
|
+
var ALLOWED = /[^a-zA-Z0-9\s:_@$#=/+,\-.]/g;
|
|
135
|
+
function clean(value, max) {
|
|
136
|
+
return value.replace(ALLOWED, "").slice(0, max);
|
|
137
|
+
}
|
|
138
|
+
var REQUEST_METADATA_HEADER = "X-Amzn-Bedrock-Request-Metadata";
|
|
139
|
+
function requestMetadata(attr2) {
|
|
140
|
+
const out = {};
|
|
141
|
+
const pairs = [
|
|
142
|
+
["product", "carrier"],
|
|
143
|
+
["org", attr2.orgId],
|
|
144
|
+
["subaccount", attr2.subAccountId],
|
|
145
|
+
["surface", attr2.surface],
|
|
146
|
+
["tier", attr2.tier]
|
|
147
|
+
];
|
|
148
|
+
for (const [k, v] of pairs) {
|
|
149
|
+
if (v === void 0 || v === "") continue;
|
|
150
|
+
const key = clean(k, MAX_KEY_LEN);
|
|
151
|
+
const value = clean(v, MAX_VALUE_LEN);
|
|
152
|
+
if (key && value && Object.keys(out).length < MAX_ENTRIES) out[key] = value;
|
|
153
|
+
}
|
|
154
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
155
|
+
}
|
|
156
|
+
var ZERO_USAGE = {
|
|
157
|
+
inputTokens: 0,
|
|
158
|
+
outputTokens: 0,
|
|
159
|
+
cacheReadTokens: 0,
|
|
160
|
+
cacheWriteTokens: 0
|
|
161
|
+
};
|
|
162
|
+
function extractUsage(payload) {
|
|
163
|
+
if (!payload || typeof payload !== "object") return ZERO_USAGE;
|
|
164
|
+
const root = payload;
|
|
165
|
+
const holder = root.usage ?? root.message?.usage;
|
|
166
|
+
if (!holder) return ZERO_USAGE;
|
|
167
|
+
const num = (v) => {
|
|
168
|
+
const n = Number(v);
|
|
169
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
inputTokens: num(holder.input_tokens),
|
|
173
|
+
outputTokens: num(holder.output_tokens),
|
|
174
|
+
cacheReadTokens: num(holder.cache_read_input_tokens),
|
|
175
|
+
cacheWriteTokens: num(holder.cache_creation_input_tokens)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
var HAIKU_RATES = {
|
|
179
|
+
inputPerMTok: 1,
|
|
180
|
+
outputPerMTok: 5,
|
|
181
|
+
cacheReadPerMTok: 0.1,
|
|
182
|
+
cacheWritePerMTok: 1.25,
|
|
183
|
+
asOf: "2026-08-22"
|
|
184
|
+
};
|
|
185
|
+
function estimateCostUsd(u, rates = HAIKU_RATES) {
|
|
186
|
+
const perTok = (mtok) => mtok / 1e6;
|
|
187
|
+
return u.inputTokens * perTok(rates.inputPerMTok) + u.outputTokens * perTok(rates.outputPerMTok) + u.cacheReadTokens * perTok(rates.cacheReadPerMTok) + u.cacheWriteTokens * perTok(rates.cacheWritePerMTok);
|
|
188
|
+
}
|
|
189
|
+
var DEFAULT_BEDROCK_REGION = "us-east-1";
|
|
190
|
+
var DEFAULT_MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0";
|
|
191
|
+
var BEDROCK_TIMEOUT_MS = 3e4;
|
|
192
|
+
var BedrockRateLimitError = class extends Error {
|
|
193
|
+
isRateLimit = true;
|
|
194
|
+
/**
|
|
195
|
+
* The raw `retry-after` header, when Bedrock sent one. Callers surface it as
|
|
196
|
+
* the delay they tell the user to wait; without it they have to guess.
|
|
197
|
+
*/
|
|
198
|
+
retryAfter;
|
|
199
|
+
constructor(message, retryAfter = null) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.name = "BedrockRateLimitError";
|
|
202
|
+
this.retryAfter = retryAfter;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var BedrockError = class extends Error {
|
|
206
|
+
constructor(message, status) {
|
|
207
|
+
super(message);
|
|
208
|
+
this.status = status;
|
|
209
|
+
this.name = "BedrockError";
|
|
210
|
+
}
|
|
211
|
+
status;
|
|
212
|
+
};
|
|
213
|
+
function client(creds) {
|
|
214
|
+
const region = creds.region ?? DEFAULT_BEDROCK_REGION;
|
|
215
|
+
return {
|
|
216
|
+
aws: new AwsClient({
|
|
217
|
+
accessKeyId: creds.accessKeyId,
|
|
218
|
+
secretAccessKey: creds.secretAccessKey,
|
|
219
|
+
...creds.sessionToken ? { sessionToken: creds.sessionToken } : {},
|
|
220
|
+
region,
|
|
221
|
+
service: "bedrock"
|
|
222
|
+
}),
|
|
223
|
+
region,
|
|
224
|
+
modelId: creds.modelId ?? DEFAULT_MODEL_ID
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function body(req) {
|
|
228
|
+
return {
|
|
229
|
+
anthropic_version: "bedrock-2023-05-31",
|
|
230
|
+
max_tokens: req.maxTokens,
|
|
231
|
+
...req.temperature !== void 0 ? { temperature: req.temperature } : {},
|
|
232
|
+
system: [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }],
|
|
233
|
+
messages: req.messages.map((m) => ({ role: m.role, content: m.content })),
|
|
234
|
+
...req.tools ? { tools: req.tools, tool_choice: { type: "any" } } : {}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function headersFor(req, accept) {
|
|
238
|
+
const headers = {
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
Accept: accept
|
|
241
|
+
};
|
|
242
|
+
const meta = req.attribution ? requestMetadata(req.attribution) : null;
|
|
243
|
+
if (meta) headers[REQUEST_METADATA_HEADER] = JSON.stringify(meta);
|
|
244
|
+
return headers;
|
|
245
|
+
}
|
|
246
|
+
function reportUsage(req, usage) {
|
|
247
|
+
if (!req.onUsage) return;
|
|
248
|
+
try {
|
|
249
|
+
req.onUsage(usage);
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function fail(resp) {
|
|
254
|
+
const text = await resp.text().catch(() => "");
|
|
255
|
+
if (resp.status === 429 || /throttl/i.test(text)) {
|
|
256
|
+
throw new BedrockRateLimitError(
|
|
257
|
+
text || "Bedrock throttled the request",
|
|
258
|
+
resp.headers.get("retry-after")
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
throw new BedrockError(text || `Bedrock returned HTTP ${resp.status}`, resp.status);
|
|
262
|
+
}
|
|
263
|
+
async function invokeTool(creds, req, signal) {
|
|
264
|
+
const { aws, region, modelId } = client(creds);
|
|
265
|
+
const controller = new AbortController();
|
|
266
|
+
const timer = setTimeout(() => controller.abort(), req.timeoutMs ?? BEDROCK_TIMEOUT_MS);
|
|
267
|
+
const onAbort = () => controller.abort();
|
|
268
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
269
|
+
try {
|
|
270
|
+
const headers = headersFor(req, "application/json");
|
|
271
|
+
const resp = await aws.fetch(
|
|
272
|
+
`https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`,
|
|
273
|
+
{
|
|
274
|
+
method: "POST",
|
|
275
|
+
headers,
|
|
276
|
+
body: JSON.stringify(body(req)),
|
|
277
|
+
signal: controller.signal
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
if (!resp.ok) await fail(resp);
|
|
281
|
+
const json = await resp.json();
|
|
282
|
+
reportUsage(req, extractUsage(json));
|
|
283
|
+
const block = json.content?.find((b) => b.type === "tool_use");
|
|
284
|
+
return block?.name ? { name: block.name, input: block.input ?? {} } : null;
|
|
285
|
+
} finally {
|
|
286
|
+
clearTimeout(timer);
|
|
287
|
+
signal?.removeEventListener("abort", onAbort);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
101
291
|
// src/billing.ts
|
|
102
292
|
var TIER_CALL_LIMITS = {
|
|
103
|
-
free:
|
|
104
|
-
pro:
|
|
105
|
-
enterprise: Infinity
|
|
293
|
+
free: 1e3,
|
|
294
|
+
pro: Infinity,
|
|
295
|
+
enterprise: Infinity,
|
|
296
|
+
superadmin: Infinity
|
|
106
297
|
};
|
|
298
|
+
var CARRIER_ASK_PRO_MONTHLY_LIMIT = AI_TIER_POLICY.pro.limit;
|
|
299
|
+
var CARRIER_ASK_FREE_DAILY_LIMIT = AI_TIER_POLICY.free.limit;
|
|
107
300
|
var UPGRADE_URL = "https://mcp.carrier.llc/upgrade";
|
|
108
|
-
function getBillingPrimary(env) {
|
|
109
|
-
const raw = env.BILLING_PRIMARY;
|
|
110
|
-
return raw === "clerk" ? "clerk" : "stripe";
|
|
111
|
-
}
|
|
112
301
|
function platformOrgId(env) {
|
|
113
302
|
const raw = env.CARRIER_PLATFORM_ORG_ID;
|
|
114
303
|
if (typeof raw !== "string") return null;
|
|
@@ -123,7 +312,7 @@ function isPlatformOperator(env, orgId) {
|
|
|
123
312
|
async function checkCallQuota(env, sub, tier) {
|
|
124
313
|
const limit = TIER_CALL_LIMITS[tier];
|
|
125
314
|
const resetAt = firstDayNextMonth();
|
|
126
|
-
if (tier === "enterprise") {
|
|
315
|
+
if (tier === "pro" || tier === "enterprise" || tier === "superadmin") {
|
|
127
316
|
return { allowed: true, remaining: Infinity, resetAt, tier };
|
|
128
317
|
}
|
|
129
318
|
if (!env.CARRIER_USERS) {
|
|
@@ -144,6 +333,7 @@ async function checkCallQuota(env, sub, tier) {
|
|
|
144
333
|
function recordUsage(env, sub, tier) {
|
|
145
334
|
if (!env.CARRIER_USERS) return;
|
|
146
335
|
(async () => {
|
|
336
|
+
if (tier === "pro" || tier === "enterprise" || tier === "superadmin") return;
|
|
147
337
|
const month = currentMonth();
|
|
148
338
|
const usageKey = `usage:${sub}:${month}`;
|
|
149
339
|
const raw = await env.CARRIER_USERS.get(usageKey, "json").catch(() => null);
|
|
@@ -154,35 +344,8 @@ function recordUsage(env, sub, tier) {
|
|
|
154
344
|
JSON.stringify({ calls: next, updated_at: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
155
345
|
{ expirationTtl: 35 * 24 * 60 * 60 }
|
|
156
346
|
);
|
|
157
|
-
if (tier === "pro" && next % 100 === 0 && getBillingPrimary(env) === "stripe") {
|
|
158
|
-
await pushStripeUsageRecord(env, sub, 100).catch(() => {
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
347
|
})();
|
|
162
348
|
}
|
|
163
|
-
async function pushStripeUsageRecord(env, sub, quantity) {
|
|
164
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
165
|
-
if (!stripeKey) return;
|
|
166
|
-
if (!env.CARRIER_USERS) return;
|
|
167
|
-
const subItemId = await env.CARRIER_USERS.get(`stripe_sub_item_id:${sub}`);
|
|
168
|
-
if (!subItemId) return;
|
|
169
|
-
const body2 = new URLSearchParams({
|
|
170
|
-
quantity: String(quantity),
|
|
171
|
-
timestamp: String(Math.floor(Date.now() / 1e3)),
|
|
172
|
-
action: "increment"
|
|
173
|
-
});
|
|
174
|
-
await fetch(
|
|
175
|
-
`https://api.stripe.com/v1/subscription_items/${subItemId}/usage_records`,
|
|
176
|
-
{
|
|
177
|
-
method: "POST",
|
|
178
|
-
headers: {
|
|
179
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
180
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
181
|
-
},
|
|
182
|
-
body: body2.toString()
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
349
|
function currentMonth() {
|
|
187
350
|
const now = /* @__PURE__ */ new Date();
|
|
188
351
|
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
@@ -531,8 +694,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler) {
|
|
|
531
694
|
{
|
|
532
695
|
type: "text",
|
|
533
696
|
text: [
|
|
534
|
-
`Quota exceeded: your ${quota.tier} plan has reached the
|
|
535
|
-
`
|
|
697
|
+
`Quota exceeded: your ${quota.tier} plan has reached the monthly limit of ${TIER_CALL_LIMITS.free.toLocaleString()} regular tool calls.`,
|
|
698
|
+
`Resets ${quota.resetAt}.`,
|
|
536
699
|
`Upgrade to Pro for unlimited calls at ${UPGRADE_URL}`
|
|
537
700
|
].join(" ")
|
|
538
701
|
}
|
|
@@ -4675,196 +4838,6 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
4675
4838
|
);
|
|
4676
4839
|
}
|
|
4677
4840
|
|
|
4678
|
-
// ../../packages/carrier-ai/dist/index.js
|
|
4679
|
-
import { AwsClient } from "aws4fetch";
|
|
4680
|
-
var AI_TIER_POLICY = {
|
|
4681
|
-
free: { limit: 5, window: "day" },
|
|
4682
|
-
pro: { limit: 1e3, window: "month" },
|
|
4683
|
-
enterprise: { limit: 1e4, window: "month" },
|
|
4684
|
-
// Finite, deliberately. This was `Infinity` when the tier was introduced in
|
|
4685
|
-
// #762, and Infinity breaks in two ways that both hide themselves:
|
|
4686
|
-
//
|
|
4687
|
-
// 1. JSON.stringify(Infinity) is "null". The quota endpoint returned
|
|
4688
|
-
// {"limit":null,"remaining":null} and the console badge rendered
|
|
4689
|
-
// "1 of null used". That is the whole reason this was investigated.
|
|
4690
|
-
// 2. An infinite allowance is unmetered Bedrock. A staff account could
|
|
4691
|
-
// spend without bound and nothing would refuse it or show a number.
|
|
4692
|
-
//
|
|
4693
|
-
// 100,000 is roughly $580 of Haiku at the measured $0.0058/call. Nobody on
|
|
4694
|
-
// staff will reach it by using the product, and a runaway loop stops.
|
|
4695
|
-
superadmin: { limit: 1e5, window: "month" }
|
|
4696
|
-
};
|
|
4697
|
-
for (const [tier, policy] of Object.entries(AI_TIER_POLICY)) {
|
|
4698
|
-
if (!Number.isFinite(policy.limit) || policy.limit <= 0) {
|
|
4699
|
-
throw new Error(
|
|
4700
|
-
`AI_TIER_POLICY.${tier}.limit must be a positive finite number, got ${policy.limit}. A non-finite limit serialises to null in JSON and leaves spend unmetered.`
|
|
4701
|
-
);
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
var PRELUDE_BYTES = 12;
|
|
4705
|
-
var MESSAGE_CRC_BYTES = 4;
|
|
4706
|
-
var MIN_FRAME_BYTES = PRELUDE_BYTES + MESSAGE_CRC_BYTES;
|
|
4707
|
-
var MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
4708
|
-
var MAX_ENTRIES = 16;
|
|
4709
|
-
var MAX_KEY_LEN = 256;
|
|
4710
|
-
var MAX_VALUE_LEN = 256;
|
|
4711
|
-
var ALLOWED = /[^a-zA-Z0-9\s:_@$#=/+,\-.]/g;
|
|
4712
|
-
function clean(value, max) {
|
|
4713
|
-
return value.replace(ALLOWED, "").slice(0, max);
|
|
4714
|
-
}
|
|
4715
|
-
var REQUEST_METADATA_HEADER = "X-Amzn-Bedrock-Request-Metadata";
|
|
4716
|
-
function requestMetadata(attr2) {
|
|
4717
|
-
const out = {};
|
|
4718
|
-
const pairs = [
|
|
4719
|
-
["product", "carrier"],
|
|
4720
|
-
["org", attr2.orgId],
|
|
4721
|
-
["subaccount", attr2.subAccountId],
|
|
4722
|
-
["surface", attr2.surface],
|
|
4723
|
-
["tier", attr2.tier]
|
|
4724
|
-
];
|
|
4725
|
-
for (const [k, v] of pairs) {
|
|
4726
|
-
if (v === void 0 || v === "") continue;
|
|
4727
|
-
const key = clean(k, MAX_KEY_LEN);
|
|
4728
|
-
const value = clean(v, MAX_VALUE_LEN);
|
|
4729
|
-
if (key && value && Object.keys(out).length < MAX_ENTRIES) out[key] = value;
|
|
4730
|
-
}
|
|
4731
|
-
return Object.keys(out).length > 0 ? out : null;
|
|
4732
|
-
}
|
|
4733
|
-
var ZERO_USAGE = {
|
|
4734
|
-
inputTokens: 0,
|
|
4735
|
-
outputTokens: 0,
|
|
4736
|
-
cacheReadTokens: 0,
|
|
4737
|
-
cacheWriteTokens: 0
|
|
4738
|
-
};
|
|
4739
|
-
function extractUsage(payload) {
|
|
4740
|
-
if (!payload || typeof payload !== "object") return ZERO_USAGE;
|
|
4741
|
-
const root = payload;
|
|
4742
|
-
const holder = root.usage ?? root.message?.usage;
|
|
4743
|
-
if (!holder) return ZERO_USAGE;
|
|
4744
|
-
const num = (v) => {
|
|
4745
|
-
const n = Number(v);
|
|
4746
|
-
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
4747
|
-
};
|
|
4748
|
-
return {
|
|
4749
|
-
inputTokens: num(holder.input_tokens),
|
|
4750
|
-
outputTokens: num(holder.output_tokens),
|
|
4751
|
-
cacheReadTokens: num(holder.cache_read_input_tokens),
|
|
4752
|
-
cacheWriteTokens: num(holder.cache_creation_input_tokens)
|
|
4753
|
-
};
|
|
4754
|
-
}
|
|
4755
|
-
var HAIKU_RATES = {
|
|
4756
|
-
inputPerMTok: 1,
|
|
4757
|
-
outputPerMTok: 5,
|
|
4758
|
-
cacheReadPerMTok: 0.1,
|
|
4759
|
-
cacheWritePerMTok: 1.25,
|
|
4760
|
-
asOf: "2026-08-22"
|
|
4761
|
-
};
|
|
4762
|
-
function estimateCostUsd(u, rates = HAIKU_RATES) {
|
|
4763
|
-
const perTok = (mtok) => mtok / 1e6;
|
|
4764
|
-
return u.inputTokens * perTok(rates.inputPerMTok) + u.outputTokens * perTok(rates.outputPerMTok) + u.cacheReadTokens * perTok(rates.cacheReadPerMTok) + u.cacheWriteTokens * perTok(rates.cacheWritePerMTok);
|
|
4765
|
-
}
|
|
4766
|
-
var DEFAULT_BEDROCK_REGION = "us-east-1";
|
|
4767
|
-
var DEFAULT_MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0";
|
|
4768
|
-
var BEDROCK_TIMEOUT_MS = 3e4;
|
|
4769
|
-
var BedrockRateLimitError = class extends Error {
|
|
4770
|
-
isRateLimit = true;
|
|
4771
|
-
/**
|
|
4772
|
-
* The raw `retry-after` header, when Bedrock sent one. Callers surface it as
|
|
4773
|
-
* the delay they tell the user to wait; without it they have to guess.
|
|
4774
|
-
*/
|
|
4775
|
-
retryAfter;
|
|
4776
|
-
constructor(message, retryAfter = null) {
|
|
4777
|
-
super(message);
|
|
4778
|
-
this.name = "BedrockRateLimitError";
|
|
4779
|
-
this.retryAfter = retryAfter;
|
|
4780
|
-
}
|
|
4781
|
-
};
|
|
4782
|
-
var BedrockError = class extends Error {
|
|
4783
|
-
constructor(message, status) {
|
|
4784
|
-
super(message);
|
|
4785
|
-
this.status = status;
|
|
4786
|
-
this.name = "BedrockError";
|
|
4787
|
-
}
|
|
4788
|
-
status;
|
|
4789
|
-
};
|
|
4790
|
-
function client(creds) {
|
|
4791
|
-
const region = creds.region ?? DEFAULT_BEDROCK_REGION;
|
|
4792
|
-
return {
|
|
4793
|
-
aws: new AwsClient({
|
|
4794
|
-
accessKeyId: creds.accessKeyId,
|
|
4795
|
-
secretAccessKey: creds.secretAccessKey,
|
|
4796
|
-
...creds.sessionToken ? { sessionToken: creds.sessionToken } : {},
|
|
4797
|
-
region,
|
|
4798
|
-
service: "bedrock"
|
|
4799
|
-
}),
|
|
4800
|
-
region,
|
|
4801
|
-
modelId: creds.modelId ?? DEFAULT_MODEL_ID
|
|
4802
|
-
};
|
|
4803
|
-
}
|
|
4804
|
-
function body(req) {
|
|
4805
|
-
return {
|
|
4806
|
-
anthropic_version: "bedrock-2023-05-31",
|
|
4807
|
-
max_tokens: req.maxTokens,
|
|
4808
|
-
...req.temperature !== void 0 ? { temperature: req.temperature } : {},
|
|
4809
|
-
system: [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }],
|
|
4810
|
-
messages: req.messages.map((m) => ({ role: m.role, content: m.content })),
|
|
4811
|
-
...req.tools ? { tools: req.tools, tool_choice: { type: "any" } } : {}
|
|
4812
|
-
};
|
|
4813
|
-
}
|
|
4814
|
-
function headersFor(req, accept) {
|
|
4815
|
-
const headers = {
|
|
4816
|
-
"Content-Type": "application/json",
|
|
4817
|
-
Accept: accept
|
|
4818
|
-
};
|
|
4819
|
-
const meta = req.attribution ? requestMetadata(req.attribution) : null;
|
|
4820
|
-
if (meta) headers[REQUEST_METADATA_HEADER] = JSON.stringify(meta);
|
|
4821
|
-
return headers;
|
|
4822
|
-
}
|
|
4823
|
-
function reportUsage(req, usage) {
|
|
4824
|
-
if (!req.onUsage) return;
|
|
4825
|
-
try {
|
|
4826
|
-
req.onUsage(usage);
|
|
4827
|
-
} catch {
|
|
4828
|
-
}
|
|
4829
|
-
}
|
|
4830
|
-
async function fail(resp) {
|
|
4831
|
-
const text = await resp.text().catch(() => "");
|
|
4832
|
-
if (resp.status === 429 || /throttl/i.test(text)) {
|
|
4833
|
-
throw new BedrockRateLimitError(
|
|
4834
|
-
text || "Bedrock throttled the request",
|
|
4835
|
-
resp.headers.get("retry-after")
|
|
4836
|
-
);
|
|
4837
|
-
}
|
|
4838
|
-
throw new BedrockError(text || `Bedrock returned HTTP ${resp.status}`, resp.status);
|
|
4839
|
-
}
|
|
4840
|
-
async function invokeTool(creds, req, signal) {
|
|
4841
|
-
const { aws, region, modelId } = client(creds);
|
|
4842
|
-
const controller = new AbortController();
|
|
4843
|
-
const timer = setTimeout(() => controller.abort(), req.timeoutMs ?? BEDROCK_TIMEOUT_MS);
|
|
4844
|
-
const onAbort = () => controller.abort();
|
|
4845
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4846
|
-
try {
|
|
4847
|
-
const headers = headersFor(req, "application/json");
|
|
4848
|
-
const resp = await aws.fetch(
|
|
4849
|
-
`https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`,
|
|
4850
|
-
{
|
|
4851
|
-
method: "POST",
|
|
4852
|
-
headers,
|
|
4853
|
-
body: JSON.stringify(body(req)),
|
|
4854
|
-
signal: controller.signal
|
|
4855
|
-
}
|
|
4856
|
-
);
|
|
4857
|
-
if (!resp.ok) await fail(resp);
|
|
4858
|
-
const json = await resp.json();
|
|
4859
|
-
reportUsage(req, extractUsage(json));
|
|
4860
|
-
const block = json.content?.find((b) => b.type === "tool_use");
|
|
4861
|
-
return block?.name ? { name: block.name, input: block.input ?? {} } : null;
|
|
4862
|
-
} finally {
|
|
4863
|
-
clearTimeout(timer);
|
|
4864
|
-
signal?.removeEventListener("abort", onAbort);
|
|
4865
|
-
}
|
|
4866
|
-
}
|
|
4867
|
-
|
|
4868
4841
|
// src/tools-carrier-ask.ts
|
|
4869
4842
|
import { z as z11 } from "zod";
|
|
4870
4843
|
|
|
@@ -5132,6 +5105,7 @@ var UI_AGENT_TOOL_SCOPES = {
|
|
|
5132
5105
|
ui_create_steering_list: "write",
|
|
5133
5106
|
ui_build_steering_list: "write",
|
|
5134
5107
|
ui_set_account_steering_list: "write",
|
|
5108
|
+
ui_request_reseller_relay_change: "write",
|
|
5135
5109
|
ui_create_account: "admin",
|
|
5136
5110
|
ui_create_destination_list: "write",
|
|
5137
5111
|
ui_edit_destination_list: "write",
|
|
@@ -5217,6 +5191,66 @@ Open account ID ${args.account_id}.
|
|
|
5217
5191
|
`
|
|
5218
5192
|
)
|
|
5219
5193
|
);
|
|
5194
|
+
server2.registerTool(
|
|
5195
|
+
"ui_request_reseller_relay_change",
|
|
5196
|
+
{
|
|
5197
|
+
title: "Request Reseller Relay Change",
|
|
5198
|
+
description: "Drafts a parent-reseller/support request for Relay Gy, calls/SMS relay, or VoIP relay when those portal controls are read-only at the current reseller tier. Does not send the request or mutate the portal.",
|
|
5199
|
+
inputSchema: {
|
|
5200
|
+
reseller_id: z4.number().optional().describe("Reseller id; defaults to the authenticated reseller"),
|
|
5201
|
+
relay_gy: z4.boolean().optional().describe("Desired Relay Gy state"),
|
|
5202
|
+
relay_calls_sms: z4.boolean().optional().describe("Desired calls/SMS relay state"),
|
|
5203
|
+
relay_voip: z4.boolean().optional().describe("Desired VoIP relay state"),
|
|
5204
|
+
reason: z4.string().optional().describe("Operational reason for the requested change")
|
|
5205
|
+
}
|
|
5206
|
+
},
|
|
5207
|
+
async (args) => {
|
|
5208
|
+
if (!ctx.props.scope.includes("write")) {
|
|
5209
|
+
return {
|
|
5210
|
+
isError: true,
|
|
5211
|
+
content: [{ type: "text", text: "Scope denied: tool 'ui_request_reseller_relay_change' requires 'write' scope." }]
|
|
5212
|
+
};
|
|
5213
|
+
}
|
|
5214
|
+
const resellerId = args.reseller_id ?? ctx.props.reseller_id;
|
|
5215
|
+
const requested = [
|
|
5216
|
+
args.relay_gy === void 0 ? null : `Relay Gy: ${args.relay_gy ? "enable" : "disable"}`,
|
|
5217
|
+
args.relay_calls_sms === void 0 ? null : `Calls/SMS relay: ${args.relay_calls_sms ? "enable" : "disable"}`,
|
|
5218
|
+
args.relay_voip === void 0 ? null : `VoIP relay: ${args.relay_voip ? "enable" : "disable"}`
|
|
5219
|
+
].filter((value) => value !== null);
|
|
5220
|
+
if (requested.length === 0) {
|
|
5221
|
+
return {
|
|
5222
|
+
isError: true,
|
|
5223
|
+
content: [{ type: "text", text: JSON.stringify({ error: "relay_state_required", message: "Choose at least one relay flag and desired state." }) }]
|
|
5224
|
+
};
|
|
5225
|
+
}
|
|
5226
|
+
const draft = [
|
|
5227
|
+
`Please update the parent-controlled OCS relay settings for reseller ${resellerId}.`,
|
|
5228
|
+
"",
|
|
5229
|
+
...requested.map((line) => `- ${line}`),
|
|
5230
|
+
...args.reason ? ["", `Reason: ${args.reason}`] : [],
|
|
5231
|
+
"",
|
|
5232
|
+
"Please confirm once the change is active so we can verify the dependent OCS operation."
|
|
5233
|
+
].join("\n");
|
|
5234
|
+
ctx.audit({
|
|
5235
|
+
tool_name: "ui_request_reseller_relay_change",
|
|
5236
|
+
ocs_method: "[ui-agent:G-20]",
|
|
5237
|
+
status: "parent_request_drafted",
|
|
5238
|
+
dry_run: true,
|
|
5239
|
+
duration_ms: 0,
|
|
5240
|
+
event_type: "ui_agent_dispatch"
|
|
5241
|
+
});
|
|
5242
|
+
return {
|
|
5243
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
5244
|
+
status: "parent_request_drafted",
|
|
5245
|
+
capability: "parent_controlled",
|
|
5246
|
+
reseller_id: resellerId,
|
|
5247
|
+
parent_reseller: resellerId === 1170 ? "Bridge4IP" : "parent reseller/support",
|
|
5248
|
+
draft,
|
|
5249
|
+
sent: false
|
|
5250
|
+
}, null, 2) }]
|
|
5251
|
+
};
|
|
5252
|
+
}
|
|
5253
|
+
);
|
|
5220
5254
|
server2.registerTool(
|
|
5221
5255
|
"ui_create_account",
|
|
5222
5256
|
{
|
|
@@ -7692,7 +7726,8 @@ function registerStorefrontDeployTools(server2) {
|
|
|
7692
7726
|
var TIER_CREDIT_ALLOTMENTS = {
|
|
7693
7727
|
free: 5e3,
|
|
7694
7728
|
pro: 1e5,
|
|
7695
|
-
enterprise: Infinity
|
|
7729
|
+
enterprise: Infinity,
|
|
7730
|
+
superadmin: Infinity
|
|
7696
7731
|
};
|
|
7697
7732
|
var DAILY_FREE_CREDITS = 50;
|
|
7698
7733
|
var SCOPE_CREDIT_COSTS = {
|
|
@@ -7715,7 +7750,7 @@ async function checkCredits(env, sub, tier, scope) {
|
|
|
7715
7750
|
const month = currentMonth2();
|
|
7716
7751
|
const today = currentDay();
|
|
7717
7752
|
const resetAt = firstDayNextMonth2();
|
|
7718
|
-
if (tier === "enterprise") {
|
|
7753
|
+
if (tier === "enterprise" || tier === "superadmin") {
|
|
7719
7754
|
return {
|
|
7720
7755
|
allowed: true,
|
|
7721
7756
|
credits_remaining: Infinity,
|
|
@@ -7755,7 +7790,7 @@ function deductCredits(env, sub, tier, scope) {
|
|
|
7755
7790
|
const cost = SCOPE_CREDIT_COSTS[scope];
|
|
7756
7791
|
const month = currentMonth2();
|
|
7757
7792
|
const today = currentDay();
|
|
7758
|
-
if (tier === "enterprise") return;
|
|
7793
|
+
if (tier === "enterprise" || tier === "superadmin") return;
|
|
7759
7794
|
const dailyFree = await getDailyFreeCredits(env, sub, today);
|
|
7760
7795
|
const dailyRemaining = dailyFree.granted - dailyFree.consumed;
|
|
7761
7796
|
if (dailyRemaining >= cost) {
|
|
@@ -7961,7 +7996,7 @@ async function resolveUserTier(env, sub) {
|
|
|
7961
7996
|
const raw = await env.CARRIER_USERS.get(`user:${sub}`, "json").catch(() => null);
|
|
7962
7997
|
if (raw && typeof raw === "object" && "tier" in raw) {
|
|
7963
7998
|
const t = raw.tier;
|
|
7964
|
-
if (t === "pro" || t === "enterprise") return t;
|
|
7999
|
+
if (t === "pro" || t === "enterprise" || t === "superadmin") return t;
|
|
7965
8000
|
}
|
|
7966
8001
|
return "free";
|
|
7967
8002
|
}
|
|
@@ -8011,6 +8046,14 @@ var DEFAULT_THRESHOLD_CONFIGS = {
|
|
|
8011
8046
|
auto_pause_on_cap: false,
|
|
8012
8047
|
notification_email: null,
|
|
8013
8048
|
webhook_url: null
|
|
8049
|
+
},
|
|
8050
|
+
superadmin: {
|
|
8051
|
+
notification_thresholds: [],
|
|
8052
|
+
hard_cap_cents: 0,
|
|
8053
|
+
invoice_threshold_cents: 0,
|
|
8054
|
+
auto_pause_on_cap: false,
|
|
8055
|
+
notification_email: null,
|
|
8056
|
+
webhook_url: null
|
|
8014
8057
|
}
|
|
8015
8058
|
};
|
|
8016
8059
|
async function getThresholdConfig(env, sub, tier) {
|
|
@@ -8033,7 +8076,7 @@ async function resolveUserTier2(env, sub) {
|
|
|
8033
8076
|
const raw = await env.CARRIER_USERS.get(`user:${sub}`, "json").catch(() => null);
|
|
8034
8077
|
if (raw && typeof raw === "object" && "tier" in raw) {
|
|
8035
8078
|
const t = raw.tier;
|
|
8036
|
-
if (t === "pro" || t === "enterprise") return t;
|
|
8079
|
+
if (t === "pro" || t === "enterprise" || t === "superadmin") return t;
|
|
8037
8080
|
}
|
|
8038
8081
|
return "free";
|
|
8039
8082
|
}
|