mcp-scraper 0.3.40 → 0.3.44
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/bin/api-server.cjs +1500 -1278
- 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 +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +58 -12
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/chunk-3Q2NSHSR.js +7 -0
- package/dist/chunk-3Q2NSHSR.js.map +1 -0
- package/dist/{chunk-QLQYNE7E.js → chunk-NONBSIES.js} +59 -13
- package/dist/chunk-NONBSIES.js.map +1 -0
- package/dist/{chunk-KJWNVQFL.js → chunk-X53SOJSL.js} +21 -4
- package/dist/chunk-X53SOJSL.js.map +1 -0
- package/dist/{server-HB7WJO3N.js → server-AN6QUH6C.js} +531 -388
- package/dist/server-AN6QUH6C.js.map +1 -0
- package/dist/{worker-TL4J65DE.js → worker-H4EUD2Q5.js} +2 -2
- package/package.json +4 -2
- package/dist/chunk-I5EFBTYE.js +0 -7
- package/dist/chunk-I5EFBTYE.js.map +0 -1
- package/dist/chunk-KJWNVQFL.js.map +0 -1
- package/dist/chunk-QLQYNE7E.js.map +0 -1
- package/dist/server-HB7WJO3N.js.map +0 -1
- /package/dist/{worker-TL4J65DE.js.map → worker-H4EUD2Q5.js.map} +0 -0
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
renderLinkReport,
|
|
17
17
|
sanitizeAttempts,
|
|
18
18
|
sanitizeHarvestResult
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-NONBSIES.js";
|
|
20
20
|
import {
|
|
21
21
|
csvRecords,
|
|
22
22
|
listWorkflowDefinitions,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
workflowStepCount,
|
|
30
30
|
workflowSupportsSteps
|
|
31
31
|
} from "./chunk-NNEIXK5L.js";
|
|
32
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-3Q2NSHSR.js";
|
|
33
33
|
import {
|
|
34
34
|
BROWSER_OPEN_MIN_BALANCE_MC,
|
|
35
35
|
CONCURRENCY_PRICE_ID,
|
|
@@ -38,6 +38,8 @@ import {
|
|
|
38
38
|
LedgerOperation,
|
|
39
39
|
MC_COSTS,
|
|
40
40
|
MC_PER_CREDIT,
|
|
41
|
+
MC_PER_USD,
|
|
42
|
+
MEMORY_AI_MARGIN_MULTIPLE,
|
|
41
43
|
MEMORY_PLANS,
|
|
42
44
|
MEMORY_PLAN_QUOTA,
|
|
43
45
|
SCHEDULING_PLANS,
|
|
@@ -50,7 +52,7 @@ import {
|
|
|
50
52
|
harvestProblemResponse,
|
|
51
53
|
insufficientBalanceResponse,
|
|
52
54
|
serializeHarvestProblem
|
|
53
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-X53SOJSL.js";
|
|
54
56
|
import {
|
|
55
57
|
BrowserDriver,
|
|
56
58
|
MapsSelectors,
|
|
@@ -3548,7 +3550,7 @@ async function fetchWithKernel(url) {
|
|
|
3548
3550
|
});
|
|
3549
3551
|
await client2.browsers.deleteByID(kb.session_id).catch(() => {
|
|
3550
3552
|
});
|
|
3551
|
-
void recordKernelSession({ kernelSessionId: kb.session_id, source: "kernel_fetch", stealth: typeof kb.stealth === "boolean" ? kb.stealth : null, headlessSent: useHeadless
|
|
3553
|
+
void recordKernelSession({ kernelSessionId: kb.session_id, source: "kernel_fetch", stealth: typeof kb.stealth === "boolean" ? kb.stealth : null, headlessSent: useHeadless, proxyUsed: typeof kb.proxy_id === "string" ? Boolean(kb.proxy_id) : null, fallback: true, openedAtMs, closedAtMs: Date.now() });
|
|
3552
3554
|
}
|
|
3553
3555
|
}
|
|
3554
3556
|
|
|
@@ -5257,7 +5259,7 @@ async function extractSite(opts) {
|
|
|
5257
5259
|
}
|
|
5258
5260
|
|
|
5259
5261
|
// src/api/server.ts
|
|
5260
|
-
import { Hono as
|
|
5262
|
+
import { Hono as Hono20 } from "hono";
|
|
5261
5263
|
import { serve as serveInngest } from "inngest/hono";
|
|
5262
5264
|
|
|
5263
5265
|
// src/inngest/client.ts
|
|
@@ -8287,8 +8289,28 @@ siteAuditApp.get("/jobs", async (c) => {
|
|
|
8287
8289
|
return c.json(jobs);
|
|
8288
8290
|
});
|
|
8289
8291
|
|
|
8290
|
-
// src/api/
|
|
8292
|
+
// src/api/internal-memory-routes.ts
|
|
8291
8293
|
import { Hono as Hono2 } from "hono";
|
|
8294
|
+
var internalMemoryApp = new Hono2();
|
|
8295
|
+
internalMemoryApp.post("/ai-debit", async (c) => {
|
|
8296
|
+
const secret2 = c.req.header("x-internal-secret");
|
|
8297
|
+
if (!secret2 || secret2 !== process.env.INTERNAL_MEMORY_DEBIT_SECRET) {
|
|
8298
|
+
return c.json({ error: "unauthorized" }, 401);
|
|
8299
|
+
}
|
|
8300
|
+
const body = await c.req.json().catch(() => ({}));
|
|
8301
|
+
if (!body.identity || typeof body.costUsd !== "number" || body.costUsd < 0) {
|
|
8302
|
+
return c.json({ error: "invalid_request" }, 400);
|
|
8303
|
+
}
|
|
8304
|
+
const user = await getUserByEmail(body.identity.trim().toLowerCase());
|
|
8305
|
+
if (!user) return c.json({ ok: false, reason: "no_user" }, 200);
|
|
8306
|
+
const mc = Math.round(body.costUsd * MEMORY_AI_MARGIN_MULTIPLE * MC_PER_USD);
|
|
8307
|
+
if (mc <= 0) return c.json({ ok: true, mc: 0, balance_mc: null }, 200);
|
|
8308
|
+
const { ok, balance_mc } = await debitMc(user.id, mc, LedgerOperation.MEMORY_AI, `${body.source ?? "ai"}:${body.op ?? ""}`);
|
|
8309
|
+
return c.json({ ok, mc, balance_mc, insufficient: !ok }, 200);
|
|
8310
|
+
});
|
|
8311
|
+
|
|
8312
|
+
// src/api/youtube-routes.ts
|
|
8313
|
+
import { Hono as Hono3 } from "hono";
|
|
8292
8314
|
|
|
8293
8315
|
// src/youtube/youtube-harvest.ts
|
|
8294
8316
|
import { promises as fs } from "fs";
|
|
@@ -9280,7 +9302,7 @@ async function extendConcurrencyGate(lockId, ttlSeconds = DEFAULT_LOCK_TTL_SECON
|
|
|
9280
9302
|
}
|
|
9281
9303
|
|
|
9282
9304
|
// src/api/youtube-routes.ts
|
|
9283
|
-
var youtubeApp = new
|
|
9305
|
+
var youtubeApp = new Hono3();
|
|
9284
9306
|
youtubeApp.post("/harvest", createApiKeyAuth(), async (c) => {
|
|
9285
9307
|
const raw = await c.req.json().catch(() => ({}));
|
|
9286
9308
|
const parsed = YoutubeHarvestBodySchema.safeParse(raw);
|
|
@@ -9439,14 +9461,14 @@ function fmtTs(secs) {
|
|
|
9439
9461
|
}
|
|
9440
9462
|
|
|
9441
9463
|
// src/api/screenshot-routes.ts
|
|
9442
|
-
import { Hono as
|
|
9464
|
+
import { Hono as Hono4 } from "hono";
|
|
9443
9465
|
import { z as z12 } from "zod";
|
|
9444
9466
|
var ScreenshotBodySchema = z12.object({
|
|
9445
9467
|
url: z12.string().trim().min(1, "url is required"),
|
|
9446
9468
|
device: z12.string().trim().optional(),
|
|
9447
9469
|
allowLocal: z12.boolean().optional()
|
|
9448
9470
|
});
|
|
9449
|
-
var screenshotApp = new
|
|
9471
|
+
var screenshotApp = new Hono4();
|
|
9450
9472
|
screenshotApp.use("*", createApiKeyAuth());
|
|
9451
9473
|
screenshotApp.post("/", async (c) => {
|
|
9452
9474
|
const raw = await c.req.json().catch(() => ({}));
|
|
@@ -9503,7 +9525,7 @@ screenshotApp.post("/", async (c) => {
|
|
|
9503
9525
|
});
|
|
9504
9526
|
|
|
9505
9527
|
// src/api/facebook-ad-routes.ts
|
|
9506
|
-
import { Hono as
|
|
9528
|
+
import { Hono as Hono5 } from "hono";
|
|
9507
9529
|
import { z as z13 } from "zod";
|
|
9508
9530
|
|
|
9509
9531
|
// src/extractor/FacebookAdExtractor.ts
|
|
@@ -10502,7 +10524,7 @@ async function kernelLaunchOptsResidential() {
|
|
|
10502
10524
|
}
|
|
10503
10525
|
return { headless: true, kernelApiKey: browserServiceApiKey(), kernelProxyId: proxyId, viewport: { width: 1280, height: 900 }, locale: "en-US" };
|
|
10504
10526
|
}
|
|
10505
|
-
var facebookAdApp = new
|
|
10527
|
+
var facebookAdApp = new Hono5();
|
|
10506
10528
|
facebookAdApp.post("/ad", createApiKeyAuth(), async (c) => {
|
|
10507
10529
|
const raw = await c.req.json().catch(() => ({}));
|
|
10508
10530
|
const parsed = FacebookAdBodySchema.safeParse(raw);
|
|
@@ -10835,7 +10857,7 @@ facebookAdApp.post("/media", createApiKeyAuth(), async (c) => {
|
|
|
10835
10857
|
});
|
|
10836
10858
|
|
|
10837
10859
|
// src/api/google-ads-routes.ts
|
|
10838
|
-
import { Hono as
|
|
10860
|
+
import { Hono as Hono6 } from "hono";
|
|
10839
10861
|
import { z as z14 } from "zod";
|
|
10840
10862
|
|
|
10841
10863
|
// src/extractor/GoogleAdsExtractor.ts
|
|
@@ -11044,7 +11066,7 @@ async function kernelLaunchOptsResidential2() {
|
|
|
11044
11066
|
function isBlockedMessage(msg) {
|
|
11045
11067
|
return msg.toLowerCase().includes("blocked") || msg.toLowerCase().includes("captcha");
|
|
11046
11068
|
}
|
|
11047
|
-
var googleAdsApp = new
|
|
11069
|
+
var googleAdsApp = new Hono6();
|
|
11048
11070
|
googleAdsApp.post("/search", createApiKeyAuth(), async (c) => {
|
|
11049
11071
|
const raw = await c.req.json().catch(() => ({}));
|
|
11050
11072
|
const parsed = GoogleAdsSearchBodySchema.safeParse(raw);
|
|
@@ -11156,7 +11178,7 @@ googleAdsApp.post("/transcribe", createApiKeyAuth(), async (c) => {
|
|
|
11156
11178
|
});
|
|
11157
11179
|
|
|
11158
11180
|
// src/api/instagram-routes.ts
|
|
11159
|
-
import { Hono as
|
|
11181
|
+
import { Hono as Hono7 } from "hono";
|
|
11160
11182
|
import { z as z15 } from "zod";
|
|
11161
11183
|
import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync3, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
11162
11184
|
import { homedir as homedir3 } from "os";
|
|
@@ -11546,7 +11568,7 @@ var InstagramMediaDownloadBodySchema = z15.object({
|
|
|
11546
11568
|
includeTranscript: z15.boolean().default(false),
|
|
11547
11569
|
mux: z15.boolean().default(true)
|
|
11548
11570
|
});
|
|
11549
|
-
var instagramApp = new
|
|
11571
|
+
var instagramApp = new Hono7();
|
|
11550
11572
|
function invalidRequest3(message) {
|
|
11551
11573
|
return { error_code: "invalid_request", message };
|
|
11552
11574
|
}
|
|
@@ -11820,7 +11842,7 @@ instagramApp.post("/media-download", createApiKeyAuth(), async (c) => {
|
|
|
11820
11842
|
});
|
|
11821
11843
|
|
|
11822
11844
|
// src/api/reddit-routes.ts
|
|
11823
|
-
import { Hono as
|
|
11845
|
+
import { Hono as Hono8 } from "hono";
|
|
11824
11846
|
import { z as z16 } from "zod";
|
|
11825
11847
|
var RedditThreadBodySchema = z16.object({
|
|
11826
11848
|
url: z16.string().trim().min(1, "url is required"),
|
|
@@ -11880,7 +11902,7 @@ var PARSE_REDDIT = `(() => {
|
|
|
11880
11902
|
});
|
|
11881
11903
|
return { title: title, author: author, score: score, postBody: postBody, blocked: blocked, numComments: comments.length, comments: comments };
|
|
11882
11904
|
})()`;
|
|
11883
|
-
var redditApp = new
|
|
11905
|
+
var redditApp = new Hono8();
|
|
11884
11906
|
redditApp.post("/thread", createApiKeyAuth(), async (c) => {
|
|
11885
11907
|
const raw = await c.req.json().catch(() => ({}));
|
|
11886
11908
|
const parsed = RedditThreadBodySchema.safeParse(raw);
|
|
@@ -11914,11 +11936,11 @@ redditApp.post("/thread", createApiKeyAuth(), async (c) => {
|
|
|
11914
11936
|
locale: "en-US"
|
|
11915
11937
|
});
|
|
11916
11938
|
const page = driver.getPage();
|
|
11917
|
-
await page.goto(oldUrl, { waitUntil: "domcontentloaded", timeout: 45e3 });
|
|
11939
|
+
await page.goto(oldUrl, { waitUntil: "domcontentloaded", timeout: attempt === 0 ? 3e4 : 45e3 });
|
|
11918
11940
|
await page.waitForTimeout(2500);
|
|
11919
11941
|
const data = await page.evaluate(PARSE_REDDIT);
|
|
11920
11942
|
if (!data.blocked && (data.postBody || data.comments.length > 0)) {
|
|
11921
|
-
result = { ...data, sourceUrl: body.url, oldRedditUrl: oldUrl, attempts: attempt + 1 };
|
|
11943
|
+
result = { ...data, sourceUrl: body.url, oldRedditUrl: oldUrl, attempts: attempt + 1, proxied: Boolean(proxyId) };
|
|
11922
11944
|
}
|
|
11923
11945
|
} catch {
|
|
11924
11946
|
} finally {
|
|
@@ -11947,8 +11969,231 @@ redditApp.post("/thread", createApiKeyAuth(), async (c) => {
|
|
|
11947
11969
|
}
|
|
11948
11970
|
});
|
|
11949
11971
|
|
|
11972
|
+
// src/api/video-routes.ts
|
|
11973
|
+
import { Hono as Hono9 } from "hono";
|
|
11974
|
+
import { z as z17 } from "zod";
|
|
11975
|
+
|
|
11976
|
+
// src/api/memory.ts
|
|
11977
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
11978
|
+
|
|
11979
|
+
// src/api/session.ts
|
|
11980
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
11981
|
+
var isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
|
|
11982
|
+
function getSessionSecret() {
|
|
11983
|
+
const configured = process.env.SESSION_SECRET?.trim();
|
|
11984
|
+
if (configured) return configured;
|
|
11985
|
+
if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
|
|
11986
|
+
return "dev-secret-change-me";
|
|
11987
|
+
}
|
|
11988
|
+
var secret = () => getSessionSecret();
|
|
11989
|
+
function safeEqualHex(a, b) {
|
|
11990
|
+
if (a.length !== b.length) return false;
|
|
11991
|
+
try {
|
|
11992
|
+
return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
11993
|
+
} catch {
|
|
11994
|
+
return false;
|
|
11995
|
+
}
|
|
11996
|
+
}
|
|
11997
|
+
function signSession(userId) {
|
|
11998
|
+
const payload = String(userId);
|
|
11999
|
+
const sig = createHmac("sha256", secret()).update(payload).digest("hex");
|
|
12000
|
+
return `${payload}.${sig}`;
|
|
12001
|
+
}
|
|
12002
|
+
function verifySession(token) {
|
|
12003
|
+
const dot = token.lastIndexOf(".");
|
|
12004
|
+
if (dot === -1) return null;
|
|
12005
|
+
const payload = token.slice(0, dot);
|
|
12006
|
+
const sig = token.slice(dot + 1);
|
|
12007
|
+
const expected = createHmac("sha256", secret()).update(payload).digest("hex");
|
|
12008
|
+
if (!safeEqualHex(sig, expected)) return null;
|
|
12009
|
+
const id = parseInt(payload);
|
|
12010
|
+
return isNaN(id) ? null : id;
|
|
12011
|
+
}
|
|
12012
|
+
|
|
12013
|
+
// src/api/memory.ts
|
|
12014
|
+
var MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://mcp-memory-omega.vercel.app").replace(/\/$/, "");
|
|
12015
|
+
var ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
12016
|
+
function isMemoryOperator(email) {
|
|
12017
|
+
if (!email) return false;
|
|
12018
|
+
const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
12019
|
+
return ops.includes(email.trim().toLowerCase());
|
|
12020
|
+
}
|
|
12021
|
+
function encKey() {
|
|
12022
|
+
return scryptSync(getSessionSecret(), "mcp-memory-key-v1", 32);
|
|
12023
|
+
}
|
|
12024
|
+
function encryptMemoryKey(secret2) {
|
|
12025
|
+
const iv = randomBytes(12);
|
|
12026
|
+
const cipher = createCipheriv("aes-256-gcm", encKey(), iv);
|
|
12027
|
+
const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
|
|
12028
|
+
const tag = cipher.getAuthTag();
|
|
12029
|
+
return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
|
|
12030
|
+
}
|
|
12031
|
+
function decryptMemoryKey(stored) {
|
|
12032
|
+
try {
|
|
12033
|
+
const [ivB, tagB, dataB] = stored.split(":");
|
|
12034
|
+
const decipher = createDecipheriv("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
|
|
12035
|
+
decipher.setAuthTag(Buffer.from(tagB, "base64"));
|
|
12036
|
+
return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
|
|
12037
|
+
} catch {
|
|
12038
|
+
return null;
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
async function memoryCall(toolName, args, userMemoryKey) {
|
|
12042
|
+
try {
|
|
12043
|
+
const res = await fetch(`${MEMORY_BASE_URL()}/api/mcp/memoryServer/tools/${toolName}/execute`, {
|
|
12044
|
+
method: "POST",
|
|
12045
|
+
headers: { "content-type": "application/json" },
|
|
12046
|
+
body: JSON.stringify({ data: { ...args, apiKey: userMemoryKey } })
|
|
12047
|
+
});
|
|
12048
|
+
const body = await res.json().catch(() => null);
|
|
12049
|
+
if (!res.ok) return { ok: false, error: body?.error ?? `memory ${toolName} failed (${res.status})` };
|
|
12050
|
+
if (body?.result) return body.result;
|
|
12051
|
+
return { ok: false, error: body?.error ?? `memory ${toolName} returned no result` };
|
|
12052
|
+
} catch (err) {
|
|
12053
|
+
return { ok: false, error: err?.message ?? "memory call failed" };
|
|
12054
|
+
}
|
|
12055
|
+
}
|
|
12056
|
+
function personalVault(user) {
|
|
12057
|
+
return `mcp-${user.id}`;
|
|
12058
|
+
}
|
|
12059
|
+
function memoryIdentity(user) {
|
|
12060
|
+
return user.email;
|
|
12061
|
+
}
|
|
12062
|
+
function resolveMemoryIdentity(user) {
|
|
12063
|
+
return memoryIdentity(user);
|
|
12064
|
+
}
|
|
12065
|
+
function memoryPlanForReads(user) {
|
|
12066
|
+
if (isMemoryOperator(user.email)) return "unlimited";
|
|
12067
|
+
if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
|
|
12068
|
+
return "free";
|
|
12069
|
+
}
|
|
12070
|
+
async function provisionMemoryForUser(user) {
|
|
12071
|
+
try {
|
|
12072
|
+
const { key } = await getOrCreateUserMemoryKey(user);
|
|
12073
|
+
if (key) await setMemoryProvisioned(user.id, true);
|
|
12074
|
+
} catch {
|
|
12075
|
+
}
|
|
12076
|
+
}
|
|
12077
|
+
async function getOrCreateUserMemoryKey(user) {
|
|
12078
|
+
const creds = await getUserMemoryCreds(user.id);
|
|
12079
|
+
if (creds.memory_key) {
|
|
12080
|
+
const decrypted = decryptMemoryKey(creds.memory_key);
|
|
12081
|
+
if (decrypted) return { key: decrypted };
|
|
12082
|
+
}
|
|
12083
|
+
const admin = ADMIN_KEY();
|
|
12084
|
+
if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
12085
|
+
const identity = memoryIdentity(user);
|
|
12086
|
+
const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
|
|
12087
|
+
const provisioned = await memoryCall(
|
|
12088
|
+
"provisionDefaultsTool",
|
|
12089
|
+
{ granteeIdentity: identity, issueKey: true, plan },
|
|
12090
|
+
admin
|
|
12091
|
+
);
|
|
12092
|
+
if (!provisioned.ok || !provisioned.secret) {
|
|
12093
|
+
return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
|
|
12094
|
+
}
|
|
12095
|
+
await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
|
|
12096
|
+
return { key: provisioned.secret };
|
|
12097
|
+
}
|
|
12098
|
+
async function syncMemoryKeyPlan(user, plan) {
|
|
12099
|
+
const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
|
|
12100
|
+
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
12101
|
+
if (!key) return { ok: false, error: error ?? "memory unavailable" };
|
|
12102
|
+
const listed = await memoryCall(
|
|
12103
|
+
"listKeysTool",
|
|
12104
|
+
{},
|
|
12105
|
+
key
|
|
12106
|
+
);
|
|
12107
|
+
if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
|
|
12108
|
+
const target = listed.keys[0];
|
|
12109
|
+
if (target.plan === effectivePlan) return { ok: true };
|
|
12110
|
+
const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
|
|
12111
|
+
return { ok: res.ok, error: res.error };
|
|
12112
|
+
}
|
|
12113
|
+
async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
|
|
12114
|
+
const admin = ADMIN_KEY();
|
|
12115
|
+
if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
12116
|
+
const identity = resolveMemoryIdentity(user);
|
|
12117
|
+
const res = await memoryCall("setScheduleEntitlementTool", {
|
|
12118
|
+
granteeIdentity: identity,
|
|
12119
|
+
enabled,
|
|
12120
|
+
quotaPerPeriod,
|
|
12121
|
+
mcpScraperApiKey: user.api_key
|
|
12122
|
+
}, admin);
|
|
12123
|
+
return { ok: res.ok, error: res.error };
|
|
12124
|
+
}
|
|
12125
|
+
|
|
12126
|
+
// src/api/video-routes.ts
|
|
12127
|
+
var videoApp = new Hono9();
|
|
12128
|
+
var AnalyzeBodySchema = z17.object({
|
|
12129
|
+
sourceUrl: z17.string().trim().url("sourceUrl must be a direct video file URL"),
|
|
12130
|
+
intervalS: z17.number().min(1).max(30).optional(),
|
|
12131
|
+
maxFrames: z17.number().int().min(1).max(120).optional(),
|
|
12132
|
+
detail: z17.enum(["fast", "standard", "deep"]).optional(),
|
|
12133
|
+
vault: z17.string().trim().min(1).optional()
|
|
12134
|
+
});
|
|
12135
|
+
function invalidRequest5(message) {
|
|
12136
|
+
return { error_code: "invalid_request", message };
|
|
12137
|
+
}
|
|
12138
|
+
videoApp.post("/analyze", createApiKeyAuth(), async (c) => {
|
|
12139
|
+
const parsed = AnalyzeBodySchema.safeParse(await c.req.json().catch(() => ({})));
|
|
12140
|
+
if (!parsed.success) return c.json(invalidRequest5(parsed.error.issues[0]?.message ?? "invalid request"), 400);
|
|
12141
|
+
const body = parsed.data;
|
|
12142
|
+
const user = c.get("user");
|
|
12143
|
+
const { key: memKey, error: memErr } = await getOrCreateUserMemoryKey(user);
|
|
12144
|
+
if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
|
|
12145
|
+
const { ok, balance_mc } = await debitMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS, body.sourceUrl);
|
|
12146
|
+
if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.video_analysis), 402);
|
|
12147
|
+
const started = await memoryCall(
|
|
12148
|
+
"videoAnalyzeStartTool",
|
|
12149
|
+
{
|
|
12150
|
+
sourceUrl: body.sourceUrl,
|
|
12151
|
+
intervalS: body.intervalS ?? 2,
|
|
12152
|
+
maxFrames: body.maxFrames ?? 120,
|
|
12153
|
+
detail: body.detail ?? "standard",
|
|
12154
|
+
vault: body.vault ?? "Library"
|
|
12155
|
+
},
|
|
12156
|
+
memKey
|
|
12157
|
+
);
|
|
12158
|
+
if (!started.ok || !started.runId) {
|
|
12159
|
+
await creditMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS_REFUND, `start-failed:${body.sourceUrl}`);
|
|
12160
|
+
return c.json({ error_code: "video_start_failed", message: started.error ?? "could not start analysis" }, 502);
|
|
12161
|
+
}
|
|
12162
|
+
return c.json({
|
|
12163
|
+
ok: true,
|
|
12164
|
+
runId: started.runId,
|
|
12165
|
+
status: "queued",
|
|
12166
|
+
message: 'Analysis started. Poll GET /video/status?runId=... every few seconds until status is "done".'
|
|
12167
|
+
});
|
|
12168
|
+
});
|
|
12169
|
+
videoApp.post("/status", createApiKeyAuth(), async (c) => {
|
|
12170
|
+
const rawBody = await c.req.json().catch(() => ({}));
|
|
12171
|
+
const runId = rawBody.runId?.trim();
|
|
12172
|
+
if (!runId) return c.json(invalidRequest5("runId is required"), 400);
|
|
12173
|
+
const user = c.get("user");
|
|
12174
|
+
const { key: memKey, error: memErr } = await getOrCreateUserMemoryKey(user);
|
|
12175
|
+
if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
|
|
12176
|
+
const st = await memoryCall("videoAnalyzeStatusTool", { runId }, memKey);
|
|
12177
|
+
if (!st.ok) return c.json({ error_code: "not_found", message: st.error ?? "run not found" }, 404);
|
|
12178
|
+
if (st.status === "failed") {
|
|
12179
|
+
const refundOp = `${LedgerOperation.VIDEO_ANALYSIS_REFUND}:${runId}`;
|
|
12180
|
+
if (!await ledgerExistsForOperation(user.id, refundOp)) {
|
|
12181
|
+
await creditMc(user.id, MC_COSTS.video_analysis, refundOp, `run-failed:${runId}`);
|
|
12182
|
+
}
|
|
12183
|
+
}
|
|
12184
|
+
return c.json({
|
|
12185
|
+
ok: true,
|
|
12186
|
+
runId,
|
|
12187
|
+
status: st.status ?? "unknown",
|
|
12188
|
+
progress: st.progress ?? null,
|
|
12189
|
+
frameCount: st.frameCount ?? null,
|
|
12190
|
+
...st.status === "done" ? { artifactPath: st.artifactPath ?? null, report: st.report ?? null } : {},
|
|
12191
|
+
...st.error ? { error: st.error } : {}
|
|
12192
|
+
});
|
|
12193
|
+
});
|
|
12194
|
+
|
|
11950
12195
|
// src/api/maps-routes.ts
|
|
11951
|
-
import { Hono as
|
|
12196
|
+
import { Hono as Hono10 } from "hono";
|
|
11952
12197
|
|
|
11953
12198
|
// src/extractor/MapsNavigator.ts
|
|
11954
12199
|
var MapsNavigator = class {
|
|
@@ -12123,7 +12368,7 @@ var MapsReviewCollector = class {
|
|
|
12123
12368
|
};
|
|
12124
12369
|
|
|
12125
12370
|
// src/extractor/MapsExtractor.ts
|
|
12126
|
-
import { z as
|
|
12371
|
+
import { z as z18 } from "zod";
|
|
12127
12372
|
var MapsExtractor = class {
|
|
12128
12373
|
constructor(driver) {
|
|
12129
12374
|
this.driver = driver;
|
|
@@ -12309,7 +12554,7 @@ var MapsExtractor = class {
|
|
|
12309
12554
|
});
|
|
12310
12555
|
return rows;
|
|
12311
12556
|
}, { hoursTable: MapsSelectors.hoursTable, hoursTableAlt: MapsSelectors.hoursTableAlt });
|
|
12312
|
-
const result =
|
|
12557
|
+
const result = z18.array(RawMapsHoursRowSchema).safeParse(raw);
|
|
12313
12558
|
if (!result.success) {
|
|
12314
12559
|
console.warn("[MapsExtractor] hours parse failed", result.error.flatten());
|
|
12315
12560
|
return [];
|
|
@@ -12373,7 +12618,7 @@ var MapsExtractor = class {
|
|
|
12373
12618
|
});
|
|
12374
12619
|
return results;
|
|
12375
12620
|
});
|
|
12376
|
-
const result =
|
|
12621
|
+
const result = z18.array(RawMapsAboutAttributeSchema).safeParse(raw);
|
|
12377
12622
|
if (!result.success) {
|
|
12378
12623
|
console.warn("[MapsExtractor] about parse failed", result.error.flatten());
|
|
12379
12624
|
return [];
|
|
@@ -12749,7 +12994,7 @@ function mapsErrorResponse(c, err, errorCode) {
|
|
|
12749
12994
|
attempts: rotationError?.attempts ?? void 0
|
|
12750
12995
|
}, retryable ? 503 : 500);
|
|
12751
12996
|
}
|
|
12752
|
-
var mapsApp = new
|
|
12997
|
+
var mapsApp = new Hono10();
|
|
12753
12998
|
mapsApp.post("/search", createApiKeyAuth(), async (c) => {
|
|
12754
12999
|
const user = c.get("user");
|
|
12755
13000
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -12887,12 +13132,12 @@ mapsApp.post("/place", createApiKeyAuth(), async (c) => {
|
|
|
12887
13132
|
});
|
|
12888
13133
|
|
|
12889
13134
|
// src/api/directory-routes.ts
|
|
12890
|
-
import { Hono as
|
|
13135
|
+
import { Hono as Hono11 } from "hono";
|
|
12891
13136
|
|
|
12892
13137
|
// src/directory/directory-workflow.ts
|
|
12893
13138
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
12894
13139
|
import { join as join6 } from "path";
|
|
12895
|
-
import { z as
|
|
13140
|
+
import { z as z19 } from "zod";
|
|
12896
13141
|
|
|
12897
13142
|
// src/directory/location-db.ts
|
|
12898
13143
|
import { access, readFile } from "fs/promises";
|
|
@@ -13053,24 +13298,24 @@ async function resolveDirectoryMarkets(options) {
|
|
|
13053
13298
|
}
|
|
13054
13299
|
|
|
13055
13300
|
// src/directory/directory-workflow.ts
|
|
13056
|
-
var DirectoryWorkflowOptionsSchema =
|
|
13057
|
-
query:
|
|
13058
|
-
state:
|
|
13059
|
-
minPopulation:
|
|
13060
|
-
populationYear:
|
|
13061
|
-
maxCities:
|
|
13062
|
-
maxResultsPerCity:
|
|
13063
|
-
concurrency:
|
|
13064
|
-
includeZipGroups:
|
|
13065
|
-
usZipsCsvPath:
|
|
13066
|
-
saveCsv:
|
|
13067
|
-
gl:
|
|
13068
|
-
hl:
|
|
13069
|
-
proxyMode:
|
|
13070
|
-
proxyZip:
|
|
13071
|
-
debug:
|
|
13072
|
-
headless:
|
|
13073
|
-
kernelApiKey:
|
|
13301
|
+
var DirectoryWorkflowOptionsSchema = z19.object({
|
|
13302
|
+
query: z19.string().min(1),
|
|
13303
|
+
state: z19.string().min(2).default("TN"),
|
|
13304
|
+
minPopulation: z19.number().int().min(0).default(1e5),
|
|
13305
|
+
populationYear: z19.union(POPULATION_YEARS.map((year) => z19.literal(year))).default(2025),
|
|
13306
|
+
maxCities: z19.number().int().min(1).max(100).default(25),
|
|
13307
|
+
maxResultsPerCity: z19.number().int().min(1).max(50).default(50),
|
|
13308
|
+
concurrency: z19.number().int().min(1).max(5).default(5),
|
|
13309
|
+
includeZipGroups: z19.boolean().default(true),
|
|
13310
|
+
usZipsCsvPath: z19.string().optional(),
|
|
13311
|
+
saveCsv: z19.boolean().default(true),
|
|
13312
|
+
gl: z19.string().length(2).default("us"),
|
|
13313
|
+
hl: z19.string().length(2).default("en"),
|
|
13314
|
+
proxyMode: z19.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
|
|
13315
|
+
proxyZip: z19.string().regex(/^\d{5}$/).optional(),
|
|
13316
|
+
debug: z19.boolean().default(false),
|
|
13317
|
+
headless: z19.boolean().default(true),
|
|
13318
|
+
kernelApiKey: z19.string().optional()
|
|
13074
13319
|
});
|
|
13075
13320
|
function errorMessage(err) {
|
|
13076
13321
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -13291,7 +13536,7 @@ async function runDirectoryWorkflowFromPlan(options, plan) {
|
|
|
13291
13536
|
}
|
|
13292
13537
|
|
|
13293
13538
|
// src/api/directory-routes.ts
|
|
13294
|
-
var directoryApp = new
|
|
13539
|
+
var directoryApp = new Hono11();
|
|
13295
13540
|
directoryApp.post("/run", createApiKeyAuth(), async (c) => {
|
|
13296
13541
|
const user = c.get("user");
|
|
13297
13542
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -13362,37 +13607,37 @@ directoryApp.post("/run", createApiKeyAuth(), async (c) => {
|
|
|
13362
13607
|
});
|
|
13363
13608
|
|
|
13364
13609
|
// src/api/workflow-routes.ts
|
|
13365
|
-
import { createHmac } from "crypto";
|
|
13610
|
+
import { createHmac as createHmac2 } from "crypto";
|
|
13366
13611
|
import { readFile as readFile2 } from "fs/promises";
|
|
13367
|
-
import { Hono as
|
|
13368
|
-
import { z as
|
|
13369
|
-
var workflowApp = new
|
|
13370
|
-
var WorkflowInputSchema =
|
|
13371
|
-
var WorkflowIdSchema =
|
|
13372
|
-
var CadenceSchema =
|
|
13373
|
-
var ScheduleStatusSchema =
|
|
13374
|
-
var RunBodySchema =
|
|
13612
|
+
import { Hono as Hono12 } from "hono";
|
|
13613
|
+
import { z as z20 } from "zod";
|
|
13614
|
+
var workflowApp = new Hono12();
|
|
13615
|
+
var WorkflowInputSchema = z20.record(z20.unknown()).default({});
|
|
13616
|
+
var WorkflowIdSchema = z20.string().min(1);
|
|
13617
|
+
var CadenceSchema = z20.enum(["daily", "weekly", "monthly"]);
|
|
13618
|
+
var ScheduleStatusSchema = z20.enum(["active", "paused"]);
|
|
13619
|
+
var RunBodySchema = z20.object({
|
|
13375
13620
|
workflowId: WorkflowIdSchema,
|
|
13376
13621
|
input: WorkflowInputSchema,
|
|
13377
|
-
webhookUrl:
|
|
13622
|
+
webhookUrl: z20.string().url().optional()
|
|
13378
13623
|
});
|
|
13379
|
-
var ScheduleCreateSchema =
|
|
13624
|
+
var ScheduleCreateSchema = z20.object({
|
|
13380
13625
|
workflowId: WorkflowIdSchema,
|
|
13381
|
-
name:
|
|
13626
|
+
name: z20.string().min(1).max(120).optional(),
|
|
13382
13627
|
input: WorkflowInputSchema,
|
|
13383
13628
|
cadence: CadenceSchema.default("weekly"),
|
|
13384
|
-
timezone:
|
|
13385
|
-
webhookUrl:
|
|
13386
|
-
nextRunAt:
|
|
13629
|
+
timezone: z20.string().min(1).max(64).default("UTC"),
|
|
13630
|
+
webhookUrl: z20.string().url().optional(),
|
|
13631
|
+
nextRunAt: z20.string().datetime().optional()
|
|
13387
13632
|
});
|
|
13388
|
-
var SchedulePatchSchema =
|
|
13389
|
-
name:
|
|
13633
|
+
var SchedulePatchSchema = z20.object({
|
|
13634
|
+
name: z20.string().min(1).max(120).optional(),
|
|
13390
13635
|
status: ScheduleStatusSchema.optional(),
|
|
13391
13636
|
input: WorkflowInputSchema.optional(),
|
|
13392
13637
|
cadence: CadenceSchema.optional(),
|
|
13393
|
-
timezone:
|
|
13394
|
-
webhookUrl:
|
|
13395
|
-
nextRunAt:
|
|
13638
|
+
timezone: z20.string().min(1).max(64).optional(),
|
|
13639
|
+
webhookUrl: z20.string().url().nullable().optional(),
|
|
13640
|
+
nextRunAt: z20.string().datetime().nullable().optional()
|
|
13396
13641
|
});
|
|
13397
13642
|
function hostedWorkflowOutputDir() {
|
|
13398
13643
|
return workflowOutputBaseDir(process.env.MCP_SCRAPER_WORKFLOW_OUTPUT_DIR?.trim() || "/tmp/mcp-scraper-workflows");
|
|
@@ -13450,7 +13695,7 @@ async function readManifestFromSummary(summary) {
|
|
|
13450
13695
|
function webhookSignature(body, timestamp) {
|
|
13451
13696
|
const secret2 = process.env.MCP_SCRAPER_WEBHOOK_SECRET?.trim();
|
|
13452
13697
|
if (!secret2) return null;
|
|
13453
|
-
return
|
|
13698
|
+
return createHmac2("sha256", secret2).update(`${timestamp}.${body}`).digest("hex");
|
|
13454
13699
|
}
|
|
13455
13700
|
async function deliverWorkflowWebhook(input) {
|
|
13456
13701
|
if (!input.webhookUrl) return;
|
|
@@ -13896,7 +14141,7 @@ workflowApp.post("/cron/dispatch", async (c) => {
|
|
|
13896
14141
|
});
|
|
13897
14142
|
|
|
13898
14143
|
// src/api/serp-intelligence-routes.ts
|
|
13899
|
-
import { Hono as
|
|
14144
|
+
import { Hono as Hono13 } from "hono";
|
|
13900
14145
|
|
|
13901
14146
|
// src/serp-intelligence/page-snapshot-extractor.ts
|
|
13902
14147
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -14208,7 +14453,7 @@ async function capturePageSnapshots(targets, options = {}) {
|
|
|
14208
14453
|
}
|
|
14209
14454
|
|
|
14210
14455
|
// src/serp-intelligence/schemas.ts
|
|
14211
|
-
import { z as
|
|
14456
|
+
import { z as z21 } from "zod";
|
|
14212
14457
|
var SerpIntelligenceDeviceValues = ["desktop", "mobile"];
|
|
14213
14458
|
var SerpIntelligenceProxyModeValues = ["location", "configured", "none"];
|
|
14214
14459
|
var SerpIntelligenceAttemptOutcomeValues = [
|
|
@@ -14270,171 +14515,171 @@ function isPublicHttpUrl(value) {
|
|
|
14270
14515
|
return false;
|
|
14271
14516
|
}
|
|
14272
14517
|
}
|
|
14273
|
-
var SerpIntelligencePublicHttpUrlSchema =
|
|
14274
|
-
var SerpIntelligenceCaptureBodySchema =
|
|
14275
|
-
query:
|
|
14276
|
-
location:
|
|
14277
|
-
gl:
|
|
14278
|
-
hl:
|
|
14279
|
-
device:
|
|
14280
|
-
proxyMode:
|
|
14281
|
-
proxyZip:
|
|
14282
|
-
pages:
|
|
14283
|
-
debug:
|
|
14284
|
-
includePageSnapshots:
|
|
14285
|
-
pageSnapshotLimit:
|
|
14518
|
+
var SerpIntelligencePublicHttpUrlSchema = z21.string().url().refine(isPublicHttpUrl, "url must be a public HTTP or HTTPS URL");
|
|
14519
|
+
var SerpIntelligenceCaptureBodySchema = z21.object({
|
|
14520
|
+
query: z21.string().trim().min(1, "query is required"),
|
|
14521
|
+
location: z21.string().trim().min(1).optional(),
|
|
14522
|
+
gl: z21.string().trim().length(2).default("us"),
|
|
14523
|
+
hl: z21.string().trim().length(2).default("en"),
|
|
14524
|
+
device: z21.enum(SerpIntelligenceDeviceValues).default("desktop"),
|
|
14525
|
+
proxyMode: z21.enum(SerpIntelligenceProxyModeValues).default(DEFAULT_PROXY_MODE),
|
|
14526
|
+
proxyZip: z21.string().regex(/^\d{5}$/).optional(),
|
|
14527
|
+
pages: z21.number().int().min(1).max(2).default(1),
|
|
14528
|
+
debug: z21.boolean().default(false),
|
|
14529
|
+
includePageSnapshots: z21.boolean().default(false),
|
|
14530
|
+
pageSnapshotLimit: z21.number().int().min(0).max(10).default(0)
|
|
14286
14531
|
}).strict();
|
|
14287
|
-
var SerpIntelligencePageSnapshotRequestSchema =
|
|
14532
|
+
var SerpIntelligencePageSnapshotRequestSchema = z21.object({
|
|
14288
14533
|
url: SerpIntelligencePublicHttpUrlSchema,
|
|
14289
|
-
sourceKind:
|
|
14290
|
-
sourcePosition:
|
|
14534
|
+
sourceKind: z21.enum(SerpPageSnapshotSourceKindValues).default("configured_target"),
|
|
14535
|
+
sourcePosition: z21.number().int().min(1).optional()
|
|
14291
14536
|
}).strict();
|
|
14292
|
-
var SerpIntelligencePageSnapshotsBodySchema =
|
|
14293
|
-
urls:
|
|
14294
|
-
targets:
|
|
14295
|
-
maxConcurrency:
|
|
14296
|
-
timeoutMs:
|
|
14297
|
-
debug:
|
|
14537
|
+
var SerpIntelligencePageSnapshotsBodySchema = z21.object({
|
|
14538
|
+
urls: z21.array(SerpIntelligencePublicHttpUrlSchema).min(1).max(25),
|
|
14539
|
+
targets: z21.array(SerpIntelligencePageSnapshotRequestSchema).min(1).max(25).optional(),
|
|
14540
|
+
maxConcurrency: z21.number().int().min(1).max(5).default(2),
|
|
14541
|
+
timeoutMs: z21.number().int().min(1e3).max(6e4).default(15e3),
|
|
14542
|
+
debug: z21.boolean().default(false)
|
|
14298
14543
|
}).strict();
|
|
14299
|
-
var SerpIntelligenceAICitationSchema =
|
|
14300
|
-
text:
|
|
14301
|
-
href:
|
|
14544
|
+
var SerpIntelligenceAICitationSchema = z21.object({
|
|
14545
|
+
text: z21.string(),
|
|
14546
|
+
href: z21.string()
|
|
14302
14547
|
}).strict();
|
|
14303
|
-
var SerpIntelligenceOrganicResultSchema =
|
|
14304
|
-
position:
|
|
14305
|
-
title:
|
|
14306
|
-
url:
|
|
14307
|
-
domain:
|
|
14308
|
-
cite:
|
|
14309
|
-
snippet:
|
|
14310
|
-
isRedditStyle:
|
|
14311
|
-
inlineRating:
|
|
14312
|
-
value:
|
|
14313
|
-
count:
|
|
14548
|
+
var SerpIntelligenceOrganicResultSchema = z21.object({
|
|
14549
|
+
position: z21.number().int().min(1),
|
|
14550
|
+
title: z21.string(),
|
|
14551
|
+
url: z21.string(),
|
|
14552
|
+
domain: z21.string(),
|
|
14553
|
+
cite: z21.string().nullable(),
|
|
14554
|
+
snippet: z21.string().nullable(),
|
|
14555
|
+
isRedditStyle: z21.boolean(),
|
|
14556
|
+
inlineRating: z21.object({
|
|
14557
|
+
value: z21.string(),
|
|
14558
|
+
count: z21.string()
|
|
14314
14559
|
}).strict().nullable()
|
|
14315
14560
|
}).strict();
|
|
14316
|
-
var SerpIntelligenceLocationEvidenceSchema =
|
|
14317
|
-
status:
|
|
14318
|
-
expected:
|
|
14319
|
-
city:
|
|
14320
|
-
regionCode:
|
|
14321
|
-
canonicalLocation:
|
|
14561
|
+
var SerpIntelligenceLocationEvidenceSchema = z21.object({
|
|
14562
|
+
status: z21.enum(SerpIntelligenceLocalizationStatusValues),
|
|
14563
|
+
expected: z21.object({
|
|
14564
|
+
city: z21.string(),
|
|
14565
|
+
regionCode: z21.string().nullable(),
|
|
14566
|
+
canonicalLocation: z21.string()
|
|
14322
14567
|
}).strict().nullable(),
|
|
14323
|
-
candidates:
|
|
14324
|
-
city:
|
|
14325
|
-
regionCode:
|
|
14326
|
-
count:
|
|
14327
|
-
examples:
|
|
14568
|
+
candidates: z21.array(z21.object({
|
|
14569
|
+
city: z21.string(),
|
|
14570
|
+
regionCode: z21.string(),
|
|
14571
|
+
count: z21.number().int().min(0),
|
|
14572
|
+
examples: z21.array(z21.string())
|
|
14328
14573
|
}).strict())
|
|
14329
14574
|
}).strict();
|
|
14330
|
-
var SerpIntelligenceHarvestResultSchema =
|
|
14331
|
-
seed:
|
|
14332
|
-
location:
|
|
14333
|
-
extractedAt:
|
|
14334
|
-
totalQuestions:
|
|
14335
|
-
surface:
|
|
14336
|
-
aiOverview:
|
|
14337
|
-
detected:
|
|
14338
|
-
text:
|
|
14339
|
-
citations:
|
|
14340
|
-
expanded:
|
|
14341
|
-
fullyExpanded:
|
|
14342
|
-
sections:
|
|
14575
|
+
var SerpIntelligenceHarvestResultSchema = z21.object({
|
|
14576
|
+
seed: z21.string(),
|
|
14577
|
+
location: z21.string().nullable(),
|
|
14578
|
+
extractedAt: z21.string(),
|
|
14579
|
+
totalQuestions: z21.number().int().min(0),
|
|
14580
|
+
surface: z21.enum(["web", "aim", "unknown"]),
|
|
14581
|
+
aiOverview: z21.object({
|
|
14582
|
+
detected: z21.boolean(),
|
|
14583
|
+
text: z21.string().nullable(),
|
|
14584
|
+
citations: z21.array(SerpIntelligenceAICitationSchema),
|
|
14585
|
+
expanded: z21.boolean().optional(),
|
|
14586
|
+
fullyExpanded: z21.boolean().optional(),
|
|
14587
|
+
sections: z21.array(z21.string()).optional()
|
|
14343
14588
|
}).strict(),
|
|
14344
|
-
aiMode:
|
|
14345
|
-
detected:
|
|
14346
|
-
text:
|
|
14347
|
-
citations:
|
|
14589
|
+
aiMode: z21.object({
|
|
14590
|
+
detected: z21.boolean(),
|
|
14591
|
+
text: z21.string().nullable(),
|
|
14592
|
+
citations: z21.array(SerpIntelligenceAICitationSchema)
|
|
14348
14593
|
}).strict(),
|
|
14349
|
-
tree:
|
|
14350
|
-
flat:
|
|
14351
|
-
videos:
|
|
14352
|
-
forums:
|
|
14353
|
-
organicResults:
|
|
14354
|
-
localPack:
|
|
14355
|
-
entityIds:
|
|
14356
|
-
entities:
|
|
14357
|
-
name:
|
|
14358
|
-
kgId:
|
|
14359
|
-
cid:
|
|
14360
|
-
gcid:
|
|
14594
|
+
tree: z21.array(z21.unknown()),
|
|
14595
|
+
flat: z21.array(z21.unknown()),
|
|
14596
|
+
videos: z21.array(z21.unknown()),
|
|
14597
|
+
forums: z21.array(z21.unknown()),
|
|
14598
|
+
organicResults: z21.array(SerpIntelligenceOrganicResultSchema),
|
|
14599
|
+
localPack: z21.array(z21.unknown()),
|
|
14600
|
+
entityIds: z21.object({
|
|
14601
|
+
entities: z21.array(z21.object({
|
|
14602
|
+
name: z21.string(),
|
|
14603
|
+
kgId: z21.string().nullable(),
|
|
14604
|
+
cid: z21.string().nullable(),
|
|
14605
|
+
gcid: z21.string().nullable()
|
|
14361
14606
|
}).strict()),
|
|
14362
|
-
kgIds:
|
|
14363
|
-
cids:
|
|
14364
|
-
gcids:
|
|
14607
|
+
kgIds: z21.array(z21.string()),
|
|
14608
|
+
cids: z21.array(z21.string()),
|
|
14609
|
+
gcids: z21.array(z21.string())
|
|
14365
14610
|
}).strict(),
|
|
14366
|
-
stats:
|
|
14367
|
-
seed:
|
|
14368
|
-
totalQuestions:
|
|
14369
|
-
maxDepthReached:
|
|
14370
|
-
durationMs:
|
|
14371
|
-
errorCount:
|
|
14611
|
+
stats: z21.object({
|
|
14612
|
+
seed: z21.string(),
|
|
14613
|
+
totalQuestions: z21.number().int().min(0),
|
|
14614
|
+
maxDepthReached: z21.number().int().min(0),
|
|
14615
|
+
durationMs: z21.number().min(0),
|
|
14616
|
+
errorCount: z21.number().int().min(0)
|
|
14372
14617
|
}).strict(),
|
|
14373
|
-
diagnostics:
|
|
14374
|
-
completionStatus:
|
|
14375
|
-
problem:
|
|
14376
|
-
warnings:
|
|
14377
|
-
debug:
|
|
14618
|
+
diagnostics: z21.object({
|
|
14619
|
+
completionStatus: z21.enum(["paa_found", "no_paa", "serp_only"]),
|
|
14620
|
+
problem: z21.null(),
|
|
14621
|
+
warnings: z21.array(z21.unknown()).optional(),
|
|
14622
|
+
debug: z21.object({
|
|
14378
14623
|
locationEvidence: SerpIntelligenceLocationEvidenceSchema.optional()
|
|
14379
14624
|
}).passthrough().optional()
|
|
14380
14625
|
}).passthrough(),
|
|
14381
|
-
whatPeopleSaying:
|
|
14626
|
+
whatPeopleSaying: z21.array(z21.unknown())
|
|
14382
14627
|
}).strict();
|
|
14383
|
-
var SerpIntelligenceCaptureAttemptSchema =
|
|
14384
|
-
attemptNumber:
|
|
14385
|
-
outcome:
|
|
14386
|
-
startedAt:
|
|
14387
|
-
completedAt:
|
|
14388
|
-
durationMs:
|
|
14389
|
-
problemCode:
|
|
14390
|
-
message:
|
|
14391
|
-
kernelSessionId:
|
|
14392
|
-
cleanupSucceeded:
|
|
14628
|
+
var SerpIntelligenceCaptureAttemptSchema = z21.object({
|
|
14629
|
+
attemptNumber: z21.number().int().min(1),
|
|
14630
|
+
outcome: z21.enum(SerpIntelligenceAttemptOutcomeValues),
|
|
14631
|
+
startedAt: z21.string().optional(),
|
|
14632
|
+
completedAt: z21.string().optional(),
|
|
14633
|
+
durationMs: z21.number().min(0).optional(),
|
|
14634
|
+
problemCode: z21.string().optional(),
|
|
14635
|
+
message: z21.string().optional(),
|
|
14636
|
+
kernelSessionId: z21.string().nullable().optional(),
|
|
14637
|
+
cleanupSucceeded: z21.boolean().nullable().optional()
|
|
14393
14638
|
}).strict();
|
|
14394
|
-
var SerpPageSnapshotCaptureSchema =
|
|
14639
|
+
var SerpPageSnapshotCaptureSchema = z21.object({
|
|
14395
14640
|
url: SerpIntelligencePublicHttpUrlSchema,
|
|
14396
14641
|
requestedUrl: SerpIntelligencePublicHttpUrlSchema,
|
|
14397
14642
|
finalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),
|
|
14398
|
-
sourceKind:
|
|
14399
|
-
sourcePosition:
|
|
14400
|
-
status:
|
|
14401
|
-
fetchedVia:
|
|
14402
|
-
httpStatus:
|
|
14403
|
-
contentType:
|
|
14404
|
-
title:
|
|
14643
|
+
sourceKind: z21.enum(SerpPageSnapshotSourceKindValues),
|
|
14644
|
+
sourcePosition: z21.number().int().min(1).nullable(),
|
|
14645
|
+
status: z21.enum(SerpPageFetchStatusValues),
|
|
14646
|
+
fetchedVia: z21.enum(SerpPageFetchedViaValues).nullable(),
|
|
14647
|
+
httpStatus: z21.number().int().min(100).max(599).nullable(),
|
|
14648
|
+
contentType: z21.string().nullable(),
|
|
14649
|
+
title: z21.string().nullable(),
|
|
14405
14650
|
canonicalUrl: SerpIntelligencePublicHttpUrlSchema.nullable(),
|
|
14406
|
-
metaDescription:
|
|
14407
|
-
headings:
|
|
14408
|
-
level:
|
|
14409
|
-
text:
|
|
14651
|
+
metaDescription: z21.string().nullable(),
|
|
14652
|
+
headings: z21.array(z21.object({
|
|
14653
|
+
level: z21.number().int().min(1).max(6),
|
|
14654
|
+
text: z21.string()
|
|
14410
14655
|
}).strict()).default([]),
|
|
14411
|
-
artifact:
|
|
14412
|
-
htmlBlobUrl:
|
|
14413
|
-
textBlobUrl:
|
|
14414
|
-
markdownBlobUrl:
|
|
14415
|
-
screenshotBlobUrl:
|
|
14416
|
-
contentSha256:
|
|
14417
|
-
capturedAt:
|
|
14656
|
+
artifact: z21.object({
|
|
14657
|
+
htmlBlobUrl: z21.string().url().nullable(),
|
|
14658
|
+
textBlobUrl: z21.string().url().nullable(),
|
|
14659
|
+
markdownBlobUrl: z21.string().url().nullable(),
|
|
14660
|
+
screenshotBlobUrl: z21.string().url().nullable(),
|
|
14661
|
+
contentSha256: z21.string().nullable(),
|
|
14662
|
+
capturedAt: z21.string().nullable()
|
|
14418
14663
|
}).strict(),
|
|
14419
|
-
error:
|
|
14420
|
-
code:
|
|
14421
|
-
message:
|
|
14664
|
+
error: z21.object({
|
|
14665
|
+
code: z21.string(),
|
|
14666
|
+
message: z21.string()
|
|
14422
14667
|
}).strict().nullable()
|
|
14423
14668
|
}).strict();
|
|
14424
|
-
var SerpIntelligenceCaptureResponseSchema =
|
|
14669
|
+
var SerpIntelligenceCaptureResponseSchema = z21.object({
|
|
14425
14670
|
harvestResult: SerpIntelligenceHarvestResultSchema,
|
|
14426
|
-
attempts:
|
|
14671
|
+
attempts: z21.array(SerpIntelligenceCaptureAttemptSchema),
|
|
14427
14672
|
locationEvidence: SerpIntelligenceLocationEvidenceSchema.nullable(),
|
|
14428
|
-
pageSnapshotArtifacts:
|
|
14429
|
-
billing:
|
|
14430
|
-
creditsUsed:
|
|
14431
|
-
requestId:
|
|
14432
|
-
jobId:
|
|
14673
|
+
pageSnapshotArtifacts: z21.array(SerpPageSnapshotCaptureSchema),
|
|
14674
|
+
billing: z21.object({
|
|
14675
|
+
creditsUsed: z21.number().min(0).optional(),
|
|
14676
|
+
requestId: z21.string().optional(),
|
|
14677
|
+
jobId: z21.string().optional()
|
|
14433
14678
|
}).strict().optional()
|
|
14434
14679
|
}).strict();
|
|
14435
|
-
var SerpIntelligencePageSnapshotsResponseSchema =
|
|
14436
|
-
pageSnapshotArtifacts:
|
|
14437
|
-
attempts:
|
|
14680
|
+
var SerpIntelligencePageSnapshotsResponseSchema = z21.object({
|
|
14681
|
+
pageSnapshotArtifacts: z21.array(SerpPageSnapshotCaptureSchema),
|
|
14682
|
+
attempts: z21.array(SerpIntelligenceCaptureAttemptSchema).default([])
|
|
14438
14683
|
}).strict();
|
|
14439
14684
|
|
|
14440
14685
|
// src/serp-intelligence/serp-capture-service.ts
|
|
@@ -14606,7 +14851,7 @@ var SERP_INTELLIGENCE_RATE_LIMIT = 60;
|
|
|
14606
14851
|
var SERP_INTELLIGENCE_RATE_WINDOW_SECONDS = 60;
|
|
14607
14852
|
var POST_CAPTURE_ROUTE_LABEL = "POST /capture";
|
|
14608
14853
|
var POST_PAGE_SNAPSHOTS_ROUTE_LABEL = "POST /page-snapshots";
|
|
14609
|
-
var serpIntelligenceApp = new
|
|
14854
|
+
var serpIntelligenceApp = new Hono13();
|
|
14610
14855
|
serpIntelligenceApp.use("*", createApiKeyAuth());
|
|
14611
14856
|
function structuredError(input) {
|
|
14612
14857
|
return {
|
|
@@ -14775,7 +15020,7 @@ serpIntelligenceApp.post("/page-snapshots", async (c) => {
|
|
|
14775
15020
|
});
|
|
14776
15021
|
|
|
14777
15022
|
// src/mcp/mcp-routes.ts
|
|
14778
|
-
import { Hono as
|
|
15023
|
+
import { Hono as Hono14 } from "hono";
|
|
14779
15024
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
14780
15025
|
|
|
14781
15026
|
// src/mcp/mcp-oauth.ts
|
|
@@ -14859,7 +15104,7 @@ async function requireMcpCallerKey(c) {
|
|
|
14859
15104
|
if (!user) return mcpAuthError();
|
|
14860
15105
|
return callerKey;
|
|
14861
15106
|
}
|
|
14862
|
-
var mcpApp = new
|
|
15107
|
+
var mcpApp = new Hono14();
|
|
14863
15108
|
mcpApp.all("/", async (c) => {
|
|
14864
15109
|
try {
|
|
14865
15110
|
const keyOrError = await requireMcpCallerKey(c);
|
|
@@ -14883,7 +15128,7 @@ mcpApp.all("/", async (c) => {
|
|
|
14883
15128
|
});
|
|
14884
15129
|
|
|
14885
15130
|
// src/api/browser-agent-routes.ts
|
|
14886
|
-
import { Hono as
|
|
15131
|
+
import { Hono as Hono15 } from "hono";
|
|
14887
15132
|
|
|
14888
15133
|
// src/api/browser-agent-db.ts
|
|
14889
15134
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -16317,7 +16562,7 @@ async function loadOpenSession(id, userId) {
|
|
|
16317
16562
|
return row;
|
|
16318
16563
|
}
|
|
16319
16564
|
function buildBrowserAgentRoutes() {
|
|
16320
|
-
const app2 = new
|
|
16565
|
+
const app2 = new Hono15();
|
|
16321
16566
|
app2.use("*", async (c, next) => {
|
|
16322
16567
|
await migrateBrowserAgent();
|
|
16323
16568
|
return next();
|
|
@@ -17023,161 +17268,9 @@ if (state.key) { refreshSessions(); if (state.current) selectSession(state.curre
|
|
|
17023
17268
|
|
|
17024
17269
|
// src/api/stripe-routes.ts
|
|
17025
17270
|
import Stripe from "stripe";
|
|
17026
|
-
import { Hono as
|
|
17027
|
-
|
|
17028
|
-
// src/api/memory.ts
|
|
17029
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
17030
|
-
|
|
17031
|
-
// src/api/session.ts
|
|
17032
|
-
import { createHmac as createHmac2, timingSafeEqual } from "crypto";
|
|
17033
|
-
var isProduction = () => process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
|
|
17034
|
-
function getSessionSecret() {
|
|
17035
|
-
const configured = process.env.SESSION_SECRET?.trim();
|
|
17036
|
-
if (configured) return configured;
|
|
17037
|
-
if (isProduction()) throw new Error("SESSION_SECRET is not set \u2014 add it to your Vercel env vars (see src/api/env.ts)");
|
|
17038
|
-
return "dev-secret-change-me";
|
|
17039
|
-
}
|
|
17040
|
-
var secret = () => getSessionSecret();
|
|
17041
|
-
function safeEqualHex(a, b) {
|
|
17042
|
-
if (a.length !== b.length) return false;
|
|
17043
|
-
try {
|
|
17044
|
-
return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
17045
|
-
} catch {
|
|
17046
|
-
return false;
|
|
17047
|
-
}
|
|
17048
|
-
}
|
|
17049
|
-
function signSession(userId) {
|
|
17050
|
-
const payload = String(userId);
|
|
17051
|
-
const sig = createHmac2("sha256", secret()).update(payload).digest("hex");
|
|
17052
|
-
return `${payload}.${sig}`;
|
|
17053
|
-
}
|
|
17054
|
-
function verifySession(token) {
|
|
17055
|
-
const dot = token.lastIndexOf(".");
|
|
17056
|
-
if (dot === -1) return null;
|
|
17057
|
-
const payload = token.slice(0, dot);
|
|
17058
|
-
const sig = token.slice(dot + 1);
|
|
17059
|
-
const expected = createHmac2("sha256", secret()).update(payload).digest("hex");
|
|
17060
|
-
if (!safeEqualHex(sig, expected)) return null;
|
|
17061
|
-
const id = parseInt(payload);
|
|
17062
|
-
return isNaN(id) ? null : id;
|
|
17063
|
-
}
|
|
17064
|
-
|
|
17065
|
-
// src/api/memory.ts
|
|
17066
|
-
var MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://mcp-memory-omega.vercel.app").replace(/\/$/, "");
|
|
17067
|
-
var ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
17068
|
-
function isMemoryOperator(email) {
|
|
17069
|
-
if (!email) return false;
|
|
17070
|
-
const ops = (process.env.MEMORY_OPERATOR_IDENTITIES ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
17071
|
-
return ops.includes(email.trim().toLowerCase());
|
|
17072
|
-
}
|
|
17073
|
-
function encKey() {
|
|
17074
|
-
return scryptSync(getSessionSecret(), "mcp-memory-key-v1", 32);
|
|
17075
|
-
}
|
|
17076
|
-
function encryptMemoryKey(secret2) {
|
|
17077
|
-
const iv = randomBytes(12);
|
|
17078
|
-
const cipher = createCipheriv("aes-256-gcm", encKey(), iv);
|
|
17079
|
-
const enc = Buffer.concat([cipher.update(secret2, "utf8"), cipher.final()]);
|
|
17080
|
-
const tag = cipher.getAuthTag();
|
|
17081
|
-
return `${iv.toString("base64")}:${tag.toString("base64")}:${enc.toString("base64")}`;
|
|
17082
|
-
}
|
|
17083
|
-
function decryptMemoryKey(stored) {
|
|
17084
|
-
try {
|
|
17085
|
-
const [ivB, tagB, dataB] = stored.split(":");
|
|
17086
|
-
const decipher = createDecipheriv("aes-256-gcm", encKey(), Buffer.from(ivB, "base64"));
|
|
17087
|
-
decipher.setAuthTag(Buffer.from(tagB, "base64"));
|
|
17088
|
-
return Buffer.concat([decipher.update(Buffer.from(dataB, "base64")), decipher.final()]).toString("utf8");
|
|
17089
|
-
} catch {
|
|
17090
|
-
return null;
|
|
17091
|
-
}
|
|
17092
|
-
}
|
|
17093
|
-
async function memoryCall(toolName, args, userMemoryKey) {
|
|
17094
|
-
try {
|
|
17095
|
-
const res = await fetch(`${MEMORY_BASE_URL()}/api/mcp/memoryServer/tools/${toolName}/execute`, {
|
|
17096
|
-
method: "POST",
|
|
17097
|
-
headers: { "content-type": "application/json" },
|
|
17098
|
-
body: JSON.stringify({ data: { ...args, apiKey: userMemoryKey } })
|
|
17099
|
-
});
|
|
17100
|
-
const body = await res.json().catch(() => null);
|
|
17101
|
-
if (!res.ok) return { ok: false, error: body?.error ?? `memory ${toolName} failed (${res.status})` };
|
|
17102
|
-
if (body?.result) return body.result;
|
|
17103
|
-
return { ok: false, error: body?.error ?? `memory ${toolName} returned no result` };
|
|
17104
|
-
} catch (err) {
|
|
17105
|
-
return { ok: false, error: err?.message ?? "memory call failed" };
|
|
17106
|
-
}
|
|
17107
|
-
}
|
|
17108
|
-
function personalVault(user) {
|
|
17109
|
-
return `mcp-${user.id}`;
|
|
17110
|
-
}
|
|
17111
|
-
function memoryIdentity(user) {
|
|
17112
|
-
return user.email;
|
|
17113
|
-
}
|
|
17114
|
-
function resolveMemoryIdentity(user) {
|
|
17115
|
-
return memoryIdentity(user);
|
|
17116
|
-
}
|
|
17117
|
-
function memoryPlanForReads(user) {
|
|
17118
|
-
if (isMemoryOperator(user.email)) return "unlimited";
|
|
17119
|
-
if (user.memory_plan === "pro" || user.memory_plan === "team") return user.memory_plan;
|
|
17120
|
-
return "free";
|
|
17121
|
-
}
|
|
17122
|
-
async function provisionMemoryForUser(user) {
|
|
17123
|
-
try {
|
|
17124
|
-
const { key } = await getOrCreateUserMemoryKey(user);
|
|
17125
|
-
if (key) await setMemoryProvisioned(user.id, true);
|
|
17126
|
-
} catch {
|
|
17127
|
-
}
|
|
17128
|
-
}
|
|
17129
|
-
async function getOrCreateUserMemoryKey(user) {
|
|
17130
|
-
const creds = await getUserMemoryCreds(user.id);
|
|
17131
|
-
if (creds.memory_key) {
|
|
17132
|
-
const decrypted = decryptMemoryKey(creds.memory_key);
|
|
17133
|
-
if (decrypted) return { key: decrypted };
|
|
17134
|
-
}
|
|
17135
|
-
const admin = ADMIN_KEY();
|
|
17136
|
-
if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
17137
|
-
const identity = memoryIdentity(user);
|
|
17138
|
-
const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
|
|
17139
|
-
const provisioned = await memoryCall(
|
|
17140
|
-
"provisionDefaultsTool",
|
|
17141
|
-
{ granteeIdentity: identity, issueKey: true, plan },
|
|
17142
|
-
admin
|
|
17143
|
-
);
|
|
17144
|
-
if (!provisioned.ok || !provisioned.secret) {
|
|
17145
|
-
return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
|
|
17146
|
-
}
|
|
17147
|
-
await setUserMemoryCreds(user.id, encryptMemoryKey(provisioned.secret), identity);
|
|
17148
|
-
return { key: provisioned.secret };
|
|
17149
|
-
}
|
|
17150
|
-
async function syncMemoryKeyPlan(user, plan) {
|
|
17151
|
-
const effectivePlan = isMemoryOperator(user.email) ? "unlimited" : plan;
|
|
17152
|
-
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
17153
|
-
if (!key) return { ok: false, error: error ?? "memory unavailable" };
|
|
17154
|
-
const listed = await memoryCall(
|
|
17155
|
-
"listKeysTool",
|
|
17156
|
-
{},
|
|
17157
|
-
key
|
|
17158
|
-
);
|
|
17159
|
-
if (!listed.ok || !listed.keys?.length) return { ok: false, error: listed.error ?? "no memory keys found" };
|
|
17160
|
-
const target = listed.keys[0];
|
|
17161
|
-
if (target.plan === effectivePlan) return { ok: true };
|
|
17162
|
-
const res = await memoryCall("setScopeTool", { keyId: target.keyId, plan: effectivePlan }, key);
|
|
17163
|
-
return { ok: res.ok, error: res.error };
|
|
17164
|
-
}
|
|
17165
|
-
async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
|
|
17166
|
-
const admin = ADMIN_KEY();
|
|
17167
|
-
if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
17168
|
-
const identity = resolveMemoryIdentity(user);
|
|
17169
|
-
const res = await memoryCall("setScheduleEntitlementTool", {
|
|
17170
|
-
granteeIdentity: identity,
|
|
17171
|
-
enabled,
|
|
17172
|
-
quotaPerPeriod,
|
|
17173
|
-
mcpScraperApiKey: user.api_key
|
|
17174
|
-
}, admin);
|
|
17175
|
-
return { ok: res.ok, error: res.error };
|
|
17176
|
-
}
|
|
17177
|
-
|
|
17178
|
-
// src/api/stripe-routes.ts
|
|
17271
|
+
import { Hono as Hono16 } from "hono";
|
|
17179
17272
|
var stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-02-25.clover" });
|
|
17180
|
-
var stripeApp = new
|
|
17273
|
+
var stripeApp = new Hono16();
|
|
17181
17274
|
stripeApp.post("/webhooks", async (c) => {
|
|
17182
17275
|
const sig = c.req.header("stripe-signature");
|
|
17183
17276
|
const body = await c.req.text();
|
|
@@ -17277,7 +17370,7 @@ async function resolveUser(customerId, emailFallback) {
|
|
|
17277
17370
|
}
|
|
17278
17371
|
|
|
17279
17372
|
// src/api/oauth-routes.ts
|
|
17280
|
-
import { Hono as
|
|
17373
|
+
import { Hono as Hono17 } from "hono";
|
|
17281
17374
|
import { getCookie, setCookie } from "hono/cookie";
|
|
17282
17375
|
import { createHash as createHash3, randomBytes as randomBytes2, randomUUID as randomUUID3 } from "crypto";
|
|
17283
17376
|
import { importPKCS8, exportJWK, SignJWT } from "jose";
|
|
@@ -17325,8 +17418,9 @@ function isAllowedRedirect(uri) {
|
|
|
17325
17418
|
} catch {
|
|
17326
17419
|
return false;
|
|
17327
17420
|
}
|
|
17328
|
-
if (u.protocol !== "https:") return false;
|
|
17329
17421
|
const h = u.hostname.toLowerCase();
|
|
17422
|
+
if (u.protocol === "http:" && (h === "localhost" || h === "127.0.0.1" || h === "::1")) return true;
|
|
17423
|
+
if (u.protocol !== "https:") return false;
|
|
17330
17424
|
if (["claude.ai", "www.claude.ai", "claude.com", "www.claude.com"].includes(h)) return true;
|
|
17331
17425
|
if (h.endsWith(".claude.ai") || h.endsWith(".claude.com")) return true;
|
|
17332
17426
|
if (["chatgpt.com", "www.chatgpt.com", "chat.openai.com", "www.chat.openai.com"].includes(h)) return true;
|
|
@@ -17444,7 +17538,7 @@ function redirectWithError(redirectUri, state, error) {
|
|
|
17444
17538
|
if (state) u.searchParams.set("state", state);
|
|
17445
17539
|
return new Response(null, { status: 302, headers: { Location: u.toString() } });
|
|
17446
17540
|
}
|
|
17447
|
-
var oauthApp = new
|
|
17541
|
+
var oauthApp = new Hono17();
|
|
17448
17542
|
oauthApp.use("*", async (c, next) => {
|
|
17449
17543
|
c.header("Access-Control-Allow-Origin", "*");
|
|
17450
17544
|
c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
@@ -17452,51 +17546,42 @@ oauthApp.use("*", async (c, next) => {
|
|
|
17452
17546
|
if (c.req.method === "OPTIONS") return c.body(null, 204);
|
|
17453
17547
|
return next();
|
|
17454
17548
|
});
|
|
17455
|
-
function authServerMetadata(
|
|
17549
|
+
function authServerMetadata() {
|
|
17456
17550
|
return {
|
|
17457
17551
|
issuer: ISSUER,
|
|
17458
17552
|
authorization_endpoint: `${ISSUER}/oauth/authorize`,
|
|
17459
17553
|
token_endpoint: `${ISSUER}/oauth/token`,
|
|
17460
17554
|
registration_endpoint: `${ISSUER}/oauth/register`,
|
|
17461
17555
|
jwks_uri: `${ISSUER}/.well-known/jwks.json`,
|
|
17462
|
-
scopes_supported: [...
|
|
17556
|
+
scopes_supported: [...SUPPORTED_SCOPES],
|
|
17463
17557
|
response_types_supported: ["code"],
|
|
17464
17558
|
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
17465
17559
|
code_challenge_methods_supported: ["S256"],
|
|
17466
17560
|
token_endpoint_auth_methods_supported: ["none"]
|
|
17467
17561
|
};
|
|
17468
17562
|
}
|
|
17469
|
-
function openidConfiguration(
|
|
17563
|
+
function openidConfiguration() {
|
|
17470
17564
|
return {
|
|
17471
|
-
...authServerMetadata(
|
|
17565
|
+
...authServerMetadata(),
|
|
17472
17566
|
subject_types_supported: ["public"],
|
|
17473
17567
|
id_token_signing_alg_values_supported: ["RS256"]
|
|
17474
17568
|
};
|
|
17475
17569
|
}
|
|
17476
|
-
function scopesForRequest(c) {
|
|
17477
|
-
const host = (c.req.header("host") ?? "").toLowerCase();
|
|
17478
|
-
if (!host) return SUPPORTED_SCOPES;
|
|
17479
|
-
const suffix2 = c.req.path.replace(/^\/\.well-known\/(oauth-authorization-server|openid-configuration)/, "");
|
|
17480
|
-
const candidate = `https://${host}${suffix2}`.replace(/\/$/, "");
|
|
17481
|
-
if (candidate === SCRAPER_RESOURCE()) return SCRAPER_SCOPES;
|
|
17482
|
-
if (candidate === RESOURCE().replace(/\/$/, "")) return MEMORY_SCOPES;
|
|
17483
|
-
return SUPPORTED_SCOPES;
|
|
17484
|
-
}
|
|
17485
17570
|
oauthApp.get("/.well-known/oauth-authorization-server", (c) => {
|
|
17486
17571
|
console.log("[oauth-dcr] GET /.well-known/oauth-authorization-server ua=%s", c.req.header("user-agent") ?? "");
|
|
17487
|
-
return c.json(authServerMetadata(
|
|
17572
|
+
return c.json(authServerMetadata());
|
|
17488
17573
|
});
|
|
17489
17574
|
oauthApp.get("/.well-known/oauth-authorization-server/*", (c) => {
|
|
17490
17575
|
console.log("[oauth-dcr] GET %s (path-aware AS) ua=%s", c.req.path, c.req.header("user-agent") ?? "");
|
|
17491
|
-
return c.json(authServerMetadata(
|
|
17576
|
+
return c.json(authServerMetadata());
|
|
17492
17577
|
});
|
|
17493
17578
|
oauthApp.get("/.well-known/openid-configuration", (c) => {
|
|
17494
17579
|
console.log("[oauth-dcr] GET /.well-known/openid-configuration ua=%s", c.req.header("user-agent") ?? "");
|
|
17495
|
-
return c.json(openidConfiguration(
|
|
17580
|
+
return c.json(openidConfiguration());
|
|
17496
17581
|
});
|
|
17497
17582
|
oauthApp.get("/.well-known/openid-configuration/*", (c) => {
|
|
17498
17583
|
console.log("[oauth-dcr] GET %s (path-aware OIDC) ua=%s", c.req.path, c.req.header("user-agent") ?? "");
|
|
17499
|
-
return c.json(openidConfiguration(
|
|
17584
|
+
return c.json(openidConfiguration());
|
|
17500
17585
|
});
|
|
17501
17586
|
oauthApp.get("/.well-known/jwks.json", async (c) => {
|
|
17502
17587
|
const { publicJwk } = await getKeys();
|
|
@@ -17700,8 +17785,8 @@ oauthApp.post("/oauth/token", async (c) => {
|
|
|
17700
17785
|
});
|
|
17701
17786
|
|
|
17702
17787
|
// src/api/chat-routes.ts
|
|
17703
|
-
import { Hono as
|
|
17704
|
-
var chatApp = new
|
|
17788
|
+
import { Hono as Hono18 } from "hono";
|
|
17789
|
+
var chatApp = new Hono18();
|
|
17705
17790
|
function tokenOk(token) {
|
|
17706
17791
|
return !!token && token.startsWith("mk_") && token.length > 10;
|
|
17707
17792
|
}
|
|
@@ -18371,8 +18456,8 @@ loadChannels();
|
|
|
18371
18456
|
}
|
|
18372
18457
|
|
|
18373
18458
|
// src/api/schedule-routes.ts
|
|
18374
|
-
import { Hono as
|
|
18375
|
-
var scheduleApp = new
|
|
18459
|
+
import { Hono as Hono19 } from "hono";
|
|
18460
|
+
var scheduleApp = new Hono19();
|
|
18376
18461
|
function tokenOk2(token) {
|
|
18377
18462
|
return !!token && token.startsWith("mk_") && token.length > 10;
|
|
18378
18463
|
}
|
|
@@ -18911,6 +18996,21 @@ async function entitledVaultRows(identity) {
|
|
|
18911
18996
|
}
|
|
18912
18997
|
return [...seen.values()];
|
|
18913
18998
|
}
|
|
18999
|
+
var EMBED_BYTES_PER_CHUNK = Number(process.env.JINA_EMBED_DIM ?? 1024) * 4;
|
|
19000
|
+
async function embedBytesByPhysical(physicals) {
|
|
19001
|
+
const m = /* @__PURE__ */ new Map();
|
|
19002
|
+
try {
|
|
19003
|
+
const rows = await sql().query(
|
|
19004
|
+
`SELECT metadata->>'vault' AS vault, COUNT(*)::bigint AS chunks
|
|
19005
|
+
FROM "memory" WHERE metadata->>'vault' = ANY($1) GROUP BY 1`,
|
|
19006
|
+
[physicals]
|
|
19007
|
+
);
|
|
19008
|
+
for (const r of rows) if (r.vault) m.set(r.vault, Number(r.chunks) * EMBED_BYTES_PER_CHUNK);
|
|
19009
|
+
} catch {
|
|
19010
|
+
return m;
|
|
19011
|
+
}
|
|
19012
|
+
return m;
|
|
19013
|
+
}
|
|
18914
19014
|
async function storageByPhysical(physicals) {
|
|
18915
19015
|
if (physicals.length === 0) return /* @__PURE__ */ new Map();
|
|
18916
19016
|
const rows = await sql().query(
|
|
@@ -18918,8 +19018,9 @@ async function storageByPhysical(physicals) {
|
|
|
18918
19018
|
FROM mem_notes WHERE vault = ANY($1) GROUP BY vault`,
|
|
18919
19019
|
[physicals]
|
|
18920
19020
|
);
|
|
19021
|
+
const embed = await embedBytesByPhysical(physicals);
|
|
18921
19022
|
const m = /* @__PURE__ */ new Map();
|
|
18922
|
-
for (const r of rows) m.set(r.vault, { notes: Number(r.notes), bytes: Number(r.bytes) });
|
|
19023
|
+
for (const r of rows) m.set(r.vault, { notes: Number(r.notes), bytes: Number(r.bytes) + (embed.get(r.vault) ?? 0) });
|
|
18923
19024
|
return m;
|
|
18924
19025
|
}
|
|
18925
19026
|
async function dbListVaults(identity) {
|
|
@@ -19111,7 +19212,7 @@ var sessionAuth = createMiddleware3(async (c, next) => {
|
|
|
19111
19212
|
c.set("sessionUser", { ...refreshed, balance_mc: balanceMc });
|
|
19112
19213
|
return next();
|
|
19113
19214
|
});
|
|
19114
|
-
var app = new
|
|
19215
|
+
var app = new Hono20();
|
|
19115
19216
|
var STRIPE_API_VERSION = "2026-02-25.clover";
|
|
19116
19217
|
function requireStripeSecret() {
|
|
19117
19218
|
const secret2 = process.env.STRIPE_SECRET_KEY?.trim();
|
|
@@ -19156,6 +19257,7 @@ function opForCostPath(p) {
|
|
|
19156
19257
|
if (p.startsWith("/instagram/")) return "instagram";
|
|
19157
19258
|
if (p === "/reddit/thread") return "reddit_thread";
|
|
19158
19259
|
if (p.startsWith("/api/internal/site-architecture-auditor")) return "audit_site";
|
|
19260
|
+
if (p.startsWith("/api/internal/memory")) return "memory_ai";
|
|
19159
19261
|
if (p.startsWith("/agent/")) return "browser_session";
|
|
19160
19262
|
return null;
|
|
19161
19263
|
}
|
|
@@ -19233,6 +19335,27 @@ app.post("/auth/logout", requireAllowedOrigin, (c) => {
|
|
|
19233
19335
|
deleteCookie(c, "session", { path: "/", secure: secureCookies2, sameSite: "Strict" });
|
|
19234
19336
|
return c.json({ ok: true });
|
|
19235
19337
|
});
|
|
19338
|
+
app.post("/account/delete", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
19339
|
+
const user = c.get("sessionUser");
|
|
19340
|
+
const secret2 = requireStripeSecret();
|
|
19341
|
+
if (secret2 && user.stripe_customer_id) {
|
|
19342
|
+
const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
19343
|
+
const subs = await stripeClient.subscriptions.list({ customer: user.stripe_customer_id, status: "all", limit: 100 });
|
|
19344
|
+
for (const sub of subs.data) {
|
|
19345
|
+
if (sub.status === "active" || sub.status === "trialing" || sub.status === "past_due") {
|
|
19346
|
+
try {
|
|
19347
|
+
await stripeClient.subscriptions.cancel(sub.id);
|
|
19348
|
+
} catch (err) {
|
|
19349
|
+
console.error("[account/delete] failed to cancel subscription", sub.id, err instanceof Error ? err.message : String(err));
|
|
19350
|
+
return c.json({ error: "Could not cancel an active subscription \u2014 please try again or contact support." }, 500);
|
|
19351
|
+
}
|
|
19352
|
+
}
|
|
19353
|
+
}
|
|
19354
|
+
}
|
|
19355
|
+
await deactivateUser(user.id);
|
|
19356
|
+
deleteCookie(c, "session", { path: "/", secure: secureCookies2, sameSite: "Strict" });
|
|
19357
|
+
return c.json({ ok: true });
|
|
19358
|
+
});
|
|
19236
19359
|
app.post("/auth/forgot-password", requireAllowedOrigin, async (c) => {
|
|
19237
19360
|
const { email } = await c.req.json();
|
|
19238
19361
|
const normalizedEmail = email?.trim().toLowerCase();
|
|
@@ -20255,6 +20378,24 @@ app.post("/billing/subscribe", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
|
20255
20378
|
return c.json({ error: message }, 500);
|
|
20256
20379
|
}
|
|
20257
20380
|
});
|
|
20381
|
+
app.post("/billing/portal", requireAllowedOrigin, sessionAuth, async (c) => {
|
|
20382
|
+
try {
|
|
20383
|
+
const user = c.get("sessionUser");
|
|
20384
|
+
if (!user.stripe_customer_id) return c.json({ error: "No billing account yet \u2014 subscribe first." }, 409);
|
|
20385
|
+
const secret2 = requireStripeSecret();
|
|
20386
|
+
if (!secret2) return c.json({ error: "Stripe is not configured." }, 503);
|
|
20387
|
+
const stripeClient = new Stripe2(secret2, { apiVersion: STRIPE_API_VERSION });
|
|
20388
|
+
const session = await stripeClient.billingPortal.sessions.create({
|
|
20389
|
+
customer: user.stripe_customer_id,
|
|
20390
|
+
return_url: `${appOrigin()}/billing`
|
|
20391
|
+
});
|
|
20392
|
+
return c.json({ url: session.url });
|
|
20393
|
+
} catch (err) {
|
|
20394
|
+
const message = err instanceof Error ? err.message : "Unable to open billing portal.";
|
|
20395
|
+
console.error("[billing/portal]", message);
|
|
20396
|
+
return c.json({ error: message }, 500);
|
|
20397
|
+
}
|
|
20398
|
+
});
|
|
20258
20399
|
app.post("/billing/concurrency/terminal-checkout", auth2, async (c) => {
|
|
20259
20400
|
try {
|
|
20260
20401
|
const user = c.get("user");
|
|
@@ -20423,7 +20564,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
20423
20564
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
20424
20565
|
return c.json({ error: "Unauthorized" }, 401);
|
|
20425
20566
|
}
|
|
20426
|
-
const { drainQueue } = await import("./worker-
|
|
20567
|
+
const { drainQueue } = await import("./worker-H4EUD2Q5.js");
|
|
20427
20568
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
20428
20569
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
20429
20570
|
const [results, sweepResult, reapResult, expiredResult, blobCleanup] = await Promise.all([
|
|
@@ -20437,12 +20578,14 @@ app.get("/cron/tick", async (c) => {
|
|
|
20437
20578
|
});
|
|
20438
20579
|
app.on(["GET", "POST", "PUT"], "/api/inngest", serveInngest({ client: inngest, functions: [siteAuditFn, siteExtractFn] }));
|
|
20439
20580
|
app.route("/api/internal/site-architecture-auditor", siteAuditApp);
|
|
20581
|
+
app.route("/api/internal/memory", internalMemoryApp);
|
|
20440
20582
|
app.route("/youtube", youtubeApp);
|
|
20441
20583
|
app.route("/screenshot", screenshotApp);
|
|
20442
20584
|
app.route("/facebook", facebookAdApp);
|
|
20443
20585
|
app.route("/google-ads", googleAdsApp);
|
|
20444
20586
|
app.route("/instagram", instagramApp);
|
|
20445
20587
|
app.route("/reddit", redditApp);
|
|
20588
|
+
app.route("/video", videoApp);
|
|
20446
20589
|
app.route("/maps", mapsApp);
|
|
20447
20590
|
app.route("/directory", directoryApp);
|
|
20448
20591
|
app.route("/workflows", workflowApp);
|
|
@@ -20574,4 +20717,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
20574
20717
|
export {
|
|
20575
20718
|
app
|
|
20576
20719
|
};
|
|
20577
|
-
//# sourceMappingURL=server-
|
|
20720
|
+
//# sourceMappingURL=server-AN6QUH6C.js.map
|