mcp-scraper 0.4.0 → 0.4.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/dist/bin/api-server.cjs +934 -221
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +2 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +3 -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 +83 -22
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +4 -4
- package/dist/bin/paa-harvest.cjs +1 -0
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -2
- package/dist/{chunk-6TDUY7CA.js → chunk-4MFYMUIF.js} +25 -3
- package/dist/chunk-4MFYMUIF.js.map +1 -0
- package/dist/{chunk-T3VZD2I4.js → chunk-5HNHYUJ5.js} +3 -2
- package/dist/chunk-5HNHYUJ5.js.map +1 -0
- package/dist/chunk-7HICT4GY.js +7 -0
- package/dist/chunk-7HICT4GY.js.map +1 -0
- package/dist/{chunk-NNEIXK5L.js → chunk-FUWZWKGO.js} +2 -2
- package/dist/{chunk-3NIPPJGP.js → chunk-GFY6XHZS.js} +80 -21
- package/dist/chunk-GFY6XHZS.js.map +1 -0
- package/dist/{chunk-HBC4C75S.js → chunk-NJK5BTUK.js} +2 -2
- package/dist/{chunk-JOEZJL4I.js → chunk-NVUBFCJK.js} +2 -2
- package/dist/{chunk-BP27CZ5Q.js → chunk-XGIPATLV.js} +2 -1
- package/dist/chunk-XGIPATLV.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{server-Z4WKTE6F.js → server-6IYQQK6X.js} +793 -210
- package/dist/server-6IYQQK6X.js.map +1 -0
- package/dist/{site-extract-repository-2MQBAOX5.js → site-extract-repository-4NVO5KQA.js} +3 -3
- package/dist/{worker-RVZXIALX.js → worker-7XRZNLKB.js} +4 -4
- package/package.json +2 -1
- package/dist/chunk-3NIPPJGP.js.map +0 -1
- package/dist/chunk-6TDUY7CA.js.map +0 -1
- package/dist/chunk-BP27CZ5Q.js.map +0 -1
- package/dist/chunk-IXDYL7TE.js +0 -7
- package/dist/chunk-IXDYL7TE.js.map +0 -1
- package/dist/chunk-T3VZD2I4.js.map +0 -1
- package/dist/server-Z4WKTE6F.js.map +0 -1
- /package/dist/{chunk-NNEIXK5L.js.map → chunk-FUWZWKGO.js.map} +0 -0
- /package/dist/{chunk-HBC4C75S.js.map → chunk-NJK5BTUK.js.map} +0 -0
- /package/dist/{chunk-JOEZJL4I.js.map → chunk-NVUBFCJK.js.map} +0 -0
- /package/dist/{site-extract-repository-2MQBAOX5.js.map → site-extract-repository-4NVO5KQA.js.map} +0 -0
- /package/dist/{worker-RVZXIALX.js.map → worker-7XRZNLKB.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -10042,7 +10042,9 @@ var init_rates = __esm({
|
|
|
10042
10042
|
instagram_transcribe: 50,
|
|
10043
10043
|
browser_minute: 12e3,
|
|
10044
10044
|
reddit_thread: 2e3,
|
|
10045
|
-
video_analysis: 666667
|
|
10045
|
+
video_analysis: 666667,
|
|
10046
|
+
trustpilot_reviews: 500,
|
|
10047
|
+
trustpilot_review: 100
|
|
10046
10048
|
};
|
|
10047
10049
|
MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 6e4;
|
|
10048
10050
|
BROWSER_OPEN_MIN_BALANCE_MC = 1e3;
|
|
@@ -10213,6 +10215,22 @@ var init_rates = __esm({
|
|
|
10213
10215
|
credits: mcToCredits(MC_COSTS.video_analysis),
|
|
10214
10216
|
unit: "per 120 frames (max 480)",
|
|
10215
10217
|
notes: "Full multi-lens video breakdown: samples frames across a video (up to 30 minutes), analyzes each with vision AI, transcribes the audio, then produces summary, pacing/energy, words-per-minute, topic outline, key points, hook analysis, visual style, and a how-to-replicate recipe with a quality-control pass. $1 per 120 frames requested (max 480 = $4); billed down automatically if the video cannot use the requested frames, and refunded fully if the run fails."
|
|
10218
|
+
},
|
|
10219
|
+
{
|
|
10220
|
+
key: "trustpilot_reviews",
|
|
10221
|
+
label: "Trustpilot review harvest",
|
|
10222
|
+
aliases: ["trustpilot_reviews", "trustpilot", "trustpilot reviews"],
|
|
10223
|
+
credits: mcToCredits(MC_COSTS.trustpilot_reviews),
|
|
10224
|
+
unit: "per call",
|
|
10225
|
+
notes: "Base lookup. Reviews billed separately per card at trustpilot_review rate. Refunded if no reviews are found."
|
|
10226
|
+
},
|
|
10227
|
+
{
|
|
10228
|
+
key: "trustpilot_review",
|
|
10229
|
+
label: "Trustpilot review card",
|
|
10230
|
+
aliases: ["trustpilot_review", "trustpilot card"],
|
|
10231
|
+
credits: mcToCredits(MC_COSTS.trustpilot_review),
|
|
10232
|
+
unit: "per review card",
|
|
10233
|
+
notes: "Charged per review actually extracted, billed down automatically if the balance runs out mid-page."
|
|
10216
10234
|
}
|
|
10217
10235
|
];
|
|
10218
10236
|
CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
@@ -10290,7 +10308,11 @@ var init_rates = __esm({
|
|
|
10290
10308
|
VIDEO_ANALYSIS_REFUND: "video_analysis_refund",
|
|
10291
10309
|
VIDEO_ANALYSIS_ADJUST: "video_analysis_adjust",
|
|
10292
10310
|
MEMORY_AI: "memory_ai",
|
|
10293
|
-
MEMORY_AI_REFUND: "memory_ai_refund"
|
|
10311
|
+
MEMORY_AI_REFUND: "memory_ai_refund",
|
|
10312
|
+
TRUSTPILOT_REVIEWS: "trustpilot_reviews",
|
|
10313
|
+
TRUSTPILOT_REVIEWS_REFUND: "trustpilot_reviews_refund",
|
|
10314
|
+
TRUSTPILOT_REVIEW: "trustpilot_review",
|
|
10315
|
+
TRUSTPILOT_REVIEW_REFUND: "trustpilot_review_refund"
|
|
10294
10316
|
};
|
|
10295
10317
|
MEMORY_AI_MARGIN_MULTIPLE = 3;
|
|
10296
10318
|
MC_PER_USD = SUBSCRIPTION_TIERS["price_1TmiHRS8aAcsk3TGwmSNfNIa"].credits_mc / SUBSCRIPTION_TIERS["price_1TmiHRS8aAcsk3TGwmSNfNIa"].monthly_usd;
|
|
@@ -15063,6 +15085,7 @@ var init_schemas3 = __esm({
|
|
|
15063
15085
|
hl: import_zod15.z.string().length(2).default("en"),
|
|
15064
15086
|
includeReviews: import_zod15.z.boolean().default(false),
|
|
15065
15087
|
maxReviews: import_zod15.z.number().int().min(1).max(500).default(50),
|
|
15088
|
+
includeServices: import_zod15.z.boolean().default(false),
|
|
15066
15089
|
kernelApiKey: import_zod15.z.string().optional(),
|
|
15067
15090
|
kernelProxyId: import_zod15.z.string().optional(),
|
|
15068
15091
|
headless: import_zod15.z.boolean().default(true)
|
|
@@ -16889,6 +16912,23 @@ async function provisionMemoryForUser(user) {
|
|
|
16889
16912
|
} catch {
|
|
16890
16913
|
}
|
|
16891
16914
|
}
|
|
16915
|
+
function memoryPlanForUser(user) {
|
|
16916
|
+
switch (user.subscription_tier) {
|
|
16917
|
+
case "starter":
|
|
16918
|
+
return "pro";
|
|
16919
|
+
case "growth":
|
|
16920
|
+
return "team";
|
|
16921
|
+
case "scale":
|
|
16922
|
+
return "enterprise";
|
|
16923
|
+
default:
|
|
16924
|
+
return "free";
|
|
16925
|
+
}
|
|
16926
|
+
}
|
|
16927
|
+
function resolveEffectiveMemoryPlan(user) {
|
|
16928
|
+
const tierPlan = memoryPlanForUser(user);
|
|
16929
|
+
const memoryProPlan = user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
|
|
16930
|
+
return MEMORY_PLAN_RANK[tierPlan] >= MEMORY_PLAN_RANK[memoryProPlan] ? tierPlan : memoryProPlan;
|
|
16931
|
+
}
|
|
16892
16932
|
async function getOrCreateUserMemoryKey(user) {
|
|
16893
16933
|
const creds = await getUserMemoryCreds(user.id);
|
|
16894
16934
|
if (creds.memory_key) {
|
|
@@ -16899,10 +16939,9 @@ async function getOrCreateUserMemoryKey(user) {
|
|
|
16899
16939
|
if (!admin) return { key: null, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
16900
16940
|
const identity = memoryIdentity(user);
|
|
16901
16941
|
const plan = isMemoryOperator(user.email) ? "unlimited" : (await getMemoryPlan(user.id)).plan;
|
|
16902
|
-
const provisioned = await
|
|
16903
|
-
|
|
16904
|
-
{
|
|
16905
|
-
admin
|
|
16942
|
+
const provisioned = await import_provision_defaults.provisionDefaultsTool.execute(
|
|
16943
|
+
{ apiKey: admin, granteeIdentity: identity, issueKey: true, plan },
|
|
16944
|
+
{}
|
|
16906
16945
|
);
|
|
16907
16946
|
if (!provisioned.ok || !provisioned.secret) {
|
|
16908
16947
|
return { key: null, error: provisioned.error ?? "failed to provision default vaults" };
|
|
@@ -16929,23 +16968,27 @@ async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
|
|
|
16929
16968
|
const admin = ADMIN_KEY();
|
|
16930
16969
|
if (!admin) return { ok: false, error: "memory not configured (MCP_MEMORY_ADMIN_KEY missing)" };
|
|
16931
16970
|
const identity = resolveMemoryIdentity(user);
|
|
16932
|
-
const res = await
|
|
16971
|
+
const res = await import_set_schedule_entitlement.setScheduleEntitlementTool.execute({
|
|
16972
|
+
apiKey: admin,
|
|
16933
16973
|
granteeIdentity: identity,
|
|
16934
16974
|
enabled,
|
|
16935
16975
|
quotaPerPeriod,
|
|
16936
16976
|
mcpScraperApiKey: user.api_key
|
|
16937
|
-
},
|
|
16977
|
+
}, {});
|
|
16938
16978
|
return { ok: res.ok, error: res.error };
|
|
16939
16979
|
}
|
|
16940
|
-
var import_node_crypto6, MEMORY_BASE_URL, ADMIN_KEY;
|
|
16980
|
+
var import_node_crypto6, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY, MEMORY_PLAN_RANK;
|
|
16941
16981
|
var init_memory = __esm({
|
|
16942
16982
|
"src/api/memory.ts"() {
|
|
16943
16983
|
"use strict";
|
|
16944
16984
|
import_node_crypto6 = require("crypto");
|
|
16945
16985
|
init_session();
|
|
16946
16986
|
init_db();
|
|
16987
|
+
import_provision_defaults = require("mcpscraper-memory-tools/tools/vaults/provision-defaults");
|
|
16988
|
+
import_set_schedule_entitlement = require("mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement");
|
|
16947
16989
|
MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
|
|
16948
16990
|
ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
16991
|
+
MEMORY_PLAN_RANK = { free: 0, pro: 1, team: 2, enterprise: 3 };
|
|
16949
16992
|
}
|
|
16950
16993
|
});
|
|
16951
16994
|
|
|
@@ -17119,6 +17162,33 @@ var init_MapsNavigator = __esm({
|
|
|
17119
17162
|
await this.page.waitForSelector('[role="main"] h2', { timeout: 5e3 }).catch(() => {
|
|
17120
17163
|
});
|
|
17121
17164
|
}
|
|
17165
|
+
async navigateToSerpBusinessPanel(businessName, location2) {
|
|
17166
|
+
const query = `${businessName} ${location2}`;
|
|
17167
|
+
const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}&udm=1`;
|
|
17168
|
+
await this.page.goto(searchUrl, { waitUntil: "domcontentloaded", timeout: 45e3 }).catch(() => {
|
|
17169
|
+
});
|
|
17170
|
+
await this.page.waitForTimeout(2e3);
|
|
17171
|
+
const clicked = await this.page.evaluate((name) => {
|
|
17172
|
+
const trimmedName = name.trim();
|
|
17173
|
+
const words = trimmedName.split(/\s+/);
|
|
17174
|
+
const prefix = words.slice(0, Math.min(2, words.length)).join(" ");
|
|
17175
|
+
const headings = Array.from(document.querySelectorAll('h3, div[role="heading"]'));
|
|
17176
|
+
const heading = headings.find((el2) => el2.innerText?.trim() === trimmedName) ?? headings.find((el2) => el2.innerText?.trim().startsWith(prefix));
|
|
17177
|
+
const clickable = heading?.closest('a, div[role="link"], div[jsaction]');
|
|
17178
|
+
if (clickable) {
|
|
17179
|
+
clickable.click();
|
|
17180
|
+
return true;
|
|
17181
|
+
}
|
|
17182
|
+
if (heading) {
|
|
17183
|
+
heading.click();
|
|
17184
|
+
return true;
|
|
17185
|
+
}
|
|
17186
|
+
return false;
|
|
17187
|
+
}, businessName);
|
|
17188
|
+
if (!clicked) return false;
|
|
17189
|
+
await this.page.waitForTimeout(2e3);
|
|
17190
|
+
return true;
|
|
17191
|
+
}
|
|
17122
17192
|
buildReviewsUrl(placeUrl) {
|
|
17123
17193
|
const fid = placeUrl.match(/!1s(0x[0-9a-f]+:0x[0-9a-f]+)/i);
|
|
17124
17194
|
const pin = placeUrl.match(/!3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/);
|
|
@@ -17305,6 +17375,25 @@ var init_MapsExtractor = __esm({
|
|
|
17305
17375
|
}
|
|
17306
17376
|
await navigator.navigateToAboutTab();
|
|
17307
17377
|
const aboutAttributes = await this.extractAbout(page);
|
|
17378
|
+
let services = [];
|
|
17379
|
+
let areasServed = [];
|
|
17380
|
+
let servicesStatus = "not_requested";
|
|
17381
|
+
if (options.includeServices) {
|
|
17382
|
+
try {
|
|
17383
|
+
const opened = await navigator.navigateToSerpBusinessPanel(options.businessName, options.location);
|
|
17384
|
+
if (opened) {
|
|
17385
|
+
const result = await this.extractServicesAndAreasServed(page);
|
|
17386
|
+
services = result.services;
|
|
17387
|
+
areasServed = result.areasServed;
|
|
17388
|
+
servicesStatus = services.length > 0 || areasServed.length > 0 ? "collected" : "none_exist";
|
|
17389
|
+
} else {
|
|
17390
|
+
servicesStatus = "unavailable";
|
|
17391
|
+
}
|
|
17392
|
+
} catch (err) {
|
|
17393
|
+
console.warn("[MapsExtractor] services/areas-served extraction failed:", err instanceof Error ? err.message : String(err));
|
|
17394
|
+
servicesStatus = "unavailable";
|
|
17395
|
+
}
|
|
17396
|
+
}
|
|
17308
17397
|
const reviewCountFinal = overview.reviewCount ?? (reviewStats.reviewHistogram.length > 0 ? reviewStats.reviewHistogram.reduce((s, r) => s + parseInt(r.count, 10), 0).toString() : null);
|
|
17309
17398
|
return {
|
|
17310
17399
|
businessName: options.businessName,
|
|
@@ -17320,6 +17409,9 @@ var init_MapsExtractor = __esm({
|
|
|
17320
17409
|
aboutAttributes,
|
|
17321
17410
|
reviews,
|
|
17322
17411
|
reviewsStatus,
|
|
17412
|
+
services,
|
|
17413
|
+
areasServed,
|
|
17414
|
+
servicesStatus,
|
|
17323
17415
|
durationMs: Date.now() - startMs
|
|
17324
17416
|
};
|
|
17325
17417
|
} finally {
|
|
@@ -17508,6 +17600,81 @@ var init_MapsExtractor = __esm({
|
|
|
17508
17600
|
}
|
|
17509
17601
|
return result.data;
|
|
17510
17602
|
}
|
|
17603
|
+
async extractServicesAndAreasServed(page) {
|
|
17604
|
+
await page.waitForTimeout(1500);
|
|
17605
|
+
let services = [];
|
|
17606
|
+
if (await this.clickRowStartingWithRetry(page, "Services:")) {
|
|
17607
|
+
await page.waitForSelector('[role="dialog"]', { timeout: 5e3 }).catch(() => {
|
|
17608
|
+
});
|
|
17609
|
+
await page.waitForTimeout(800);
|
|
17610
|
+
services = await this.extractOpenDialogItems(page, "Services");
|
|
17611
|
+
await this.closeOpenDialog(page, "Services");
|
|
17612
|
+
await page.waitForTimeout(500);
|
|
17613
|
+
}
|
|
17614
|
+
let areasServed = [];
|
|
17615
|
+
if (await this.clickRowStartingWithRetry(page, "Serves ")) {
|
|
17616
|
+
await page.waitForSelector('[role="dialog"]', { timeout: 5e3 }).catch(() => {
|
|
17617
|
+
});
|
|
17618
|
+
await page.waitForTimeout(800);
|
|
17619
|
+
areasServed = await this.extractOpenDialogItems(page, "Areas Served");
|
|
17620
|
+
await this.closeOpenDialog(page, "Areas Served");
|
|
17621
|
+
}
|
|
17622
|
+
return { services, areasServed };
|
|
17623
|
+
}
|
|
17624
|
+
async clickRowStartingWithRetry(page, prefix) {
|
|
17625
|
+
if (await this.clickRowStartingWith(page, prefix)) return true;
|
|
17626
|
+
await page.waitForTimeout(2e3);
|
|
17627
|
+
return this.clickRowStartingWith(page, prefix);
|
|
17628
|
+
}
|
|
17629
|
+
async clickRowStartingWith(page, prefix) {
|
|
17630
|
+
return page.evaluate((p) => {
|
|
17631
|
+
const el2 = Array.from(document.querySelectorAll('button, a, div[role="button"]')).find(
|
|
17632
|
+
(e) => (e.innerText ?? "").trim().startsWith(p)
|
|
17633
|
+
);
|
|
17634
|
+
if (!el2) return false;
|
|
17635
|
+
el2.click();
|
|
17636
|
+
return true;
|
|
17637
|
+
}, prefix);
|
|
17638
|
+
}
|
|
17639
|
+
async extractOpenDialogItems(page, expectedHeading) {
|
|
17640
|
+
return page.evaluate((expected) => {
|
|
17641
|
+
const dialogs = Array.from(document.querySelectorAll('[role="dialog"]'));
|
|
17642
|
+
const withHeading = dialogs.find((d) => {
|
|
17643
|
+
const h = d.querySelector('h1, h2, [role="heading"]');
|
|
17644
|
+
const headingText = h?.innerText?.trim() ?? "";
|
|
17645
|
+
return headingText.startsWith(expected);
|
|
17646
|
+
});
|
|
17647
|
+
if (!withHeading) return [];
|
|
17648
|
+
const headingEl = withHeading.querySelector('h1, h2, [role="heading"]');
|
|
17649
|
+
const headingLines = new Set(
|
|
17650
|
+
(headingEl?.innerText ?? "").split("\n").map((s) => s.trim()).filter(Boolean)
|
|
17651
|
+
);
|
|
17652
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17653
|
+
const items = [];
|
|
17654
|
+
withHeading.querySelectorAll("*").forEach((el2) => {
|
|
17655
|
+
if (el2.children.length > 0) return;
|
|
17656
|
+
const text = el2.innerText?.trim() ?? el2.textContent?.trim() ?? "";
|
|
17657
|
+
if (!text || headingLines.has(text)) return;
|
|
17658
|
+
if (/^(back|close|done|thanks)$/i.test(text)) return;
|
|
17659
|
+
if (/^serves\s.*following/i.test(text)) return;
|
|
17660
|
+
if (seen.has(text)) return;
|
|
17661
|
+
seen.add(text);
|
|
17662
|
+
items.push(text);
|
|
17663
|
+
});
|
|
17664
|
+
return items;
|
|
17665
|
+
}, expectedHeading);
|
|
17666
|
+
}
|
|
17667
|
+
async closeOpenDialog(page, expectedHeading) {
|
|
17668
|
+
await page.evaluate((expected) => {
|
|
17669
|
+
const dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find((d) => {
|
|
17670
|
+
const h = d.querySelector('h1, h2, [role="heading"]');
|
|
17671
|
+
const headingText = h?.innerText?.trim() ?? "";
|
|
17672
|
+
return headingText.startsWith(expected);
|
|
17673
|
+
});
|
|
17674
|
+
const closeBtn = dialog?.querySelector('[aria-label*="Close" i], button[aria-label*="close" i]');
|
|
17675
|
+
closeBtn?.click();
|
|
17676
|
+
}, expectedHeading);
|
|
17677
|
+
}
|
|
17511
17678
|
};
|
|
17512
17679
|
}
|
|
17513
17680
|
});
|
|
@@ -18168,6 +18335,17 @@ var init_workflow_catalog = __esm({
|
|
|
18168
18335
|
produces: ["content gaps", "page comparison CSV", "PAA questions CSV", "section map", "writer brief"],
|
|
18169
18336
|
runHint: "Use workflow_run with workflowId serp-comparison for competitor gaps or paa-expansion-brief when the user mainly wants a section map from PAA data."
|
|
18170
18337
|
},
|
|
18338
|
+
{
|
|
18339
|
+
id: "recurring_tracking_and_memory",
|
|
18340
|
+
title: "Recurring Tracking & Memory",
|
|
18341
|
+
description: "Run a check on a daily, weekly, or custom cadence with no external cron needed, and save each result somewhere searchable so you can compare against past runs later - rankings, prices, competitor changes, anything you would otherwise check by hand and forget to log.",
|
|
18342
|
+
primaryWorkflowId: null,
|
|
18343
|
+
recommendedTools: ["create-scheduled-action", "search_serp", "table-create", "table-insert-rows", "table-query", "memory-put", "memory-search", "list-scheduled-actions"],
|
|
18344
|
+
requiredInputs: ["plain-language description of what to check and how often"],
|
|
18345
|
+
optionalInputs: ["vault to save into", "timeOfDay", "timezone"],
|
|
18346
|
+
produces: ["a live scheduled action that runs automatically on cadence", "a table row or note per run for structured history", "a searchable trend you can compare run-over-run"],
|
|
18347
|
+
runHint: "Use create-scheduled-action with a plain description and cadence; the run itself checks list-vaults, uses table-insert-rows for repeating structured data (e.g. a keyword rank check) or memory-put for a one-off note, and reads prior runs with table-query or memory-search before writing the new one. For a one-time blueprint to build your own external cron/DB instead of a live scheduled action, use rank_tracker_workflow."
|
|
18348
|
+
},
|
|
18171
18349
|
{
|
|
18172
18350
|
id: "ai_search_visibility_audit",
|
|
18173
18351
|
title: "AI Search Visibility Audit",
|
|
@@ -19883,6 +20061,9 @@ function formatMapsPlaceIntel(raw, input) {
|
|
|
19883
20061
|
const about = d.aboutAttributes ?? [];
|
|
19884
20062
|
const reviews = d.reviews ?? [];
|
|
19885
20063
|
const reviewsStatus = d.reviewsStatus ?? "not_requested";
|
|
20064
|
+
const services = d.services ?? [];
|
|
20065
|
+
const areasServed = d.areasServed ?? [];
|
|
20066
|
+
const servicesStatus = d.servicesStatus ?? "not_requested";
|
|
19886
20067
|
const hoursTable = d.hoursTable ?? [];
|
|
19887
20068
|
const ratingLine = [rating, reviewCount ? `(${reviewCount} reviews)` : null].filter(Boolean).join(" ");
|
|
19888
20069
|
const basicLines = [
|
|
@@ -19937,6 +20118,20 @@ ${reviews.map((r, i) => {
|
|
|
19937
20118
|
${r.text ?? ""}`;
|
|
19938
20119
|
}).join("\n\n")}`;
|
|
19939
20120
|
})();
|
|
20121
|
+
const servicesSection = (() => {
|
|
20122
|
+
if (servicesStatus === "not_requested") return "";
|
|
20123
|
+
if (servicesStatus === "unavailable") return "\n## Services & Areas Served\n> Could not be retrieved this run \u2014 retry with `includeServices: true`.";
|
|
20124
|
+
if (servicesStatus === "none_exist") return "\n## Services & Areas Served\n*Not listed on this business's profile.*";
|
|
20125
|
+
const parts = [
|
|
20126
|
+
services.length ? `**Services**
|
|
20127
|
+
${services.map((s) => `- ${s}`).join("\n")}` : null,
|
|
20128
|
+
areasServed.length ? `**Areas Served**
|
|
20129
|
+
${areasServed.map((a) => `- ${a}`).join("\n")}` : null
|
|
20130
|
+
].filter(Boolean);
|
|
20131
|
+
return parts.length ? `
|
|
20132
|
+
## Services & Areas Served
|
|
20133
|
+
${parts.join("\n\n")}` : "";
|
|
20134
|
+
})();
|
|
19940
20135
|
const full = [
|
|
19941
20136
|
`# ${name}`,
|
|
19942
20137
|
category ? `*${category}*` : null,
|
|
@@ -19952,6 +20147,7 @@ ${basicLines}` : null,
|
|
|
19952
20147
|
## Entity IDs
|
|
19953
20148
|
${entitySection}` : null,
|
|
19954
20149
|
reviewsSection,
|
|
20150
|
+
servicesSection,
|
|
19955
20151
|
durationMs != null ? `
|
|
19956
20152
|
---
|
|
19957
20153
|
*Extracted in ${(durationMs / 1e3).toFixed(1)}s*` : null
|
|
@@ -19975,7 +20171,10 @@ ${entitySection}` : null,
|
|
|
19975
20171
|
lng: lng ?? null,
|
|
19976
20172
|
reviewsStatus,
|
|
19977
20173
|
reviewsCollected: reviews.length,
|
|
19978
|
-
reviewTopics: topics.map((t) => ({ label: String(t.label ?? ""), count: String(t.count ?? "") }))
|
|
20174
|
+
reviewTopics: topics.map((t) => ({ label: String(t.label ?? ""), count: String(t.count ?? "") })),
|
|
20175
|
+
services,
|
|
20176
|
+
areasServed,
|
|
20177
|
+
servicesStatus
|
|
19979
20178
|
}
|
|
19980
20179
|
};
|
|
19981
20180
|
}
|
|
@@ -26325,7 +26524,7 @@ var PACKAGE_VERSION;
|
|
|
26325
26524
|
var init_version = __esm({
|
|
26326
26525
|
"src/version.ts"() {
|
|
26327
26526
|
"use strict";
|
|
26328
|
-
PACKAGE_VERSION = "0.4.
|
|
26527
|
+
PACKAGE_VERSION = "0.4.2";
|
|
26329
26528
|
}
|
|
26330
26529
|
});
|
|
26331
26530
|
|
|
@@ -26422,6 +26621,23 @@ Multi-step orchestrations \u2014 prefer these over hand-chaining primitives when
|
|
|
26422
26621
|
answers the question (plain search/extract before browser agents).
|
|
26423
26622
|
- Large results are saved to disk or an artifact and returned as a summary plus a path or artifactId;
|
|
26424
26623
|
read it back for full detail rather than expecting the whole payload inline.
|
|
26624
|
+
|
|
26625
|
+
## Memory
|
|
26626
|
+
mcp-scraper also exposes 74 persistent per-user memory tools (notes, facts, vaults,
|
|
26627
|
+
scheduled actions, tables, channels) backed by memory.mcpscraper.dev. Every account starts with 13
|
|
26628
|
+
vaults \u2014 call **list-vaults** to see what exists before creating anything new. Pick the vault whose job
|
|
26629
|
+
matches the content: **Ideas** (unvalidated concepts), **Knowledge** (distilled lessons/how-tos),
|
|
26630
|
+
**Library** (raw source material \u2014 articles, transcripts), **People** (one durable note per person/org),
|
|
26631
|
+
**Communications** (individual conversation records), **Calendar** (time-anchored entries), **Tasks**
|
|
26632
|
+
(action items with a status), **Projects** (durable named initiatives), **Issues** (something broken),
|
|
26633
|
+
**Improvement Log** (receipts of changes made and whether they worked), **Experiments** (hypothesis +
|
|
26634
|
+
measured result), **Sprint** (current cycle's scope), **Inspiration** (other people's work worth
|
|
26635
|
+
studying).
|
|
26636
|
+
Before writing, call **memory-suggest** to check whether something's already captured and to find the
|
|
26637
|
+
right vault's hub note to link. Use **memory-put** for narrative notes (prose, a title, a body); use
|
|
26638
|
+
**table-create**/**table-insert-rows**/**table-query** instead when the caller wants row-shaped data
|
|
26639
|
+
they'll filter/sort by exact value. **memory-search** is meaning-based over full content (embeds, slower);
|
|
26640
|
+
**memory-list** filters one vault by kind/tags (fast, exact).
|
|
26425
26641
|
`.trim();
|
|
26426
26642
|
}
|
|
26427
26643
|
var SERVER_INSTRUCTIONS;
|
|
@@ -26575,7 +26791,8 @@ var init_mcp_tool_schemas = __esm({
|
|
|
26575
26791
|
gl: import_zod31.z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
26576
26792
|
hl: import_zod31.z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
26577
26793
|
includeReviews: import_zod31.z.boolean().default(false).describe("Fetch individual review cards \u2014 for reviews, customer pain, complaints, or praise themes."),
|
|
26578
|
-
maxReviews: import_zod31.z.number().int().min(1).max(500).default(50).describe("Max review cards when includeReviews is true. Default 50, maximum 500.")
|
|
26794
|
+
maxReviews: import_zod31.z.number().int().min(1).max(500).default(50).describe("Max review cards when includeReviews is true. Default 50, maximum 500."),
|
|
26795
|
+
includeServices: import_zod31.z.boolean().default(false).describe("Fetch the business's configured services list and areas-served list, when the profile has them. Adds one extra page visit; not present for every business.")
|
|
26579
26796
|
};
|
|
26580
26797
|
MapsSearchInputSchema = {
|
|
26581
26798
|
query: import_zod31.z.string().min(1).describe('Business category, niche, or search term, e.g. "roofers". Do not include location here \u2014 use location instead.'),
|
|
@@ -26888,7 +27105,10 @@ var init_mcp_tool_schemas = __esm({
|
|
|
26888
27105
|
reviewTopics: import_zod31.z.array(import_zod31.z.object({
|
|
26889
27106
|
label: import_zod31.z.string(),
|
|
26890
27107
|
count: import_zod31.z.string()
|
|
26891
|
-
}))
|
|
27108
|
+
})),
|
|
27109
|
+
services: import_zod31.z.array(import_zod31.z.string()),
|
|
27110
|
+
areasServed: import_zod31.z.array(import_zod31.z.string()),
|
|
27111
|
+
servicesStatus: import_zod31.z.string()
|
|
26892
27112
|
};
|
|
26893
27113
|
CreditsInfoOutputSchema = {
|
|
26894
27114
|
balanceCredits: import_zod31.z.number().nullable(),
|
|
@@ -28714,7 +28934,7 @@ var init_browser_agent_tool_schemas = __esm({
|
|
|
28714
28934
|
BrowserProfileConnectOutputSchema = {
|
|
28715
28935
|
ok: import_zod32.z.boolean(),
|
|
28716
28936
|
tool: import_zod32.z.literal("browser_profile_connect"),
|
|
28717
|
-
session_id: NullableString2,
|
|
28937
|
+
session_id: NullableString2.describe("The underlying live browser session id backing this sign-in."),
|
|
28718
28938
|
auth_connection_id: import_zod32.z.string(),
|
|
28719
28939
|
watch_url: import_zod32.z.string().describe("mcpscraper.dev sign-in link to give the user so they can complete this login."),
|
|
28720
28940
|
live_view_url: NullableString2.describe("Deprecated; always null. Open watch_url to view the sign-in."),
|
|
@@ -28724,10 +28944,10 @@ var init_browser_agent_tool_schemas = __esm({
|
|
|
28724
28944
|
account_email: NullableString2,
|
|
28725
28945
|
note: NullableString2,
|
|
28726
28946
|
status: import_zod32.z.string(),
|
|
28727
|
-
flow_status: NullableString2,
|
|
28728
|
-
flow_step: NullableString2,
|
|
28729
|
-
flow_expires_at: NullableString2,
|
|
28730
|
-
post_login_url: NullableString2,
|
|
28947
|
+
flow_status: NullableString2.describe("Deprecated; always null."),
|
|
28948
|
+
flow_step: NullableString2.describe("Deprecated; always null."),
|
|
28949
|
+
flow_expires_at: NullableString2.describe("Deprecated; always null."),
|
|
28950
|
+
post_login_url: NullableString2.describe("Deprecated; always null."),
|
|
28731
28951
|
connected_logins: import_zod32.z.array(BrowserProfileLogin).describe("Every login currently saved in this profile, so you can see what else is connected."),
|
|
28732
28952
|
next_steps: import_zod32.z.array(import_zod32.z.string()),
|
|
28733
28953
|
raw: BrowserRawObject.optional()
|
|
@@ -29160,6 +29380,11 @@ function slugPart(value) {
|
|
|
29160
29380
|
if (!trimmed) return null;
|
|
29161
29381
|
return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
29162
29382
|
}
|
|
29383
|
+
function normalizeDomain4(value) {
|
|
29384
|
+
const trimmed = value?.trim().toLowerCase();
|
|
29385
|
+
if (!trimmed) return null;
|
|
29386
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9.-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
29387
|
+
}
|
|
29163
29388
|
function savedProfileNameFromEmail(email) {
|
|
29164
29389
|
const value = slugPart(email);
|
|
29165
29390
|
if (!value) return null;
|
|
@@ -29269,13 +29494,13 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
29269
29494
|
"browser_profile_connect",
|
|
29270
29495
|
{
|
|
29271
29496
|
title: "Save a Site Login to a Profile",
|
|
29272
|
-
description:
|
|
29497
|
+
description: 'Open a live hosted browser session so the user can sign into a site (ChatGPT, Claude, Reddit, any account-gated site) directly in their own browser tab, then save the login to a named profile. Returns a watch_url \u2014 give it to the user; they sign in fresh on the real site (existing browser cookies are NOT imported), then click "Done" on that page to save the session and close it. ONE profile holds MANY logins \u2014 call again with the same profile and a different domain to stack another account. NOT for one-off scraping (use extract_url) or driving the browser (use browser_open). Billed at the standard live-browser rate while the sign-in session stays open. After the user clicks Done, poll browser_profile_list until AUTHENTICATED, then browser_open with the profile.',
|
|
29273
29498
|
inputSchema: BrowserProfileConnectInputSchema,
|
|
29274
29499
|
outputSchema: recordOutputSchema("browser_profile_connect", BrowserProfileConnectOutputSchema),
|
|
29275
29500
|
annotations: annotations("Save a Site Login to a Profile")
|
|
29276
29501
|
},
|
|
29277
29502
|
async (input) => {
|
|
29278
|
-
const domain =
|
|
29503
|
+
const domain = normalizeDomain4(input.domain) || "chatgpt.com";
|
|
29279
29504
|
const setupUrl = input.login_url ?? input.url ?? (domain === "chatgpt.com" ? "https://chatgpt.com/" : `https://${domain}/`);
|
|
29280
29505
|
const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email) || browserServiceProfileName();
|
|
29281
29506
|
if (!profile) {
|
|
@@ -29291,14 +29516,15 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
29291
29516
|
domain,
|
|
29292
29517
|
login_url: setupUrl,
|
|
29293
29518
|
...accountEmail ? { account_email: accountEmail } : {},
|
|
29294
|
-
...note ? { note } : {}
|
|
29519
|
+
...note ? { note } : {},
|
|
29520
|
+
...typeof input.timeout_seconds === "number" ? { timeout_seconds: input.timeout_seconds } : {}
|
|
29295
29521
|
});
|
|
29296
29522
|
if (!open.ok) return errorResult("browser_profile_connect", open.data);
|
|
29297
29523
|
const connectedLogins = await fetchConnectedLogins(profile).catch(() => []);
|
|
29298
29524
|
return structuredResult({
|
|
29299
29525
|
ok: true,
|
|
29300
29526
|
tool: "browser_profile_connect",
|
|
29301
|
-
session_id: null,
|
|
29527
|
+
session_id: open.data.browser_session_id ?? null,
|
|
29302
29528
|
auth_connection_id: open.data.connection_id,
|
|
29303
29529
|
watch_url: `${consoleBase}/console/auth/${open.data.connection_id}`,
|
|
29304
29530
|
live_view_url: null,
|
|
@@ -29308,13 +29534,13 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
29308
29534
|
account_email: accountEmail,
|
|
29309
29535
|
note,
|
|
29310
29536
|
status: open.data.status ?? null,
|
|
29311
|
-
flow_status:
|
|
29312
|
-
flow_step:
|
|
29313
|
-
flow_expires_at:
|
|
29314
|
-
post_login_url:
|
|
29537
|
+
flow_status: null,
|
|
29538
|
+
flow_step: null,
|
|
29539
|
+
flow_expires_at: null,
|
|
29540
|
+
post_login_url: null,
|
|
29315
29541
|
connected_logins: connectedLogins,
|
|
29316
29542
|
next_steps: [
|
|
29317
|
-
`Give the user watch_url so they can sign in to ${domain}.`,
|
|
29543
|
+
`Give the user watch_url so they can sign in to ${domain} directly in that page, then click "Done \u2014 I'm signed in".`,
|
|
29318
29544
|
"Poll browser_profile_list (with this profile) until the login reads AUTHENTICATED.",
|
|
29319
29545
|
"To add another account to this same profile, call browser_profile_connect again with the same profile and a different domain.",
|
|
29320
29546
|
"When authenticated, call browser_open with this profile to drive the logged-in session."
|
|
@@ -29339,7 +29565,7 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
29339
29565
|
error: "profile or email is required when BROWSER_AGENT_PROFILE_NAME is not available"
|
|
29340
29566
|
});
|
|
29341
29567
|
}
|
|
29342
|
-
const domain =
|
|
29568
|
+
const domain = normalizeDomain4(input.domain) || void 0;
|
|
29343
29569
|
const res = await req("POST", "/agent/profiles/list", {
|
|
29344
29570
|
profile,
|
|
29345
29571
|
...domain ? { domain } : {},
|
|
@@ -31940,6 +32166,486 @@ var init_memory_mcp_tool_executor = __esm({
|
|
|
31940
32166
|
}
|
|
31941
32167
|
});
|
|
31942
32168
|
|
|
32169
|
+
// src/mcp/memory-inprocess-executor.ts
|
|
32170
|
+
function registerCutover(upstreamName, handler) {
|
|
32171
|
+
CUTOVER.set(upstreamName, handler);
|
|
32172
|
+
}
|
|
32173
|
+
var CUTOVER, HybridMemoryToolExecutor;
|
|
32174
|
+
var init_memory_inprocess_executor = __esm({
|
|
32175
|
+
"src/mcp/memory-inprocess-executor.ts"() {
|
|
32176
|
+
"use strict";
|
|
32177
|
+
init_memory();
|
|
32178
|
+
CUTOVER = /* @__PURE__ */ new Map();
|
|
32179
|
+
HybridMemoryToolExecutor = class {
|
|
32180
|
+
constructor(httpExecutor, user) {
|
|
32181
|
+
this.httpExecutor = httpExecutor;
|
|
32182
|
+
this.user = user;
|
|
32183
|
+
}
|
|
32184
|
+
httpExecutor;
|
|
32185
|
+
user;
|
|
32186
|
+
mkKeyPromise = null;
|
|
32187
|
+
async resolveMkKey() {
|
|
32188
|
+
if (!this.mkKeyPromise) {
|
|
32189
|
+
this.mkKeyPromise = getOrCreateUserMemoryKey(this.user).then((r) => r.key);
|
|
32190
|
+
}
|
|
32191
|
+
return this.mkKeyPromise;
|
|
32192
|
+
}
|
|
32193
|
+
async callMemoryTool(upstreamName, args) {
|
|
32194
|
+
const handler = CUTOVER.get(upstreamName);
|
|
32195
|
+
if (!handler) return this.httpExecutor.callMemoryTool(upstreamName, args);
|
|
32196
|
+
const mkKey = await this.resolveMkKey();
|
|
32197
|
+
if (!mkKey) return { content: [{ type: "text", text: "memory unavailable" }], isError: true };
|
|
32198
|
+
try {
|
|
32199
|
+
return await handler(args, mkKey);
|
|
32200
|
+
} catch (err) {
|
|
32201
|
+
const message = err instanceof Error ? err.message : `memory ${upstreamName} failed in-process`;
|
|
32202
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
32203
|
+
}
|
|
32204
|
+
}
|
|
32205
|
+
};
|
|
32206
|
+
}
|
|
32207
|
+
});
|
|
32208
|
+
|
|
32209
|
+
// src/mcp/memory-cutover/reads.ts
|
|
32210
|
+
function registerReadCutovers() {
|
|
32211
|
+
if (registered) return;
|
|
32212
|
+
registered = true;
|
|
32213
|
+
registerCutover("listApprovedSendersTool", async (input, apiKey) => {
|
|
32214
|
+
const result = await import_list_approved_senders.listApprovedSendersTool.execute({ ...input, apiKey }, {});
|
|
32215
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32216
|
+
});
|
|
32217
|
+
registerCutover("noteInboxTool", async (input, apiKey) => {
|
|
32218
|
+
const result = await import_note_inbox.noteInboxTool.execute({ ...input, apiKey }, {});
|
|
32219
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32220
|
+
});
|
|
32221
|
+
registerCutover("getMessageNoteTool", async (input, apiKey) => {
|
|
32222
|
+
const result = await import_get_message_note.getMessageNoteTool.execute({ ...input, apiKey }, {});
|
|
32223
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32224
|
+
});
|
|
32225
|
+
registerCutover("listChannelMembersTool", async (input, apiKey) => {
|
|
32226
|
+
const result = await import_list_channel_members.listChannelMembersTool.execute({ ...input, apiKey }, {});
|
|
32227
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32228
|
+
});
|
|
32229
|
+
registerCutover("myMentionsTool", async (input, apiKey) => {
|
|
32230
|
+
const result = await import_my_mentions.myMentionsTool.execute({ ...input, apiKey }, {});
|
|
32231
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32232
|
+
});
|
|
32233
|
+
registerCutover("factHistoryTool", async (input, apiKey) => {
|
|
32234
|
+
const result = await import_fact_history.factHistoryTool.execute({ ...input, apiKey }, {});
|
|
32235
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32236
|
+
});
|
|
32237
|
+
registerCutover("exportTool", async (input, apiKey) => {
|
|
32238
|
+
const result = await import_export2.exportTool.execute({ ...input, apiKey }, {});
|
|
32239
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32240
|
+
});
|
|
32241
|
+
registerCutover("getTool", async (input, apiKey) => {
|
|
32242
|
+
const result = await import_get.getTool.execute({ ...input, apiKey }, {});
|
|
32243
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32244
|
+
});
|
|
32245
|
+
registerCutover("listTool", async (input, apiKey) => {
|
|
32246
|
+
const result = await import_list.listTool.execute({ ...input, apiKey }, {});
|
|
32247
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32248
|
+
});
|
|
32249
|
+
registerCutover("searchTool", async (input, apiKey) => {
|
|
32250
|
+
const result = await import_search.searchTool.execute({ ...input, apiKey }, {});
|
|
32251
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32252
|
+
});
|
|
32253
|
+
registerCutover("suggestTool", async (input, apiKey) => {
|
|
32254
|
+
const result = await import_suggest.suggestTool.execute({ ...input, apiKey }, {});
|
|
32255
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32256
|
+
});
|
|
32257
|
+
registerCutover("temporalRecallTool", async (input, apiKey) => {
|
|
32258
|
+
const result = await import_temporal_recall.temporalRecallTool.execute({ ...input, apiKey }, {});
|
|
32259
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32260
|
+
});
|
|
32261
|
+
registerCutover("listScheduledActionsTool", async (input, apiKey) => {
|
|
32262
|
+
const result = await import_list_scheduled_actions.listScheduledActionsTool.execute({ ...input, apiKey }, {});
|
|
32263
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32264
|
+
});
|
|
32265
|
+
registerCutover("costUsageTool", async (input, apiKey) => {
|
|
32266
|
+
const result = await import_cost_usage.costUsageTool.execute({ ...input, apiKey }, {});
|
|
32267
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32268
|
+
});
|
|
32269
|
+
registerCutover("storageUsageTool", async (input, apiKey) => {
|
|
32270
|
+
const result = await import_storage_usage.storageUsageTool.execute({ ...input, apiKey }, {});
|
|
32271
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32272
|
+
});
|
|
32273
|
+
registerCutover("describeTableTool", async (input, apiKey) => {
|
|
32274
|
+
const result = await import_table_describe.describeTableTool.execute({ ...input, apiKey }, {});
|
|
32275
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32276
|
+
});
|
|
32277
|
+
registerCutover("listTablesTool", async (input, apiKey) => {
|
|
32278
|
+
const result = await import_table_list.listTablesTool.execute({ ...input, apiKey }, {});
|
|
32279
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32280
|
+
});
|
|
32281
|
+
registerCutover("queryTableTool", async (input, apiKey) => {
|
|
32282
|
+
const result = await import_table_query.queryTableTool.execute({ ...input, apiKey }, {});
|
|
32283
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32284
|
+
});
|
|
32285
|
+
registerCutover("listSharedWithMeTool", async (input, apiKey) => {
|
|
32286
|
+
const result = await import_list_shared_with_me.listSharedWithMeTool.execute({ ...input, apiKey }, {});
|
|
32287
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32288
|
+
});
|
|
32289
|
+
registerCutover("listVaultsTool", async (input, apiKey) => {
|
|
32290
|
+
const result = await import_list_vaults.listVaultsTool.execute({ ...input, apiKey }, {});
|
|
32291
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32292
|
+
});
|
|
32293
|
+
registerCutover("videoAnalyzeStatusTool", async (input, apiKey) => {
|
|
32294
|
+
const result = await import_status.videoAnalyzeStatusTool.execute({ ...input, apiKey }, {});
|
|
32295
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32296
|
+
});
|
|
32297
|
+
registerCutover("listWebhooksTool", async (input, apiKey) => {
|
|
32298
|
+
const result = await import_list_webhooks.listWebhooksTool.execute({ ...input, apiKey }, {});
|
|
32299
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32300
|
+
});
|
|
32301
|
+
}
|
|
32302
|
+
var import_list_approved_senders, import_note_inbox, import_get_message_note, import_list_channel_members, import_my_mentions, import_fact_history, import_export2, import_get, import_list, import_search, import_suggest, import_temporal_recall, import_list_scheduled_actions, import_cost_usage, import_storage_usage, import_table_describe, import_table_list, import_table_query, import_list_shared_with_me, import_list_vaults, import_status, import_list_webhooks, registered;
|
|
32303
|
+
var init_reads = __esm({
|
|
32304
|
+
"src/mcp/memory-cutover/reads.ts"() {
|
|
32305
|
+
"use strict";
|
|
32306
|
+
init_memory_inprocess_executor();
|
|
32307
|
+
import_list_approved_senders = require("mcpscraper-memory-tools/tools/access/list-approved-senders");
|
|
32308
|
+
import_note_inbox = require("mcpscraper-memory-tools/tools/access/note-inbox");
|
|
32309
|
+
import_get_message_note = require("mcpscraper-memory-tools/tools/channels/get-message-note");
|
|
32310
|
+
import_list_channel_members = require("mcpscraper-memory-tools/tools/channels/list-channel-members");
|
|
32311
|
+
import_my_mentions = require("mcpscraper-memory-tools/tools/channels/my-mentions");
|
|
32312
|
+
import_fact_history = require("mcpscraper-memory-tools/tools/facts/fact-history");
|
|
32313
|
+
import_export2 = require("mcpscraper-memory-tools/tools/memory/export");
|
|
32314
|
+
import_get = require("mcpscraper-memory-tools/tools/memory/get");
|
|
32315
|
+
import_list = require("mcpscraper-memory-tools/tools/memory/list");
|
|
32316
|
+
import_search = require("mcpscraper-memory-tools/tools/memory/search");
|
|
32317
|
+
import_suggest = require("mcpscraper-memory-tools/tools/memory/suggest");
|
|
32318
|
+
import_temporal_recall = require("mcpscraper-memory-tools/tools/recall/temporal-recall");
|
|
32319
|
+
import_list_scheduled_actions = require("mcpscraper-memory-tools/tools/schedule/list-scheduled-actions");
|
|
32320
|
+
import_cost_usage = require("mcpscraper-memory-tools/tools/storage/cost-usage");
|
|
32321
|
+
import_storage_usage = require("mcpscraper-memory-tools/tools/storage/storage-usage");
|
|
32322
|
+
import_table_describe = require("mcpscraper-memory-tools/tools/tables/table-describe");
|
|
32323
|
+
import_table_list = require("mcpscraper-memory-tools/tools/tables/table-list");
|
|
32324
|
+
import_table_query = require("mcpscraper-memory-tools/tools/tables/table-query");
|
|
32325
|
+
import_list_shared_with_me = require("mcpscraper-memory-tools/tools/vaults/list-shared-with-me");
|
|
32326
|
+
import_list_vaults = require("mcpscraper-memory-tools/tools/vaults/list-vaults");
|
|
32327
|
+
import_status = require("mcpscraper-memory-tools/tools/video/status");
|
|
32328
|
+
import_list_webhooks = require("mcpscraper-memory-tools/tools/webhooks/list-webhooks");
|
|
32329
|
+
registered = false;
|
|
32330
|
+
}
|
|
32331
|
+
});
|
|
32332
|
+
|
|
32333
|
+
// src/mcp/memory-cutover/writes.ts
|
|
32334
|
+
function registerWriteCutovers() {
|
|
32335
|
+
if (registered2) return;
|
|
32336
|
+
registered2 = true;
|
|
32337
|
+
registerCutover("acceptShareTool", async (input, apiKey) => {
|
|
32338
|
+
const result = await import_accept_share.acceptShareTool.execute({ ...input, apiKey }, {});
|
|
32339
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32340
|
+
});
|
|
32341
|
+
registerCutover("approveSenderTool", async (input, apiKey) => {
|
|
32342
|
+
const result = await import_approve_sender.approveSenderTool.execute({ ...input, apiKey }, {});
|
|
32343
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32344
|
+
});
|
|
32345
|
+
registerCutover("declineShareTool", async (input, apiKey) => {
|
|
32346
|
+
const result = await import_decline_share.declineShareTool.execute({ ...input, apiKey }, {});
|
|
32347
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32348
|
+
});
|
|
32349
|
+
registerCutover("inboxSettingsTool", async (input, apiKey) => {
|
|
32350
|
+
const result = await import_inbox_settings.inboxSettingsTool.execute({ ...input, apiKey }, {});
|
|
32351
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32352
|
+
});
|
|
32353
|
+
registerCutover("removeApprovedSenderTool", async (input, apiKey) => {
|
|
32354
|
+
const result = await import_remove_approved_sender.removeApprovedSenderTool.execute({ ...input, apiKey }, {});
|
|
32355
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32356
|
+
});
|
|
32357
|
+
registerCutover("revokeShareTool", async (input, apiKey) => {
|
|
32358
|
+
const result = await import_revoke_share.revokeShareTool.execute({ ...input, apiKey }, {});
|
|
32359
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32360
|
+
});
|
|
32361
|
+
registerCutover("setAgentIdentityTool", async (input, apiKey) => {
|
|
32362
|
+
const result = await import_set_agent_identity.setAgentIdentityTool.execute({ ...input, apiKey }, {});
|
|
32363
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32364
|
+
});
|
|
32365
|
+
registerCutover("shareNoteTool", async (input, apiKey) => {
|
|
32366
|
+
const result = await import_share_note.shareNoteTool.execute({ ...input, apiKey }, {});
|
|
32367
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32368
|
+
});
|
|
32369
|
+
registerCutover("shareVaultTool", async (input, apiKey) => {
|
|
32370
|
+
const result = await import_share_vault.shareVaultTool.execute({ ...input, apiKey }, {});
|
|
32371
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32372
|
+
});
|
|
32373
|
+
registerCutover("swapVaultTool", async (input, apiKey) => {
|
|
32374
|
+
const result = await import_swap_vault.swapVaultTool.execute({ ...input, apiKey }, {});
|
|
32375
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32376
|
+
});
|
|
32377
|
+
registerCutover("switchAccountTool", async (input, apiKey) => {
|
|
32378
|
+
const result = await import_switch_account.switchAccountTool.execute({ ...input, apiKey }, {});
|
|
32379
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32380
|
+
});
|
|
32381
|
+
registerCutover("unlinkShareTool", async (input, apiKey) => {
|
|
32382
|
+
const result = await import_unlink_share.unlinkShareTool.execute({ ...input, apiKey }, {});
|
|
32383
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32384
|
+
});
|
|
32385
|
+
registerCutover("memoryQuestionsTool", async (input, apiKey) => {
|
|
32386
|
+
const result = await import_memory_questions.memoryQuestionsTool.execute({ ...input, apiKey }, {});
|
|
32387
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32388
|
+
});
|
|
32389
|
+
registerCutover("createChannelTool", async (input, apiKey) => {
|
|
32390
|
+
const result = await import_create_channel.createChannelTool.execute({ ...input, apiKey }, {});
|
|
32391
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32392
|
+
});
|
|
32393
|
+
registerCutover("listChannelMessagesTool", async (input, apiKey) => {
|
|
32394
|
+
const result = await import_list_channel_messages.listChannelMessagesTool.execute({ ...input, apiKey }, {});
|
|
32395
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32396
|
+
});
|
|
32397
|
+
registerCutover("pollChannelTool", async (input, apiKey) => {
|
|
32398
|
+
const result = await import_poll_channel.pollChannelTool.execute({ ...input, apiKey }, {});
|
|
32399
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32400
|
+
});
|
|
32401
|
+
registerCutover("postMessageTool", async (input, apiKey) => {
|
|
32402
|
+
const result = await import_post_message.postMessageTool.execute({ ...input, apiKey }, {});
|
|
32403
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32404
|
+
});
|
|
32405
|
+
registerCutover("reactMessageTool", async (input, apiKey) => {
|
|
32406
|
+
const result = await import_react_message.reactMessageTool.execute({ ...input, apiKey }, {});
|
|
32407
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32408
|
+
});
|
|
32409
|
+
registerCutover("removeChannelMemberTool", async (input, apiKey) => {
|
|
32410
|
+
const result = await import_remove_channel_member.removeChannelMemberTool.execute({ ...input, apiKey }, {});
|
|
32411
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32412
|
+
});
|
|
32413
|
+
registerCutover("replyMessageTool", async (input, apiKey) => {
|
|
32414
|
+
const result = await import_reply_message.replyMessageTool.execute({ ...input, apiKey }, {});
|
|
32415
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32416
|
+
});
|
|
32417
|
+
registerCutover("recordFactTool", async (input, apiKey) => {
|
|
32418
|
+
const result = await import_record_fact.recordFactTool.execute({ ...input, apiKey }, {});
|
|
32419
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32420
|
+
});
|
|
32421
|
+
registerCutover("libraryIngestTool", async (input, apiKey) => {
|
|
32422
|
+
const result = await import_library_ingest.libraryIngestTool.execute({ ...input, apiKey }, {});
|
|
32423
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32424
|
+
});
|
|
32425
|
+
registerCutover("deleteNoteTool", async (input, apiKey) => {
|
|
32426
|
+
const result = await import_delete_note.deleteNoteTool.execute({ ...input, apiKey }, {});
|
|
32427
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32428
|
+
});
|
|
32429
|
+
registerCutover("putTool", async (input, apiKey) => {
|
|
32430
|
+
const result = await import_put.putTool.execute({ ...input, apiKey }, {});
|
|
32431
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32432
|
+
});
|
|
32433
|
+
registerCutover("uploadTool", async (input, apiKey) => {
|
|
32434
|
+
const result = await import_upload.uploadTool.execute({ ...input, apiKey }, {});
|
|
32435
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32436
|
+
});
|
|
32437
|
+
registerCutover("deleteScheduledActionTool", async (input, apiKey) => {
|
|
32438
|
+
const result = await import_delete_scheduled_action.deleteScheduledActionTool.execute({ ...input, apiKey }, {});
|
|
32439
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32440
|
+
});
|
|
32441
|
+
registerCutover("pauseScheduledActionTool", async (input, apiKey) => {
|
|
32442
|
+
const result = await import_pause_scheduled_action.pauseScheduledActionTool.execute({ ...input, apiKey }, {});
|
|
32443
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32444
|
+
});
|
|
32445
|
+
registerCutover("resumeScheduledActionTool", async (input, apiKey) => {
|
|
32446
|
+
const result = await import_resume_scheduled_action.resumeScheduledActionTool.execute({ ...input, apiKey }, {});
|
|
32447
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32448
|
+
});
|
|
32449
|
+
registerCutover("createTableTool", async (input, apiKey) => {
|
|
32450
|
+
const result = await import_table_create.createTableTool.execute({ ...input, apiKey }, {});
|
|
32451
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32452
|
+
});
|
|
32453
|
+
registerCutover("deleteTableRowsTool", async (input, apiKey) => {
|
|
32454
|
+
const result = await import_table_delete_rows.deleteTableRowsTool.execute({ ...input, apiKey }, {});
|
|
32455
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32456
|
+
});
|
|
32457
|
+
registerCutover("dropTableTool", async (input, apiKey) => {
|
|
32458
|
+
const result = await import_table_drop.dropTableTool.execute({ ...input, apiKey }, {});
|
|
32459
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32460
|
+
});
|
|
32461
|
+
registerCutover("insertTableRowsTool", async (input, apiKey) => {
|
|
32462
|
+
const result = await import_table_insert_rows.insertTableRowsTool.execute({ ...input, apiKey }, {});
|
|
32463
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32464
|
+
});
|
|
32465
|
+
registerCutover("addVaultTool", async (input, apiKey) => {
|
|
32466
|
+
const result = await import_add_vault.addVaultTool.execute({ ...input, apiKey }, {});
|
|
32467
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32468
|
+
});
|
|
32469
|
+
registerCutover("deleteVaultTool", async (input, apiKey) => {
|
|
32470
|
+
const result = await import_delete_vault.deleteVaultTool.execute({ ...input, apiKey }, {});
|
|
32471
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32472
|
+
});
|
|
32473
|
+
}
|
|
32474
|
+
var import_accept_share, import_approve_sender, import_decline_share, import_inbox_settings, import_remove_approved_sender, import_revoke_share, import_set_agent_identity, import_share_note, import_share_vault, import_swap_vault, import_switch_account, import_unlink_share, import_memory_questions, import_create_channel, import_list_channel_messages, import_poll_channel, import_post_message, import_react_message, import_remove_channel_member, import_reply_message, import_record_fact, import_library_ingest, import_delete_note, import_put, import_upload, import_delete_scheduled_action, import_pause_scheduled_action, import_resume_scheduled_action, import_table_create, import_table_delete_rows, import_table_drop, import_table_insert_rows, import_add_vault, import_delete_vault, registered2;
|
|
32475
|
+
var init_writes = __esm({
|
|
32476
|
+
"src/mcp/memory-cutover/writes.ts"() {
|
|
32477
|
+
"use strict";
|
|
32478
|
+
init_memory_inprocess_executor();
|
|
32479
|
+
import_accept_share = require("mcpscraper-memory-tools/tools/access/accept-share");
|
|
32480
|
+
import_approve_sender = require("mcpscraper-memory-tools/tools/access/approve-sender");
|
|
32481
|
+
import_decline_share = require("mcpscraper-memory-tools/tools/access/decline-share");
|
|
32482
|
+
import_inbox_settings = require("mcpscraper-memory-tools/tools/access/inbox-settings");
|
|
32483
|
+
import_remove_approved_sender = require("mcpscraper-memory-tools/tools/access/remove-approved-sender");
|
|
32484
|
+
import_revoke_share = require("mcpscraper-memory-tools/tools/access/revoke-share");
|
|
32485
|
+
import_set_agent_identity = require("mcpscraper-memory-tools/tools/access/set-agent-identity");
|
|
32486
|
+
import_share_note = require("mcpscraper-memory-tools/tools/access/share-note");
|
|
32487
|
+
import_share_vault = require("mcpscraper-memory-tools/tools/access/share-vault");
|
|
32488
|
+
import_swap_vault = require("mcpscraper-memory-tools/tools/access/swap-vault");
|
|
32489
|
+
import_switch_account = require("mcpscraper-memory-tools/tools/access/switch-account");
|
|
32490
|
+
import_unlink_share = require("mcpscraper-memory-tools/tools/access/unlink-share");
|
|
32491
|
+
import_memory_questions = require("mcpscraper-memory-tools/tools/capture/memory-questions");
|
|
32492
|
+
import_create_channel = require("mcpscraper-memory-tools/tools/channels/create-channel");
|
|
32493
|
+
import_list_channel_messages = require("mcpscraper-memory-tools/tools/channels/list-channel-messages");
|
|
32494
|
+
import_poll_channel = require("mcpscraper-memory-tools/tools/channels/poll-channel");
|
|
32495
|
+
import_post_message = require("mcpscraper-memory-tools/tools/channels/post-message");
|
|
32496
|
+
import_react_message = require("mcpscraper-memory-tools/tools/channels/react-message");
|
|
32497
|
+
import_remove_channel_member = require("mcpscraper-memory-tools/tools/channels/remove-channel-member");
|
|
32498
|
+
import_reply_message = require("mcpscraper-memory-tools/tools/channels/reply-message");
|
|
32499
|
+
import_record_fact = require("mcpscraper-memory-tools/tools/facts/record-fact");
|
|
32500
|
+
import_library_ingest = require("mcpscraper-memory-tools/tools/library/library-ingest");
|
|
32501
|
+
import_delete_note = require("mcpscraper-memory-tools/tools/memory/delete-note");
|
|
32502
|
+
import_put = require("mcpscraper-memory-tools/tools/memory/put");
|
|
32503
|
+
import_upload = require("mcpscraper-memory-tools/tools/memory/upload");
|
|
32504
|
+
import_delete_scheduled_action = require("mcpscraper-memory-tools/tools/schedule/delete-scheduled-action");
|
|
32505
|
+
import_pause_scheduled_action = require("mcpscraper-memory-tools/tools/schedule/pause-scheduled-action");
|
|
32506
|
+
import_resume_scheduled_action = require("mcpscraper-memory-tools/tools/schedule/resume-scheduled-action");
|
|
32507
|
+
import_table_create = require("mcpscraper-memory-tools/tools/tables/table-create");
|
|
32508
|
+
import_table_delete_rows = require("mcpscraper-memory-tools/tools/tables/table-delete-rows");
|
|
32509
|
+
import_table_drop = require("mcpscraper-memory-tools/tools/tables/table-drop");
|
|
32510
|
+
import_table_insert_rows = require("mcpscraper-memory-tools/tools/tables/table-insert-rows");
|
|
32511
|
+
import_add_vault = require("mcpscraper-memory-tools/tools/vaults/add-vault");
|
|
32512
|
+
import_delete_vault = require("mcpscraper-memory-tools/tools/vaults/delete-vault");
|
|
32513
|
+
registered2 = false;
|
|
32514
|
+
}
|
|
32515
|
+
});
|
|
32516
|
+
|
|
32517
|
+
// src/mcp/memory-cutover/billing.ts
|
|
32518
|
+
function registerBillingCutovers() {
|
|
32519
|
+
if (registered3) return;
|
|
32520
|
+
registered3 = true;
|
|
32521
|
+
registerCutover("issueKeyTool", async (input, apiKey) => {
|
|
32522
|
+
const result = await import_issue_key.issueKeyTool.execute({ ...input, apiKey }, {});
|
|
32523
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32524
|
+
});
|
|
32525
|
+
registerCutover("listKeysTool", async (input, apiKey) => {
|
|
32526
|
+
const result = await import_list_keys.listKeysTool.execute({ ...input, apiKey }, {});
|
|
32527
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32528
|
+
});
|
|
32529
|
+
registerCutover("revokeKeyTool", async (input, apiKey) => {
|
|
32530
|
+
const result = await import_revoke_key.revokeKeyTool.execute({ ...input, apiKey }, {});
|
|
32531
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32532
|
+
});
|
|
32533
|
+
registerCutover("setScopeTool", async (input, apiKey) => {
|
|
32534
|
+
const result = await import_set_scope.setScopeTool.execute({ ...input, apiKey }, {});
|
|
32535
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32536
|
+
});
|
|
32537
|
+
registerCutover("inviteAccountTool", async (input, apiKey) => {
|
|
32538
|
+
const result = await import_invite_account.inviteAccountTool.execute({ ...input, apiKey }, {});
|
|
32539
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32540
|
+
});
|
|
32541
|
+
registerCutover("getChatLinkTool", async (input, apiKey) => {
|
|
32542
|
+
const result = await import_get_chat_link.getChatLinkTool.execute({ ...input, apiKey }, {});
|
|
32543
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32544
|
+
});
|
|
32545
|
+
registerCutover("revokeChatLinkTool", async (input, apiKey) => {
|
|
32546
|
+
const result = await import_revoke_chat_link.revokeChatLinkTool.execute({ ...input, apiKey }, {});
|
|
32547
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32548
|
+
});
|
|
32549
|
+
registerCutover("provisionDefaultsTool", async (input, apiKey) => {
|
|
32550
|
+
const result = await import_provision_defaults2.provisionDefaultsTool.execute({ ...input, apiKey }, {});
|
|
32551
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32552
|
+
});
|
|
32553
|
+
registerCutover("createSecureVaultTool", async (input, apiKey) => {
|
|
32554
|
+
const result = await import_create_secure_vault.createSecureVaultTool.execute({ ...input, apiKey }, {});
|
|
32555
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32556
|
+
});
|
|
32557
|
+
registerCutover("createScheduledActionTool", async (input, apiKey) => {
|
|
32558
|
+
const result = await import_create_scheduled_action.createScheduledActionTool.execute({ ...input, apiKey }, {});
|
|
32559
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32560
|
+
});
|
|
32561
|
+
registerCutover("getScheduleStatusTool", async (input, apiKey) => {
|
|
32562
|
+
const result = await import_get_schedule_status.getScheduleStatusTool.execute({ ...input, apiKey }, {});
|
|
32563
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32564
|
+
});
|
|
32565
|
+
registerCutover("getScheduleLinkTool", async (input, apiKey) => {
|
|
32566
|
+
const result = await import_get_schedule_link.getScheduleLinkTool.execute({ ...input, apiKey }, {});
|
|
32567
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32568
|
+
});
|
|
32569
|
+
registerCutover("revokeScheduleLinkTool", async (input, apiKey) => {
|
|
32570
|
+
const result = await import_revoke_schedule_link.revokeScheduleLinkTool.execute({ ...input, apiKey }, {});
|
|
32571
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32572
|
+
});
|
|
32573
|
+
registerCutover("setScheduleEntitlementTool", async (input, apiKey) => {
|
|
32574
|
+
const result = await import_set_schedule_entitlement2.setScheduleEntitlementTool.execute({ ...input, apiKey }, {});
|
|
32575
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32576
|
+
});
|
|
32577
|
+
registerCutover("createWebhookTool", async (input, apiKey) => {
|
|
32578
|
+
const result = await import_create_webhook.createWebhookTool.execute({ ...input, apiKey }, {});
|
|
32579
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32580
|
+
});
|
|
32581
|
+
registerCutover("revokeWebhookTool", async (input, apiKey) => {
|
|
32582
|
+
const result = await import_revoke_webhook.revokeWebhookTool.execute({ ...input, apiKey }, {});
|
|
32583
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32584
|
+
});
|
|
32585
|
+
}
|
|
32586
|
+
var import_issue_key, import_list_keys, import_revoke_key, import_set_scope, import_invite_account, import_get_chat_link, import_revoke_chat_link, import_provision_defaults2, import_create_secure_vault, import_create_scheduled_action, import_get_schedule_status, import_get_schedule_link, import_revoke_schedule_link, import_set_schedule_entitlement2, import_create_webhook, import_revoke_webhook, registered3;
|
|
32587
|
+
var init_billing = __esm({
|
|
32588
|
+
"src/mcp/memory-cutover/billing.ts"() {
|
|
32589
|
+
"use strict";
|
|
32590
|
+
init_memory_inprocess_executor();
|
|
32591
|
+
import_issue_key = require("mcpscraper-memory-tools/tools/access/issue-key");
|
|
32592
|
+
import_list_keys = require("mcpscraper-memory-tools/tools/access/list-keys");
|
|
32593
|
+
import_revoke_key = require("mcpscraper-memory-tools/tools/access/revoke-key");
|
|
32594
|
+
import_set_scope = require("mcpscraper-memory-tools/tools/access/set-scope");
|
|
32595
|
+
import_invite_account = require("mcpscraper-memory-tools/tools/access/invite-account");
|
|
32596
|
+
import_get_chat_link = require("mcpscraper-memory-tools/tools/access/get-chat-link");
|
|
32597
|
+
import_revoke_chat_link = require("mcpscraper-memory-tools/tools/access/revoke-chat-link");
|
|
32598
|
+
import_provision_defaults2 = require("mcpscraper-memory-tools/tools/vaults/provision-defaults");
|
|
32599
|
+
import_create_secure_vault = require("mcpscraper-memory-tools/tools/vaults/create-secure-vault");
|
|
32600
|
+
import_create_scheduled_action = require("mcpscraper-memory-tools/tools/schedule/create-scheduled-action");
|
|
32601
|
+
import_get_schedule_status = require("mcpscraper-memory-tools/tools/schedule/get-schedule-status");
|
|
32602
|
+
import_get_schedule_link = require("mcpscraper-memory-tools/tools/schedule/get-schedule-link");
|
|
32603
|
+
import_revoke_schedule_link = require("mcpscraper-memory-tools/tools/schedule/revoke-schedule-link");
|
|
32604
|
+
import_set_schedule_entitlement2 = require("mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement");
|
|
32605
|
+
import_create_webhook = require("mcpscraper-memory-tools/tools/webhooks/create-webhook");
|
|
32606
|
+
import_revoke_webhook = require("mcpscraper-memory-tools/tools/webhooks/revoke-webhook");
|
|
32607
|
+
registered3 = false;
|
|
32608
|
+
}
|
|
32609
|
+
});
|
|
32610
|
+
|
|
32611
|
+
// src/mcp/memory-cutover/cross-service.ts
|
|
32612
|
+
function registerCrossServiceCutovers() {
|
|
32613
|
+
if (registered4) return;
|
|
32614
|
+
registered4 = true;
|
|
32615
|
+
registerCutover("proposeScheduledActionTool", async (input, apiKey) => {
|
|
32616
|
+
const result = await import_propose_scheduled_action.proposeScheduledActionTool.execute({ ...input, apiKey }, {});
|
|
32617
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32618
|
+
});
|
|
32619
|
+
}
|
|
32620
|
+
var import_propose_scheduled_action, registered4;
|
|
32621
|
+
var init_cross_service = __esm({
|
|
32622
|
+
"src/mcp/memory-cutover/cross-service.ts"() {
|
|
32623
|
+
"use strict";
|
|
32624
|
+
init_memory_inprocess_executor();
|
|
32625
|
+
import_propose_scheduled_action = require("mcpscraper-memory-tools/tools/schedule/propose-scheduled-action");
|
|
32626
|
+
registered4 = false;
|
|
32627
|
+
}
|
|
32628
|
+
});
|
|
32629
|
+
|
|
32630
|
+
// src/mcp/memory-cutover/video.ts
|
|
32631
|
+
function registerVideoCutovers() {
|
|
32632
|
+
if (registered5) return;
|
|
32633
|
+
registered5 = true;
|
|
32634
|
+
registerCutover("videoAnalyzeStartTool", async (input, apiKey) => {
|
|
32635
|
+
const result = await import_start.videoAnalyzeStartTool.execute({ ...input, apiKey }, {});
|
|
32636
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? {}) }], structuredContent: result ?? {}, isError: false };
|
|
32637
|
+
});
|
|
32638
|
+
}
|
|
32639
|
+
var import_start, registered5;
|
|
32640
|
+
var init_video = __esm({
|
|
32641
|
+
"src/mcp/memory-cutover/video.ts"() {
|
|
32642
|
+
"use strict";
|
|
32643
|
+
init_memory_inprocess_executor();
|
|
32644
|
+
import_start = require("mcpscraper-memory-tools/tools/video/start");
|
|
32645
|
+
registered5 = false;
|
|
32646
|
+
}
|
|
32647
|
+
});
|
|
32648
|
+
|
|
31943
32649
|
// src/mcp/mcp-oauth.ts
|
|
31944
32650
|
function oauthIssuer() {
|
|
31945
32651
|
return (process.env.OAUTH_ISSUER ?? "https://mcpscraper.dev").replace(/\/$/, "");
|
|
@@ -32018,13 +32724,13 @@ async function requireMcpCallerKey(c) {
|
|
|
32018
32724
|
if (!identity) return mcpAuthError("Bearer token has no subject");
|
|
32019
32725
|
const user2 = await getUserByEmail(identity);
|
|
32020
32726
|
if (!user2 || !user2.api_key || user2.key_active !== 1) return mcpAuthError("No active MCP Scraper account for this identity");
|
|
32021
|
-
return user2.api_key;
|
|
32727
|
+
return { key: user2.api_key, user: user2 };
|
|
32022
32728
|
}
|
|
32023
32729
|
const callerKey = xApiKey ?? bearerToken;
|
|
32024
32730
|
if (!callerKey) return mcpAuthError();
|
|
32025
32731
|
const user = await getUserByApiKey(callerKey);
|
|
32026
32732
|
if (!user) return mcpAuthError();
|
|
32027
|
-
return callerKey;
|
|
32733
|
+
return { key: callerKey, user };
|
|
32028
32734
|
}
|
|
32029
32735
|
var import_hono14, import_webStandardStreamableHttp, mcpApp;
|
|
32030
32736
|
var init_mcp_routes = __esm({
|
|
@@ -32037,16 +32743,27 @@ var init_mcp_routes = __esm({
|
|
|
32037
32743
|
init_browser_agent_mcp_server();
|
|
32038
32744
|
init_memory_mcp_server();
|
|
32039
32745
|
init_memory_mcp_tool_executor();
|
|
32746
|
+
init_memory_inprocess_executor();
|
|
32747
|
+
init_reads();
|
|
32748
|
+
init_writes();
|
|
32749
|
+
init_billing();
|
|
32750
|
+
init_cross_service();
|
|
32751
|
+
init_video();
|
|
32040
32752
|
init_mcp_response_formatter();
|
|
32041
32753
|
init_db();
|
|
32042
32754
|
init_mcp_oauth();
|
|
32755
|
+
registerReadCutovers();
|
|
32756
|
+
registerWriteCutovers();
|
|
32757
|
+
registerBillingCutovers();
|
|
32758
|
+
registerCrossServiceCutovers();
|
|
32759
|
+
registerVideoCutovers();
|
|
32043
32760
|
configureReportSaving(false);
|
|
32044
32761
|
mcpApp = new import_hono14.Hono();
|
|
32045
32762
|
mcpApp.all("/", async (c) => {
|
|
32046
32763
|
try {
|
|
32047
32764
|
const keyOrError = await requireMcpCallerKey(c);
|
|
32048
32765
|
if (keyOrError instanceof Response) return keyOrError;
|
|
32049
|
-
const callerKey = keyOrError;
|
|
32766
|
+
const { key: callerKey, user } = keyOrError;
|
|
32050
32767
|
const baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() ?? process.env.MCP_BASE_URL?.trim() ?? "https://mcpscraper.dev";
|
|
32051
32768
|
const executor = new HttpMcpToolExecutor(baseUrl, callerKey);
|
|
32052
32769
|
const transport = new import_webStandardStreamableHttp.WebStandardStreamableHTTPServerTransport({
|
|
@@ -32057,7 +32774,8 @@ var init_mcp_routes = __esm({
|
|
|
32057
32774
|
const server = buildPaaExtractorMcpServer(executor, { savesReportsLocally: false, ownerId: hashOwnerId(callerKey) });
|
|
32058
32775
|
registerSerpIntelligenceCaptureTools(server, executor);
|
|
32059
32776
|
registerBrowserAgentMcpTools(server, { baseUrl, apiKey: callerKey, consoleBaseUrl, savesReportsLocally: false });
|
|
32060
|
-
|
|
32777
|
+
const memoryExecutor = new HybridMemoryToolExecutor(new MemoryMcpToolExecutor(baseUrl, callerKey), user);
|
|
32778
|
+
registerMemoryMcpTools(server, memoryExecutor);
|
|
32061
32779
|
await server.connect(transport);
|
|
32062
32780
|
return transport.handleRequest(c.req.raw);
|
|
32063
32781
|
} catch {
|
|
@@ -32142,23 +32860,34 @@ async function migrateBrowserAgent() {
|
|
|
32142
32860
|
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN updated_at TEXT`);
|
|
32143
32861
|
} catch {
|
|
32144
32862
|
}
|
|
32863
|
+
try {
|
|
32864
|
+
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN status TEXT NOT NULL DEFAULT 'NEEDS_AUTH'`);
|
|
32865
|
+
} catch {
|
|
32866
|
+
}
|
|
32867
|
+
try {
|
|
32868
|
+
await db.execute(`ALTER TABLE browser_auth_connections ADD COLUMN browser_agent_session_id TEXT`);
|
|
32869
|
+
} catch {
|
|
32870
|
+
}
|
|
32145
32871
|
await db.execute(`CREATE INDEX IF NOT EXISTS browser_auth_connections_profile ON browser_auth_connections(profile)`);
|
|
32146
32872
|
_ready2 = true;
|
|
32147
32873
|
}
|
|
32148
|
-
async function
|
|
32149
|
-
|
|
32874
|
+
async function createAuthConnectionRow(input) {
|
|
32875
|
+
const db = getDb();
|
|
32876
|
+
const connectionId = `authc_${(0, import_node_crypto11.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
32877
|
+
await db.execute({
|
|
32878
|
+
sql: `INSERT INTO browser_auth_connections (connection_id, domain, profile, account_email, note, status, browser_agent_session_id)
|
|
32879
|
+
VALUES (?, ?, ?, ?, ?, 'NEEDS_AUTH', ?)`,
|
|
32880
|
+
args: [connectionId, input.domain, input.profile, input.accountEmail, input.note, input.browserAgentSessionId]
|
|
32881
|
+
});
|
|
32882
|
+
const row = await getAuthConnectionRow(connectionId);
|
|
32883
|
+
if (!row) throw new Error("auth connection insert failed");
|
|
32884
|
+
return row;
|
|
32885
|
+
}
|
|
32886
|
+
async function markAuthConnectionAuthenticated(connectionId) {
|
|
32150
32887
|
const db = getDb();
|
|
32151
32888
|
await db.execute({
|
|
32152
|
-
sql: `
|
|
32153
|
-
|
|
32154
|
-
ON CONFLICT(connection_id) DO UPDATE SET
|
|
32155
|
-
hosted_url = COALESCE(excluded.hosted_url, browser_auth_connections.hosted_url),
|
|
32156
|
-
domain = COALESCE(excluded.domain, browser_auth_connections.domain),
|
|
32157
|
-
profile = COALESCE(excluded.profile, browser_auth_connections.profile),
|
|
32158
|
-
account_email = COALESCE(excluded.account_email, browser_auth_connections.account_email),
|
|
32159
|
-
note = COALESCE(excluded.note, browser_auth_connections.note),
|
|
32160
|
-
updated_at = datetime('now')`,
|
|
32161
|
-
args: [input.connectionId, input.hostedUrl, input.domain, input.profile, input.accountEmail ?? null, input.note ?? null]
|
|
32889
|
+
sql: `UPDATE browser_auth_connections SET status = 'AUTHENTICATED', updated_at = datetime('now') WHERE connection_id = ?`,
|
|
32890
|
+
args: [connectionId]
|
|
32162
32891
|
});
|
|
32163
32892
|
}
|
|
32164
32893
|
async function getAuthConnectionRow(connectionId) {
|
|
@@ -33065,79 +33794,10 @@ async function ensureProfile(k, name) {
|
|
|
33065
33794
|
if (!isProfileConflict(err)) throw err;
|
|
33066
33795
|
}
|
|
33067
33796
|
}
|
|
33068
|
-
function profileConnectionMatches(connection, profileName, domain) {
|
|
33069
|
-
if (!connection || typeof connection !== "object") return false;
|
|
33070
|
-
const data = connection;
|
|
33071
|
-
return data.profile_name === profileName && data.domain === domain;
|
|
33072
|
-
}
|
|
33073
|
-
async function findProfileConnection(k, profileName, domain) {
|
|
33074
|
-
for await (const connection of k.auth.connections.list({ profile_name: profileName, domain })) {
|
|
33075
|
-
if (profileConnectionMatches(connection, profileName, domain)) return connection;
|
|
33076
|
-
}
|
|
33077
|
-
return null;
|
|
33078
|
-
}
|
|
33079
|
-
async function listProfileConnections(profileName) {
|
|
33080
|
-
if (!profileName) return [];
|
|
33081
|
-
const k = client();
|
|
33082
|
-
const out = [];
|
|
33083
|
-
for await (const connection of k.auth.connections.list({ profile_name: profileName })) {
|
|
33084
|
-
const data = connection;
|
|
33085
|
-
if (data.profile_name && data.profile_name !== profileName) continue;
|
|
33086
|
-
out.push(profileOnboardResult(connection));
|
|
33087
|
-
}
|
|
33088
|
-
return out;
|
|
33089
|
-
}
|
|
33090
|
-
async function findOrCreateProfileConnection(k, opts) {
|
|
33091
|
-
try {
|
|
33092
|
-
return await k.auth.connections.create({
|
|
33093
|
-
domain: opts.domain,
|
|
33094
|
-
profile_name: opts.profileName,
|
|
33095
|
-
...opts.loginUrl ? { login_url: opts.loginUrl } : {}
|
|
33096
|
-
});
|
|
33097
|
-
} catch (err) {
|
|
33098
|
-
if (!isProfileConflict(err)) throw err;
|
|
33099
|
-
const existing = await findProfileConnection(k, opts.profileName, opts.domain);
|
|
33100
|
-
if (existing) return existing;
|
|
33101
|
-
throw err;
|
|
33102
|
-
}
|
|
33103
|
-
}
|
|
33104
|
-
function profileOnboardResult(connection, hostedUrl = null, liveViewUrl = null) {
|
|
33105
|
-
return {
|
|
33106
|
-
connectionId: String(connection.id),
|
|
33107
|
-
profileName: String(connection.profile_name),
|
|
33108
|
-
domain: String(connection.domain),
|
|
33109
|
-
status: String(connection.status),
|
|
33110
|
-
flowStatus: typeof connection.flow_status === "string" ? connection.flow_status : null,
|
|
33111
|
-
flowStep: typeof connection.flow_step === "string" ? connection.flow_step : null,
|
|
33112
|
-
flowExpiresAt: typeof connection.flow_expires_at === "string" ? connection.flow_expires_at : null,
|
|
33113
|
-
hostedUrl: hostedUrl ?? (typeof connection.hosted_url === "string" ? connection.hosted_url : null),
|
|
33114
|
-
liveViewUrl: liveViewUrl ?? (typeof connection.live_view_url === "string" ? connection.live_view_url : null),
|
|
33115
|
-
browserSessionId: typeof connection.browser_session_id === "string" ? connection.browser_session_id : null,
|
|
33116
|
-
postLoginUrl: typeof connection.post_login_url === "string" ? connection.post_login_url : null
|
|
33117
|
-
};
|
|
33118
|
-
}
|
|
33119
33797
|
function explicitProxyId(value) {
|
|
33120
33798
|
const trimmed = value?.trim();
|
|
33121
33799
|
return trimmed || void 0;
|
|
33122
33800
|
}
|
|
33123
|
-
async function startProfileOnboarding(opts) {
|
|
33124
|
-
const k = client();
|
|
33125
|
-
const connection = await findOrCreateProfileConnection(k, opts);
|
|
33126
|
-
const login = await k.auth.connections.login(connection.id);
|
|
33127
|
-
const state = await k.auth.connections.retrieve(connection.id);
|
|
33128
|
-
return profileOnboardResult(state, login.hosted_url ?? null, login.live_view_url ?? null);
|
|
33129
|
-
}
|
|
33130
|
-
async function getProfileOnboardingStatus(input) {
|
|
33131
|
-
const k = client();
|
|
33132
|
-
let connection = null;
|
|
33133
|
-
if (input.connectionId) {
|
|
33134
|
-
connection = await k.auth.connections.retrieve(input.connectionId);
|
|
33135
|
-
} else if (input.profileName && input.domain) {
|
|
33136
|
-
connection = await findProfileConnection(k, input.profileName, input.domain);
|
|
33137
|
-
}
|
|
33138
|
-
if (!connection) throw new Error("profile auth connection not found");
|
|
33139
|
-
return profileOnboardResult(connection);
|
|
33140
|
-
}
|
|
33141
33801
|
async function createSession(opts = {}) {
|
|
33142
33802
|
const k = client();
|
|
33143
33803
|
const resolvedProxyId = explicitProxyId(opts.proxyId);
|
|
@@ -33488,24 +34148,39 @@ async function charge(sessionId, userId, _startedAtMs) {
|
|
|
33488
34148
|
if (res.ok) await setBilledMc(sessionId, owed);
|
|
33489
34149
|
}
|
|
33490
34150
|
}
|
|
34151
|
+
async function closeAndSettleSession(row) {
|
|
34152
|
+
if (row.user_id != null) {
|
|
34153
|
+
try {
|
|
34154
|
+
await charge(row.id, row.user_id);
|
|
34155
|
+
} catch {
|
|
34156
|
+
}
|
|
34157
|
+
}
|
|
34158
|
+
try {
|
|
34159
|
+
await closeSession(row.runtime_session_id);
|
|
34160
|
+
} catch {
|
|
34161
|
+
}
|
|
34162
|
+
await markSessionClosed(row.id).catch(() => {
|
|
34163
|
+
});
|
|
34164
|
+
await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
34165
|
+
});
|
|
34166
|
+
}
|
|
34167
|
+
async function completeAuthConnection(connectionId) {
|
|
34168
|
+
const connection = await getAuthConnectionRow(connectionId);
|
|
34169
|
+
if (!connection) return { ok: false, error: "not found" };
|
|
34170
|
+
if (connection.browser_agent_session_id) {
|
|
34171
|
+
const sessionRow = await getSessionRow(connection.browser_agent_session_id);
|
|
34172
|
+
if (sessionRow && sessionRow.status === "open") {
|
|
34173
|
+
await closeAndSettleSession(sessionRow);
|
|
34174
|
+
}
|
|
34175
|
+
}
|
|
34176
|
+
await markAuthConnectionAuthenticated(connectionId);
|
|
34177
|
+
return { ok: true };
|
|
34178
|
+
}
|
|
33491
34179
|
async function reapIdleBrowserSessions(idleSeconds = 120) {
|
|
33492
34180
|
const rows = await listIdleOpenSessionRows(idleSeconds);
|
|
33493
34181
|
let reaped = 0;
|
|
33494
34182
|
for (const row of rows) {
|
|
33495
|
-
|
|
33496
|
-
try {
|
|
33497
|
-
await charge(row.id, row.user_id);
|
|
33498
|
-
} catch {
|
|
33499
|
-
}
|
|
33500
|
-
}
|
|
33501
|
-
try {
|
|
33502
|
-
await closeSession(row.runtime_session_id);
|
|
33503
|
-
} catch {
|
|
33504
|
-
}
|
|
33505
|
-
await markSessionClosed(row.id).catch(() => {
|
|
33506
|
-
});
|
|
33507
|
-
if (row.concurrency_lock_id) await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
33508
|
-
});
|
|
34183
|
+
await closeAndSettleSession(row);
|
|
33509
34184
|
reaped++;
|
|
33510
34185
|
}
|
|
33511
34186
|
return { reaped };
|
|
@@ -33573,74 +34248,87 @@ function buildBrowserAgentRoutes() {
|
|
|
33573
34248
|
});
|
|
33574
34249
|
app2.use("*", auth);
|
|
33575
34250
|
app2.post("/profiles/onboard", async (c) => {
|
|
34251
|
+
const user = c.get("user");
|
|
34252
|
+
if (Number(user.balance_mc ?? 0) < BROWSER_OPEN_MIN_BALANCE_MC) {
|
|
34253
|
+
return c.json(insufficientBalanceResponse(Number(user.balance_mc ?? 0), BROWSER_OPEN_MIN_BALANCE_MC), 402);
|
|
34254
|
+
}
|
|
33576
34255
|
const body = await c.req.json().catch(() => ({}));
|
|
33577
34256
|
const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
|
|
33578
34257
|
const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : "chatgpt.com";
|
|
33579
34258
|
const loginUrl = typeof body.login_url === "string" ? body.login_url.trim() : void 0;
|
|
33580
34259
|
const accountEmail = typeof body.account_email === "string" ? body.account_email.trim() || null : null;
|
|
33581
34260
|
const note = typeof body.note === "string" ? body.note.trim() || null : null;
|
|
34261
|
+
const timeoutSeconds = typeof body.timeout_seconds === "number" ? body.timeout_seconds : void 0;
|
|
33582
34262
|
if (!profileName) return c.json({ error: "profile is required" }, 400);
|
|
33583
34263
|
if (!domain) return c.json({ error: "domain is required" }, 400);
|
|
34264
|
+
const gate = await acquireConcurrencyGate(user, "browser_agent_session", {
|
|
34265
|
+
ttlSeconds: browserSessionLockTtlSeconds(timeoutSeconds),
|
|
34266
|
+
metadata: { label: typeof body.label === "string" ? body.label : null }
|
|
34267
|
+
});
|
|
34268
|
+
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
34269
|
+
let runtimeSessionId = null;
|
|
33584
34270
|
try {
|
|
33585
|
-
const
|
|
33586
|
-
|
|
33587
|
-
|
|
33588
|
-
|
|
33589
|
-
|
|
33590
|
-
|
|
33591
|
-
|
|
33592
|
-
|
|
33593
|
-
|
|
33594
|
-
|
|
33595
|
-
|
|
33596
|
-
|
|
34271
|
+
const created = await createSession({
|
|
34272
|
+
timeoutSeconds,
|
|
34273
|
+
profileName,
|
|
34274
|
+
saveProfileChanges: true,
|
|
34275
|
+
startUrl: loginUrl || `https://${domain}/`,
|
|
34276
|
+
disableDefaultProxy: true
|
|
34277
|
+
});
|
|
34278
|
+
runtimeSessionId = created.runtimeSessionId;
|
|
34279
|
+
const sessionRow = await createSessionRow({
|
|
34280
|
+
runtimeSessionId: created.runtimeSessionId,
|
|
34281
|
+
liveViewUrl: created.liveViewUrl,
|
|
34282
|
+
cdpWsUrl: created.cdpWsUrl,
|
|
34283
|
+
label: typeof body.label === "string" ? body.label : `Login setup: ${domain} \u2192 ${profileName}`,
|
|
34284
|
+
userId: user.id,
|
|
34285
|
+
concurrencyLockId: gate.lockId
|
|
34286
|
+
});
|
|
34287
|
+
const connection = await createAuthConnectionRow({
|
|
34288
|
+
domain,
|
|
34289
|
+
profile: profileName,
|
|
34290
|
+
accountEmail,
|
|
34291
|
+
note,
|
|
34292
|
+
browserAgentSessionId: sessionRow.id
|
|
34293
|
+
});
|
|
33597
34294
|
return c.json({
|
|
33598
|
-
connection_id:
|
|
33599
|
-
profile:
|
|
33600
|
-
domain
|
|
33601
|
-
status:
|
|
33602
|
-
|
|
33603
|
-
flow_step: result.flowStep,
|
|
33604
|
-
flow_expires_at: result.flowExpiresAt,
|
|
33605
|
-
browser_session_id: result.browserSessionId,
|
|
33606
|
-
post_login_url: result.postLoginUrl
|
|
34295
|
+
connection_id: connection.connection_id,
|
|
34296
|
+
profile: profileName,
|
|
34297
|
+
domain,
|
|
34298
|
+
status: connection.status,
|
|
34299
|
+
browser_session_id: sessionRow.id
|
|
33607
34300
|
});
|
|
33608
34301
|
} catch (err) {
|
|
34302
|
+
if (runtimeSessionId) {
|
|
34303
|
+
try {
|
|
34304
|
+
await closeSession(runtimeSessionId);
|
|
34305
|
+
} catch {
|
|
34306
|
+
}
|
|
34307
|
+
}
|
|
34308
|
+
await releaseConcurrencyGate(gate.lockId);
|
|
33609
34309
|
return c.json(failure(err), 502);
|
|
33610
34310
|
}
|
|
33611
34311
|
});
|
|
33612
34312
|
app2.post("/profiles/status", async (c) => {
|
|
33613
34313
|
const body = await c.req.json().catch(() => ({}));
|
|
33614
|
-
const connectionId = typeof body.connection_id === "string" ? body.connection_id.trim() :
|
|
33615
|
-
const profileName = typeof body.profile === "string" ? body.profile.trim() :
|
|
34314
|
+
const connectionId = typeof body.connection_id === "string" ? body.connection_id.trim() : "";
|
|
34315
|
+
const profileName = typeof body.profile === "string" ? body.profile.trim() : "";
|
|
33616
34316
|
const domain = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
|
|
33617
|
-
|
|
33618
|
-
|
|
33619
|
-
|
|
33620
|
-
|
|
33621
|
-
|
|
33622
|
-
|
|
33623
|
-
|
|
33624
|
-
|
|
33625
|
-
|
|
33626
|
-
|
|
33627
|
-
|
|
33628
|
-
|
|
33629
|
-
|
|
33630
|
-
|
|
33631
|
-
|
|
33632
|
-
profile: result.profileName,
|
|
33633
|
-
domain: result.domain,
|
|
33634
|
-
status: result.status,
|
|
33635
|
-
flow_status: result.flowStatus,
|
|
33636
|
-
flow_step: result.flowStep,
|
|
33637
|
-
flow_expires_at: result.flowExpiresAt,
|
|
33638
|
-
browser_session_id: result.browserSessionId,
|
|
33639
|
-
post_login_url: result.postLoginUrl
|
|
33640
|
-
});
|
|
33641
|
-
} catch (err) {
|
|
33642
|
-
return c.json(failure(err), 404);
|
|
33643
|
-
}
|
|
34317
|
+
let row = null;
|
|
34318
|
+
if (connectionId) {
|
|
34319
|
+
row = await getAuthConnectionRow(connectionId);
|
|
34320
|
+
} else if (profileName && domain) {
|
|
34321
|
+
const rows = await listAuthConnectionsByProfile(profileName);
|
|
34322
|
+
row = rows.find((r) => r.domain === domain) ?? null;
|
|
34323
|
+
}
|
|
34324
|
+
if (!row) return c.json({ error: "auth connection not found" }, 404);
|
|
34325
|
+
return c.json({
|
|
34326
|
+
connection_id: row.connection_id,
|
|
34327
|
+
profile: row.profile,
|
|
34328
|
+
domain: row.domain,
|
|
34329
|
+
status: row.status,
|
|
34330
|
+
browser_session_id: row.browser_agent_session_id
|
|
34331
|
+
});
|
|
33644
34332
|
});
|
|
33645
34333
|
app2.post("/profiles/list", async (c) => {
|
|
33646
34334
|
const body = await c.req.json().catch(() => ({}));
|
|
@@ -33649,23 +34337,15 @@ function buildBrowserAgentRoutes() {
|
|
|
33649
34337
|
const domainFilter = typeof body.domain === "string" ? body.domain.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "") : void 0;
|
|
33650
34338
|
const connectionIdFilter = typeof body.connection_id === "string" ? body.connection_id.trim() : void 0;
|
|
33651
34339
|
try {
|
|
33652
|
-
const
|
|
33653
|
-
|
|
33654
|
-
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
|
|
33658
|
-
|
|
33659
|
-
|
|
33660
|
-
|
|
33661
|
-
connection_id: conn.connectionId,
|
|
33662
|
-
domain: conn.domain,
|
|
33663
|
-
status: conn.status,
|
|
33664
|
-
account_email: record?.account_email ?? null,
|
|
33665
|
-
note: record?.note ?? null,
|
|
33666
|
-
last_connected_at: record?.updated_at ?? record?.created_at ?? null
|
|
33667
|
-
};
|
|
33668
|
-
});
|
|
34340
|
+
const stored = await listAuthConnectionsByProfile(profileName);
|
|
34341
|
+
let connections = stored.map((row) => ({
|
|
34342
|
+
connection_id: row.connection_id,
|
|
34343
|
+
domain: row.domain,
|
|
34344
|
+
status: row.status,
|
|
34345
|
+
account_email: row.account_email,
|
|
34346
|
+
note: row.note,
|
|
34347
|
+
last_connected_at: row.updated_at ?? row.created_at
|
|
34348
|
+
}));
|
|
33669
34349
|
if (connectionIdFilter) connections = connections.filter((c2) => c2.connection_id === connectionIdFilter);
|
|
33670
34350
|
if (domainFilter) connections = connections.filter((c2) => c2.domain === domainFilter);
|
|
33671
34351
|
return c.json({ profile: profileName, connections });
|
|
@@ -33740,13 +34420,7 @@ function buildBrowserAgentRoutes() {
|
|
|
33740
34420
|
const user = c.get("user");
|
|
33741
34421
|
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
33742
34422
|
if (!row) return c.json({ error: "not found" }, 404);
|
|
33743
|
-
await
|
|
33744
|
-
try {
|
|
33745
|
-
await closeSession(row.runtime_session_id);
|
|
33746
|
-
} catch {
|
|
33747
|
-
}
|
|
33748
|
-
await markSessionClosed(row.id);
|
|
33749
|
-
await releaseConcurrencyGate(row.concurrency_lock_id);
|
|
34423
|
+
await closeAndSettleSession(row);
|
|
33750
34424
|
return c.json({ ok: true });
|
|
33751
34425
|
});
|
|
33752
34426
|
app2.post("/sessions/:id/goto", async (c) => {
|
|
@@ -34046,17 +34720,30 @@ var init_browser_agent_routes = __esm({
|
|
|
34046
34720
|
});
|
|
34047
34721
|
|
|
34048
34722
|
// src/api/browser-agent-console.ts
|
|
34049
|
-
function renderAuthConsoleHtml(connectionId,
|
|
34050
|
-
const
|
|
34051
|
-
const
|
|
34052
|
-
const
|
|
34053
|
-
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34723
|
+
function renderAuthConsoleHtml(connectionId, info) {
|
|
34724
|
+
const esc2 = (s) => String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
34725
|
+
const safeId = esc2(connectionId);
|
|
34726
|
+
const domainLabel = info.domain ? esc2(info.domain) : "this site";
|
|
34727
|
+
let body;
|
|
34728
|
+
if (info.status === "AUTHENTICATED") {
|
|
34729
|
+
body = `<div class="empty">
|
|
34730
|
+
<div class="check">\u2713</div>
|
|
34731
|
+
<div>You're signed in to ${domainLabel}.</div>
|
|
34732
|
+
<div class="muted">This login is saved. You can close this tab.</div>
|
|
34733
|
+
</div>`;
|
|
34734
|
+
} else if (info.sessionOpen && info.liveViewUrl) {
|
|
34735
|
+
body = `<iframe class="stage" allow="clipboard-read; clipboard-write" src="${esc2(info.liveViewUrl)}"></iframe>
|
|
34736
|
+
<div class="footer">
|
|
34737
|
+
<span class="muted">Log into ${domainLabel} directly in the window above, then confirm below.</span>
|
|
34738
|
+
<span class="spacer"></span>
|
|
34739
|
+
<button class="btn" id="done">Done \u2014 I'm signed in</button>
|
|
34740
|
+
</div>`;
|
|
34741
|
+
} else {
|
|
34742
|
+
body = `<div class="empty">
|
|
34057
34743
|
<div>This sign-in link is no longer active.</div>
|
|
34058
34744
|
<div class="muted">Ask the assistant to reconnect the profile, then open the new link.</div>
|
|
34059
34745
|
</div>`;
|
|
34746
|
+
}
|
|
34060
34747
|
return `<!DOCTYPE html>
|
|
34061
34748
|
<html lang="en">
|
|
34062
34749
|
<head>
|
|
@@ -34072,9 +34759,11 @@ function renderAuthConsoleHtml(connectionId, hostedUrl) {
|
|
|
34072
34759
|
header .muted { color: #6b7689; font-size: 12px; }
|
|
34073
34760
|
.spacer { flex: 1; }
|
|
34074
34761
|
.stage { flex: 1; width: 100%; border: 0; display: block; background: #05070b; }
|
|
34075
|
-
.
|
|
34076
|
-
.btn { background: #2b6cff; color: #fff; border-radius: 7px; padding:
|
|
34762
|
+
.footer { display: flex; align-items: center; gap: 12px; padding: 10px 18px; border-top: 1px solid #1c2230; background: #0f131c; font-size: 13px; color: #8893a7; }
|
|
34763
|
+
.btn { background: #2b6cff; color: #fff; border: 0; border-radius: 7px; padding: 8px 14px; text-decoration: none; font-weight: 500; font: inherit; cursor: pointer; }
|
|
34764
|
+
.btn:disabled { opacity: .6; cursor: default; }
|
|
34077
34765
|
.empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #8893a7; }
|
|
34766
|
+
.check { font-size: 32px; color: #36d399; }
|
|
34078
34767
|
.muted { color: #6b7689; font-size: 12px; }
|
|
34079
34768
|
</style>
|
|
34080
34769
|
${ATTRIBUTION_PIXEL_SCRIPT}
|
|
@@ -34084,9 +34773,23 @@ ${ATTRIBUTION_PIXEL_SCRIPT}
|
|
|
34084
34773
|
<h1>MCP Scraper</h1>
|
|
34085
34774
|
<span class="muted">Secure sign-in \xB7 ${safeId}</span>
|
|
34086
34775
|
<span class="spacer"></span>
|
|
34087
|
-
<span class="muted">
|
|
34776
|
+
<span class="muted">Log in directly to the real site above \u2014 your session is saved to your profile.</span>
|
|
34088
34777
|
</header>
|
|
34089
34778
|
${body}
|
|
34779
|
+
<script>
|
|
34780
|
+
(function(){
|
|
34781
|
+
var doneBtn = document.getElementById('done');
|
|
34782
|
+
if (!doneBtn) return;
|
|
34783
|
+
doneBtn.onclick = function() {
|
|
34784
|
+
doneBtn.disabled = true;
|
|
34785
|
+
doneBtn.textContent = 'Saving\u2026';
|
|
34786
|
+
fetch('/console/auth/${encodeURIComponent(connectionId)}/complete', { method: 'POST' })
|
|
34787
|
+
.then(function(r){ return r.json().catch(function(){ return {}; }); })
|
|
34788
|
+
.then(function(){ location.reload(); })
|
|
34789
|
+
.catch(function(){ doneBtn.disabled = false; doneBtn.textContent = "Done \u2014 I'm signed in"; });
|
|
34790
|
+
};
|
|
34791
|
+
})();
|
|
34792
|
+
</script>
|
|
34090
34793
|
</body>
|
|
34091
34794
|
</html>`;
|
|
34092
34795
|
}
|
|
@@ -34349,8 +35052,9 @@ var init_stripe_routes = __esm({
|
|
|
34349
35052
|
const user2 = await resolveUser(sub.customer, sub.customer_email);
|
|
34350
35053
|
if (user2) {
|
|
34351
35054
|
const live = sub.status === "active" || sub.status === "trialing";
|
|
34352
|
-
|
|
34353
|
-
await
|
|
35055
|
+
const newMemoryPlan = live ? mem.plan : "free";
|
|
35056
|
+
await setMemoryPlan(user2.id, newMemoryPlan, live ? sub.id : null);
|
|
35057
|
+
await syncMemoryKeyPlan(user2, resolveEffectiveMemoryPlan({ ...user2, memory_plan: newMemoryPlan }));
|
|
34354
35058
|
}
|
|
34355
35059
|
return c.json({ received: true });
|
|
34356
35060
|
}
|
|
@@ -34368,7 +35072,9 @@ var init_stripe_routes = __esm({
|
|
|
34368
35072
|
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
34369
35073
|
const tier = SUBSCRIPTION_TIERS[priceId];
|
|
34370
35074
|
const live = sub.status === "active" || sub.status === "trialing";
|
|
34371
|
-
|
|
35075
|
+
const newTier = live ? tier.tier : null;
|
|
35076
|
+
await setSubscriptionTier(user.id, newTier, live ? tier.concurrency : 0, live ? sub.id : null);
|
|
35077
|
+
await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: newTier }));
|
|
34372
35078
|
} else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
|
|
34373
35079
|
await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
|
|
34374
35080
|
await setConcurrencySubId(user.id, sub.id);
|
|
@@ -34381,7 +35087,7 @@ var init_stripe_routes = __esm({
|
|
|
34381
35087
|
const user2 = await resolveUserByMemorySubscription(sub.id) ?? await getUserByStripeCustomerId(sub.customer);
|
|
34382
35088
|
if (user2) {
|
|
34383
35089
|
await setMemoryPlan(user2.id, "free", null);
|
|
34384
|
-
await syncMemoryKeyPlan(user2, "free");
|
|
35090
|
+
await syncMemoryKeyPlan(user2, resolveEffectiveMemoryPlan({ ...user2, memory_plan: "free" }));
|
|
34385
35091
|
}
|
|
34386
35092
|
return c.json({ received: true });
|
|
34387
35093
|
}
|
|
@@ -34394,6 +35100,7 @@ var init_stripe_routes = __esm({
|
|
|
34394
35100
|
if (!user) return c.json({ received: true });
|
|
34395
35101
|
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
34396
35102
|
await setSubscriptionTier(user.id, null, 0, null);
|
|
35103
|
+
await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: null }));
|
|
34397
35104
|
} else if (priceId === CONCURRENCY_PRICE_ID) {
|
|
34398
35105
|
await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
|
|
34399
35106
|
await setConcurrencySubId(user.id, null);
|
|
@@ -37973,14 +38680,20 @@ var init_server = __esm({
|
|
|
37973
38680
|
const id = c.req.param("id");
|
|
37974
38681
|
await migrateBrowserAgent();
|
|
37975
38682
|
const row = await getAuthConnectionRow(id);
|
|
37976
|
-
|
|
38683
|
+
const sessionRow = row?.browser_agent_session_id ? await getSessionRow(row.browser_agent_session_id) : null;
|
|
38684
|
+
return c.html(renderAuthConsoleHtml(id, {
|
|
38685
|
+
domain: row?.domain ?? null,
|
|
38686
|
+
status: row?.status ?? null,
|
|
38687
|
+
liveViewUrl: sessionRow?.live_view_url ?? null,
|
|
38688
|
+
sessionOpen: sessionRow?.status === "open"
|
|
38689
|
+
}));
|
|
37977
38690
|
});
|
|
37978
|
-
app.
|
|
38691
|
+
app.post("/console/auth/:id/complete", async (c) => {
|
|
37979
38692
|
const id = c.req.param("id");
|
|
37980
38693
|
await migrateBrowserAgent();
|
|
37981
|
-
const
|
|
37982
|
-
if (!
|
|
37983
|
-
return c.
|
|
38694
|
+
const result = await completeAuthConnection(id);
|
|
38695
|
+
if (!result.ok) return c.json({ error: result.error }, 404);
|
|
38696
|
+
return c.json({ ok: true });
|
|
37984
38697
|
});
|
|
37985
38698
|
app.get("/console/:id", (c) => c.html(renderConsoleHtml(c.req.param("id"))));
|
|
37986
38699
|
app.route("/stripe", stripeApp);
|