mcp-scraper 0.21.1 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/bin/api-server.cjs +368 -224
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +12 -8
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +4 -4
- package/dist/{chunk-HXGZMMW5.js → chunk-2FF4GBQ4.js} +1 -5
- package/dist/{chunk-HXGZMMW5.js.map → chunk-2FF4GBQ4.js.map} +1 -1
- package/dist/chunk-NBF4F4UT.js +7 -0
- package/dist/chunk-NBF4F4UT.js.map +1 -0
- package/dist/{chunk-TUSLATZY.js → chunk-X3NIXHHG.js} +2 -2
- package/dist/{chunk-Q44DN6T2.js → chunk-YNHPZU6B.js} +2 -2
- package/dist/{chunk-Q44DN6T2.js.map → chunk-YNHPZU6B.js.map} +1 -1
- package/dist/{chunk-7BHU4SJQ.js → chunk-ZNNYPE2U.js} +13 -9
- package/dist/{chunk-7BHU4SJQ.js.map → chunk-ZNNYPE2U.js.map} +1 -1
- package/dist/{server-FRR4B4Z5.js → server-CHTX6YQA.js} +336 -207
- package/dist/server-CHTX6YQA.js.map +1 -0
- package/dist/{site-extract-repository-YNPXEZCB.js → site-extract-repository-5V5EEQ72.js} +3 -3
- package/dist/{worker-45WEWCMQ.js → worker-GE5JMHE6.js} +2 -2
- package/docs/mcp-tool-manifest.generated.json +28 -10
- package/docs/specs/google-drive-bulk-access-and-mcp-schema-passthrough-spec.md +16 -7
- package/package.json +2 -2
- package/dist/chunk-QDUFW45J.js +0 -7
- package/dist/chunk-QDUFW45J.js.map +0 -1
- package/dist/server-FRR4B4Z5.js.map +0 -1
- /package/dist/{chunk-TUSLATZY.js.map → chunk-X3NIXHHG.js.map} +0 -0
- /package/dist/{site-extract-repository-YNPXEZCB.js.map → site-extract-repository-5V5EEQ72.js.map} +0 -0
- /package/dist/{worker-45WEWCMQ.js.map → worker-GE5JMHE6.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -8525,7 +8525,7 @@ function insufficientBalanceResponse(balanceMc, requiredMc) {
|
|
|
8525
8525
|
topup_url: topupUrl
|
|
8526
8526
|
};
|
|
8527
8527
|
}
|
|
8528
|
-
var MC_COSTS, MC_PER_BROWSER_MS, BROWSER_OPEN_MIN_BALANCE_MC, MC_PER_CREDIT, BILLING_MC_PER_USD_NUMERATOR, BILLING_MC_PER_USD_DENOMINATOR, BILLING_BASIS_POINTS, USD_NANOS, SCHEDULED_RUN_BASE_CREDITS, SCHEDULED_RUN_BASE_MC, SCHEDULED_AGENT_LLM_MARKUP_BPS, SCHEDULED_AGENT_LLM_MARKUP, SCHEDULED_RUN_DEFAULT_MAX_CREDITS, CREDIT_COST_CATALOG, CONCURRENCY_PRICE_ID, SUBSCRIPTION_TIERS, SUBSCRIPTION_TIER_BY_KEY, CONCURRENCY_SLOT_PRICE_USD, CONCURRENCY_SLOT_PRICE_CURRENCY, CONCURRENCY_SLOT_PRICE_INTERVAL, CONCURRENCY_SLOT_PRICE_LABEL, CONCURRENCY_SLOT_TERMINAL_COMMAND, FREE_SIGNUP_MC, MEMORY_FREE_QUOTA_BYTES, MEMORY_PLANS, MEMORY_PLAN_QUOTA, MEMORY_MARGIN_MULTIPLE, MEMORY_PRO_COST_BUDGET_USD,
|
|
8528
|
+
var MC_COSTS, MC_PER_BROWSER_MS, BROWSER_OPEN_MIN_BALANCE_MC, MC_PER_CREDIT, BILLING_MC_PER_USD_NUMERATOR, BILLING_MC_PER_USD_DENOMINATOR, BILLING_BASIS_POINTS, USD_NANOS, SCHEDULED_RUN_BASE_CREDITS, SCHEDULED_RUN_BASE_MC, SCHEDULED_AGENT_LLM_MARKUP_BPS, SCHEDULED_AGENT_LLM_MARKUP, SCHEDULED_RUN_DEFAULT_MAX_CREDITS, CREDIT_COST_CATALOG, CONCURRENCY_PRICE_ID, SUBSCRIPTION_TIERS, SUBSCRIPTION_TIER_BY_KEY, CONCURRENCY_SLOT_PRICE_USD, CONCURRENCY_SLOT_PRICE_CURRENCY, CONCURRENCY_SLOT_PRICE_INTERVAL, CONCURRENCY_SLOT_PRICE_LABEL, CONCURRENCY_SLOT_TERMINAL_COMMAND, FREE_SIGNUP_MC, MEMORY_FREE_QUOTA_BYTES, MEMORY_PLANS, MEMORY_PLAN_QUOTA, MEMORY_MARGIN_MULTIPLE, MEMORY_PRO_COST_BUDGET_USD, LedgerOperation, MEMORY_AI_MARGIN_MULTIPLE, SITE_AUDIT_LLM_MARGIN_MULTIPLE, VIDEO_ANALYSIS_LLM_MARGIN_MULTIPLE, MEDIA_TRANSCRIBE_MARGIN_MULTIPLE, MEDIA_TRANSCRIBE_HOLD_MC, MC_PER_USD;
|
|
8529
8529
|
var init_rates = __esm({
|
|
8530
8530
|
"src/api/rates.ts"() {
|
|
8531
8531
|
"use strict";
|
|
@@ -8810,9 +8810,6 @@ var init_rates = __esm({
|
|
|
8810
8810
|
MEMORY_PLAN_QUOTA = { free: MEMORY_FREE_QUOTA_BYTES, pro: 5e9, team: 5e10 };
|
|
8811
8811
|
MEMORY_MARGIN_MULTIPLE = 3;
|
|
8812
8812
|
MEMORY_PRO_COST_BUDGET_USD = 19 / MEMORY_MARGIN_MULTIPLE;
|
|
8813
|
-
SCHEDULING_PLANS = {
|
|
8814
|
-
"price_1ToXcHS8aAcsk3TGf3cW7zHx": { price_id: "price_1ToXcHS8aAcsk3TGf3cW7zHx", label: "Scheduled Actions", interval: "month", monthly_usd: 10, quota_per_period: 1e3 }
|
|
8815
|
-
};
|
|
8816
8813
|
LedgerOperation = {
|
|
8817
8814
|
TOPUP: "topup",
|
|
8818
8815
|
SUBSCRIPTION: "subscription",
|
|
@@ -11903,6 +11900,51 @@ var init_internal_memory_routes = __esm({
|
|
|
11903
11900
|
}
|
|
11904
11901
|
});
|
|
11905
11902
|
|
|
11903
|
+
// src/api/scheduling-access.ts
|
|
11904
|
+
function hasPaidSchedulingAccess(user) {
|
|
11905
|
+
return typeof user.subscription_tier === "string" && user.subscription_tier.trim().length > 0;
|
|
11906
|
+
}
|
|
11907
|
+
function paidPlanRequiredPayload() {
|
|
11908
|
+
return {
|
|
11909
|
+
ok: false,
|
|
11910
|
+
enabled: false,
|
|
11911
|
+
code: PAID_PLAN_REQUIRED_CODE,
|
|
11912
|
+
error: PAID_PLAN_REQUIRED_MESSAGE,
|
|
11913
|
+
billingMode: "credits",
|
|
11914
|
+
runBaseCredits: SCHEDULED_RUN_BASE_CREDITS,
|
|
11915
|
+
llmCostMultiplier: SCHEDULED_AGENT_LLM_MARKUP
|
|
11916
|
+
};
|
|
11917
|
+
}
|
|
11918
|
+
function memoryScheduleAccessPayload(upstreamName, user) {
|
|
11919
|
+
if (hasPaidSchedulingAccess(user)) return null;
|
|
11920
|
+
if (upstreamName === "getScheduleStatusTool") return paidPlanRequiredPayload();
|
|
11921
|
+
if (PLAN_GATED_MEMORY_SCHEDULE_TOOLS.has(upstreamName)) {
|
|
11922
|
+
return {
|
|
11923
|
+
ok: false,
|
|
11924
|
+
code: PAID_PLAN_REQUIRED_CODE,
|
|
11925
|
+
error: PAID_PLAN_REQUIRED_MESSAGE
|
|
11926
|
+
};
|
|
11927
|
+
}
|
|
11928
|
+
return null;
|
|
11929
|
+
}
|
|
11930
|
+
function memoryScheduleToolNeedsCredentials(upstreamName) {
|
|
11931
|
+
return upstreamName === "createScheduledActionTool" || upstreamName === "getScheduleLinkTool";
|
|
11932
|
+
}
|
|
11933
|
+
var PAID_PLAN_REQUIRED_CODE, PAID_PLAN_REQUIRED_MESSAGE, PLAN_GATED_MEMORY_SCHEDULE_TOOLS;
|
|
11934
|
+
var init_scheduling_access = __esm({
|
|
11935
|
+
"src/api/scheduling-access.ts"() {
|
|
11936
|
+
"use strict";
|
|
11937
|
+
init_rates();
|
|
11938
|
+
PAID_PLAN_REQUIRED_CODE = "paid_plan_required";
|
|
11939
|
+
PAID_PLAN_REQUIRED_MESSAGE = "Scheduled Actions requires an active MCP Scraper Starter plan or higher.";
|
|
11940
|
+
PLAN_GATED_MEMORY_SCHEDULE_TOOLS = /* @__PURE__ */ new Set([
|
|
11941
|
+
"createScheduledActionTool",
|
|
11942
|
+
"getScheduleLinkTool",
|
|
11943
|
+
"resumeScheduledActionTool"
|
|
11944
|
+
]);
|
|
11945
|
+
}
|
|
11946
|
+
});
|
|
11947
|
+
|
|
11906
11948
|
// src/api/unified-billing.ts
|
|
11907
11949
|
function billingSecret() {
|
|
11908
11950
|
const secret2 = process.env.INTERNAL_BILLING_SECRET ?? process.env.INTERNAL_MEMORY_DEBIT_SECRET;
|
|
@@ -11916,6 +11958,19 @@ function normalizeIdentity(identity) {
|
|
|
11916
11958
|
}
|
|
11917
11959
|
return normalized;
|
|
11918
11960
|
}
|
|
11961
|
+
async function resolveSchedulingUser(identity) {
|
|
11962
|
+
const user = await getUserByEmail(normalizeIdentity(identity));
|
|
11963
|
+
if (!user) throw new UnifiedBillingError("user_not_found", "billing identity was not found", 404);
|
|
11964
|
+
return user;
|
|
11965
|
+
}
|
|
11966
|
+
async function getSchedulingAccess(identity) {
|
|
11967
|
+
const user = await getUserByEmail(normalizeIdentity(identity));
|
|
11968
|
+
if (!user) return { allowed: false, subscriptionTier: null };
|
|
11969
|
+
return {
|
|
11970
|
+
allowed: hasPaidSchedulingAccess(user),
|
|
11971
|
+
subscriptionTier: user.subscription_tier
|
|
11972
|
+
};
|
|
11973
|
+
}
|
|
11919
11974
|
function normalizeIdempotencyKey(value) {
|
|
11920
11975
|
const key = value.trim();
|
|
11921
11976
|
if (!key || key.length > 500) {
|
|
@@ -11997,8 +12052,10 @@ function mapEvent(row, balanceMc, duplicate) {
|
|
|
11997
12052
|
async function authorizeScheduledRun(args) {
|
|
11998
12053
|
const identity = normalizeIdentity(args.identity);
|
|
11999
12054
|
const idempotencyKey = normalizeIdempotencyKey(args.idempotencyKey);
|
|
12000
|
-
const user = await
|
|
12001
|
-
if (!user)
|
|
12055
|
+
const user = await resolveSchedulingUser(identity);
|
|
12056
|
+
if (!hasPaidSchedulingAccess(user)) {
|
|
12057
|
+
throw new UnifiedBillingError(PAID_PLAN_REQUIRED_CODE, PAID_PLAN_REQUIRED_MESSAGE, 403);
|
|
12058
|
+
}
|
|
12002
12059
|
const pending = await getDb().execute({
|
|
12003
12060
|
sql: "SELECT id FROM billing_events WHERE user_id = ? AND billing_class = ? AND status = 'cost_pending' LIMIT 1",
|
|
12004
12061
|
args: [user.id, SCHEDULED_RUN_BILLING_CLASS]
|
|
@@ -12266,6 +12323,7 @@ var init_unified_billing = __esm({
|
|
|
12266
12323
|
import_node_crypto4 = require("crypto");
|
|
12267
12324
|
init_db();
|
|
12268
12325
|
init_rates();
|
|
12326
|
+
init_scheduling_access();
|
|
12269
12327
|
SCHEDULED_RUN_BILLING_CLASS = "scheduled_agent_run";
|
|
12270
12328
|
SCHEDULED_RUN_SOURCE_SURFACE = "mastra_scheduler";
|
|
12271
12329
|
AUTHORIZATION_TTL_MS = 30 * 60 * 1e3;
|
|
@@ -12284,6 +12342,192 @@ var init_unified_billing = __esm({
|
|
|
12284
12342
|
}
|
|
12285
12343
|
});
|
|
12286
12344
|
|
|
12345
|
+
// src/api/session.ts
|
|
12346
|
+
function getSessionSecret() {
|
|
12347
|
+
const configured = process.env.SESSION_SECRET?.trim();
|
|
12348
|
+
if (configured) return configured;
|
|
12349
|
+
if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
|
|
12350
|
+
return "dev-secret-change-me";
|
|
12351
|
+
}
|
|
12352
|
+
function safeEqualHex(a, b) {
|
|
12353
|
+
if (a.length !== b.length) return false;
|
|
12354
|
+
try {
|
|
12355
|
+
return (0, import_node_crypto5.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
12356
|
+
} catch {
|
|
12357
|
+
return false;
|
|
12358
|
+
}
|
|
12359
|
+
}
|
|
12360
|
+
function signSession(userId) {
|
|
12361
|
+
const payload = String(userId);
|
|
12362
|
+
const sig = (0, import_node_crypto5.createHmac)("sha256", secret()).update(payload).digest("hex");
|
|
12363
|
+
return `${payload}.${sig}`;
|
|
12364
|
+
}
|
|
12365
|
+
function verifySession(token) {
|
|
12366
|
+
const dot = token.lastIndexOf(".");
|
|
12367
|
+
if (dot === -1) return null;
|
|
12368
|
+
const payload = token.slice(0, dot);
|
|
12369
|
+
const sig = token.slice(dot + 1);
|
|
12370
|
+
const expected = (0, import_node_crypto5.createHmac)("sha256", secret()).update(payload).digest("hex");
|
|
12371
|
+
if (!safeEqualHex(sig, expected)) return null;
|
|
12372
|
+
const id = parseInt(payload);
|
|
12373
|
+
return isNaN(id) ? null : id;
|
|
12374
|
+
}
|
|
12375
|
+
var import_node_crypto5, isProduction, secret;
|
|
12376
|
+
var init_session = __esm({
|
|
12377
|
+
"src/api/session.ts"() {
|
|
12378
|
+
"use strict";
|
|
12379
|
+
import_node_crypto5 = require("crypto");
|
|
12380
|
+
isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
|
|
12381
|
+
secret = () => getSessionSecret();
|
|
12382
|
+
}
|
|
12383
|
+
});
|
|
12384
|
+
|
|
12385
|
+
// src/api/memory.ts
|
|
12386
|
+
function parseMemoryRpcBody(text) {
|
|
12387
|
+
const dataLine = text.split("\n").find((line) => line.startsWith("data:"));
|
|
12388
|
+
const payload = dataLine ? dataLine.slice(5).trim() : text;
|
|
12389
|
+
return JSON.parse(payload);
|
|
12390
|
+
}
|
|
12391
|
+
function isMemoryOperator(email) {
|
|
12392
|
+
if (!email) return false;
|
|
12393
|
+
const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
12394
|
+
return ops.includes(email.trim().toLowerCase());
|
|
12395
|
+
}
|
|
12396
|
+
function encKey() {
|
|
12397
|
+
return (0, import_node_crypto6.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
|
|
12398
|
+
}
|
|
12399
|
+
function encryptMemoryKey(secret2) {
|
|
12400
|
+
const iv = (0, import_node_crypto6.randomBytes)(12);
|
|
12401
|
+
const cipher = (0, import_node_crypto6.createCipheriv)("aes-256-gcm", encKey(), iv);
|
|
12402
|
+
const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
|
|
12403
|
+
const tag = cipher.getAuthTag();
|
|
12404
|
+
return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
|
|
12405
|
+
}
|
|
12406
|
+
function decryptMemoryKey(stored) {
|
|
12407
|
+
try {
|
|
12408
|
+
const [ivB, tagB, dataB] = stored.split(":");
|
|
12409
|
+
const decipher = (0, import_node_crypto6.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
|
|
12410
|
+
decipher.setAuthTag(Buffer.from(tagB, "base64"));
|
|
12411
|
+
return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
|
|
12412
|
+
} catch {
|
|
12413
|
+
return null;
|
|
12414
|
+
}
|
|
12415
|
+
}
|
|
12416
|
+
async function memoryCall(toolName, args, userMemoryKey) {
|
|
12417
|
+
try {
|
|
12418
|
+
const res = await fetch(`${MEMORY_BASE_URL()}/mcp`, {
|
|
12419
|
+
method: "POST",
|
|
12420
|
+
headers: {
|
|
12421
|
+
"content-type": "application/json",
|
|
12422
|
+
accept: "application/json, text/event-stream",
|
|
12423
|
+
authorization: `Bearer ${userMemoryKey}`
|
|
12424
|
+
},
|
|
12425
|
+
body: JSON.stringify({
|
|
12426
|
+
jsonrpc: "2.0",
|
|
12427
|
+
id: 1,
|
|
12428
|
+
method: "tools/call",
|
|
12429
|
+
params: { name: toolName, arguments: args }
|
|
12430
|
+
})
|
|
12431
|
+
});
|
|
12432
|
+
const text = await res.text();
|
|
12433
|
+
if (!res.ok) return { ok: false, error: `memory ${toolName} failed (${res.status})` };
|
|
12434
|
+
const body = parseMemoryRpcBody(text);
|
|
12435
|
+
if (body.error) return { ok: false, error: body.error.message ?? `memory ${toolName} failed` };
|
|
12436
|
+
if (body.result?.structuredContent) return body.result.structuredContent;
|
|
12437
|
+
const textContent = body.result?.content?.find((c) => c.type === "text")?.text;
|
|
12438
|
+
if (textContent) {
|
|
12439
|
+
try {
|
|
12440
|
+
return JSON.parse(textContent);
|
|
12441
|
+
} catch {
|
|
12442
|
+
return { ok: false, error: textContent };
|
|
12443
|
+
}
|
|
12444
|
+
}
|
|
12445
|
+
return { ok: false, error: `memory ${toolName} returned no result` };
|
|
12446
|
+
} catch (err) {
|
|
12447
|
+
return { ok: false, error: err?.message ?? "memory call failed" };
|
|
12448
|
+
}
|
|
12449
|
+
}
|
|
12450
|
+
function memoryIdentity(user) {
|
|
12451
|
+
return user.email;
|
|
12452
|
+
}
|
|
12453
|
+
function resolveMemoryIdentity(user) {
|
|
12454
|
+
return memoryIdentity(user);
|
|
12455
|
+
}
|
|
12456
|
+
function memoryPlanForReads(user) {
|
|
12457
|
+
if (isMemoryOperator(user.email)) return "unlimited";
|
|
12458
|
+
if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
|
|
12459
|
+
return "free";
|
|
12460
|
+
}
|
|
12461
|
+
async function provisionMemoryForUser(user) {
|
|
12462
|
+
try {
|
|
12463
|
+
const { key } = await getOrCreateUserMemoryKey(user);
|
|
12464
|
+
if (key) await setMemoryProvisioned(user.id, true);
|
|
12465
|
+
} catch {
|
|
12466
|
+
}
|
|
12467
|
+
}
|
|
12468
|
+
function resolveEffectiveMemoryPlan(user) {
|
|
12469
|
+
return user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
|
|
12470
|
+
}
|
|
12471
|
+
async function getOrCreateUserMemoryKey(user) {
|
|
12472
|
+
const creds = await getUserMemoryCreds(user.id);
|
|
12473
|
+
if (creds.memory_key) {
|
|
12474
|
+
const decrypted = decryptMemoryKey(creds.memory_key);
|
|
12475
|
+
if (decrypted) return { key: decrypted };
|
|
12476
|
+
}
|
|
12477
|
+
const admin = ADMIN_KEY();
|
|
12478
|
+
if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
12479
|
+
const identity = memoryIdentity(user);
|
|
12480
|
+
const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
|
|
12481
|
+
const provisioned = await import_provision_defaults.provisionDefaultsTool.execute(
|
|
12482
|
+
{ apiKey: admin, granteeIdentity: identity, issueKey: true, plan },
|
|
12483
|
+
{}
|
|
12484
|
+
);
|
|
12485
|
+
if (!provisioned.ok || !provisioned.secret) {
|
|
12486
|
+
return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
|
|
12487
|
+
}
|
|
12488
|
+
await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
|
|
12489
|
+
return { key: provisioned.secret };
|
|
12490
|
+
}
|
|
12491
|
+
async function syncMemoryKeyPlan(user, plan) {
|
|
12492
|
+
const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
|
|
12493
|
+
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
12494
|
+
if (!key) return { ok: false, error: error ?? "memory unavailable" };
|
|
12495
|
+
const listed = await memoryCall(
|
|
12496
|
+
"listKeysTool",
|
|
12497
|
+
{},
|
|
12498
|
+
key
|
|
12499
|
+
);
|
|
12500
|
+
if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
|
|
12501
|
+
const target = listed.keys[0];
|
|
12502
|
+
if (target.plan === effectivePlan) return { ok: true };
|
|
12503
|
+
const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
|
|
12504
|
+
return { ok: res.ok, error: res.error };
|
|
12505
|
+
}
|
|
12506
|
+
async function syncScheduledActionCredentials(user) {
|
|
12507
|
+
const admin = ADMIN_KEY();
|
|
12508
|
+
if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
12509
|
+
const identity = resolveMemoryIdentity(user);
|
|
12510
|
+
const res = await import_set_schedule_entitlement.setScheduleEntitlementTool.execute({
|
|
12511
|
+
apiKey: admin,
|
|
12512
|
+
granteeIdentity: identity,
|
|
12513
|
+
mcpScraperApiKey: user.api_key
|
|
12514
|
+
}, {});
|
|
12515
|
+
return { ok: res.ok, error: res.error };
|
|
12516
|
+
}
|
|
12517
|
+
var import_node_crypto6, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
|
|
12518
|
+
var init_memory = __esm({
|
|
12519
|
+
"src/api/memory.ts"() {
|
|
12520
|
+
"use strict";
|
|
12521
|
+
import_node_crypto6 = require("crypto");
|
|
12522
|
+
init_session();
|
|
12523
|
+
init_db();
|
|
12524
|
+
import_provision_defaults = require("mcpscraper-memory-tools/tools/vaults/provision-defaults");
|
|
12525
|
+
import_set_schedule_entitlement = require("mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement");
|
|
12526
|
+
MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
|
|
12527
|
+
ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
12528
|
+
}
|
|
12529
|
+
});
|
|
12530
|
+
|
|
12287
12531
|
// src/api/internal-billing-routes.ts
|
|
12288
12532
|
function internalRequestAuthorized(header) {
|
|
12289
12533
|
const expected = process.env.INTERNAL_BILLING_SECRET ?? process.env.INTERNAL_MEMORY_DEBIT_SECRET;
|
|
@@ -12307,15 +12551,22 @@ function billingError(error) {
|
|
|
12307
12551
|
status: 500
|
|
12308
12552
|
};
|
|
12309
12553
|
}
|
|
12310
|
-
var import_hono3, import_zod12, internalBillingApp, metadataSchema, authorizeSchema, settleSchema, voidSchema;
|
|
12554
|
+
var import_hono3, import_zod12, internalBillingApp, metadataSchema, schedulingAccessSchema, authorizeSchema, settleSchema, voidSchema;
|
|
12311
12555
|
var init_internal_billing_routes = __esm({
|
|
12312
12556
|
"src/api/internal-billing-routes.ts"() {
|
|
12313
12557
|
"use strict";
|
|
12314
12558
|
import_hono3 = require("hono");
|
|
12315
12559
|
import_zod12 = require("zod");
|
|
12316
12560
|
init_unified_billing();
|
|
12561
|
+
init_scheduling_access();
|
|
12562
|
+
init_db();
|
|
12563
|
+
init_memory();
|
|
12317
12564
|
internalBillingApp = new import_hono3.Hono();
|
|
12318
12565
|
metadataSchema = import_zod12.z.record(import_zod12.z.unknown()).optional();
|
|
12566
|
+
schedulingAccessSchema = import_zod12.z.object({
|
|
12567
|
+
identity: import_zod12.z.string().min(1).max(320),
|
|
12568
|
+
provisionCredentials: import_zod12.z.boolean().optional()
|
|
12569
|
+
});
|
|
12319
12570
|
authorizeSchema = import_zod12.z.object({
|
|
12320
12571
|
identity: import_zod12.z.string().min(1).max(320),
|
|
12321
12572
|
idempotencyKey: import_zod12.z.string().min(1).max(500),
|
|
@@ -12359,6 +12610,41 @@ var init_internal_billing_routes = __esm({
|
|
|
12359
12610
|
return c.json(response.body, response.status);
|
|
12360
12611
|
}
|
|
12361
12612
|
});
|
|
12613
|
+
internalBillingApp.post("/scheduling-access", async (c) => {
|
|
12614
|
+
const parsed = schedulingAccessSchema.safeParse(await c.req.json().catch(() => null));
|
|
12615
|
+
if (!parsed.success) {
|
|
12616
|
+
return c.json({ ok: false, error_code: "invalid_request", error: "invalid scheduling access request" }, 400);
|
|
12617
|
+
}
|
|
12618
|
+
try {
|
|
12619
|
+
const access2 = await getSchedulingAccess(parsed.data.identity);
|
|
12620
|
+
if (access2.allowed && parsed.data.provisionCredentials) {
|
|
12621
|
+
const user = await getUserByEmail(parsed.data.identity.trim().toLowerCase());
|
|
12622
|
+
if (!user) {
|
|
12623
|
+
return c.json({ ok: false, error_code: "user_not_found", error: "billing identity was not found" }, 404);
|
|
12624
|
+
}
|
|
12625
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
12626
|
+
if (!credentials.ok) {
|
|
12627
|
+
return c.json({
|
|
12628
|
+
ok: false,
|
|
12629
|
+
error_code: "schedule_credentials_unavailable",
|
|
12630
|
+
error: credentials.error ?? "Scheduled Actions credentials are unavailable."
|
|
12631
|
+
}, 503);
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12634
|
+
return c.json({
|
|
12635
|
+
ok: true,
|
|
12636
|
+
allowed: access2.allowed,
|
|
12637
|
+
subscriptionTier: access2.subscriptionTier,
|
|
12638
|
+
...access2.allowed ? {} : {
|
|
12639
|
+
error_code: PAID_PLAN_REQUIRED_CODE,
|
|
12640
|
+
error: PAID_PLAN_REQUIRED_MESSAGE
|
|
12641
|
+
}
|
|
12642
|
+
});
|
|
12643
|
+
} catch (error) {
|
|
12644
|
+
const response = billingError(error);
|
|
12645
|
+
return c.json(response.body, response.status);
|
|
12646
|
+
}
|
|
12647
|
+
});
|
|
12362
12648
|
internalBillingApp.post("/scheduled-run/settle", async (c) => {
|
|
12363
12649
|
const parsed = settleSchema.safeParse(await c.req.json().catch(() => null));
|
|
12364
12650
|
if (!parsed.success) {
|
|
@@ -14785,7 +15071,7 @@ async function acquireConcurrencyGate(user, operation, options = {}) {
|
|
|
14785
15071
|
return { ok: true, lockId: null, active: await countActiveUsageForUser(user.id), limit, operation, reused: true };
|
|
14786
15072
|
}
|
|
14787
15073
|
await expireConcurrencyLocksForUser(user.id);
|
|
14788
|
-
const lockId = `cl_${(0,
|
|
15074
|
+
const lockId = `cl_${(0, import_node_crypto7.randomUUID)().replace(/-/g, "").slice(0, 24)}`;
|
|
14789
15075
|
const res = await getDb().execute({
|
|
14790
15076
|
sql: `INSERT INTO concurrency_locks (id, user_id, operation, status, expires_at, metadata)
|
|
14791
15077
|
SELECT ?, ?, ?, 'active', datetime('now', ?), ?
|
|
@@ -14839,11 +15125,11 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
|
|
|
14839
15125
|
args: [lockTtlModifier(ttlSeconds), lockId]
|
|
14840
15126
|
});
|
|
14841
15127
|
}
|
|
14842
|
-
var
|
|
15128
|
+
var import_node_crypto7, DEFAULT_LOCK_TTL_SECONDS, DEFAULT_RETRY_AFTER_SECONDS, MAX_LOCK_TTL_SECONDS, schemaReady;
|
|
14843
15129
|
var init_concurrency_gates = __esm({
|
|
14844
15130
|
"src/api/concurrency-gates.ts"() {
|
|
14845
15131
|
"use strict";
|
|
14846
|
-
|
|
15132
|
+
import_node_crypto7 = require("crypto");
|
|
14847
15133
|
init_db();
|
|
14848
15134
|
init_rates();
|
|
14849
15135
|
DEFAULT_LOCK_TTL_SECONDS = 15 * 60;
|
|
@@ -17786,194 +18072,6 @@ var init_reddit_routes = __esm({
|
|
|
17786
18072
|
}
|
|
17787
18073
|
});
|
|
17788
18074
|
|
|
17789
|
-
// src/api/session.ts
|
|
17790
|
-
function getSessionSecret() {
|
|
17791
|
-
const configured = process.env.SESSION_SECRET?.trim();
|
|
17792
|
-
if (configured) return configured;
|
|
17793
|
-
if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
|
|
17794
|
-
return "dev-secret-change-me";
|
|
17795
|
-
}
|
|
17796
|
-
function safeEqualHex(a, b) {
|
|
17797
|
-
if (a.length !== b.length) return false;
|
|
17798
|
-
try {
|
|
17799
|
-
return (0, import_node_crypto6.timingSafeEqual)(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
17800
|
-
} catch {
|
|
17801
|
-
return false;
|
|
17802
|
-
}
|
|
17803
|
-
}
|
|
17804
|
-
function signSession(userId) {
|
|
17805
|
-
const payload = String(userId);
|
|
17806
|
-
const sig = (0, import_node_crypto6.createHmac)("sha256", secret()).update(payload).digest("hex");
|
|
17807
|
-
return `${payload}.${sig}`;
|
|
17808
|
-
}
|
|
17809
|
-
function verifySession(token) {
|
|
17810
|
-
const dot = token.lastIndexOf(".");
|
|
17811
|
-
if (dot === -1) return null;
|
|
17812
|
-
const payload = token.slice(0, dot);
|
|
17813
|
-
const sig = token.slice(dot + 1);
|
|
17814
|
-
const expected = (0, import_node_crypto6.createHmac)("sha256", secret()).update(payload).digest("hex");
|
|
17815
|
-
if (!safeEqualHex(sig, expected)) return null;
|
|
17816
|
-
const id = parseInt(payload);
|
|
17817
|
-
return isNaN(id) ? null : id;
|
|
17818
|
-
}
|
|
17819
|
-
var import_node_crypto6, isProduction, secret;
|
|
17820
|
-
var init_session = __esm({
|
|
17821
|
-
"src/api/session.ts"() {
|
|
17822
|
-
"use strict";
|
|
17823
|
-
import_node_crypto6 = require("crypto");
|
|
17824
|
-
isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
|
|
17825
|
-
secret = () => getSessionSecret();
|
|
17826
|
-
}
|
|
17827
|
-
});
|
|
17828
|
-
|
|
17829
|
-
// src/api/memory.ts
|
|
17830
|
-
function parseMemoryRpcBody(text) {
|
|
17831
|
-
const dataLine = text.split("\n").find((line) => line.startsWith("data:"));
|
|
17832
|
-
const payload = dataLine ? dataLine.slice(5).trim() : text;
|
|
17833
|
-
return JSON.parse(payload);
|
|
17834
|
-
}
|
|
17835
|
-
function isMemoryOperator(email) {
|
|
17836
|
-
if (!email) return false;
|
|
17837
|
-
const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
17838
|
-
return ops.includes(email.trim().toLowerCase());
|
|
17839
|
-
}
|
|
17840
|
-
function encKey() {
|
|
17841
|
-
return (0, import_node_crypto7.scryptSync)(getSessionSecret(), "mcp-memory-key-v1", 32);
|
|
17842
|
-
}
|
|
17843
|
-
function encryptMemoryKey(secret2) {
|
|
17844
|
-
const iv = (0, import_node_crypto7.randomBytes)(12);
|
|
17845
|
-
const cipher = (0, import_node_crypto7.createCipheriv)("aes-256-gcm", encKey(), iv);
|
|
17846
|
-
const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
|
|
17847
|
-
const tag = cipher.getAuthTag();
|
|
17848
|
-
return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
|
|
17849
|
-
}
|
|
17850
|
-
function decryptMemoryKey(stored) {
|
|
17851
|
-
try {
|
|
17852
|
-
const [ivB, tagB, dataB] = stored.split(":");
|
|
17853
|
-
const decipher = (0, import_node_crypto7.createDecipheriv)("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
|
|
17854
|
-
decipher.setAuthTag(Buffer.from(tagB, "base64"));
|
|
17855
|
-
return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
|
|
17856
|
-
} catch {
|
|
17857
|
-
return null;
|
|
17858
|
-
}
|
|
17859
|
-
}
|
|
17860
|
-
async function memoryCall(toolName, args, userMemoryKey) {
|
|
17861
|
-
try {
|
|
17862
|
-
const res = await fetch(`${MEMORY_BASE_URL()}/mcp`, {
|
|
17863
|
-
method: "POST",
|
|
17864
|
-
headers: {
|
|
17865
|
-
"content-type": "application/json",
|
|
17866
|
-
accept: "application/json, text/event-stream",
|
|
17867
|
-
authorization: `Bearer ${userMemoryKey}`
|
|
17868
|
-
},
|
|
17869
|
-
body: JSON.stringify({
|
|
17870
|
-
jsonrpc: "2.0",
|
|
17871
|
-
id: 1,
|
|
17872
|
-
method: "tools/call",
|
|
17873
|
-
params: { name: toolName, arguments: args }
|
|
17874
|
-
})
|
|
17875
|
-
});
|
|
17876
|
-
const text = await res.text();
|
|
17877
|
-
if (!res.ok) return { ok: false, error: `memory ${toolName} failed (${res.status})` };
|
|
17878
|
-
const body = parseMemoryRpcBody(text);
|
|
17879
|
-
if (body.error) return { ok: false, error: body.error.message ?? `memory ${toolName} failed` };
|
|
17880
|
-
if (body.result?.structuredContent) return body.result.structuredContent;
|
|
17881
|
-
const textContent = body.result?.content?.find((c) => c.type === "text")?.text;
|
|
17882
|
-
if (textContent) {
|
|
17883
|
-
try {
|
|
17884
|
-
return JSON.parse(textContent);
|
|
17885
|
-
} catch {
|
|
17886
|
-
return { ok: false, error: textContent };
|
|
17887
|
-
}
|
|
17888
|
-
}
|
|
17889
|
-
return { ok: false, error: `memory ${toolName} returned no result` };
|
|
17890
|
-
} catch (err) {
|
|
17891
|
-
return { ok: false, error: err?.message ?? "memory call failed" };
|
|
17892
|
-
}
|
|
17893
|
-
}
|
|
17894
|
-
function memoryIdentity(user) {
|
|
17895
|
-
return user.email;
|
|
17896
|
-
}
|
|
17897
|
-
function resolveMemoryIdentity(user) {
|
|
17898
|
-
return memoryIdentity(user);
|
|
17899
|
-
}
|
|
17900
|
-
function memoryPlanForReads(user) {
|
|
17901
|
-
if (isMemoryOperator(user.email)) return "unlimited";
|
|
17902
|
-
if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
|
|
17903
|
-
return "free";
|
|
17904
|
-
}
|
|
17905
|
-
async function provisionMemoryForUser(user) {
|
|
17906
|
-
try {
|
|
17907
|
-
const { key } = await getOrCreateUserMemoryKey(user);
|
|
17908
|
-
if (key) await setMemoryProvisioned(user.id, true);
|
|
17909
|
-
} catch {
|
|
17910
|
-
}
|
|
17911
|
-
}
|
|
17912
|
-
function resolveEffectiveMemoryPlan(user) {
|
|
17913
|
-
return user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
|
|
17914
|
-
}
|
|
17915
|
-
async function getOrCreateUserMemoryKey(user) {
|
|
17916
|
-
const creds = await getUserMemoryCreds(user.id);
|
|
17917
|
-
if (creds.memory_key) {
|
|
17918
|
-
const decrypted = decryptMemoryKey(creds.memory_key);
|
|
17919
|
-
if (decrypted) return { key: decrypted };
|
|
17920
|
-
}
|
|
17921
|
-
const admin = ADMIN_KEY();
|
|
17922
|
-
if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
17923
|
-
const identity = memoryIdentity(user);
|
|
17924
|
-
const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
|
|
17925
|
-
const provisioned = await import_provision_defaults.provisionDefaultsTool.execute(
|
|
17926
|
-
{ apiKey: admin, granteeIdentity: identity, issueKey: true, plan },
|
|
17927
|
-
{}
|
|
17928
|
-
);
|
|
17929
|
-
if (!provisioned.ok || !provisioned.secret) {
|
|
17930
|
-
return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
|
|
17931
|
-
}
|
|
17932
|
-
await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
|
|
17933
|
-
return { key: provisioned.secret };
|
|
17934
|
-
}
|
|
17935
|
-
async function syncMemoryKeyPlan(user, plan) {
|
|
17936
|
-
const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
|
|
17937
|
-
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
17938
|
-
if (!key) return { ok: false, error: error ?? "memory unavailable" };
|
|
17939
|
-
const listed = await memoryCall(
|
|
17940
|
-
"listKeysTool",
|
|
17941
|
-
{},
|
|
17942
|
-
key
|
|
17943
|
-
);
|
|
17944
|
-
if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
|
|
17945
|
-
const target = listed.keys[0];
|
|
17946
|
-
if (target.plan === effectivePlan) return { ok: true };
|
|
17947
|
-
const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
|
|
17948
|
-
return { ok: res.ok, error: res.error };
|
|
17949
|
-
}
|
|
17950
|
-
async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
|
|
17951
|
-
const admin = ADMIN_KEY();
|
|
17952
|
-
if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
17953
|
-
const identity = resolveMemoryIdentity(user);
|
|
17954
|
-
const res = await import_set_schedule_entitlement.setScheduleEntitlementTool.execute({
|
|
17955
|
-
apiKey: admin,
|
|
17956
|
-
granteeIdentity: identity,
|
|
17957
|
-
enabled,
|
|
17958
|
-
quotaPerPeriod,
|
|
17959
|
-
mcpScraperApiKey: user.api_key
|
|
17960
|
-
}, {});
|
|
17961
|
-
return { ok: res.ok, error: res.error };
|
|
17962
|
-
}
|
|
17963
|
-
var import_node_crypto7, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
|
|
17964
|
-
var init_memory = __esm({
|
|
17965
|
-
"src/api/memory.ts"() {
|
|
17966
|
-
"use strict";
|
|
17967
|
-
import_node_crypto7 = require("crypto");
|
|
17968
|
-
init_session();
|
|
17969
|
-
init_db();
|
|
17970
|
-
import_provision_defaults = require("mcpscraper-memory-tools/tools/vaults/provision-defaults");
|
|
17971
|
-
import_set_schedule_entitlement = require("mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement");
|
|
17972
|
-
MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
|
|
17973
|
-
ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
17974
|
-
}
|
|
17975
|
-
});
|
|
17976
|
-
|
|
17977
18075
|
// src/api/video-routes.ts
|
|
17978
18076
|
function invalidRequest5(message) {
|
|
17979
18077
|
return { error_code: "invalid_request", message };
|
|
@@ -28401,7 +28499,7 @@ var PACKAGE_VERSION;
|
|
|
28401
28499
|
var init_version = __esm({
|
|
28402
28500
|
"src/version.ts"() {
|
|
28403
28501
|
"use strict";
|
|
28404
|
-
PACKAGE_VERSION = "0.21.
|
|
28502
|
+
PACKAGE_VERSION = "0.21.2";
|
|
28405
28503
|
}
|
|
28406
28504
|
});
|
|
28407
28505
|
|
|
@@ -34324,7 +34422,7 @@ var init_memory_tool_schemas = __esm({
|
|
|
34324
34422
|
CreateScheduledActionSchema = {
|
|
34325
34423
|
id: "create-scheduled-action",
|
|
34326
34424
|
upstreamName: "createScheduledActionTool",
|
|
34327
|
-
description: "Create a Credit-metered scheduled action in agent mode (default) or connection_sync mode. Each execution has a 75-Credit base charge; agent model usage is added at 1.5 times OpenRouter's actual reported cost. Agent mode follows the description and writes a result into the target vault. connection_sync deterministically runs approved read-only tools on bound service connections and ingests their data. Cadence 'once' runs a single time then completes permanently. Requires write access to the target vault.",
|
|
34425
|
+
description: "Create a Credit-metered scheduled action for an active MCP Scraper Starter plan or higher, in agent mode (default) or connection_sync mode. Each execution has a 75-Credit base charge; agent model usage is added at 1.5 times OpenRouter's actual reported cost. Agent mode follows the description and writes a result into the target vault. connection_sync deterministically runs approved read-only tools on bound service connections and ingests their data. Cadence 'once' runs a single time then completes permanently. Requires write access to the target vault.",
|
|
34328
34426
|
input: {
|
|
34329
34427
|
description: import_zod36.z.string().min(1).describe("Free-text description of what this action should do each time it runs."),
|
|
34330
34428
|
vault: import_zod36.z.string().min(1).describe("The vault this action writes its results into. You must already have write access to it."),
|
|
@@ -34373,12 +34471,13 @@ var init_memory_tool_schemas = __esm({
|
|
|
34373
34471
|
GetScheduleLinkSchema = {
|
|
34374
34472
|
id: "get-schedule-link",
|
|
34375
34473
|
upstreamName: "getScheduleLinkTool",
|
|
34376
|
-
description: "Get your durable, bookmarkable link to the hosted Scheduled Actions page
|
|
34474
|
+
description: "Get your durable, bookmarkable link to the hosted Scheduled Actions page. Requires an active MCP Scraper Starter plan or higher. The embedded secret is shown only once, on first call; it cannot be re-shown, only revoked and reissued via revoke-schedule-link.",
|
|
34377
34475
|
input: {},
|
|
34378
34476
|
output: {
|
|
34379
34477
|
ok: import_zod36.z.boolean().describe("True on success; false on auth error."),
|
|
34380
34478
|
url: import_zod36.z.string().optional().describe("The schedule link. Present only the first time a link is minted for this identity."),
|
|
34381
34479
|
alreadyExists: import_zod36.z.boolean().optional().describe("True when a link already exists and was NOT re-shown. Use revoke-schedule-link then call this again to get a fresh one."),
|
|
34480
|
+
code: import_zod36.z.string().optional().describe("Machine-readable denial code when the link cannot be issued."),
|
|
34382
34481
|
error: import_zod36.z.string().optional().describe("Human-readable failure reason when ok is false.")
|
|
34383
34482
|
},
|
|
34384
34483
|
annotations: {
|
|
@@ -34392,7 +34491,7 @@ var init_memory_tool_schemas = __esm({
|
|
|
34392
34491
|
GetScheduleStatusSchema = {
|
|
34393
34492
|
id: "get-schedule-status",
|
|
34394
34493
|
upstreamName: "getScheduleStatusTool",
|
|
34395
|
-
description: "Get the Credit-metered Scheduled Actions policy. Scheduling
|
|
34494
|
+
description: "Get the Credit-metered Scheduled Actions access and billing policy. Scheduling requires an active MCP Scraper Starter plan or higher but has no separate subscription: each execution has a 75-Credit base charge, and agent model usage is billed at 1.5 times OpenRouter's actual reported cost.",
|
|
34396
34495
|
input: {},
|
|
34397
34496
|
output: {
|
|
34398
34497
|
ok: import_zod36.z.boolean(),
|
|
@@ -34403,6 +34502,8 @@ var init_memory_tool_schemas = __esm({
|
|
|
34403
34502
|
quotaPerPeriod: import_zod36.z.number().optional(),
|
|
34404
34503
|
usedThisPeriod: import_zod36.z.number().optional(),
|
|
34405
34504
|
periodStart: import_zod36.z.string().optional(),
|
|
34505
|
+
defaultTimezone: import_zod36.z.string().nullable().optional().describe("IANA timezone applied to new schedules that name a time without a zone; null means UTC."),
|
|
34506
|
+
code: import_zod36.z.string().optional().describe("Machine-readable reason scheduling is unavailable."),
|
|
34406
34507
|
error: import_zod36.z.string().optional()
|
|
34407
34508
|
},
|
|
34408
34509
|
annotations: {
|
|
@@ -34496,6 +34597,7 @@ var init_memory_tool_schemas = __esm({
|
|
|
34496
34597
|
output: {
|
|
34497
34598
|
ok: import_zod36.z.boolean(),
|
|
34498
34599
|
nextRunAt: import_zod36.z.string().optional(),
|
|
34600
|
+
code: import_zod36.z.string().optional().describe("Machine-readable denial code when the action cannot be resumed."),
|
|
34499
34601
|
error: import_zod36.z.string().optional()
|
|
34500
34602
|
},
|
|
34501
34603
|
annotations: {
|
|
@@ -34527,11 +34629,11 @@ var init_memory_tool_schemas = __esm({
|
|
|
34527
34629
|
SetScheduleEntitlementSchema = {
|
|
34528
34630
|
id: "set-schedule-entitlement",
|
|
34529
34631
|
upstreamName: "setScheduleEntitlementTool",
|
|
34530
|
-
description: "
|
|
34632
|
+
description: "Admin-only scheduled-action credential provisioning and historical-row recovery. Rotate the encrypted delegated MCP Scraper API key without changing legacy enabled/quota fields by omitting those fields. Legacy entitlement values are not runtime access controls; paid-plan access is checked against MCP Scraper directly.",
|
|
34531
34633
|
input: {
|
|
34532
34634
|
granteeIdentity: import_zod36.z.string().min(1).describe("Identity whose scheduling entitlement is being set (e.g. an email)."),
|
|
34533
|
-
enabled: import_zod36.z.boolean().describe("
|
|
34534
|
-
quotaPerPeriod: import_zod36.z.number().optional().describe("
|
|
34635
|
+
enabled: import_zod36.z.boolean().optional().describe("Historical entitlement value for migration/recovery only. Omit to preserve the stored value."),
|
|
34636
|
+
quotaPerPeriod: import_zod36.z.number().optional().describe("Historical monthly execution quota retained only for migration compatibility. Omit to preserve the stored value."),
|
|
34535
34637
|
mcpScraperApiKey: import_zod36.z.string().optional().describe("The identity's mcp-scraper API key, stored encrypted, used to reach mcp-scraper tools during scheduled-action execution.")
|
|
34536
34638
|
},
|
|
34537
34639
|
output: {
|
|
@@ -35269,6 +35371,7 @@ var init_memory_inprocess_executor = __esm({
|
|
|
35269
35371
|
"src/mcp/memory-inprocess-executor.ts"() {
|
|
35270
35372
|
"use strict";
|
|
35271
35373
|
init_memory();
|
|
35374
|
+
init_scheduling_access();
|
|
35272
35375
|
CUTOVER = /* @__PURE__ */ new Map();
|
|
35273
35376
|
HybridMemoryToolExecutor = class {
|
|
35274
35377
|
constructor(httpExecutor, user) {
|
|
@@ -35285,6 +35388,29 @@ var init_memory_inprocess_executor = __esm({
|
|
|
35285
35388
|
return this.mkKeyPromise;
|
|
35286
35389
|
}
|
|
35287
35390
|
async callMemoryTool(upstreamName, args) {
|
|
35391
|
+
const accessDenied = memoryScheduleAccessPayload(upstreamName, this.user);
|
|
35392
|
+
if (accessDenied) {
|
|
35393
|
+
return {
|
|
35394
|
+
content: [{ type: "text", text: JSON.stringify(accessDenied) }],
|
|
35395
|
+
structuredContent: accessDenied,
|
|
35396
|
+
isError: false
|
|
35397
|
+
};
|
|
35398
|
+
}
|
|
35399
|
+
if (memoryScheduleToolNeedsCredentials(upstreamName)) {
|
|
35400
|
+
const credentials = await syncScheduledActionCredentials(this.user);
|
|
35401
|
+
if (!credentials.ok) {
|
|
35402
|
+
const result = {
|
|
35403
|
+
ok: false,
|
|
35404
|
+
code: "schedule_credentials_unavailable",
|
|
35405
|
+
error: credentials.error ?? "Scheduled Actions credentials are unavailable."
|
|
35406
|
+
};
|
|
35407
|
+
return {
|
|
35408
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
35409
|
+
structuredContent: result,
|
|
35410
|
+
isError: false
|
|
35411
|
+
};
|
|
35412
|
+
}
|
|
35413
|
+
}
|
|
35288
35414
|
const handler = CUTOVER.get(upstreamName);
|
|
35289
35415
|
if (!handler) return this.httpExecutor.callMemoryTool(upstreamName, args);
|
|
35290
35416
|
const mkKey = await this.resolveMkKey();
|
|
@@ -38331,6 +38457,8 @@ var init_stripe_routes = __esm({
|
|
|
38331
38457
|
if (user && invoice.id && !await ledgerExistsForStripePI(invoice.id)) {
|
|
38332
38458
|
await creditMc(user.id, tier.credits_mc, LedgerOperation.SUBSCRIPTION, `${tier.label} subscription credits`, invoice.id);
|
|
38333
38459
|
await setSubscriptionTier(user.id, tier.tier, tier.concurrency, invoice.subscription ?? user.subscription_id);
|
|
38460
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
38461
|
+
if (!credentials.ok) console.warn("[stripe] scheduled-action credential sync failed:", credentials.error ?? "unknown error");
|
|
38334
38462
|
}
|
|
38335
38463
|
}
|
|
38336
38464
|
}
|
|
@@ -38348,15 +38476,6 @@ var init_stripe_routes = __esm({
|
|
|
38348
38476
|
}
|
|
38349
38477
|
return c.json({ received: true });
|
|
38350
38478
|
}
|
|
38351
|
-
if (priceId && priceId in SCHEDULING_PLANS) {
|
|
38352
|
-
const sched = SCHEDULING_PLANS[priceId];
|
|
38353
|
-
const user2 = await resolveUser(sub.customer, sub.customer_email);
|
|
38354
|
-
if (user2) {
|
|
38355
|
-
const live = sub.status === "active" || sub.status === "trialing";
|
|
38356
|
-
await syncScheduleEntitlement(user2, live, sched.quota_per_period);
|
|
38357
|
-
}
|
|
38358
|
-
return c.json({ received: true });
|
|
38359
|
-
}
|
|
38360
38479
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
38361
38480
|
if (!user) return c.json({ received: true });
|
|
38362
38481
|
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
@@ -38364,6 +38483,10 @@ var init_stripe_routes = __esm({
|
|
|
38364
38483
|
const live = sub.status === "active" || sub.status === "trialing";
|
|
38365
38484
|
const newTier = live ? tier.tier : null;
|
|
38366
38485
|
await setSubscriptionTier(user.id, newTier, live ? tier.concurrency : 0, live ? sub.id : null);
|
|
38486
|
+
if (live) {
|
|
38487
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
38488
|
+
if (!credentials.ok) console.warn("[stripe] scheduled-action credential sync failed:", credentials.error ?? "unknown error");
|
|
38489
|
+
}
|
|
38367
38490
|
} else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
|
|
38368
38491
|
await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
|
|
38369
38492
|
await setConcurrencySubId(user.id, sub.id);
|
|
@@ -38380,11 +38503,6 @@ var init_stripe_routes = __esm({
|
|
|
38380
38503
|
}
|
|
38381
38504
|
return c.json({ received: true });
|
|
38382
38505
|
}
|
|
38383
|
-
if (priceId && priceId in SCHEDULING_PLANS) {
|
|
38384
|
-
const user2 = await getUserByStripeCustomerId(sub.customer);
|
|
38385
|
-
if (user2) await syncScheduleEntitlement(user2, false);
|
|
38386
|
-
return c.json({ received: true });
|
|
38387
|
-
}
|
|
38388
38506
|
const user = await getUserByStripeCustomerId(sub.customer);
|
|
38389
38507
|
if (!user) return c.json({ received: true });
|
|
38390
38508
|
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
@@ -42514,7 +42632,7 @@ async function checkHarvestLimits(user, reuseLockId) {
|
|
|
42514
42632
|
}
|
|
42515
42633
|
return null;
|
|
42516
42634
|
}
|
|
42517
|
-
var import_resend2, import_hono24, import_hono25, import_factory6, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth2, adminAuth, sessionAuth, requireIntegrationsTier, app, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
|
|
42635
|
+
var import_resend2, import_hono24, import_hono25, import_factory6, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth2, adminAuth, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
|
|
42518
42636
|
var init_server = __esm({
|
|
42519
42637
|
"src/api/server.ts"() {
|
|
42520
42638
|
"use strict";
|
|
@@ -42586,6 +42704,7 @@ var init_server = __esm({
|
|
|
42586
42704
|
init_harvest_attempt_events();
|
|
42587
42705
|
init_session();
|
|
42588
42706
|
init_memory();
|
|
42707
|
+
init_scheduling_access();
|
|
42589
42708
|
init_memory_db();
|
|
42590
42709
|
init_nango_control();
|
|
42591
42710
|
init_resend_control();
|
|
@@ -42645,6 +42764,11 @@ var init_server = __esm({
|
|
|
42645
42764
|
}
|
|
42646
42765
|
return next();
|
|
42647
42766
|
});
|
|
42767
|
+
requirePaidSchedulingTier = (0, import_factory6.createMiddleware)(async (c, next) => {
|
|
42768
|
+
const user = c.get("user");
|
|
42769
|
+
if (!hasPaidSchedulingAccess(user)) return c.json(paidPlanRequiredPayload(), 403);
|
|
42770
|
+
return next();
|
|
42771
|
+
});
|
|
42648
42772
|
app = new import_hono24.Hono();
|
|
42649
42773
|
STRIPE_API_VERSION = "2026-02-25.clover";
|
|
42650
42774
|
app.use("*", async (c, next) => {
|
|
@@ -42894,6 +43018,18 @@ var init_server = __esm({
|
|
|
42894
43018
|
const user = c.get("user");
|
|
42895
43019
|
const body = await c.req.json().catch(() => ({}));
|
|
42896
43020
|
if (!body.toolName) return c.json({ ok: false, error: "toolName is required" }, 400);
|
|
43021
|
+
const accessDenied = memoryScheduleAccessPayload(body.toolName, user);
|
|
43022
|
+
if (accessDenied) return c.json(accessDenied);
|
|
43023
|
+
if (memoryScheduleToolNeedsCredentials(body.toolName)) {
|
|
43024
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
43025
|
+
if (!credentials.ok) {
|
|
43026
|
+
return c.json({
|
|
43027
|
+
ok: false,
|
|
43028
|
+
code: "schedule_credentials_unavailable",
|
|
43029
|
+
error: credentials.error ?? "Scheduled Actions credentials are unavailable."
|
|
43030
|
+
}, 503);
|
|
43031
|
+
}
|
|
43032
|
+
}
|
|
42897
43033
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
42898
43034
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 502);
|
|
42899
43035
|
const result = await memoryCall(body.toolName, body.args ?? {}, key);
|
|
@@ -43093,7 +43229,7 @@ var init_server = __esm({
|
|
|
43093
43229
|
llmCostMultiplier: SCHEDULED_AGENT_LLM_MARKUP
|
|
43094
43230
|
}, 410);
|
|
43095
43231
|
});
|
|
43096
|
-
app.get("/schedule-status", auth2, async (c) => {
|
|
43232
|
+
app.get("/schedule-status", auth2, requirePaidSchedulingTier, async (c) => {
|
|
43097
43233
|
try {
|
|
43098
43234
|
const user = c.get("user");
|
|
43099
43235
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
@@ -43460,9 +43596,13 @@ var init_server = __esm({
|
|
|
43460
43596
|
return scheduleConnectionError(c, err, "Unable to run this connection action.");
|
|
43461
43597
|
}
|
|
43462
43598
|
});
|
|
43463
|
-
app.post("/schedule-link", auth2, async (c) => {
|
|
43599
|
+
app.post("/schedule-link", auth2, requirePaidSchedulingTier, async (c) => {
|
|
43464
43600
|
try {
|
|
43465
43601
|
const user = c.get("user");
|
|
43602
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
43603
|
+
if (!credentials.ok) {
|
|
43604
|
+
return c.json({ ok: false, code: "schedule_credentials_unavailable", error: credentials.error ?? "Scheduled Actions credentials are unavailable." }, 503);
|
|
43605
|
+
}
|
|
43466
43606
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
43467
43607
|
if (!key) return c.json({ error: error ?? "memory unavailable" }, 503);
|
|
43468
43608
|
const r = await memoryCall("getScheduleLinkTool", {}, key);
|
|
@@ -43500,8 +43640,12 @@ var init_server = __esm({
|
|
|
43500
43640
|
});
|
|
43501
43641
|
}
|
|
43502
43642
|
});
|
|
43503
|
-
app.post("/schedule-actions", auth2, async (c) => {
|
|
43643
|
+
app.post("/schedule-actions", auth2, requirePaidSchedulingTier, async (c) => {
|
|
43504
43644
|
const user = c.get("user");
|
|
43645
|
+
const credentials = await syncScheduledActionCredentials(user);
|
|
43646
|
+
if (!credentials.ok) {
|
|
43647
|
+
return c.json({ ok: false, code: "schedule_credentials_unavailable", error: credentials.error ?? "Scheduled Actions credentials are unavailable." }, 503);
|
|
43648
|
+
}
|
|
43505
43649
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
43506
43650
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|
|
43507
43651
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -43593,7 +43737,7 @@ var init_server = __esm({
|
|
|
43593
43737
|
const r = await memoryCall("pauseScheduledActionTool", { id: c.req.param("id") }, key);
|
|
43594
43738
|
return c.json(r);
|
|
43595
43739
|
});
|
|
43596
|
-
app.post("/schedule-actions/:id/resume", auth2, async (c) => {
|
|
43740
|
+
app.post("/schedule-actions/:id/resume", auth2, requirePaidSchedulingTier, async (c) => {
|
|
43597
43741
|
const user = c.get("user");
|
|
43598
43742
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
43599
43743
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|