mcp-scraper 0.4.4 → 0.4.5
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 +265 -35
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +109 -3
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/chunk-UGQC2FOX.js +7 -0
- package/dist/chunk-UGQC2FOX.js.map +1 -0
- package/dist/{chunk-Z34NGK64.js → chunk-WI4A3KFQ.js} +110 -4
- package/dist/chunk-WI4A3KFQ.js.map +1 -0
- package/dist/{server-IKT3QVFB.js → server-XCDFHHLE.js} +149 -25
- package/dist/{server-IKT3QVFB.js.map → server-XCDFHHLE.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-TZRPP45I.js +0 -7
- package/dist/chunk-TZRPP45I.js.map +0 -1
- package/dist/chunk-Z34NGK64.js.map +0 -1
package/dist/bin/api-server.cjs
CHANGED
|
@@ -16934,22 +16934,8 @@ async function provisionMemoryForUser(user) {
|
|
|
16934
16934
|
} catch {
|
|
16935
16935
|
}
|
|
16936
16936
|
}
|
|
16937
|
-
function memoryPlanForUser(user) {
|
|
16938
|
-
switch (user.subscription_tier) {
|
|
16939
|
-
case "starter":
|
|
16940
|
-
return "pro";
|
|
16941
|
-
case "growth":
|
|
16942
|
-
return "team";
|
|
16943
|
-
case "scale":
|
|
16944
|
-
return "enterprise";
|
|
16945
|
-
default:
|
|
16946
|
-
return "free";
|
|
16947
|
-
}
|
|
16948
|
-
}
|
|
16949
16937
|
function resolveEffectiveMemoryPlan(user) {
|
|
16950
|
-
|
|
16951
|
-
const memoryProPlan = user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
|
|
16952
|
-
return MEMORY_PLAN_RANK[tierPlan] >= MEMORY_PLAN_RANK[memoryProPlan] ? tierPlan : memoryProPlan;
|
|
16938
|
+
return user.memory_plan === "pro" || user.memory_plan === "team" ? user.memory_plan : "free";
|
|
16953
16939
|
}
|
|
16954
16940
|
async function getOrCreateUserMemoryKey(user) {
|
|
16955
16941
|
const creds = await getUserMemoryCreds(user.id);
|
|
@@ -16999,7 +16985,7 @@ async function syncScheduleEntitlement(user, enabled, quotaPerPeriod) {
|
|
|
16999
16985
|
}, {});
|
|
17000
16986
|
return { ok: res.ok, error: res.error };
|
|
17001
16987
|
}
|
|
17002
|
-
var import_node_crypto6, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY
|
|
16988
|
+
var import_node_crypto6, import_provision_defaults, import_set_schedule_entitlement, MEMORY_BASE_URL, ADMIN_KEY;
|
|
17003
16989
|
var init_memory = __esm({
|
|
17004
16990
|
"src/api/memory.ts"() {
|
|
17005
16991
|
"use strict";
|
|
@@ -17010,7 +16996,6 @@ var init_memory = __esm({
|
|
|
17010
16996
|
import_set_schedule_entitlement = require("mcpscraper-memory-tools/tools/schedule/set-schedule-entitlement");
|
|
17011
16997
|
MEMORY_BASE_URL = () => (process.env.MCP_MEMORY_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
|
|
17012
16998
|
ADMIN_KEY = () => process.env.MCP_MEMORY_ADMIN_KEY?.trim() ?? "";
|
|
17013
|
-
MEMORY_PLAN_RANK = { free: 0, pro: 1, team: 2, enterprise: 3 };
|
|
17014
16999
|
}
|
|
17015
17000
|
});
|
|
17016
17001
|
|
|
@@ -27018,7 +27003,7 @@ var PACKAGE_VERSION;
|
|
|
27018
27003
|
var init_version = __esm({
|
|
27019
27004
|
"src/version.ts"() {
|
|
27020
27005
|
"use strict";
|
|
27021
|
-
PACKAGE_VERSION = "0.4.
|
|
27006
|
+
PACKAGE_VERSION = "0.4.5";
|
|
27022
27007
|
}
|
|
27023
27008
|
});
|
|
27024
27009
|
|
|
@@ -29277,7 +29262,7 @@ var init_export = __esm({
|
|
|
29277
29262
|
});
|
|
29278
29263
|
|
|
29279
29264
|
// src/mcp/browser-agent-tool-schemas.ts
|
|
29280
|
-
var import_zod34, NullableString2, BrowserRawObject, BrowserElementOutput, BrowserBaseOutput, BrowserReplayBaseOutput, BrowserOpenInputSchema, BrowserProfileConnectInputSchema, BrowserProfileListInputSchema, BrowserSessionInputSchema, BrowserLocateTargetSchema, BrowserLocateInputSchema, BrowserGotoInputSchema, BrowserClickInputSchema, BrowserTypeInputSchema, BrowserScrollInputSchema, BrowserPressInputSchema, BrowserReplayStopInputSchema, BrowserReplayDownloadInputSchema, BrowserReplayAnnotationSchema, BrowserReplayMarkInputSchema, BrowserReplayAnnotateInputSchema, BrowserListInputSchema, BrowserCaptureFanoutInputSchema, FanoutSourceOutput, BrowserCaptureFanoutOutputSchema, BrowserOpenOutputSchema, BrowserProfileLogin, BrowserProfileConnectOutputSchema, BrowserProfileListOutputSchema, BrowserScreenshotOutputSchema, BrowserReadOutputSchema, BrowserLocateOutputSchema, BrowserActionOutputSchema, BrowserReplayStartOutputSchema, BrowserReplayStopOutputSchema, BrowserListReplaysOutputSchema, BrowserReplayDownloadOutputSchema, BrowserReplayMarkOutputSchema, BrowserReplayAnnotateOutputSchema, BrowserCloseOutputSchema, BrowserListSessionsOutputSchema;
|
|
29265
|
+
var import_zod34, NullableString2, BrowserRawObject, BrowserElementOutput, BrowserBaseOutput, BrowserReplayBaseOutput, BrowserOpenInputSchema, BrowserProfileConnectInputSchema, BrowserProfileListInputSchema, BrowserSessionInputSchema, BrowserLocateTargetSchema, BrowserLocateInputSchema, BrowserGotoInputSchema, BrowserClickInputSchema, BrowserTypeInputSchema, BrowserScrollInputSchema, BrowserPressInputSchema, BrowserReplayStopInputSchema, BrowserReplayDownloadInputSchema, BrowserReplayAnnotationSchema, BrowserReplayMarkInputSchema, BrowserReplayAnnotateInputSchema, BrowserListInputSchema, BrowserCaptureFanoutInputSchema, FanoutSourceOutput, BrowserCaptureFanoutOutputSchema, BrowserOpenOutputSchema, BrowserProfileLogin, BrowserProfileConnectOutputSchema, BrowserProfileListOutputSchema, BrowserExtensionImportInputSchema, BrowserExtensionImportOutputSchema, BrowserExtensionSummary, BrowserExtensionListInputSchema, BrowserExtensionListOutputSchema, BrowserExtensionDeleteInputSchema, BrowserExtensionDeleteOutputSchema, BrowserScreenshotOutputSchema, BrowserReadOutputSchema, BrowserLocateOutputSchema, BrowserActionOutputSchema, BrowserReplayStartOutputSchema, BrowserReplayStopOutputSchema, BrowserListReplaysOutputSchema, BrowserReplayDownloadOutputSchema, BrowserReplayMarkOutputSchema, BrowserReplayAnnotateOutputSchema, BrowserCloseOutputSchema, BrowserListSessionsOutputSchema;
|
|
29281
29266
|
var init_browser_agent_tool_schemas = __esm({
|
|
29282
29267
|
"src/mcp/browser-agent-tool-schemas.ts"() {
|
|
29283
29268
|
"use strict";
|
|
@@ -29299,7 +29284,10 @@ var init_browser_agent_tool_schemas = __esm({
|
|
|
29299
29284
|
url: import_zod34.z.string().url().optional().describe("Optional URL to navigate to immediately after opening."),
|
|
29300
29285
|
profile: import_zod34.z.string().optional().describe("Optional saved hosted profile name to load a logged-in session for a site."),
|
|
29301
29286
|
save_profile_changes: import_zod34.z.boolean().optional().describe("Persist cookies/storage back to the named profile on close. Avoid parallel sessions writing to the same profile."),
|
|
29302
|
-
timeout_seconds: import_zod34.z.number().int().min(60).max(259200).optional().describe("Session lifetime before auto-termination. Defaults to 600.")
|
|
29287
|
+
timeout_seconds: import_zod34.z.number().int().min(60).max(259200).optional().describe("Session lifetime before auto-termination. Defaults to 600."),
|
|
29288
|
+
extension_names: import_zod34.z.array(import_zod34.z.string()).optional().describe(
|
|
29289
|
+
"Names of extensions previously added with browser_extension_import (see browser_extension_list for what's available) to load into this session. Loading extensions restarts the browser, adding a few seconds to startup."
|
|
29290
|
+
)
|
|
29303
29291
|
};
|
|
29304
29292
|
BrowserProfileConnectInputSchema = {
|
|
29305
29293
|
email: import_zod34.z.string().optional().describe("Account email for the login. Derives a stable profile name and is recorded as a note. Does NOT import existing cookies \u2014 the user signs in fresh."),
|
|
@@ -29526,6 +29514,43 @@ var init_browser_agent_tool_schemas = __esm({
|
|
|
29526
29514
|
connections: import_zod34.z.array(BrowserProfileLogin).describe("All site logins saved in this profile, each with its current auth status and note."),
|
|
29527
29515
|
count: import_zod34.z.number().int().min(0)
|
|
29528
29516
|
};
|
|
29517
|
+
BrowserExtensionImportInputSchema = {
|
|
29518
|
+
store_url: import_zod34.z.string().url().describe("Chrome Web Store URL of the extension to add, e.g. https://chromewebstore.google.com/detail/<slug>/<id>."),
|
|
29519
|
+
name: import_zod34.z.string().min(1).max(64).describe('Short name to save this extension under, e.g. "ani-ai". Reuse it later in extension_names on browser_open.')
|
|
29520
|
+
};
|
|
29521
|
+
BrowserExtensionImportOutputSchema = {
|
|
29522
|
+
ok: import_zod34.z.boolean(),
|
|
29523
|
+
tool: import_zod34.z.literal("browser_extension_import"),
|
|
29524
|
+
session_id: import_zod34.z.null(),
|
|
29525
|
+
name: import_zod34.z.string().describe("The name this extension was saved under."),
|
|
29526
|
+
source_url: import_zod34.z.string().describe("The store URL this extension was imported from."),
|
|
29527
|
+
size_bytes: import_zod34.z.number().nullable().describe("Size of the extension package in bytes.")
|
|
29528
|
+
};
|
|
29529
|
+
BrowserExtensionSummary = import_zod34.z.object({
|
|
29530
|
+
name: import_zod34.z.string(),
|
|
29531
|
+
source: import_zod34.z.string().describe('Always "store" for extensions added via browser_extension_import.'),
|
|
29532
|
+
source_url: NullableString2,
|
|
29533
|
+
size_bytes: import_zod34.z.number().nullable(),
|
|
29534
|
+
created_at: import_zod34.z.string()
|
|
29535
|
+
});
|
|
29536
|
+
BrowserExtensionListInputSchema = {};
|
|
29537
|
+
BrowserExtensionListOutputSchema = {
|
|
29538
|
+
ok: import_zod34.z.boolean(),
|
|
29539
|
+
tool: import_zod34.z.literal("browser_extension_list"),
|
|
29540
|
+
session_id: import_zod34.z.null(),
|
|
29541
|
+
extensions: import_zod34.z.array(BrowserExtensionSummary).describe("Every extension available to load via extension_names on browser_open."),
|
|
29542
|
+
count: import_zod34.z.number().int().min(0)
|
|
29543
|
+
};
|
|
29544
|
+
BrowserExtensionDeleteInputSchema = {
|
|
29545
|
+
name: import_zod34.z.string().min(1).describe("Name of the extension to remove, as returned by browser_extension_list.")
|
|
29546
|
+
};
|
|
29547
|
+
BrowserExtensionDeleteOutputSchema = {
|
|
29548
|
+
ok: import_zod34.z.boolean(),
|
|
29549
|
+
tool: import_zod34.z.literal("browser_extension_delete"),
|
|
29550
|
+
session_id: import_zod34.z.null(),
|
|
29551
|
+
name: import_zod34.z.string(),
|
|
29552
|
+
deleted: import_zod34.z.boolean()
|
|
29553
|
+
};
|
|
29529
29554
|
BrowserScreenshotOutputSchema = {
|
|
29530
29555
|
...BrowserBaseOutput,
|
|
29531
29556
|
tool: import_zod34.z.literal("browser_screenshot"),
|
|
@@ -30149,6 +30174,71 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
30149
30174
|
});
|
|
30150
30175
|
}
|
|
30151
30176
|
);
|
|
30177
|
+
server.registerTool(
|
|
30178
|
+
"browser_extension_import",
|
|
30179
|
+
{
|
|
30180
|
+
title: "Add Browser Extension",
|
|
30181
|
+
description: "Add a Chrome extension from its Chrome Web Store page so it can be loaded into browser_open sessions via extension_names. One-time setup per extension \u2014 check what's already added with browser_extension_list first. The extension starts logged out in any session; sign into it once inside a session, pairing with a saved profile (browser_open's profile + save_profile_changes) to keep it signed in on future opens.",
|
|
30182
|
+
inputSchema: BrowserExtensionImportInputSchema,
|
|
30183
|
+
outputSchema: recordOutputSchema("browser_extension_import", BrowserExtensionImportOutputSchema),
|
|
30184
|
+
annotations: annotations("Add Browser Extension")
|
|
30185
|
+
},
|
|
30186
|
+
async (input) => {
|
|
30187
|
+
const res = await req("POST", "/agent/extensions/import", { store_url: input.store_url, name: input.name });
|
|
30188
|
+
if (!res.ok) return errorResult("browser_extension_import", res.data);
|
|
30189
|
+
return structuredResult({
|
|
30190
|
+
ok: true,
|
|
30191
|
+
tool: "browser_extension_import",
|
|
30192
|
+
session_id: null,
|
|
30193
|
+
name: res.data.name,
|
|
30194
|
+
source_url: res.data.source_url,
|
|
30195
|
+
size_bytes: res.data.size_bytes ?? null
|
|
30196
|
+
});
|
|
30197
|
+
}
|
|
30198
|
+
);
|
|
30199
|
+
server.registerTool(
|
|
30200
|
+
"browser_extension_list",
|
|
30201
|
+
{
|
|
30202
|
+
title: "List Browser Extensions",
|
|
30203
|
+
description: "List extensions added via browser_extension_import, for use as extension_names on browser_open. Read-only, no cost.",
|
|
30204
|
+
inputSchema: BrowserExtensionListInputSchema,
|
|
30205
|
+
outputSchema: recordOutputSchema("browser_extension_list", BrowserExtensionListOutputSchema),
|
|
30206
|
+
annotations: annotations("List Browser Extensions", true)
|
|
30207
|
+
},
|
|
30208
|
+
async () => {
|
|
30209
|
+
const res = await req("GET", "/agent/extensions");
|
|
30210
|
+
if (!res.ok) return errorResult("browser_extension_list", res.data);
|
|
30211
|
+
const extensions = Array.isArray(res.data?.extensions) ? res.data.extensions : [];
|
|
30212
|
+
return structuredResult({
|
|
30213
|
+
ok: true,
|
|
30214
|
+
tool: "browser_extension_list",
|
|
30215
|
+
session_id: null,
|
|
30216
|
+
extensions,
|
|
30217
|
+
count: extensions.length
|
|
30218
|
+
});
|
|
30219
|
+
}
|
|
30220
|
+
);
|
|
30221
|
+
server.registerTool(
|
|
30222
|
+
"browser_extension_delete",
|
|
30223
|
+
{
|
|
30224
|
+
title: "Remove Browser Extension",
|
|
30225
|
+
description: "Remove a previously added extension by name so it can no longer be loaded via extension_names.",
|
|
30226
|
+
inputSchema: BrowserExtensionDeleteInputSchema,
|
|
30227
|
+
outputSchema: recordOutputSchema("browser_extension_delete", BrowserExtensionDeleteOutputSchema),
|
|
30228
|
+
annotations: { title: "Remove Browser Extension", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
|
|
30229
|
+
},
|
|
30230
|
+
async (input) => {
|
|
30231
|
+
const res = await req("DELETE", `/agent/extensions/${encodeURIComponent(input.name)}`);
|
|
30232
|
+
if (!res.ok) return errorResult("browser_extension_delete", res.data);
|
|
30233
|
+
return structuredResult({
|
|
30234
|
+
ok: true,
|
|
30235
|
+
tool: "browser_extension_delete",
|
|
30236
|
+
session_id: null,
|
|
30237
|
+
name: input.name,
|
|
30238
|
+
deleted: true
|
|
30239
|
+
});
|
|
30240
|
+
}
|
|
30241
|
+
);
|
|
30152
30242
|
server.registerTool(
|
|
30153
30243
|
"browser_open",
|
|
30154
30244
|
{
|
|
@@ -30167,7 +30257,8 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
30167
30257
|
...profile && typeof saveProfileChanges === "boolean" ? { save_profile_changes: saveProfileChanges } : {},
|
|
30168
30258
|
disable_default_proxy: true,
|
|
30169
30259
|
timeout_seconds: input.timeout_seconds,
|
|
30170
|
-
...input.url ? { url: input.url } : {}
|
|
30260
|
+
...input.url ? { url: input.url } : {},
|
|
30261
|
+
...input.extension_names?.length ? { extension_names: input.extension_names } : {}
|
|
30171
30262
|
});
|
|
30172
30263
|
if (!open.ok) return errorResult("browser_open", open.data);
|
|
30173
30264
|
const session = open.data;
|
|
@@ -33435,8 +33526,58 @@ async function migrateBrowserAgent() {
|
|
|
33435
33526
|
} catch {
|
|
33436
33527
|
}
|
|
33437
33528
|
await db.execute(`CREATE INDEX IF NOT EXISTS browser_auth_connections_profile ON browser_auth_connections(profile)`);
|
|
33529
|
+
await db.execute(`
|
|
33530
|
+
CREATE TABLE IF NOT EXISTS browser_agent_extensions (
|
|
33531
|
+
id TEXT PRIMARY KEY,
|
|
33532
|
+
user_id INTEGER NOT NULL,
|
|
33533
|
+
name TEXT NOT NULL,
|
|
33534
|
+
backend_id TEXT NOT NULL,
|
|
33535
|
+
backend_name TEXT NOT NULL,
|
|
33536
|
+
source TEXT NOT NULL DEFAULT 'store',
|
|
33537
|
+
source_url TEXT,
|
|
33538
|
+
size_bytes INTEGER,
|
|
33539
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
33540
|
+
)
|
|
33541
|
+
`);
|
|
33542
|
+
await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS browser_agent_extensions_user_name ON browser_agent_extensions(user_id, name)`);
|
|
33543
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS browser_agent_extensions_user ON browser_agent_extensions(user_id)`);
|
|
33438
33544
|
_ready2 = true;
|
|
33439
33545
|
}
|
|
33546
|
+
async function createExtensionRow(input) {
|
|
33547
|
+
const db = getDb();
|
|
33548
|
+
const id = `bext_${(0, import_node_crypto11.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
33549
|
+
await db.execute({
|
|
33550
|
+
sql: `INSERT INTO browser_agent_extensions (id, user_id, name, backend_id, backend_name, source, source_url, size_bytes)
|
|
33551
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
33552
|
+
args: [id, input.userId, input.name, input.backendId, input.backendName, input.source, input.sourceUrl, input.sizeBytes]
|
|
33553
|
+
});
|
|
33554
|
+
const row = await getExtensionRow(input.userId, input.name);
|
|
33555
|
+
if (!row) throw new Error("extension insert failed");
|
|
33556
|
+
return row;
|
|
33557
|
+
}
|
|
33558
|
+
async function getExtensionRow(userId, name) {
|
|
33559
|
+
const db = getDb();
|
|
33560
|
+
const res = await db.execute({
|
|
33561
|
+
sql: `SELECT * FROM browser_agent_extensions WHERE user_id = ? AND name = ?`,
|
|
33562
|
+
args: [userId, name]
|
|
33563
|
+
});
|
|
33564
|
+
return res.rows[0] ?? null;
|
|
33565
|
+
}
|
|
33566
|
+
async function listExtensionRows(userId) {
|
|
33567
|
+
const db = getDb();
|
|
33568
|
+
const res = await db.execute({
|
|
33569
|
+
sql: `SELECT * FROM browser_agent_extensions WHERE user_id = ? ORDER BY created_at DESC`,
|
|
33570
|
+
args: [userId]
|
|
33571
|
+
});
|
|
33572
|
+
return res.rows;
|
|
33573
|
+
}
|
|
33574
|
+
async function deleteExtensionRow(userId, name) {
|
|
33575
|
+
const db = getDb();
|
|
33576
|
+
await db.execute({
|
|
33577
|
+
sql: `DELETE FROM browser_agent_extensions WHERE user_id = ? AND name = ?`,
|
|
33578
|
+
args: [userId, name]
|
|
33579
|
+
});
|
|
33580
|
+
}
|
|
33440
33581
|
async function createAuthConnectionRow(input) {
|
|
33441
33582
|
const db = getDb();
|
|
33442
33583
|
const connectionId = `authc_${(0, import_node_crypto11.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
@@ -34353,6 +34494,28 @@ function isProfileConflict(err) {
|
|
|
34353
34494
|
const message = err instanceof Error ? err.message : String(err);
|
|
34354
34495
|
return /\b409\b|conflict|already exists/i.test(message);
|
|
34355
34496
|
}
|
|
34497
|
+
function isNotFound(err) {
|
|
34498
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34499
|
+
return /\b404\b|not found/i.test(message);
|
|
34500
|
+
}
|
|
34501
|
+
async function importExtensionFromStore(storeUrl, backendName) {
|
|
34502
|
+
const k = client();
|
|
34503
|
+
const archive = await k.extensions.downloadFromChromeStore({ url: storeUrl });
|
|
34504
|
+
const uploaded = await k.extensions.upload({ file: archive, name: backendName });
|
|
34505
|
+
return {
|
|
34506
|
+
backendId: uploaded.id,
|
|
34507
|
+
backendName: uploaded.name ?? backendName,
|
|
34508
|
+
sizeBytes: typeof uploaded.size_bytes === "number" ? uploaded.size_bytes : null
|
|
34509
|
+
};
|
|
34510
|
+
}
|
|
34511
|
+
async function deleteExtensionBackend(backendName) {
|
|
34512
|
+
const k = client();
|
|
34513
|
+
try {
|
|
34514
|
+
await k.extensions.delete(backendName);
|
|
34515
|
+
} catch (err) {
|
|
34516
|
+
if (!isNotFound(err)) throw err;
|
|
34517
|
+
}
|
|
34518
|
+
}
|
|
34356
34519
|
async function ensureProfile(k, name) {
|
|
34357
34520
|
try {
|
|
34358
34521
|
await k.profiles.create({ name });
|
|
@@ -34371,6 +34534,7 @@ async function createSession(opts = {}) {
|
|
|
34371
34534
|
const resolvedSaveProfileChanges = opts.saveProfileChanges ?? browserServiceProfileSaveChanges();
|
|
34372
34535
|
const disableDefaultProxy = opts.disableDefaultProxy ?? !resolvedProxyId;
|
|
34373
34536
|
const startUrl = opts.startUrl?.trim();
|
|
34537
|
+
const extensionNames = (opts.extensionNames ?? []).map((name) => name.trim()).filter(Boolean);
|
|
34374
34538
|
if (resolvedProfileName && resolvedSaveProfileChanges === true) {
|
|
34375
34539
|
await ensureProfile(k, resolvedProfileName);
|
|
34376
34540
|
}
|
|
@@ -34384,7 +34548,8 @@ async function createSession(opts = {}) {
|
|
|
34384
34548
|
name: resolvedProfileName,
|
|
34385
34549
|
...typeof resolvedSaveProfileChanges === "boolean" ? { save_changes: resolvedSaveProfileChanges } : {}
|
|
34386
34550
|
}
|
|
34387
|
-
} : {}
|
|
34551
|
+
} : {},
|
|
34552
|
+
...extensionNames.length ? { extensions: extensionNames.map((name) => ({ name })) } : {}
|
|
34388
34553
|
};
|
|
34389
34554
|
const browser = await k.browsers.create(createPayload);
|
|
34390
34555
|
const runtimeSessionId = browser.session_id;
|
|
@@ -34781,6 +34946,15 @@ function parseAgentDate(value) {
|
|
|
34781
34946
|
const legacyParsed = Date.parse(`${value.replace(" ", "T")}Z`);
|
|
34782
34947
|
return Number.isFinite(legacyParsed) ? legacyParsed : null;
|
|
34783
34948
|
}
|
|
34949
|
+
function publicExtension(row) {
|
|
34950
|
+
return {
|
|
34951
|
+
name: row.name,
|
|
34952
|
+
source: row.source,
|
|
34953
|
+
source_url: row.source_url,
|
|
34954
|
+
size_bytes: row.size_bytes,
|
|
34955
|
+
created_at: row.created_at
|
|
34956
|
+
};
|
|
34957
|
+
}
|
|
34784
34958
|
function replayClock(row) {
|
|
34785
34959
|
if (!row) return null;
|
|
34786
34960
|
const startedAtMs = parseAgentDate(row.started_at);
|
|
@@ -34927,6 +35101,15 @@ function buildBrowserAgentRoutes() {
|
|
|
34927
35101
|
const body = await c.req.json().catch(() => ({}));
|
|
34928
35102
|
const timeoutSeconds = typeof body.timeout_seconds === "number" ? body.timeout_seconds : void 0;
|
|
34929
35103
|
const startUrl = typeof body.url === "string" ? body.url : void 0;
|
|
35104
|
+
const extensionNames = Array.isArray(body.extension_names) ? body.extension_names.filter((v) => typeof v === "string") : void 0;
|
|
35105
|
+
let resolvedExtensionBackendNames;
|
|
35106
|
+
if (extensionNames && extensionNames.length) {
|
|
35107
|
+
const names = extensionNames;
|
|
35108
|
+
const rows = await Promise.all(names.map((name) => getExtensionRow(user.id, name)));
|
|
35109
|
+
const missing = names.filter((_, i) => !rows[i]);
|
|
35110
|
+
if (missing.length) return c.json({ error: `extension(s) not found: ${missing.join(", ")}` }, 400);
|
|
35111
|
+
resolvedExtensionBackendNames = rows.map((row) => row.backend_name);
|
|
35112
|
+
}
|
|
34930
35113
|
const gate = await acquireConcurrencyGate(user, "browser_agent_session", {
|
|
34931
35114
|
ttlSeconds: browserSessionLockTtlSeconds(timeoutSeconds),
|
|
34932
35115
|
metadata: { label: typeof body.label === "string" ? body.label : null }
|
|
@@ -34941,7 +35124,8 @@ function buildBrowserAgentRoutes() {
|
|
|
34941
35124
|
saveProfileChanges: typeof body.save_profile_changes === "boolean" ? body.save_profile_changes : void 0,
|
|
34942
35125
|
startUrl,
|
|
34943
35126
|
disableDefaultProxy: typeof body.disable_default_proxy === "boolean" ? body.disable_default_proxy : void 0,
|
|
34944
|
-
viewport: body.viewport && typeof body.viewport === "object" ? body.viewport : void 0
|
|
35127
|
+
viewport: body.viewport && typeof body.viewport === "object" ? body.viewport : void 0,
|
|
35128
|
+
extensionNames: resolvedExtensionBackendNames
|
|
34945
35129
|
});
|
|
34946
35130
|
runtimeSessionId = created.runtimeSessionId;
|
|
34947
35131
|
const row = await createSessionRow({
|
|
@@ -35244,6 +35428,52 @@ function buildBrowserAgentRoutes() {
|
|
|
35244
35428
|
}))
|
|
35245
35429
|
});
|
|
35246
35430
|
});
|
|
35431
|
+
app2.post("/extensions/import", async (c) => {
|
|
35432
|
+
const user = c.get("user");
|
|
35433
|
+
const body = await c.req.json().catch(() => ({}));
|
|
35434
|
+
const storeUrl = typeof body.store_url === "string" ? body.store_url.trim() : "";
|
|
35435
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
35436
|
+
if (!storeUrl) return c.json({ error: "store_url is required" }, 400);
|
|
35437
|
+
if (!name || !EXTENSION_NAME_RE.test(name)) {
|
|
35438
|
+
return c.json({ error: "name is required (1-64 characters: letters, numbers, spaces, dots, underscores, hyphens)" }, 400);
|
|
35439
|
+
}
|
|
35440
|
+
const existing = await getExtensionRow(user.id, name);
|
|
35441
|
+
if (existing) return c.json({ error: `an extension named "${name}" already exists \u2014 delete it first or pick another name` }, 409);
|
|
35442
|
+
const backendName = `u${user.id}_${(0, import_node_crypto12.randomUUID)().replace(/-/g, "")}`;
|
|
35443
|
+
try {
|
|
35444
|
+
const imported = await importExtensionFromStore(storeUrl, backendName);
|
|
35445
|
+
const row = await createExtensionRow({
|
|
35446
|
+
userId: user.id,
|
|
35447
|
+
name,
|
|
35448
|
+
backendId: imported.backendId,
|
|
35449
|
+
backendName: imported.backendName,
|
|
35450
|
+
source: "store",
|
|
35451
|
+
sourceUrl: storeUrl,
|
|
35452
|
+
sizeBytes: imported.sizeBytes
|
|
35453
|
+
});
|
|
35454
|
+
return c.json(publicExtension(row));
|
|
35455
|
+
} catch (err) {
|
|
35456
|
+
return c.json(failure(err), 502);
|
|
35457
|
+
}
|
|
35458
|
+
});
|
|
35459
|
+
app2.get("/extensions", async (c) => {
|
|
35460
|
+
const user = c.get("user");
|
|
35461
|
+
const rows = await listExtensionRows(user.id);
|
|
35462
|
+
return c.json({ extensions: rows.map(publicExtension) });
|
|
35463
|
+
});
|
|
35464
|
+
app2.delete("/extensions/:name", async (c) => {
|
|
35465
|
+
const user = c.get("user");
|
|
35466
|
+
const name = c.req.param("name");
|
|
35467
|
+
const row = await getExtensionRow(user.id, name);
|
|
35468
|
+
if (!row) return c.json({ error: "not found" }, 404);
|
|
35469
|
+
try {
|
|
35470
|
+
await deleteExtensionBackend(row.backend_name);
|
|
35471
|
+
} catch (err) {
|
|
35472
|
+
return c.json(failure(err), 502);
|
|
35473
|
+
}
|
|
35474
|
+
await deleteExtensionRow(user.id, name);
|
|
35475
|
+
return c.json({ ok: true });
|
|
35476
|
+
});
|
|
35247
35477
|
app2.get("/sessions/:id/replays/:replayId/download", async (c) => {
|
|
35248
35478
|
const user = c.get("user");
|
|
35249
35479
|
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
@@ -35268,10 +35498,11 @@ function buildBrowserAgentRoutes() {
|
|
|
35268
35498
|
});
|
|
35269
35499
|
return app2;
|
|
35270
35500
|
}
|
|
35271
|
-
var import_hono17, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS;
|
|
35501
|
+
var import_node_crypto12, import_hono17, auth, DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS, EXTENSION_NAME_RE;
|
|
35272
35502
|
var init_browser_agent_routes = __esm({
|
|
35273
35503
|
"src/api/browser-agent-routes.ts"() {
|
|
35274
35504
|
"use strict";
|
|
35505
|
+
import_node_crypto12 = require("crypto");
|
|
35275
35506
|
import_hono17 = require("hono");
|
|
35276
35507
|
init_api_auth();
|
|
35277
35508
|
init_errors();
|
|
@@ -35282,6 +35513,7 @@ var init_browser_agent_routes = __esm({
|
|
|
35282
35513
|
init_concurrency_gates();
|
|
35283
35514
|
auth = createApiKeyAuth();
|
|
35284
35515
|
DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS = 2 * 60 * 60;
|
|
35516
|
+
EXTENSION_NAME_RE = /^[\w .-]{1,64}$/;
|
|
35285
35517
|
}
|
|
35286
35518
|
});
|
|
35287
35519
|
|
|
@@ -35640,7 +35872,6 @@ var init_stripe_routes = __esm({
|
|
|
35640
35872
|
const live = sub.status === "active" || sub.status === "trialing";
|
|
35641
35873
|
const newTier = live ? tier.tier : null;
|
|
35642
35874
|
await setSubscriptionTier(user.id, newTier, live ? tier.concurrency : 0, live ? sub.id : null);
|
|
35643
|
-
await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: newTier }));
|
|
35644
35875
|
} else if (priceId === CONCURRENCY_PRICE_ID && event.type === "customer.subscription.created") {
|
|
35645
35876
|
await setExtraConcurrencySlots(user.id, user.extra_concurrency_slots + 1);
|
|
35646
35877
|
await setConcurrencySubId(user.id, sub.id);
|
|
@@ -35666,7 +35897,6 @@ var init_stripe_routes = __esm({
|
|
|
35666
35897
|
if (!user) return c.json({ received: true });
|
|
35667
35898
|
if (priceId && priceId in SUBSCRIPTION_TIERS) {
|
|
35668
35899
|
await setSubscriptionTier(user.id, null, 0, null);
|
|
35669
|
-
await syncMemoryKeyPlan(user, resolveEffectiveMemoryPlan({ ...user, subscription_tier: null }));
|
|
35670
35900
|
} else if (priceId === CONCURRENCY_PRICE_ID) {
|
|
35671
35901
|
await setExtraConcurrencySlots(user.id, Math.max(0, user.extra_concurrency_slots - 1));
|
|
35672
35902
|
await setConcurrencySubId(user.id, null);
|
|
@@ -35690,7 +35920,7 @@ async function getKeys() {
|
|
|
35690
35920
|
const privateKey = await (0, import_jose2.importPKCS8)(pem, "RS256", { extractable: true });
|
|
35691
35921
|
const full = await (0, import_jose2.exportJWK)(privateKey);
|
|
35692
35922
|
const publicJwk = { kty: full.kty, n: full.n, e: full.e };
|
|
35693
|
-
const kid = (0,
|
|
35923
|
+
const kid = (0, import_node_crypto13.createHash)("sha256").update(JSON.stringify({ e: publicJwk.e, kty: publicJwk.kty, n: publicJwk.n })).digest("base64url").slice(0, 16);
|
|
35694
35924
|
publicJwk.kid = kid;
|
|
35695
35925
|
publicJwk.alg = "RS256";
|
|
35696
35926
|
publicJwk.use = "sig";
|
|
@@ -35869,23 +36099,23 @@ async function validateAuthRequest(p) {
|
|
|
35869
36099
|
}
|
|
35870
36100
|
function pkceMatches(verifier, challenge) {
|
|
35871
36101
|
if (!verifier) return false;
|
|
35872
|
-
const computed = (0,
|
|
36102
|
+
const computed = (0, import_node_crypto13.createHash)("sha256").update(verifier).digest("base64url");
|
|
35873
36103
|
return computed === challenge;
|
|
35874
36104
|
}
|
|
35875
36105
|
async function mintAccessToken(identity, scope, plan, audience) {
|
|
35876
36106
|
const { privateKey, kid } = await getKeys();
|
|
35877
|
-
return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0,
|
|
36107
|
+
return new import_jose2.SignJWT({ scope, plan }).setProtectedHeader({ alg: "RS256", kid }).setIssuer(ISSUER).setSubject(identity).setAudience(audience).setIssuedAt().setJti((0, import_node_crypto13.randomUUID)()).setExpirationTime(`${ACCESS_TTL_SECONDS}s`).sign(privateKey);
|
|
35878
36108
|
}
|
|
35879
36109
|
function tokenErrorResponse(c, error, description, status) {
|
|
35880
36110
|
return c.json({ error, error_description: description }, status);
|
|
35881
36111
|
}
|
|
35882
|
-
var import_hono19, import_cookie,
|
|
36112
|
+
var import_hono19, import_cookie, import_node_crypto13, import_jose2, ISSUER, RESOURCE, SCRAPER_RESOURCE, MEMORY_SCOPES, SCRAPER_SCOPES, SUPPORTED_SCOPES, ACCESS_TTL_SECONDS, REFRESH_TTL_SECONDS, CODE_TTL_SECONDS, ROTATION_GRACE_SECONDS, secureCookies, sessionCookieOptions, cachedKeys, oauthApp;
|
|
35883
36113
|
var init_oauth_routes = __esm({
|
|
35884
36114
|
"src/api/oauth-routes.ts"() {
|
|
35885
36115
|
"use strict";
|
|
35886
36116
|
import_hono19 = require("hono");
|
|
35887
36117
|
import_cookie = require("hono/cookie");
|
|
35888
|
-
|
|
36118
|
+
import_node_crypto13 = require("crypto");
|
|
35889
36119
|
import_jose2 = require("jose");
|
|
35890
36120
|
init_session();
|
|
35891
36121
|
init_db();
|
|
@@ -35971,7 +36201,7 @@ var init_oauth_routes = __esm({
|
|
|
35971
36201
|
}
|
|
35972
36202
|
}
|
|
35973
36203
|
const clientName = typeof body.client_name === "string" ? body.client_name : null;
|
|
35974
|
-
const clientId = `client_${(0,
|
|
36204
|
+
const clientId = `client_${(0, import_node_crypto13.randomBytes)(16).toString("hex")}`;
|
|
35975
36205
|
await registerClient(clientId, redirectUris, clientName);
|
|
35976
36206
|
console.log("[oauth-dcr] register OK client_id=%s redirect_uris=%s", clientId, JSON.stringify(redirectUris));
|
|
35977
36207
|
return c.json({
|
|
@@ -36024,7 +36254,7 @@ var init_oauth_routes = __esm({
|
|
|
36024
36254
|
if (action === "deny") return redirectWithError(p.redirect_uri, p.state, "access_denied");
|
|
36025
36255
|
if (action !== "approve") return c.text("unsupported action", 400);
|
|
36026
36256
|
const scope = negotiateScope(p.scope, user, p.resource);
|
|
36027
|
-
const code = `code_${(0,
|
|
36257
|
+
const code = `code_${(0, import_node_crypto13.randomBytes)(32).toString("base64url")}`;
|
|
36028
36258
|
const expiresAt = new Date(Date.now() + CODE_TTL_SECONDS * 1e3).toISOString();
|
|
36029
36259
|
await putCode({
|
|
36030
36260
|
code,
|
|
@@ -36063,7 +36293,7 @@ var init_oauth_routes = __esm({
|
|
|
36063
36293
|
const plan = user ? resolvePlan(user) : "free";
|
|
36064
36294
|
const audience = record.resource ?? RESOURCE();
|
|
36065
36295
|
const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
|
|
36066
|
-
const refreshToken = `rt_${(0,
|
|
36296
|
+
const refreshToken = `rt_${(0, import_node_crypto13.randomBytes)(40).toString("base64url")}`;
|
|
36067
36297
|
await putRefresh({
|
|
36068
36298
|
refresh_token: refreshToken,
|
|
36069
36299
|
client_id: clientId,
|
|
@@ -36093,7 +36323,7 @@ var init_oauth_routes = __esm({
|
|
|
36093
36323
|
const plan = user ? resolvePlan(user) : "free";
|
|
36094
36324
|
const audience = record.resource ?? RESOURCE();
|
|
36095
36325
|
const accessToken = await mintAccessToken(record.identity, record.scope, plan, audience);
|
|
36096
|
-
const nextRefresh = `rt_${(0,
|
|
36326
|
+
const nextRefresh = `rt_${(0, import_node_crypto13.randomBytes)(40).toString("base64url")}`;
|
|
36097
36327
|
await rotateRefresh(refreshToken, {
|
|
36098
36328
|
refresh_token: nextRefresh,
|
|
36099
36329
|
client_id: record.client_id,
|