@hasna/browser 0.0.7 → 0.0.9
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/cli/index.js +857 -276
- package/dist/db/sessions.d.ts +15 -0
- package/dist/db/sessions.d.ts.map +1 -1
- package/dist/db/timeline.d.ts +11 -0
- package/dist/db/timeline.d.ts.map +1 -0
- package/dist/engines/playwright.d.ts +1 -1
- package/dist/engines/playwright.d.ts.map +1 -1
- package/dist/index.js +117 -12
- package/dist/lib/session.d.ts +7 -0
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/mcp/index.js +946 -397
- package/dist/server/index.js +98 -14
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -2309,6 +2309,38 @@ function runMigrations(db) {
|
|
|
2309
2309
|
CREATE INDEX IF NOT EXISTS idx_gallery_favorite ON gallery_entries(is_favorite);
|
|
2310
2310
|
CREATE INDEX IF NOT EXISTS idx_gallery_created ON gallery_entries(created_at);
|
|
2311
2311
|
`
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
version: 3,
|
|
2315
|
+
sql: `
|
|
2316
|
+
-- Session lock/claim for multi-agent ownership
|
|
2317
|
+
ALTER TABLE sessions ADD COLUMN locked_by TEXT;
|
|
2318
|
+
ALTER TABLE sessions ADD COLUMN locked_at TEXT;
|
|
2319
|
+
`
|
|
2320
|
+
},
|
|
2321
|
+
{
|
|
2322
|
+
version: 4,
|
|
2323
|
+
sql: `
|
|
2324
|
+
CREATE TABLE IF NOT EXISTS session_events (
|
|
2325
|
+
id TEXT PRIMARY KEY,
|
|
2326
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
2327
|
+
event_type TEXT NOT NULL,
|
|
2328
|
+
details TEXT DEFAULT '{}',
|
|
2329
|
+
timestamp TEXT DEFAULT (datetime('now'))
|
|
2330
|
+
);
|
|
2331
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id, timestamp);
|
|
2332
|
+
`
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
version: 5,
|
|
2336
|
+
sql: `
|
|
2337
|
+
CREATE TABLE IF NOT EXISTS session_tags (
|
|
2338
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
2339
|
+
tag TEXT NOT NULL,
|
|
2340
|
+
PRIMARY KEY (session_id, tag)
|
|
2341
|
+
);
|
|
2342
|
+
CREATE INDEX IF NOT EXISTS idx_session_tags_tag ON session_tags(tag);
|
|
2343
|
+
`
|
|
2312
2344
|
}
|
|
2313
2345
|
];
|
|
2314
2346
|
for (const m of migrations) {
|
|
@@ -2324,6 +2356,28 @@ var _db = null, _dbPath = null;
|
|
|
2324
2356
|
var init_schema = () => {};
|
|
2325
2357
|
|
|
2326
2358
|
// src/db/sessions.ts
|
|
2359
|
+
var exports_sessions = {};
|
|
2360
|
+
__export(exports_sessions, {
|
|
2361
|
+
updateSessionStatus: () => updateSessionStatus,
|
|
2362
|
+
unlockSession: () => unlockSession,
|
|
2363
|
+
transferSession: () => transferSession,
|
|
2364
|
+
renameSession: () => renameSession,
|
|
2365
|
+
removeSessionTag: () => removeSessionTag,
|
|
2366
|
+
lockSession: () => lockSession,
|
|
2367
|
+
listSessionsByTag: () => listSessionsByTag,
|
|
2368
|
+
listSessions: () => listSessions,
|
|
2369
|
+
isSessionLocked: () => isSessionLocked,
|
|
2370
|
+
getSessionTags: () => getSessionTags,
|
|
2371
|
+
getSessionByName: () => getSessionByName,
|
|
2372
|
+
getSession: () => getSession,
|
|
2373
|
+
getDefaultActiveSession: () => getDefaultActiveSession,
|
|
2374
|
+
getActiveSessionForAgent: () => getActiveSessionForAgent,
|
|
2375
|
+
deleteSession: () => deleteSession,
|
|
2376
|
+
createSession: () => createSession,
|
|
2377
|
+
countActiveSessions: () => countActiveSessions,
|
|
2378
|
+
closeSession: () => closeSession,
|
|
2379
|
+
addSessionTag: () => addSessionTag
|
|
2380
|
+
});
|
|
2327
2381
|
import { randomUUID } from "crypto";
|
|
2328
2382
|
function createSession(data) {
|
|
2329
2383
|
const db = getDatabase();
|
|
@@ -2376,8 +2430,81 @@ function updateSessionStatus(id, status) {
|
|
|
2376
2430
|
return getSession(id);
|
|
2377
2431
|
}
|
|
2378
2432
|
function closeSession(id) {
|
|
2433
|
+
const db = getDatabase();
|
|
2434
|
+
db.prepare("UPDATE sessions SET locked_by = NULL, locked_at = NULL WHERE id = ?").run(id);
|
|
2379
2435
|
return updateSessionStatus(id, "closed");
|
|
2380
2436
|
}
|
|
2437
|
+
function lockSession(id, agentId) {
|
|
2438
|
+
const db = getDatabase();
|
|
2439
|
+
const session = getSession(id);
|
|
2440
|
+
if (session.status !== "active")
|
|
2441
|
+
throw new SessionNotFoundError(id);
|
|
2442
|
+
const row = db.query("SELECT locked_by FROM sessions WHERE id = ?").get(id);
|
|
2443
|
+
if (row?.locked_by && row.locked_by !== agentId) {
|
|
2444
|
+
throw new Error(`Session locked by agent ${row.locked_by}`);
|
|
2445
|
+
}
|
|
2446
|
+
db.prepare("UPDATE sessions SET locked_by = ?, locked_at = datetime('now') WHERE id = ?").run(agentId, id);
|
|
2447
|
+
return getSession(id);
|
|
2448
|
+
}
|
|
2449
|
+
function unlockSession(id, agentId) {
|
|
2450
|
+
const db = getDatabase();
|
|
2451
|
+
if (agentId) {
|
|
2452
|
+
const row = db.query("SELECT locked_by FROM sessions WHERE id = ?").get(id);
|
|
2453
|
+
if (row?.locked_by && row.locked_by !== agentId) {
|
|
2454
|
+
throw new Error(`Session locked by agent ${row.locked_by}, not ${agentId}`);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
db.prepare("UPDATE sessions SET locked_by = NULL, locked_at = NULL WHERE id = ?").run(id);
|
|
2458
|
+
return getSession(id);
|
|
2459
|
+
}
|
|
2460
|
+
function isSessionLocked(id) {
|
|
2461
|
+
const db = getDatabase();
|
|
2462
|
+
const row = db.query("SELECT locked_by, locked_at FROM sessions WHERE id = ?").get(id);
|
|
2463
|
+
if (!row)
|
|
2464
|
+
throw new SessionNotFoundError(id);
|
|
2465
|
+
return { locked: !!row.locked_by, locked_by: row.locked_by ?? undefined, locked_at: row.locked_at ?? undefined };
|
|
2466
|
+
}
|
|
2467
|
+
function transferSession(id, toAgentId) {
|
|
2468
|
+
const db = getDatabase();
|
|
2469
|
+
db.prepare("UPDATE sessions SET agent_id = ?, locked_by = ?, locked_at = datetime('now') WHERE id = ?").run(toAgentId, toAgentId, id);
|
|
2470
|
+
return getSession(id);
|
|
2471
|
+
}
|
|
2472
|
+
function getActiveSessionForAgent(agentId) {
|
|
2473
|
+
const db = getDatabase();
|
|
2474
|
+
return db.query("SELECT * FROM sessions WHERE agent_id = ? AND status = 'active' ORDER BY created_at DESC LIMIT 1").get(agentId) ?? null;
|
|
2475
|
+
}
|
|
2476
|
+
function getDefaultActiveSession() {
|
|
2477
|
+
const db = getDatabase();
|
|
2478
|
+
const rows = db.query("SELECT * FROM sessions WHERE status = 'active' ORDER BY created_at DESC LIMIT 2").all();
|
|
2479
|
+
return rows.length === 1 ? rows[0] : null;
|
|
2480
|
+
}
|
|
2481
|
+
function countActiveSessions() {
|
|
2482
|
+
const db = getDatabase();
|
|
2483
|
+
const row = db.query("SELECT COUNT(*) as count FROM sessions WHERE status = 'active'").get();
|
|
2484
|
+
return row?.count ?? 0;
|
|
2485
|
+
}
|
|
2486
|
+
function deleteSession(id) {
|
|
2487
|
+
const db = getDatabase();
|
|
2488
|
+
db.prepare("DELETE FROM sessions WHERE id = ?").run(id);
|
|
2489
|
+
}
|
|
2490
|
+
function addSessionTag(id, tag) {
|
|
2491
|
+
const db = getDatabase();
|
|
2492
|
+
db.prepare("INSERT OR IGNORE INTO session_tags (session_id, tag) VALUES (?, ?)").run(id, tag);
|
|
2493
|
+
return getSessionTags(id);
|
|
2494
|
+
}
|
|
2495
|
+
function removeSessionTag(id, tag) {
|
|
2496
|
+
const db = getDatabase();
|
|
2497
|
+
db.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(id, tag);
|
|
2498
|
+
return getSessionTags(id);
|
|
2499
|
+
}
|
|
2500
|
+
function getSessionTags(id) {
|
|
2501
|
+
const db = getDatabase();
|
|
2502
|
+
return db.query("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(id).map((r) => r.tag);
|
|
2503
|
+
}
|
|
2504
|
+
function listSessionsByTag(tag) {
|
|
2505
|
+
const db = getDatabase();
|
|
2506
|
+
return db.query("SELECT s.* FROM sessions s JOIN session_tags t ON s.id = t.session_id WHERE t.tag = ? ORDER BY s.created_at DESC").all(tag);
|
|
2507
|
+
}
|
|
2381
2508
|
var init_sessions = __esm(() => {
|
|
2382
2509
|
init_schema();
|
|
2383
2510
|
init_types();
|
|
@@ -2403,10 +2530,52 @@ async function getPage(browser, options) {
|
|
|
2403
2530
|
});
|
|
2404
2531
|
return context.newPage();
|
|
2405
2532
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2533
|
+
|
|
2534
|
+
class BrowserPool {
|
|
2535
|
+
pool = [];
|
|
2536
|
+
maxSize;
|
|
2537
|
+
options;
|
|
2538
|
+
constructor(maxSize = 3, options) {
|
|
2539
|
+
this.maxSize = maxSize;
|
|
2540
|
+
this.options = options;
|
|
2541
|
+
}
|
|
2542
|
+
async acquire(headless = true) {
|
|
2543
|
+
const available = this.pool.find((e) => !e.inUse);
|
|
2544
|
+
if (available) {
|
|
2545
|
+
available.inUse = true;
|
|
2546
|
+
return available.browser;
|
|
2547
|
+
}
|
|
2548
|
+
if (this.pool.length < this.maxSize) {
|
|
2549
|
+
const browser = await launchPlaywright({ ...this.options, headless });
|
|
2550
|
+
this.pool.push({ browser, inUse: true, createdAt: Date.now() });
|
|
2551
|
+
return browser;
|
|
2552
|
+
}
|
|
2553
|
+
return new Promise((resolve) => {
|
|
2554
|
+
const interval = setInterval(() => {
|
|
2555
|
+
const free = this.pool.find((e) => !e.inUse);
|
|
2556
|
+
if (free) {
|
|
2557
|
+
clearInterval(interval);
|
|
2558
|
+
free.inUse = true;
|
|
2559
|
+
resolve(free.browser);
|
|
2560
|
+
}
|
|
2561
|
+
}, 100);
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
release(browser) {
|
|
2565
|
+
const entry = this.pool.find((e) => e.browser === browser);
|
|
2566
|
+
if (entry)
|
|
2567
|
+
entry.inUse = false;
|
|
2568
|
+
}
|
|
2569
|
+
async destroyAll() {
|
|
2570
|
+
await Promise.all(this.pool.map((e) => e.browser.close().catch(() => {})));
|
|
2571
|
+
this.pool = [];
|
|
2572
|
+
}
|
|
2573
|
+
get size() {
|
|
2574
|
+
return this.pool.length;
|
|
2575
|
+
}
|
|
2576
|
+
get available() {
|
|
2577
|
+
return this.pool.filter((e) => !e.inUse).length;
|
|
2578
|
+
}
|
|
2410
2579
|
}
|
|
2411
2580
|
var DEFAULT_VIEWPORT;
|
|
2412
2581
|
var init_playwright = __esm(() => {
|
|
@@ -3294,6 +3463,7 @@ __export(exports_session, {
|
|
|
3294
3463
|
renameSession: () => renameSession2,
|
|
3295
3464
|
listSessions: () => listSessions2,
|
|
3296
3465
|
isBunSession: () => isBunSession,
|
|
3466
|
+
isAutoGallery: () => isAutoGallery,
|
|
3297
3467
|
hasActiveHandle: () => hasActiveHandle,
|
|
3298
3468
|
getTokenBudget: () => getTokenBudget,
|
|
3299
3469
|
getSessionPage: () => getSessionPage,
|
|
@@ -3302,10 +3472,14 @@ __export(exports_session, {
|
|
|
3302
3472
|
getSessionBunView: () => getSessionBunView,
|
|
3303
3473
|
getSessionBrowser: () => getSessionBrowser,
|
|
3304
3474
|
getSession: () => getSession2,
|
|
3475
|
+
getDefaultSession: () => getDefaultSession,
|
|
3305
3476
|
getActiveSessions: () => getActiveSessions,
|
|
3477
|
+
getActiveSessionForAgent: () => getActiveSessionForAgent2,
|
|
3306
3478
|
createSession: () => createSession2,
|
|
3479
|
+
countActiveSessions: () => countActiveSessions2,
|
|
3307
3480
|
closeSession: () => closeSession2,
|
|
3308
|
-
closeAllSessions: () => closeAllSessions
|
|
3481
|
+
closeAllSessions: () => closeAllSessions,
|
|
3482
|
+
browserPool: () => pool
|
|
3309
3483
|
});
|
|
3310
3484
|
function createBunProxy(view) {
|
|
3311
3485
|
return view;
|
|
@@ -3335,11 +3509,7 @@ async function createSession2(opts = {}) {
|
|
|
3335
3509
|
const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
|
|
3336
3510
|
page = await context.newPage();
|
|
3337
3511
|
} else {
|
|
3338
|
-
browser = await
|
|
3339
|
-
headless: opts.headless ?? true,
|
|
3340
|
-
viewport: opts.viewport,
|
|
3341
|
-
userAgent: opts.userAgent
|
|
3342
|
-
});
|
|
3512
|
+
browser = await pool.acquire(opts.headless ?? true);
|
|
3343
3513
|
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
3344
3514
|
}
|
|
3345
3515
|
const sessionName = opts.name ?? (opts.startUrl ? (() => {
|
|
@@ -3393,7 +3563,7 @@ async function createSession2(opts = {}) {
|
|
|
3393
3563
|
} catch {}
|
|
3394
3564
|
}
|
|
3395
3565
|
}
|
|
3396
|
-
handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 } });
|
|
3566
|
+
handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
3397
3567
|
if (opts.startUrl) {
|
|
3398
3568
|
try {
|
|
3399
3569
|
if (bunView) {
|
|
@@ -3419,6 +3589,7 @@ function getSessionPage(sessionId) {
|
|
|
3419
3589
|
handles.delete(sessionId);
|
|
3420
3590
|
throw new SessionNotFoundError(sessionId);
|
|
3421
3591
|
}
|
|
3592
|
+
handle.lastActivity = Date.now();
|
|
3422
3593
|
return handle.page;
|
|
3423
3594
|
}
|
|
3424
3595
|
function getSessionBunView(sessionId) {
|
|
@@ -3466,10 +3637,8 @@ async function closeSession2(sessionId) {
|
|
|
3466
3637
|
try {
|
|
3467
3638
|
await handle.page.context().close();
|
|
3468
3639
|
} catch {}
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
await closeBrowser(handle.browser);
|
|
3472
|
-
} catch {}
|
|
3640
|
+
if (handle.browser)
|
|
3641
|
+
pool.release(handle.browser);
|
|
3473
3642
|
}
|
|
3474
3643
|
handles.delete(sessionId);
|
|
3475
3644
|
}
|
|
@@ -3488,6 +3657,7 @@ async function closeAllSessions() {
|
|
|
3488
3657
|
for (const [id] of handles) {
|
|
3489
3658
|
await closeSession2(id).catch(() => {});
|
|
3490
3659
|
}
|
|
3660
|
+
await pool.destroyAll();
|
|
3491
3661
|
}
|
|
3492
3662
|
function getSessionByName2(name) {
|
|
3493
3663
|
return getSessionByName(name);
|
|
@@ -3499,7 +3669,49 @@ function getTokenBudget(sessionId) {
|
|
|
3499
3669
|
const handle = handles.get(sessionId);
|
|
3500
3670
|
return handle ? handle.tokenBudget : null;
|
|
3501
3671
|
}
|
|
3502
|
-
|
|
3672
|
+
function getActiveSessionForAgent2(agentId) {
|
|
3673
|
+
const session = getActiveSessionForAgent(agentId);
|
|
3674
|
+
if (!session)
|
|
3675
|
+
return null;
|
|
3676
|
+
const handle = handles.get(session.id);
|
|
3677
|
+
if (!handle)
|
|
3678
|
+
return null;
|
|
3679
|
+
try {
|
|
3680
|
+
if (handle.bunView)
|
|
3681
|
+
handle.bunView.url();
|
|
3682
|
+
else
|
|
3683
|
+
handle.page.url();
|
|
3684
|
+
} catch {
|
|
3685
|
+
handles.delete(session.id);
|
|
3686
|
+
return null;
|
|
3687
|
+
}
|
|
3688
|
+
return { session, page: handle.page };
|
|
3689
|
+
}
|
|
3690
|
+
function getDefaultSession() {
|
|
3691
|
+
const session = getDefaultActiveSession();
|
|
3692
|
+
if (!session)
|
|
3693
|
+
return null;
|
|
3694
|
+
const handle = handles.get(session.id);
|
|
3695
|
+
if (!handle)
|
|
3696
|
+
return null;
|
|
3697
|
+
try {
|
|
3698
|
+
if (handle.bunView)
|
|
3699
|
+
handle.bunView.url();
|
|
3700
|
+
else
|
|
3701
|
+
handle.page.url();
|
|
3702
|
+
} catch {
|
|
3703
|
+
handles.delete(session.id);
|
|
3704
|
+
return null;
|
|
3705
|
+
}
|
|
3706
|
+
return { session, page: handle.page };
|
|
3707
|
+
}
|
|
3708
|
+
function isAutoGallery(sessionId) {
|
|
3709
|
+
return handles.get(sessionId)?.autoGallery ?? false;
|
|
3710
|
+
}
|
|
3711
|
+
function countActiveSessions2() {
|
|
3712
|
+
return countActiveSessions();
|
|
3713
|
+
}
|
|
3714
|
+
var handles, pool, SESSION_TTL_MS, ttlInterval;
|
|
3503
3715
|
var init_session = __esm(() => {
|
|
3504
3716
|
init_types();
|
|
3505
3717
|
init_types();
|
|
@@ -3513,6 +3725,20 @@ var init_session = __esm(() => {
|
|
|
3513
3725
|
init_stealth();
|
|
3514
3726
|
init_dialogs();
|
|
3515
3727
|
handles = new Map;
|
|
3728
|
+
pool = new BrowserPool(5);
|
|
3729
|
+
SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
|
|
3730
|
+
ttlInterval = setInterval(async () => {
|
|
3731
|
+
const now = Date.now();
|
|
3732
|
+
for (const [id, handle] of handles) {
|
|
3733
|
+
if (now - handle.lastActivity > SESSION_TTL_MS) {
|
|
3734
|
+
try {
|
|
3735
|
+
await closeSession2(id);
|
|
3736
|
+
} catch {}
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
}, 60000);
|
|
3740
|
+
if (ttlInterval.unref)
|
|
3741
|
+
ttlInterval.unref();
|
|
3516
3742
|
});
|
|
3517
3743
|
|
|
3518
3744
|
// src/lib/snapshot.ts
|
|
@@ -15844,6 +16070,20 @@ async function persistFile(localPath, opts) {
|
|
|
15844
16070
|
}
|
|
15845
16071
|
var init_files_integration = () => {};
|
|
15846
16072
|
|
|
16073
|
+
// src/db/timeline.ts
|
|
16074
|
+
function logEvent(sessionId, eventType, details = {}) {
|
|
16075
|
+
const db = getDatabase();
|
|
16076
|
+
const id = crypto.randomUUID();
|
|
16077
|
+
db.prepare("INSERT INTO session_events (id, session_id, event_type, details) VALUES (?, ?, ?, ?)").run(id, sessionId, eventType, JSON.stringify(details));
|
|
16078
|
+
}
|
|
16079
|
+
function getTimeline(sessionId, limit = 100) {
|
|
16080
|
+
const db = getDatabase();
|
|
16081
|
+
return db.query("SELECT * FROM session_events WHERE session_id = ? ORDER BY timestamp DESC LIMIT ?").all(sessionId, limit);
|
|
16082
|
+
}
|
|
16083
|
+
var init_timeline = __esm(() => {
|
|
16084
|
+
init_schema();
|
|
16085
|
+
});
|
|
16086
|
+
|
|
15847
16087
|
// src/lib/tabs.ts
|
|
15848
16088
|
async function newTab(page, url) {
|
|
15849
16089
|
const context = page.context();
|
|
@@ -16235,6 +16475,7 @@ __export(exports_dist, {
|
|
|
16235
16475
|
shortUuid: () => shortUuid,
|
|
16236
16476
|
setFocus: () => setFocus,
|
|
16237
16477
|
setActiveProfile: () => setActiveProfile,
|
|
16478
|
+
setActiveModel: () => setActiveModel,
|
|
16238
16479
|
searchMemories: () => searchMemories,
|
|
16239
16480
|
runCleanup: () => runCleanup,
|
|
16240
16481
|
resolveProjectId: () => resolveProjectId,
|
|
@@ -16285,6 +16526,8 @@ __export(exports_dist, {
|
|
|
16285
16526
|
getAutoMemoryStats: () => getAutoMemoryStats,
|
|
16286
16527
|
getAgent: () => getAgent2,
|
|
16287
16528
|
getActiveProfile: () => getActiveProfile,
|
|
16529
|
+
getActiveModel: () => getActiveModel,
|
|
16530
|
+
gatherTrainingData: () => gatherTrainingData,
|
|
16288
16531
|
focusFilterSQL: () => focusFilterSQL,
|
|
16289
16532
|
findPath: () => findPath,
|
|
16290
16533
|
enforceQuotas: () => enforceQuotas,
|
|
@@ -16301,6 +16544,7 @@ __export(exports_dist, {
|
|
|
16301
16544
|
containsSecrets: () => containsSecrets,
|
|
16302
16545
|
configureAutoMemory: () => configureAutoMemory,
|
|
16303
16546
|
closeDatabase: () => closeDatabase,
|
|
16547
|
+
clearActiveModel: () => clearActiveModel,
|
|
16304
16548
|
cleanExpiredMemories: () => cleanExpiredMemories,
|
|
16305
16549
|
cleanExpiredLocks: () => cleanExpiredLocks,
|
|
16306
16550
|
checkMemoryWriteLock: () => checkMemoryWriteLock,
|
|
@@ -16321,6 +16565,7 @@ __export(exports_dist, {
|
|
|
16321
16565
|
InvalidScopeError: () => InvalidScopeError,
|
|
16322
16566
|
EntityNotFoundError: () => EntityNotFoundError,
|
|
16323
16567
|
DuplicateMemoryError: () => DuplicateMemoryError,
|
|
16568
|
+
DEFAULT_MODEL: () => DEFAULT_MODEL,
|
|
16324
16569
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
16325
16570
|
});
|
|
16326
16571
|
import { Database as Database2 } from "bun:sqlite";
|
|
@@ -16332,6 +16577,9 @@ import { basename as basename2, dirname as dirname2, join as join22, resolve as
|
|
|
16332
16577
|
import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
16333
16578
|
import { homedir as homedir22 } from "os";
|
|
16334
16579
|
import { join as join32 } from "path";
|
|
16580
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync42, readFileSync as readFileSync32, writeFileSync as writeFileSync32 } from "fs";
|
|
16581
|
+
import { homedir as homedir32 } from "os";
|
|
16582
|
+
import { join as join42 } from "path";
|
|
16335
16583
|
function isInMemoryDb(path) {
|
|
16336
16584
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
16337
16585
|
}
|
|
@@ -19365,6 +19613,85 @@ function jaccardSimilarity(a, b) {
|
|
|
19365
19613
|
const union = new Set([...a, ...b]).size;
|
|
19366
19614
|
return intersection / union;
|
|
19367
19615
|
}
|
|
19616
|
+
function memoryToRecallExample(memory) {
|
|
19617
|
+
return {
|
|
19618
|
+
messages: [
|
|
19619
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
19620
|
+
{
|
|
19621
|
+
role: "user",
|
|
19622
|
+
content: `What do you remember about "${memory.key}"?`
|
|
19623
|
+
},
|
|
19624
|
+
{
|
|
19625
|
+
role: "assistant",
|
|
19626
|
+
content: memory.summary ? `${memory.value}
|
|
19627
|
+
|
|
19628
|
+
Summary: ${memory.summary}` : memory.value
|
|
19629
|
+
}
|
|
19630
|
+
]
|
|
19631
|
+
};
|
|
19632
|
+
}
|
|
19633
|
+
function memoryToSaveExample(memory) {
|
|
19634
|
+
const tags = memory.tags ?? [];
|
|
19635
|
+
return {
|
|
19636
|
+
messages: [
|
|
19637
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
19638
|
+
{
|
|
19639
|
+
role: "user",
|
|
19640
|
+
content: `Remember this for me: ${memory.key} = ${memory.value}${tags.length ? ` (tags: ${tags.join(", ")})` : ""}`
|
|
19641
|
+
},
|
|
19642
|
+
{
|
|
19643
|
+
role: "assistant",
|
|
19644
|
+
content: `Saved to memory: "${memory.key}" with ${memory.category} category, importance ${memory.importance}/10, scope: ${memory.scope}.`
|
|
19645
|
+
}
|
|
19646
|
+
]
|
|
19647
|
+
};
|
|
19648
|
+
}
|
|
19649
|
+
function memoryToSearchExample(memories, category) {
|
|
19650
|
+
const matched = memories.filter((m) => m.category === category && m.status === "active").slice(0, 5);
|
|
19651
|
+
return {
|
|
19652
|
+
messages: [
|
|
19653
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
19654
|
+
{ role: "user", content: `What ${category} memories do you have?` },
|
|
19655
|
+
{
|
|
19656
|
+
role: "assistant",
|
|
19657
|
+
content: matched.length > 0 ? `Here are my ${category} memories:
|
|
19658
|
+
${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120 ? "..." : ""}`).join(`
|
|
19659
|
+
`)}` : `I don't have any ${category} memories stored yet.`
|
|
19660
|
+
}
|
|
19661
|
+
]
|
|
19662
|
+
};
|
|
19663
|
+
}
|
|
19664
|
+
function readConfig() {
|
|
19665
|
+
if (!existsSync42(CONFIG_PATH))
|
|
19666
|
+
return {};
|
|
19667
|
+
try {
|
|
19668
|
+
const raw = readFileSync32(CONFIG_PATH, "utf-8");
|
|
19669
|
+
return JSON.parse(raw);
|
|
19670
|
+
} catch {
|
|
19671
|
+
return {};
|
|
19672
|
+
}
|
|
19673
|
+
}
|
|
19674
|
+
function writeConfig(config) {
|
|
19675
|
+
if (!existsSync42(CONFIG_DIR)) {
|
|
19676
|
+
mkdirSync42(CONFIG_DIR, { recursive: true });
|
|
19677
|
+
}
|
|
19678
|
+
writeFileSync32(CONFIG_PATH, JSON.stringify(config, null, 2) + `
|
|
19679
|
+
`, "utf-8");
|
|
19680
|
+
}
|
|
19681
|
+
function getActiveModel() {
|
|
19682
|
+
const config = readConfig();
|
|
19683
|
+
return config.activeModel ?? DEFAULT_MODEL;
|
|
19684
|
+
}
|
|
19685
|
+
function setActiveModel(modelId) {
|
|
19686
|
+
const config = readConfig();
|
|
19687
|
+
config.activeModel = modelId;
|
|
19688
|
+
writeConfig(config);
|
|
19689
|
+
}
|
|
19690
|
+
function clearActiveModel() {
|
|
19691
|
+
const config = readConfig();
|
|
19692
|
+
delete config.activeModel;
|
|
19693
|
+
writeConfig(config);
|
|
19694
|
+
}
|
|
19368
19695
|
var __defProp2, __export2 = (target, all) => {
|
|
19369
19696
|
for (var name in all)
|
|
19370
19697
|
__defProp2(target, name, {
|
|
@@ -19407,7 +19734,27 @@ Return JSON with this exact shape:
|
|
|
19407
19734
|
"relations": [
|
|
19408
19735
|
{ "from": string, "to": string, "type": "uses"|"knows"|"depends_on"|"created_by"|"related_to"|"contradicts"|"part_of"|"implements"|"happened_before"|"happened_after"|"caused_by"|"resulted_in"|"supersedes"|"version_of" }
|
|
19409
19736
|
]
|
|
19410
|
-
}`, ANTHROPIC_MODELS, AnthropicProvider, OpenAICompatProvider, OPENAI_MODELS, OpenAIProvider, CEREBRAS_MODELS, CerebrasProvider, GROK_MODELS, GrokProvider, providerRegistry, MAX_QUEUE_SIZE = 100, CONCURRENCY = 3, autoMemoryQueue, DEDUP_SIMILARITY_THRESHOLD = 0.85, DEFAULT_CONFIG2, _stats
|
|
19737
|
+
}`, ANTHROPIC_MODELS, AnthropicProvider, OpenAICompatProvider, OPENAI_MODELS, OpenAIProvider, CEREBRAS_MODELS, CerebrasProvider, GROK_MODELS, GrokProvider, providerRegistry, MAX_QUEUE_SIZE = 100, CONCURRENCY = 3, autoMemoryQueue, DEDUP_SIMILARITY_THRESHOLD = 0.85, DEFAULT_CONFIG2, _stats, SYSTEM_PROMPT = "You are an AI assistant with persistent memory that recalls and saves information across sessions.", gatherTrainingData = async (options = {}) => {
|
|
19738
|
+
const allMemories = listMemories({ status: "active" });
|
|
19739
|
+
const filtered = options.since ? allMemories.filter((m) => new Date(m.created_at) >= options.since) : allMemories;
|
|
19740
|
+
const sorted = filtered.slice().sort((a, b) => b.importance - a.importance);
|
|
19741
|
+
const fetchSet = options.limit ? sorted.slice(0, options.limit * 3) : sorted;
|
|
19742
|
+
const examples = [];
|
|
19743
|
+
for (const memory of fetchSet) {
|
|
19744
|
+
examples.push(memoryToRecallExample(memory));
|
|
19745
|
+
examples.push(memoryToSaveExample(memory));
|
|
19746
|
+
}
|
|
19747
|
+
const categories = [...new Set(fetchSet.map((m) => m.category))];
|
|
19748
|
+
for (const category of categories) {
|
|
19749
|
+
examples.push(memoryToSearchExample(fetchSet, category));
|
|
19750
|
+
}
|
|
19751
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
19752
|
+
return {
|
|
19753
|
+
source: "mementos",
|
|
19754
|
+
examples: finalExamples,
|
|
19755
|
+
count: finalExamples.length
|
|
19756
|
+
};
|
|
19757
|
+
}, DEFAULT_MODEL = "gpt-4o-mini", CONFIG_DIR, CONFIG_PATH;
|
|
19411
19758
|
var init_dist = __esm(() => {
|
|
19412
19759
|
__defProp2 = Object.defineProperty;
|
|
19413
19760
|
exports_database = {};
|
|
@@ -20687,6 +21034,8 @@ Return only a number 0-10.`);
|
|
|
20687
21034
|
keepLonger: true
|
|
20688
21035
|
};
|
|
20689
21036
|
_stats = { checked: 0, skipped: 0, updated: 0 };
|
|
21037
|
+
CONFIG_DIR = join42(homedir32(), ".mementos");
|
|
21038
|
+
CONFIG_PATH = join42(CONFIG_DIR, "config.json");
|
|
20690
21039
|
});
|
|
20691
21040
|
|
|
20692
21041
|
// src/lib/page-memory.ts
|
|
@@ -20847,13 +21196,13 @@ import { homedir as homedir10 } from "os";
|
|
|
20847
21196
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
20848
21197
|
import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
20849
21198
|
import { join as join33 } from "path";
|
|
20850
|
-
import { homedir as
|
|
21199
|
+
import { homedir as homedir33 } from "os";
|
|
20851
21200
|
import { readFileSync as readFileSync5 } from "fs";
|
|
20852
21201
|
import { join as join23 } from "path";
|
|
20853
21202
|
import { homedir as homedir23 } from "os";
|
|
20854
21203
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
20855
21204
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync4, mkdirSync as mkdirSync33 } from "fs";
|
|
20856
|
-
import { join as
|
|
21205
|
+
import { join as join43, dirname as dirname22 } from "path";
|
|
20857
21206
|
import { homedir as homedir42 } from "os";
|
|
20858
21207
|
function getDbPath2() {
|
|
20859
21208
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
@@ -21192,7 +21541,7 @@ function parseMessage(row) {
|
|
|
21192
21541
|
function getAttachmentsDir() {
|
|
21193
21542
|
if (process.env.CONVERSATIONS_ATTACHMENTS_DIR)
|
|
21194
21543
|
return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
|
|
21195
|
-
return join33(
|
|
21544
|
+
return join33(homedir33(), ".conversations", "attachments");
|
|
21196
21545
|
}
|
|
21197
21546
|
function guessMimeType(name) {
|
|
21198
21547
|
const ext = name.split(".").pop()?.toLowerCase();
|
|
@@ -24863,7 +25212,7 @@ Check the top-level render call using <` + parentName + ">.";
|
|
|
24863
25212
|
"zinc-eagle",
|
|
24864
25213
|
"zone-fox"
|
|
24865
25214
|
];
|
|
24866
|
-
AGENT_ID_FILE =
|
|
25215
|
+
AGENT_ID_FILE = join43(homedir42(), ".conversations", "agent-id");
|
|
24867
25216
|
init_db();
|
|
24868
25217
|
init_db();
|
|
24869
25218
|
CONFLICT_THRESHOLD_SECONDS = 30 * 60;
|
|
@@ -25205,7 +25554,7 @@ __export(exports_dist3, {
|
|
|
25205
25554
|
deleteTemplate: () => deleteTemplate,
|
|
25206
25555
|
deleteTaskList: () => deleteTaskList,
|
|
25207
25556
|
deleteTask: () => deleteTask,
|
|
25208
|
-
deleteSession: () =>
|
|
25557
|
+
deleteSession: () => deleteSession2,
|
|
25209
25558
|
deleteProject: () => deleteProject2,
|
|
25210
25559
|
deletePlan: () => deletePlan,
|
|
25211
25560
|
deleteOrg: () => deleteOrg,
|
|
@@ -25272,11 +25621,11 @@ import { existsSync as existsSync33 } from "fs";
|
|
|
25272
25621
|
import { join as join34 } from "path";
|
|
25273
25622
|
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
25274
25623
|
import { join as join24 } from "path";
|
|
25275
|
-
import { existsSync as
|
|
25276
|
-
import { join as
|
|
25624
|
+
import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
25625
|
+
import { join as join44 } from "path";
|
|
25277
25626
|
import { existsSync as existsSync52 } from "fs";
|
|
25278
25627
|
import { join as join52 } from "path";
|
|
25279
|
-
import { readFileSync as
|
|
25628
|
+
import { readFileSync as readFileSync33, statSync as statSync22 } from "fs";
|
|
25280
25629
|
import { relative, resolve as resolve22, join as join62 } from "path";
|
|
25281
25630
|
import { execSync as execSync2 } from "child_process";
|
|
25282
25631
|
|
|
@@ -27634,11 +27983,11 @@ function autoReleaseStaleAgents(db2) {
|
|
|
27634
27983
|
const result = d.run("UPDATE agents SET session_id = NULL WHERE status = 'active' AND session_id IS NOT NULL AND last_seen_at < ?", [cutoff]);
|
|
27635
27984
|
return result.changes;
|
|
27636
27985
|
}
|
|
27637
|
-
function getAvailableNamesFromPool(
|
|
27986
|
+
function getAvailableNamesFromPool(pool2, db2) {
|
|
27638
27987
|
autoReleaseStaleAgents(db2);
|
|
27639
27988
|
const cutoff = new Date(Date.now() - getActiveWindowMs()).toISOString();
|
|
27640
27989
|
const activeNames = new Set(db2.query("SELECT name FROM agents WHERE status = 'active' AND last_seen_at > ?").all(cutoff).map((r) => r.name.toLowerCase()));
|
|
27641
|
-
return
|
|
27990
|
+
return pool2.filter((name) => !activeNames.has(name.toLowerCase()));
|
|
27642
27991
|
}
|
|
27643
27992
|
function shortUuid2() {
|
|
27644
27993
|
return crypto.randomUUID().slice(0, 8);
|
|
@@ -27715,9 +28064,9 @@ function registerAgent5(input, db2) {
|
|
|
27715
28064
|
function isAgentConflict2(result) {
|
|
27716
28065
|
return result.conflict === true;
|
|
27717
28066
|
}
|
|
27718
|
-
function buildConflictError(existing, lastSeenMs,
|
|
28067
|
+
function buildConflictError(existing, lastSeenMs, pool2, d) {
|
|
27719
28068
|
const minutesAgo = Math.round((Date.now() - lastSeenMs) / 60000);
|
|
27720
|
-
const suggestions =
|
|
28069
|
+
const suggestions = pool2 ? getAvailableNamesFromPool(pool2, d) : [];
|
|
27721
28070
|
return {
|
|
27722
28071
|
conflict: true,
|
|
27723
28072
|
existing_id: existing.id,
|
|
@@ -28003,7 +28352,7 @@ function updateSessionActivity(id, db2) {
|
|
|
28003
28352
|
const d = db2 || getDatabase3();
|
|
28004
28353
|
d.run("UPDATE sessions SET last_activity = ? WHERE id = ?", [now2(), id]);
|
|
28005
28354
|
}
|
|
28006
|
-
function
|
|
28355
|
+
function deleteSession2(id, db2) {
|
|
28007
28356
|
const d = db2 || getDatabase3();
|
|
28008
28357
|
const result = d.run("DELETE FROM sessions WHERE id = ?", [id]);
|
|
28009
28358
|
return result.changes > 0;
|
|
@@ -28822,13 +29171,13 @@ function searchTasks(options, projectId, taskListId, db2) {
|
|
|
28822
29171
|
return rows.map(rowToTask3);
|
|
28823
29172
|
}
|
|
28824
29173
|
function getTaskListDir(taskListId) {
|
|
28825
|
-
return
|
|
29174
|
+
return join44(HOME, ".claude", "tasks", taskListId);
|
|
28826
29175
|
}
|
|
28827
29176
|
function readClaudeTask(dir, filename) {
|
|
28828
|
-
return readJsonFile(
|
|
29177
|
+
return readJsonFile(join44(dir, filename));
|
|
28829
29178
|
}
|
|
28830
29179
|
function writeClaudeTask(dir, task) {
|
|
28831
|
-
writeJsonFile(
|
|
29180
|
+
writeJsonFile(join44(dir, `${task.id}.json`), task);
|
|
28832
29181
|
}
|
|
28833
29182
|
function toClaudeStatus(status) {
|
|
28834
29183
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -28840,14 +29189,14 @@ function toSqliteStatus(status) {
|
|
|
28840
29189
|
return status;
|
|
28841
29190
|
}
|
|
28842
29191
|
function readPrefixCounter(dir) {
|
|
28843
|
-
const path =
|
|
28844
|
-
if (!
|
|
29192
|
+
const path = join44(dir, ".prefix-counter");
|
|
29193
|
+
if (!existsSync43(path))
|
|
28845
29194
|
return 0;
|
|
28846
29195
|
const val = parseInt(readFileSync24(path, "utf-8").trim(), 10);
|
|
28847
29196
|
return isNaN(val) ? 0 : val;
|
|
28848
29197
|
}
|
|
28849
29198
|
function writePrefixCounter(dir, value) {
|
|
28850
|
-
writeFileSync23(
|
|
29199
|
+
writeFileSync23(join44(dir, ".prefix-counter"), String(value));
|
|
28851
29200
|
}
|
|
28852
29201
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
28853
29202
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -28874,7 +29223,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
|
|
|
28874
29223
|
}
|
|
28875
29224
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
28876
29225
|
const dir = getTaskListDir(taskListId);
|
|
28877
|
-
if (!
|
|
29226
|
+
if (!existsSync43(dir))
|
|
28878
29227
|
ensureDir22(dir);
|
|
28879
29228
|
const filter = {};
|
|
28880
29229
|
if (projectId)
|
|
@@ -28883,7 +29232,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
28883
29232
|
const existingByTodosId = new Map;
|
|
28884
29233
|
const files = listJsonFiles(dir);
|
|
28885
29234
|
for (const f of files) {
|
|
28886
|
-
const path =
|
|
29235
|
+
const path = join44(dir, f);
|
|
28887
29236
|
const ct = readClaudeTask(dir, f);
|
|
28888
29237
|
if (ct?.metadata?.["todos_id"]) {
|
|
28889
29238
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -28970,7 +29319,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
28970
29319
|
}
|
|
28971
29320
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
28972
29321
|
const dir = getTaskListDir(taskListId);
|
|
28973
|
-
if (!
|
|
29322
|
+
if (!existsSync43(dir)) {
|
|
28974
29323
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
28975
29324
|
}
|
|
28976
29325
|
const files = readdirSync22(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -28990,7 +29339,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
28990
29339
|
}
|
|
28991
29340
|
for (const f of files) {
|
|
28992
29341
|
try {
|
|
28993
|
-
const filePath =
|
|
29342
|
+
const filePath = join44(dir, f);
|
|
28994
29343
|
const ct = readClaudeTask(dir, f);
|
|
28995
29344
|
if (!ct)
|
|
28996
29345
|
continue;
|
|
@@ -29401,7 +29750,7 @@ function extractTodos(options, db2) {
|
|
|
29401
29750
|
for (const file of files) {
|
|
29402
29751
|
const fullPath = statSync22(basePath).isFile() ? basePath : join62(basePath, file);
|
|
29403
29752
|
try {
|
|
29404
|
-
const source =
|
|
29753
|
+
const source = readFileSync33(fullPath, "utf-8");
|
|
29405
29754
|
const relPath = statSync22(basePath).isFile() ? relative(resolve22(basePath, ".."), fullPath) : file;
|
|
29406
29755
|
const comments = extractFromSource(source, relPath, tags);
|
|
29407
29756
|
allComments.push(...comments);
|
|
@@ -30627,7 +30976,7 @@ import { homedir as homedir11 } from "os";
|
|
|
30627
30976
|
import { fileURLToPath } from "url";
|
|
30628
30977
|
import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
|
|
30629
30978
|
import { join as join25 } from "path";
|
|
30630
|
-
import { existsSync as existsSync34, readFileSync as
|
|
30979
|
+
import { existsSync as existsSync34, readFileSync as readFileSync34, writeFileSync as writeFileSync24, mkdirSync as mkdirSync25 } from "fs";
|
|
30631
30980
|
import { join as join35, dirname as dirname23 } from "path";
|
|
30632
30981
|
import { homedir as homedir24 } from "os";
|
|
30633
30982
|
function getSkillsByCategory(category) {
|
|
@@ -31281,7 +31630,7 @@ function readConfigFile(path) {
|
|
|
31281
31630
|
if (!existsSync34(path))
|
|
31282
31631
|
return {};
|
|
31283
31632
|
try {
|
|
31284
|
-
const raw =
|
|
31633
|
+
const raw = readFileSync34(path, "utf-8");
|
|
31285
31634
|
const parsed = JSON.parse(raw);
|
|
31286
31635
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
31287
31636
|
return {};
|
|
@@ -31314,7 +31663,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
31314
31663
|
let existing = {};
|
|
31315
31664
|
if (existsSync34(filePath)) {
|
|
31316
31665
|
try {
|
|
31317
|
-
existing = JSON.parse(
|
|
31666
|
+
existing = JSON.parse(readFileSync34(filePath, "utf-8"));
|
|
31318
31667
|
if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
|
|
31319
31668
|
existing = {};
|
|
31320
31669
|
}
|
|
@@ -33256,7 +33605,7 @@ ${snap.tree.slice(0, 2000)}`;
|
|
|
33256
33605
|
const response = await client.messages.create({
|
|
33257
33606
|
model,
|
|
33258
33607
|
max_tokens: 512,
|
|
33259
|
-
system:
|
|
33608
|
+
system: SYSTEM_PROMPT2,
|
|
33260
33609
|
messages: [{
|
|
33261
33610
|
role: "user",
|
|
33262
33611
|
content: `Task: ${task}
|
|
@@ -33321,7 +33670,7 @@ What actions should I take next? Return JSON array.`
|
|
|
33321
33670
|
}
|
|
33322
33671
|
return { success: false, result: null, steps_taken: steps.length, steps, cost_estimate: totalTokens / 1000 * 0.00025, error: `Reached max steps (${maxSteps}) without completing task` };
|
|
33323
33672
|
}
|
|
33324
|
-
var
|
|
33673
|
+
var SYSTEM_PROMPT2 = `You are a browser automation agent. Given a task and the current page state, decide which browser actions to take.
|
|
33325
33674
|
|
|
33326
33675
|
Return a JSON array of at most 3 actions to execute next:
|
|
33327
33676
|
[{"tool": "navigate|click|type|scroll|evaluate|done", "args": {...}, "reason": "..."}]
|
|
@@ -33347,6 +33696,34 @@ function err(e) {
|
|
|
33347
33696
|
isError: true
|
|
33348
33697
|
};
|
|
33349
33698
|
}
|
|
33699
|
+
async function errWithScreenshot(e, sessionId) {
|
|
33700
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
33701
|
+
const code = e instanceof BrowserError ? e.code : "ERROR";
|
|
33702
|
+
let screenshot_path;
|
|
33703
|
+
if (sessionId) {
|
|
33704
|
+
try {
|
|
33705
|
+
const sid = resolveSessionId(sessionId);
|
|
33706
|
+
const page = getSessionPage(sid);
|
|
33707
|
+
const result = await takeScreenshot(page, { maxWidth: 800, quality: 50, track: false, thumbnail: false });
|
|
33708
|
+
screenshot_path = result.path;
|
|
33709
|
+
} catch {}
|
|
33710
|
+
}
|
|
33711
|
+
return {
|
|
33712
|
+
content: [{ type: "text", text: JSON.stringify({ error: msg, code, error_screenshot: screenshot_path }) }],
|
|
33713
|
+
isError: true
|
|
33714
|
+
};
|
|
33715
|
+
}
|
|
33716
|
+
function resolveSessionId(sessionId) {
|
|
33717
|
+
if (sessionId)
|
|
33718
|
+
return sessionId;
|
|
33719
|
+
const def = getDefaultSession();
|
|
33720
|
+
if (def)
|
|
33721
|
+
return def.session.id;
|
|
33722
|
+
const count = countActiveSessions2();
|
|
33723
|
+
if (count === 0)
|
|
33724
|
+
throw new BrowserError("No active sessions. Create one with browser_session_create first.", "NO_SESSION");
|
|
33725
|
+
throw new BrowserError(`${count} active sessions \u2014 specify session_id to choose one.`, "AMBIGUOUS_SESSION");
|
|
33726
|
+
}
|
|
33350
33727
|
var _pkg, networkLogCleanup, consoleCaptureCleanup, harCaptures, server, activeWatchHandles2, _startupToolCount, transport;
|
|
33351
33728
|
var init_mcp = __esm(async () => {
|
|
33352
33729
|
init_zod();
|
|
@@ -33368,6 +33745,7 @@ var init_mcp = __esm(async () => {
|
|
|
33368
33745
|
init_snapshot();
|
|
33369
33746
|
init_files_integration();
|
|
33370
33747
|
init_recordings();
|
|
33748
|
+
init_timeline();
|
|
33371
33749
|
init_dialogs();
|
|
33372
33750
|
init_profiles();
|
|
33373
33751
|
init_types();
|
|
@@ -33379,7 +33757,7 @@ var init_mcp = __esm(async () => {
|
|
|
33379
33757
|
name: "@hasna/browser",
|
|
33380
33758
|
version: "0.0.1"
|
|
33381
33759
|
});
|
|
33382
|
-
server.tool("browser_session_create", "Create a new browser session
|
|
33760
|
+
server.tool("browser_session_create", "Create a new browser session. If agent_id is set and already has an active session, returns the existing one (use force_new to override). If session_id is omitted on other tools, the single active session is auto-selected.", {
|
|
33383
33761
|
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
|
|
33384
33762
|
use_case: exports_external.string().optional(),
|
|
33385
33763
|
project_id: exports_external.string().optional(),
|
|
@@ -33388,9 +33766,17 @@ var init_mcp = __esm(async () => {
|
|
|
33388
33766
|
headless: exports_external.boolean().optional().default(true),
|
|
33389
33767
|
viewport_width: exports_external.number().optional().default(1280),
|
|
33390
33768
|
viewport_height: exports_external.number().optional().default(720),
|
|
33391
|
-
stealth: exports_external.boolean().optional().default(false)
|
|
33392
|
-
|
|
33769
|
+
stealth: exports_external.boolean().optional().default(false),
|
|
33770
|
+
auto_gallery: exports_external.boolean().optional().default(false),
|
|
33771
|
+
force_new: exports_external.boolean().optional().default(false).describe("Force create a new session even if agent already has one"),
|
|
33772
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
33773
|
+
}, async ({ engine, use_case, project_id, agent_id, start_url, headless, viewport_width, viewport_height, stealth, auto_gallery, force_new, tags }) => {
|
|
33393
33774
|
try {
|
|
33775
|
+
if (agent_id && !force_new) {
|
|
33776
|
+
const existing = getActiveSessionForAgent2(agent_id);
|
|
33777
|
+
if (existing)
|
|
33778
|
+
return json({ session: existing.session, reused: true });
|
|
33779
|
+
}
|
|
33394
33780
|
const { session } = await createSession2({
|
|
33395
33781
|
engine,
|
|
33396
33782
|
useCase: use_case,
|
|
@@ -33399,44 +33785,66 @@ var init_mcp = __esm(async () => {
|
|
|
33399
33785
|
startUrl: start_url,
|
|
33400
33786
|
headless,
|
|
33401
33787
|
viewport: { width: viewport_width, height: viewport_height },
|
|
33402
|
-
stealth
|
|
33788
|
+
stealth,
|
|
33789
|
+
autoGallery: auto_gallery
|
|
33403
33790
|
});
|
|
33404
|
-
|
|
33791
|
+
if (tags?.length) {
|
|
33792
|
+
const { addSessionTag: addSessionTag2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
33793
|
+
for (const tag of tags)
|
|
33794
|
+
addSessionTag2(session.id, tag);
|
|
33795
|
+
}
|
|
33796
|
+
logEvent(session.id, "session_created", { engine: session.engine });
|
|
33797
|
+
return json({ session, reused: false });
|
|
33405
33798
|
} catch (e) {
|
|
33406
33799
|
return err(e);
|
|
33407
33800
|
}
|
|
33408
33801
|
});
|
|
33409
|
-
server.tool("browser_session_list", "List all browser sessions", { status: exports_external.enum(["active", "closed", "error"]).optional(), project_id: exports_external.string().optional() }, async ({ status, project_id }) => {
|
|
33802
|
+
server.tool("browser_session_list", "List all browser sessions. Optionally filter by tag.", { status: exports_external.enum(["active", "closed", "error"]).optional(), project_id: exports_external.string().optional(), tag: exports_external.string().optional() }, async ({ status, project_id, tag }) => {
|
|
33410
33803
|
try {
|
|
33804
|
+
if (tag) {
|
|
33805
|
+
const { listSessionsByTag: listSessionsByTag2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
33806
|
+
return json({ sessions: listSessionsByTag2(tag) });
|
|
33807
|
+
}
|
|
33411
33808
|
return json({ sessions: listSessions2({ status, projectId: project_id }) });
|
|
33412
33809
|
} catch (e) {
|
|
33413
33810
|
return err(e);
|
|
33414
33811
|
}
|
|
33415
33812
|
});
|
|
33416
|
-
server.tool("browser_session_close", "Close a browser session", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
33813
|
+
server.tool("browser_session_close", "Close a browser session", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33417
33814
|
try {
|
|
33418
|
-
const
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
|
|
33423
|
-
|
|
33815
|
+
const sid = resolveSessionId(session_id);
|
|
33816
|
+
const session = await closeSession2(sid);
|
|
33817
|
+
networkLogCleanup.get(sid)?.();
|
|
33818
|
+
consoleCaptureCleanup.get(sid)?.();
|
|
33819
|
+
networkLogCleanup.delete(sid);
|
|
33820
|
+
consoleCaptureCleanup.delete(sid);
|
|
33821
|
+
harCaptures.delete(sid);
|
|
33424
33822
|
return json({ session });
|
|
33425
33823
|
} catch (e) {
|
|
33426
33824
|
return err(e);
|
|
33427
33825
|
}
|
|
33428
33826
|
});
|
|
33827
|
+
server.tool("browser_session_timeline", "Get chronological action log for a session", { session_id: exports_external.string().optional(), limit: exports_external.number().optional().default(50) }, async ({ session_id, limit }) => {
|
|
33828
|
+
try {
|
|
33829
|
+
const sid = resolveSessionId(session_id);
|
|
33830
|
+
const events = getTimeline(sid, limit);
|
|
33831
|
+
return json({ events, count: events.length });
|
|
33832
|
+
} catch (e) {
|
|
33833
|
+
return err(e);
|
|
33834
|
+
}
|
|
33835
|
+
});
|
|
33429
33836
|
server.tool("browser_navigate", "Navigate to a URL. Auto-detects redirects, auto-names session, returns compact refs + thumbnail.", {
|
|
33430
|
-
session_id: exports_external.string(),
|
|
33837
|
+
session_id: exports_external.string().optional(),
|
|
33431
33838
|
url: exports_external.string(),
|
|
33432
33839
|
timeout: exports_external.number().optional().default(30000),
|
|
33433
33840
|
auto_snapshot: exports_external.boolean().optional().default(true),
|
|
33434
33841
|
auto_thumbnail: exports_external.boolean().optional().default(true)
|
|
33435
33842
|
}, async ({ session_id, url, timeout, auto_snapshot, auto_thumbnail }) => {
|
|
33436
33843
|
try {
|
|
33437
|
-
const
|
|
33438
|
-
|
|
33439
|
-
|
|
33844
|
+
const sid = resolveSessionId(session_id);
|
|
33845
|
+
const page = getSessionPage(sid);
|
|
33846
|
+
if (isBunSession(sid)) {
|
|
33847
|
+
const bunView = getSessionBunView(sid);
|
|
33440
33848
|
await bunView.goto(url, { timeout });
|
|
33441
33849
|
await new Promise((r) => setTimeout(r, 500));
|
|
33442
33850
|
} else {
|
|
@@ -33463,10 +33871,10 @@ var init_mcp = __esm(async () => {
|
|
|
33463
33871
|
} catch {}
|
|
33464
33872
|
}
|
|
33465
33873
|
try {
|
|
33466
|
-
const session = getSession2(
|
|
33874
|
+
const session = getSession2(sid);
|
|
33467
33875
|
if (!session.name) {
|
|
33468
33876
|
const hostname = new URL(current_url).hostname;
|
|
33469
|
-
renameSession2(
|
|
33877
|
+
renameSession2(sid, hostname);
|
|
33470
33878
|
}
|
|
33471
33879
|
} catch {}
|
|
33472
33880
|
const result = {
|
|
@@ -33482,86 +33890,104 @@ var init_mcp = __esm(async () => {
|
|
|
33482
33890
|
result.thumbnail_base64 = ss.base64.length > 50000 ? "" : ss.base64;
|
|
33483
33891
|
} catch {}
|
|
33484
33892
|
}
|
|
33485
|
-
if (
|
|
33893
|
+
if (isAutoGallery(sid)) {
|
|
33894
|
+
try {
|
|
33895
|
+
const ss = await takeScreenshot(page, { maxWidth: 1280, quality: 70, thumbnail: true });
|
|
33896
|
+
const { createEntry: createEntry2 } = await Promise.resolve().then(() => (init_gallery(), exports_gallery));
|
|
33897
|
+
createEntry2({ session_id: sid, url: current_url, title, path: ss.path, thumbnail_path: ss.thumbnail_path, format: "webp", width: ss.width, height: ss.height, original_size_bytes: ss.original_size_bytes, compressed_size_bytes: ss.compressed_size_bytes, compression_ratio: ss.compression_ratio, tags: [], is_favorite: false });
|
|
33898
|
+
} catch {}
|
|
33899
|
+
}
|
|
33900
|
+
if (isBunSession(sid) && auto_snapshot) {
|
|
33486
33901
|
await new Promise((r) => setTimeout(r, 200));
|
|
33487
33902
|
}
|
|
33488
33903
|
if (auto_snapshot) {
|
|
33489
33904
|
try {
|
|
33490
|
-
const snap = await takeSnapshot(page,
|
|
33491
|
-
setLastSnapshot(
|
|
33905
|
+
const snap = await takeSnapshot(page, sid);
|
|
33906
|
+
setLastSnapshot(sid, snap);
|
|
33492
33907
|
const refEntries = Object.entries(snap.refs).slice(0, 30);
|
|
33493
33908
|
result.snapshot_refs = refEntries.map(([ref, info]) => `${info.role}:${info.name.slice(0, 50)} [${ref}]`).join(", ");
|
|
33494
33909
|
result.interactive_count = snap.interactive_count;
|
|
33495
|
-
result.has_errors = getConsoleLog(
|
|
33910
|
+
result.has_errors = getConsoleLog(sid, "error").length > 0;
|
|
33496
33911
|
} catch {}
|
|
33497
33912
|
}
|
|
33913
|
+
logEvent(sid, "navigate", { url, title, current_url });
|
|
33498
33914
|
return json(result);
|
|
33499
33915
|
} catch (e) {
|
|
33500
|
-
return
|
|
33916
|
+
return errWithScreenshot(e, session_id);
|
|
33501
33917
|
}
|
|
33502
33918
|
});
|
|
33503
|
-
server.tool("browser_back", "Navigate back in browser history", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
33919
|
+
server.tool("browser_back", "Navigate back in browser history", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33504
33920
|
try {
|
|
33505
|
-
const
|
|
33921
|
+
const sid = resolveSessionId(session_id);
|
|
33922
|
+
const page = getSessionPage(sid);
|
|
33506
33923
|
await goBack(page);
|
|
33507
33924
|
return json({ url: page.url() });
|
|
33508
33925
|
} catch (e) {
|
|
33509
33926
|
return err(e);
|
|
33510
33927
|
}
|
|
33511
33928
|
});
|
|
33512
|
-
server.tool("browser_forward", "Navigate forward in browser history", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
33929
|
+
server.tool("browser_forward", "Navigate forward in browser history", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33513
33930
|
try {
|
|
33514
|
-
const
|
|
33931
|
+
const sid = resolveSessionId(session_id);
|
|
33932
|
+
const page = getSessionPage(sid);
|
|
33515
33933
|
await goForward(page);
|
|
33516
33934
|
return json({ url: page.url() });
|
|
33517
33935
|
} catch (e) {
|
|
33518
33936
|
return err(e);
|
|
33519
33937
|
}
|
|
33520
33938
|
});
|
|
33521
|
-
server.tool("browser_reload", "Reload the current page", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
33939
|
+
server.tool("browser_reload", "Reload the current page", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33522
33940
|
try {
|
|
33523
|
-
const
|
|
33941
|
+
const sid = resolveSessionId(session_id);
|
|
33942
|
+
const page = getSessionPage(sid);
|
|
33524
33943
|
await reload(page);
|
|
33525
33944
|
return json({ url: page.url() });
|
|
33526
33945
|
} catch (e) {
|
|
33527
33946
|
return err(e);
|
|
33528
33947
|
}
|
|
33529
33948
|
});
|
|
33530
|
-
server.tool("browser_click", "Click an element by ref (from snapshot) or CSS selector. Prefer ref for reliability.", { session_id: exports_external.string(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), button: exports_external.enum(["left", "right", "middle"]).optional(), timeout: exports_external.number().optional() }, async ({ session_id, selector, ref, button, timeout }) => {
|
|
33949
|
+
server.tool("browser_click", "Click an element by ref (from snapshot) or CSS selector. Prefer ref for reliability.", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), button: exports_external.enum(["left", "right", "middle"]).optional(), timeout: exports_external.number().optional() }, async ({ session_id, selector, ref, button, timeout }) => {
|
|
33531
33950
|
try {
|
|
33532
|
-
const
|
|
33951
|
+
const sid = resolveSessionId(session_id);
|
|
33952
|
+
const page = getSessionPage(sid);
|
|
33533
33953
|
if (ref) {
|
|
33534
|
-
await clickRef(page,
|
|
33954
|
+
await clickRef(page, sid, ref, { timeout });
|
|
33955
|
+
logEvent(sid, "click", { selector: ref, method: "ref" });
|
|
33535
33956
|
return json({ clicked: ref, method: "ref" });
|
|
33536
33957
|
}
|
|
33537
33958
|
if (!selector)
|
|
33538
33959
|
return err(new Error("Either ref or selector is required"));
|
|
33539
33960
|
await click(page, selector, { button, timeout });
|
|
33961
|
+
logEvent(sid, "click", { selector, method: "selector" });
|
|
33540
33962
|
return json({ clicked: selector, method: "selector" });
|
|
33541
33963
|
} catch (e) {
|
|
33542
|
-
return
|
|
33964
|
+
return errWithScreenshot(e, session_id);
|
|
33543
33965
|
}
|
|
33544
33966
|
});
|
|
33545
|
-
server.tool("browser_type", "Type text into an element by ref or selector. Prefer ref.", { session_id: exports_external.string(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), text: exports_external.string(), clear: exports_external.boolean().optional().default(false), delay: exports_external.number().optional() }, async ({ session_id, selector, ref, text, clear, delay }) => {
|
|
33967
|
+
server.tool("browser_type", "Type text into an element by ref or selector. Prefer ref.", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), text: exports_external.string(), clear: exports_external.boolean().optional().default(false), delay: exports_external.number().optional() }, async ({ session_id, selector, ref, text, clear, delay }) => {
|
|
33546
33968
|
try {
|
|
33547
|
-
const
|
|
33969
|
+
const sid = resolveSessionId(session_id);
|
|
33970
|
+
const page = getSessionPage(sid);
|
|
33548
33971
|
if (ref) {
|
|
33549
|
-
await typeRef(page,
|
|
33972
|
+
await typeRef(page, sid, ref, text, { clear, delay });
|
|
33973
|
+
logEvent(sid, "type", { selector: ref, text: text.slice(0, 100) });
|
|
33550
33974
|
return json({ typed: text, ref, method: "ref" });
|
|
33551
33975
|
}
|
|
33552
33976
|
if (!selector)
|
|
33553
33977
|
return err(new Error("Either ref or selector is required"));
|
|
33554
33978
|
await type(page, selector, text, { clear, delay });
|
|
33979
|
+
logEvent(sid, "type", { selector, text: text.slice(0, 100) });
|
|
33555
33980
|
return json({ typed: text, selector, method: "selector" });
|
|
33556
33981
|
} catch (e) {
|
|
33557
|
-
return
|
|
33982
|
+
return errWithScreenshot(e, session_id);
|
|
33558
33983
|
}
|
|
33559
33984
|
});
|
|
33560
|
-
server.tool("browser_hover", "Hover over an element by ref or selector", { session_id: exports_external.string(), selector: exports_external.string().optional(), ref: exports_external.string().optional() }, async ({ session_id, selector, ref }) => {
|
|
33985
|
+
server.tool("browser_hover", "Hover over an element by ref or selector", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), ref: exports_external.string().optional() }, async ({ session_id, selector, ref }) => {
|
|
33561
33986
|
try {
|
|
33562
|
-
const
|
|
33987
|
+
const sid = resolveSessionId(session_id);
|
|
33988
|
+
const page = getSessionPage(sid);
|
|
33563
33989
|
if (ref) {
|
|
33564
|
-
await hoverRef(page,
|
|
33990
|
+
await hoverRef(page, sid, ref);
|
|
33565
33991
|
return json({ hovered: ref, method: "ref" });
|
|
33566
33992
|
}
|
|
33567
33993
|
if (!selector)
|
|
@@ -33572,20 +33998,22 @@ var init_mcp = __esm(async () => {
|
|
|
33572
33998
|
return err(e);
|
|
33573
33999
|
}
|
|
33574
34000
|
});
|
|
33575
|
-
server.tool("browser_scroll", "Scroll the page", { session_id: exports_external.string(), direction: exports_external.enum(["up", "down", "left", "right"]).optional().default("down"), amount: exports_external.number().optional().default(300) }, async ({ session_id, direction, amount }) => {
|
|
34001
|
+
server.tool("browser_scroll", "Scroll the page", { session_id: exports_external.string().optional(), direction: exports_external.enum(["up", "down", "left", "right"]).optional().default("down"), amount: exports_external.number().optional().default(300) }, async ({ session_id, direction, amount }) => {
|
|
33576
34002
|
try {
|
|
33577
|
-
const
|
|
34003
|
+
const sid = resolveSessionId(session_id);
|
|
34004
|
+
const page = getSessionPage(sid);
|
|
33578
34005
|
await scroll(page, direction, amount);
|
|
33579
34006
|
return json({ scrolled: direction, amount });
|
|
33580
34007
|
} catch (e) {
|
|
33581
34008
|
return err(e);
|
|
33582
34009
|
}
|
|
33583
34010
|
});
|
|
33584
|
-
server.tool("browser_select", "Select a dropdown option by ref or selector", { session_id: exports_external.string(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), value: exports_external.string() }, async ({ session_id, selector, ref, value }) => {
|
|
34011
|
+
server.tool("browser_select", "Select a dropdown option by ref or selector", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), value: exports_external.string() }, async ({ session_id, selector, ref, value }) => {
|
|
33585
34012
|
try {
|
|
33586
|
-
const
|
|
34013
|
+
const sid = resolveSessionId(session_id);
|
|
34014
|
+
const page = getSessionPage(sid);
|
|
33587
34015
|
if (ref) {
|
|
33588
|
-
const selected2 = await selectRef(page,
|
|
34016
|
+
const selected2 = await selectRef(page, sid, ref, value);
|
|
33589
34017
|
return json({ selected: selected2, method: "ref" });
|
|
33590
34018
|
}
|
|
33591
34019
|
if (!selector)
|
|
@@ -33596,11 +34024,12 @@ var init_mcp = __esm(async () => {
|
|
|
33596
34024
|
return err(e);
|
|
33597
34025
|
}
|
|
33598
34026
|
});
|
|
33599
|
-
server.tool("browser_toggle", "Check or uncheck a checkbox by ref or selector", { session_id: exports_external.string(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), checked: exports_external.boolean() }, async ({ session_id, selector, ref, checked }) => {
|
|
34027
|
+
server.tool("browser_toggle", "Check or uncheck a checkbox by ref or selector", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), ref: exports_external.string().optional(), checked: exports_external.boolean() }, async ({ session_id, selector, ref, checked }) => {
|
|
33600
34028
|
try {
|
|
33601
|
-
const
|
|
34029
|
+
const sid = resolveSessionId(session_id);
|
|
34030
|
+
const page = getSessionPage(sid);
|
|
33602
34031
|
if (ref) {
|
|
33603
|
-
await checkRef(page,
|
|
34032
|
+
await checkRef(page, sid, ref, checked);
|
|
33604
34033
|
return json({ checked, ref, method: "ref" });
|
|
33605
34034
|
}
|
|
33606
34035
|
if (!selector)
|
|
@@ -33611,52 +34040,58 @@ var init_mcp = __esm(async () => {
|
|
|
33611
34040
|
return err(e);
|
|
33612
34041
|
}
|
|
33613
34042
|
});
|
|
33614
|
-
server.tool("browser_upload", "Upload a file to an input element", { session_id: exports_external.string(), selector: exports_external.string(), file_path: exports_external.string() }, async ({ session_id, selector, file_path }) => {
|
|
34043
|
+
server.tool("browser_upload", "Upload a file to an input element", { session_id: exports_external.string().optional(), selector: exports_external.string(), file_path: exports_external.string() }, async ({ session_id, selector, file_path }) => {
|
|
33615
34044
|
try {
|
|
33616
|
-
const
|
|
34045
|
+
const sid = resolveSessionId(session_id);
|
|
34046
|
+
const page = getSessionPage(sid);
|
|
33617
34047
|
await uploadFile(page, selector, file_path);
|
|
33618
34048
|
return json({ uploaded: file_path, selector });
|
|
33619
34049
|
} catch (e) {
|
|
33620
34050
|
return err(e);
|
|
33621
34051
|
}
|
|
33622
34052
|
});
|
|
33623
|
-
server.tool("browser_press_key", "Press a keyboard key", { session_id: exports_external.string(), key: exports_external.string() }, async ({ session_id, key }) => {
|
|
34053
|
+
server.tool("browser_press_key", "Press a keyboard key", { session_id: exports_external.string().optional(), key: exports_external.string() }, async ({ session_id, key }) => {
|
|
33624
34054
|
try {
|
|
33625
|
-
const
|
|
34055
|
+
const sid = resolveSessionId(session_id);
|
|
34056
|
+
const page = getSessionPage(sid);
|
|
33626
34057
|
await pressKey(page, key);
|
|
33627
34058
|
return json({ pressed: key });
|
|
33628
34059
|
} catch (e) {
|
|
33629
34060
|
return err(e);
|
|
33630
34061
|
}
|
|
33631
34062
|
});
|
|
33632
|
-
server.tool("browser_wait", "Wait for a selector to appear", { session_id: exports_external.string(), selector: exports_external.string(), state: exports_external.enum(["attached", "detached", "visible", "hidden"]).optional(), timeout: exports_external.number().optional() }, async ({ session_id, selector, state, timeout }) => {
|
|
34063
|
+
server.tool("browser_wait", "Wait for a selector to appear", { session_id: exports_external.string().optional(), selector: exports_external.string(), state: exports_external.enum(["attached", "detached", "visible", "hidden"]).optional(), timeout: exports_external.number().optional() }, async ({ session_id, selector, state, timeout }) => {
|
|
33633
34064
|
try {
|
|
33634
|
-
const
|
|
34065
|
+
const sid = resolveSessionId(session_id);
|
|
34066
|
+
const page = getSessionPage(sid);
|
|
33635
34067
|
await waitForSelector(page, selector, { state, timeout });
|
|
33636
34068
|
return json({ ready: selector });
|
|
33637
34069
|
} catch (e) {
|
|
33638
34070
|
return err(e);
|
|
33639
34071
|
}
|
|
33640
34072
|
});
|
|
33641
|
-
server.tool("browser_get_text", "Get text content from the page or a selector", { session_id: exports_external.string(), selector: exports_external.string().optional() }, async ({ session_id, selector }) => {
|
|
34073
|
+
server.tool("browser_get_text", "Get text content from the page or a selector", { session_id: exports_external.string().optional(), selector: exports_external.string().optional() }, async ({ session_id, selector }) => {
|
|
33642
34074
|
try {
|
|
33643
|
-
const
|
|
34075
|
+
const sid = resolveSessionId(session_id);
|
|
34076
|
+
const page = getSessionPage(sid);
|
|
33644
34077
|
return json({ text: await getText(page, selector) });
|
|
33645
34078
|
} catch (e) {
|
|
33646
34079
|
return err(e);
|
|
33647
34080
|
}
|
|
33648
34081
|
});
|
|
33649
|
-
server.tool("browser_get_html", "Get HTML content from the page or a selector", { session_id: exports_external.string(), selector: exports_external.string().optional() }, async ({ session_id, selector }) => {
|
|
34082
|
+
server.tool("browser_get_html", "Get HTML content from the page or a selector", { session_id: exports_external.string().optional(), selector: exports_external.string().optional() }, async ({ session_id, selector }) => {
|
|
33650
34083
|
try {
|
|
33651
|
-
const
|
|
34084
|
+
const sid = resolveSessionId(session_id);
|
|
34085
|
+
const page = getSessionPage(sid);
|
|
33652
34086
|
return json({ html: await getHTML(page, selector) });
|
|
33653
34087
|
} catch (e) {
|
|
33654
34088
|
return err(e);
|
|
33655
34089
|
}
|
|
33656
34090
|
});
|
|
33657
|
-
server.tool("browser_get_links", "Get all links from the current page", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34091
|
+
server.tool("browser_get_links", "Get all links from the current page", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33658
34092
|
try {
|
|
33659
|
-
const
|
|
34093
|
+
const sid = resolveSessionId(session_id);
|
|
34094
|
+
const page = getSessionPage(sid);
|
|
33660
34095
|
const links = await getLinks(page);
|
|
33661
34096
|
return json({ links, count: links.length });
|
|
33662
34097
|
} catch (e) {
|
|
@@ -33664,22 +34099,24 @@ var init_mcp = __esm(async () => {
|
|
|
33664
34099
|
}
|
|
33665
34100
|
});
|
|
33666
34101
|
server.tool("browser_extract", "Extract content from the page in a specified format", {
|
|
33667
|
-
session_id: exports_external.string(),
|
|
34102
|
+
session_id: exports_external.string().optional(),
|
|
33668
34103
|
format: exports_external.enum(["text", "html", "links", "table", "structured"]).optional().default("text"),
|
|
33669
34104
|
selector: exports_external.string().optional(),
|
|
33670
34105
|
schema: exports_external.record(exports_external.string()).optional()
|
|
33671
34106
|
}, async ({ session_id, format, selector, schema }) => {
|
|
33672
34107
|
try {
|
|
33673
|
-
const
|
|
34108
|
+
const sid = resolveSessionId(session_id);
|
|
34109
|
+
const page = getSessionPage(sid);
|
|
33674
34110
|
const result = await extract(page, { format, selector, schema });
|
|
33675
34111
|
return json(result);
|
|
33676
34112
|
} catch (e) {
|
|
33677
34113
|
return err(e);
|
|
33678
34114
|
}
|
|
33679
34115
|
});
|
|
33680
|
-
server.tool("browser_find", "Find elements matching a selector and return their text", { session_id: exports_external.string(), selector: exports_external.string() }, async ({ session_id, selector }) => {
|
|
34116
|
+
server.tool("browser_find", "Find elements matching a selector and return their text", { session_id: exports_external.string().optional(), selector: exports_external.string() }, async ({ session_id, selector }) => {
|
|
33681
34117
|
try {
|
|
33682
|
-
const
|
|
34118
|
+
const sid = resolveSessionId(session_id);
|
|
34119
|
+
const page = getSessionPage(sid);
|
|
33683
34120
|
const elements = await findElements(page, selector);
|
|
33684
34121
|
const texts = await Promise.all(elements.map((el) => el.textContent()));
|
|
33685
34122
|
return json({ count: elements.length, texts });
|
|
@@ -33688,15 +34125,16 @@ var init_mcp = __esm(async () => {
|
|
|
33688
34125
|
}
|
|
33689
34126
|
});
|
|
33690
34127
|
server.tool("browser_snapshot", "Get accessibility snapshot with element refs (@e0, @e1...). Use compact=true (default) for token-efficient output. Use refs in browser_click, browser_type, etc.", {
|
|
33691
|
-
session_id: exports_external.string(),
|
|
34128
|
+
session_id: exports_external.string().optional(),
|
|
33692
34129
|
compact: exports_external.boolean().optional().default(true),
|
|
33693
34130
|
max_refs: exports_external.number().optional().default(50),
|
|
33694
34131
|
full_tree: exports_external.boolean().optional().default(false)
|
|
33695
34132
|
}, async ({ session_id, compact, max_refs, full_tree }) => {
|
|
33696
34133
|
try {
|
|
33697
|
-
const
|
|
33698
|
-
const
|
|
33699
|
-
|
|
34134
|
+
const sid = resolveSessionId(session_id);
|
|
34135
|
+
const page = getSessionPage(sid);
|
|
34136
|
+
const result = await takeSnapshot(page, sid);
|
|
34137
|
+
setLastSnapshot(sid, result);
|
|
33700
34138
|
const refEntries = Object.entries(result.refs).slice(0, max_refs);
|
|
33701
34139
|
const limitedRefs = Object.fromEntries(refEntries);
|
|
33702
34140
|
const truncated = Object.keys(result.refs).length > max_refs;
|
|
@@ -33719,21 +34157,22 @@ var init_mcp = __esm(async () => {
|
|
|
33719
34157
|
}
|
|
33720
34158
|
});
|
|
33721
34159
|
server.tool("browser_screenshot", "Take a screenshot. Use annotate=true to overlay numbered labels on interactive elements for visual+ref workflows.", {
|
|
33722
|
-
session_id: exports_external.string(),
|
|
34160
|
+
session_id: exports_external.string().optional(),
|
|
33723
34161
|
selector: exports_external.string().optional(),
|
|
33724
34162
|
full_page: exports_external.boolean().optional().default(false),
|
|
33725
34163
|
format: exports_external.enum(["png", "jpeg", "webp"]).optional().default("webp"),
|
|
33726
|
-
quality: exports_external.number().optional(),
|
|
33727
|
-
max_width: exports_external.number().optional().default(
|
|
34164
|
+
quality: exports_external.number().optional().default(60),
|
|
34165
|
+
max_width: exports_external.number().optional().default(800),
|
|
33728
34166
|
compress: exports_external.boolean().optional().default(true),
|
|
33729
34167
|
thumbnail: exports_external.boolean().optional().default(true),
|
|
33730
34168
|
annotate: exports_external.boolean().optional().default(false)
|
|
33731
34169
|
}, async ({ session_id, selector, full_page, format, quality, max_width, compress, thumbnail, annotate }) => {
|
|
33732
34170
|
try {
|
|
33733
|
-
const
|
|
34171
|
+
const sid = resolveSessionId(session_id);
|
|
34172
|
+
const page = getSessionPage(sid);
|
|
33734
34173
|
if (annotate && !selector && !full_page) {
|
|
33735
34174
|
const { annotateScreenshot: annotateScreenshot2 } = await Promise.resolve().then(() => (init_annotate(), exports_annotate));
|
|
33736
|
-
const annotated = await annotateScreenshot2(page,
|
|
34175
|
+
const annotated = await annotateScreenshot2(page, sid);
|
|
33737
34176
|
const base64 = annotated.buffer.toString("base64");
|
|
33738
34177
|
return json({
|
|
33739
34178
|
base64: base64.length > 50000 ? undefined : base64,
|
|
@@ -33749,32 +34188,35 @@ var init_mcp = __esm(async () => {
|
|
|
33749
34188
|
try {
|
|
33750
34189
|
const buf = Buffer.from(result.base64, "base64");
|
|
33751
34190
|
const filename = result.path.split("/").pop() ?? `screenshot.${format ?? "webp"}`;
|
|
33752
|
-
const dl = saveToDownloads(buf, filename, { sessionId:
|
|
34191
|
+
const dl = saveToDownloads(buf, filename, { sessionId: sid, type: "screenshot", sourceUrl: page.url() });
|
|
33753
34192
|
result.download_id = dl.id;
|
|
33754
34193
|
} catch {}
|
|
33755
|
-
|
|
34194
|
+
result.estimated_tokens = Math.ceil(result.base64.length / 4);
|
|
34195
|
+
if (result.base64.length > 20000) {
|
|
33756
34196
|
result.base64_truncated = true;
|
|
33757
34197
|
result.full_image_path = result.path;
|
|
33758
34198
|
result.base64 = result.thumbnail_base64 ?? "";
|
|
33759
34199
|
}
|
|
34200
|
+
logEvent(sid, "screenshot", { path: result.path });
|
|
33760
34201
|
return json(result);
|
|
33761
34202
|
} catch (e) {
|
|
33762
34203
|
return err(e);
|
|
33763
34204
|
}
|
|
33764
34205
|
});
|
|
33765
34206
|
server.tool("browser_pdf", "Generate a PDF of the current page", {
|
|
33766
|
-
session_id: exports_external.string(),
|
|
34207
|
+
session_id: exports_external.string().optional(),
|
|
33767
34208
|
format: exports_external.enum(["A4", "Letter", "A3", "A5"]).optional().default("A4"),
|
|
33768
34209
|
landscape: exports_external.boolean().optional().default(false),
|
|
33769
34210
|
print_background: exports_external.boolean().optional().default(true)
|
|
33770
34211
|
}, async ({ session_id, format, landscape, print_background }) => {
|
|
33771
34212
|
try {
|
|
33772
|
-
const
|
|
34213
|
+
const sid = resolveSessionId(session_id);
|
|
34214
|
+
const page = getSessionPage(sid);
|
|
33773
34215
|
const result = await generatePDF(page, { format, landscape, printBackground: print_background });
|
|
33774
34216
|
try {
|
|
33775
34217
|
const buf = Buffer.from(result.base64, "base64");
|
|
33776
34218
|
const filename = result.path.split("/").pop() ?? "document.pdf";
|
|
33777
|
-
const dl = saveToDownloads(buf, filename, { sessionId:
|
|
34219
|
+
const dl = saveToDownloads(buf, filename, { sessionId: sid, type: "pdf", sourceUrl: page.url() });
|
|
33778
34220
|
result.download_id = dl.id;
|
|
33779
34221
|
} catch {}
|
|
33780
34222
|
return json(result);
|
|
@@ -33782,25 +34224,27 @@ var init_mcp = __esm(async () => {
|
|
|
33782
34224
|
return err(e);
|
|
33783
34225
|
}
|
|
33784
34226
|
});
|
|
33785
|
-
server.tool("browser_evaluate", "Execute JavaScript in the page context", { session_id: exports_external.string(), script: exports_external.string() }, async ({ session_id, script }) => {
|
|
34227
|
+
server.tool("browser_evaluate", "Execute JavaScript in the page context", { session_id: exports_external.string().optional(), script: exports_external.string() }, async ({ session_id, script }) => {
|
|
33786
34228
|
try {
|
|
33787
|
-
const
|
|
34229
|
+
const sid = resolveSessionId(session_id);
|
|
34230
|
+
const page = getSessionPage(sid);
|
|
33788
34231
|
const result = await page.evaluate(script);
|
|
33789
34232
|
return json({ result });
|
|
33790
34233
|
} catch (e) {
|
|
33791
|
-
return
|
|
34234
|
+
return errWithScreenshot(e, session_id);
|
|
33792
34235
|
}
|
|
33793
34236
|
});
|
|
33794
|
-
server.tool("browser_cookies_get", "Get cookies from the current session", { session_id: exports_external.string(), name: exports_external.string().optional(), domain: exports_external.string().optional() }, async ({ session_id, name, domain }) => {
|
|
34237
|
+
server.tool("browser_cookies_get", "Get cookies from the current session", { session_id: exports_external.string().optional(), name: exports_external.string().optional(), domain: exports_external.string().optional() }, async ({ session_id, name, domain }) => {
|
|
33795
34238
|
try {
|
|
33796
|
-
const
|
|
34239
|
+
const sid = resolveSessionId(session_id);
|
|
34240
|
+
const page = getSessionPage(sid);
|
|
33797
34241
|
return json({ cookies: await getCookies(page, { name, domain }) });
|
|
33798
34242
|
} catch (e) {
|
|
33799
34243
|
return err(e);
|
|
33800
34244
|
}
|
|
33801
34245
|
});
|
|
33802
34246
|
server.tool("browser_cookies_set", "Set a cookie in the current session", {
|
|
33803
|
-
session_id: exports_external.string(),
|
|
34247
|
+
session_id: exports_external.string().optional(),
|
|
33804
34248
|
name: exports_external.string(),
|
|
33805
34249
|
value: exports_external.string(),
|
|
33806
34250
|
domain: exports_external.string().optional(),
|
|
@@ -33810,7 +34254,8 @@ var init_mcp = __esm(async () => {
|
|
|
33810
34254
|
secure: exports_external.boolean().optional().default(false)
|
|
33811
34255
|
}, async ({ session_id, name, value, domain, path, expires, http_only, secure }) => {
|
|
33812
34256
|
try {
|
|
33813
|
-
const
|
|
34257
|
+
const sid = resolveSessionId(session_id);
|
|
34258
|
+
const page = getSessionPage(sid);
|
|
33814
34259
|
await setCookie(page, {
|
|
33815
34260
|
name,
|
|
33816
34261
|
value,
|
|
@@ -33826,27 +34271,30 @@ var init_mcp = __esm(async () => {
|
|
|
33826
34271
|
return err(e);
|
|
33827
34272
|
}
|
|
33828
34273
|
});
|
|
33829
|
-
server.tool("browser_cookies_clear", "Clear cookies from the current session", { session_id: exports_external.string(), name: exports_external.string().optional(), domain: exports_external.string().optional() }, async ({ session_id, name, domain }) => {
|
|
34274
|
+
server.tool("browser_cookies_clear", "Clear cookies from the current session", { session_id: exports_external.string().optional(), name: exports_external.string().optional(), domain: exports_external.string().optional() }, async ({ session_id, name, domain }) => {
|
|
33830
34275
|
try {
|
|
33831
|
-
const
|
|
34276
|
+
const sid = resolveSessionId(session_id);
|
|
34277
|
+
const page = getSessionPage(sid);
|
|
33832
34278
|
await clearCookies(page, name || domain ? { name, domain } : undefined);
|
|
33833
34279
|
return json({ cleared: true });
|
|
33834
34280
|
} catch (e) {
|
|
33835
34281
|
return err(e);
|
|
33836
34282
|
}
|
|
33837
34283
|
});
|
|
33838
|
-
server.tool("browser_storage_get", "Get localStorage or sessionStorage values", { session_id: exports_external.string(), key: exports_external.string().optional(), storage_type: exports_external.enum(["local", "session"]).optional().default("local") }, async ({ session_id, key, storage_type }) => {
|
|
34284
|
+
server.tool("browser_storage_get", "Get localStorage or sessionStorage values", { session_id: exports_external.string().optional(), key: exports_external.string().optional(), storage_type: exports_external.enum(["local", "session"]).optional().default("local") }, async ({ session_id, key, storage_type }) => {
|
|
33839
34285
|
try {
|
|
33840
|
-
const
|
|
34286
|
+
const sid = resolveSessionId(session_id);
|
|
34287
|
+
const page = getSessionPage(sid);
|
|
33841
34288
|
const value = storage_type === "session" ? await getSessionStorage(page, key) : await getLocalStorage(page, key);
|
|
33842
34289
|
return json({ value });
|
|
33843
34290
|
} catch (e) {
|
|
33844
34291
|
return err(e);
|
|
33845
34292
|
}
|
|
33846
34293
|
});
|
|
33847
|
-
server.tool("browser_storage_set", "Set a localStorage or sessionStorage value", { session_id: exports_external.string(), key: exports_external.string(), value: exports_external.string(), storage_type: exports_external.enum(["local", "session"]).optional().default("local") }, async ({ session_id, key, value, storage_type }) => {
|
|
34294
|
+
server.tool("browser_storage_set", "Set a localStorage or sessionStorage value", { session_id: exports_external.string().optional(), key: exports_external.string(), value: exports_external.string(), storage_type: exports_external.enum(["local", "session"]).optional().default("local") }, async ({ session_id, key, value, storage_type }) => {
|
|
33848
34295
|
try {
|
|
33849
|
-
const
|
|
34296
|
+
const sid = resolveSessionId(session_id);
|
|
34297
|
+
const page = getSessionPage(sid);
|
|
33850
34298
|
if (storage_type === "session") {
|
|
33851
34299
|
await setSessionStorage(page, key, value);
|
|
33852
34300
|
} else {
|
|
@@ -33857,28 +34305,30 @@ var init_mcp = __esm(async () => {
|
|
|
33857
34305
|
return err(e);
|
|
33858
34306
|
}
|
|
33859
34307
|
});
|
|
33860
|
-
server.tool("browser_network_log", "Get captured network requests for a session", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34308
|
+
server.tool("browser_network_log", "Get captured network requests for a session", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33861
34309
|
try {
|
|
33862
|
-
|
|
33863
|
-
|
|
33864
|
-
const
|
|
33865
|
-
|
|
34310
|
+
const sid = resolveSessionId(session_id);
|
|
34311
|
+
if (!networkLogCleanup.has(sid)) {
|
|
34312
|
+
const page = getSessionPage(sid);
|
|
34313
|
+
const cleanup = enableNetworkLogging(page, sid);
|
|
34314
|
+
networkLogCleanup.set(sid, cleanup);
|
|
33866
34315
|
}
|
|
33867
|
-
const log = getNetworkLog(
|
|
34316
|
+
const log = getNetworkLog(sid);
|
|
33868
34317
|
return json({ requests: log, count: log.length });
|
|
33869
34318
|
} catch (e) {
|
|
33870
34319
|
return err(e);
|
|
33871
34320
|
}
|
|
33872
34321
|
});
|
|
33873
34322
|
server.tool("browser_network_intercept", "Add a network interception rule", {
|
|
33874
|
-
session_id: exports_external.string(),
|
|
34323
|
+
session_id: exports_external.string().optional(),
|
|
33875
34324
|
pattern: exports_external.string(),
|
|
33876
34325
|
action: exports_external.enum(["block", "modify", "log"]),
|
|
33877
34326
|
response_status: exports_external.number().optional(),
|
|
33878
34327
|
response_body: exports_external.string().optional()
|
|
33879
34328
|
}, async ({ session_id, pattern, action, response_status, response_body }) => {
|
|
33880
34329
|
try {
|
|
33881
|
-
const
|
|
34330
|
+
const sid = resolveSessionId(session_id);
|
|
34331
|
+
const page = getSessionPage(sid);
|
|
33882
34332
|
await addInterceptRule(page, {
|
|
33883
34333
|
pattern,
|
|
33884
34334
|
action,
|
|
@@ -33889,27 +34339,29 @@ var init_mcp = __esm(async () => {
|
|
|
33889
34339
|
return err(e);
|
|
33890
34340
|
}
|
|
33891
34341
|
});
|
|
33892
|
-
server.tool("browser_har_start", "Start HAR capture for a session", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34342
|
+
server.tool("browser_har_start", "Start HAR capture for a session", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33893
34343
|
try {
|
|
33894
|
-
const
|
|
34344
|
+
const sid = resolveSessionId(session_id);
|
|
34345
|
+
const page = getSessionPage(sid);
|
|
33895
34346
|
const capture = startHAR(page);
|
|
33896
|
-
harCaptures.set(
|
|
34347
|
+
harCaptures.set(sid, capture);
|
|
33897
34348
|
return json({ started: true });
|
|
33898
34349
|
} catch (e) {
|
|
33899
34350
|
return err(e);
|
|
33900
34351
|
}
|
|
33901
34352
|
});
|
|
33902
|
-
server.tool("browser_har_stop", "Stop HAR capture and return the HAR data", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34353
|
+
server.tool("browser_har_stop", "Stop HAR capture and return the HAR data", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33903
34354
|
try {
|
|
33904
|
-
const
|
|
34355
|
+
const sid = resolveSessionId(session_id);
|
|
34356
|
+
const capture = harCaptures.get(sid);
|
|
33905
34357
|
if (!capture)
|
|
33906
34358
|
return err(new Error("No active HAR capture for this session"));
|
|
33907
34359
|
const har = capture.stop();
|
|
33908
|
-
harCaptures.delete(
|
|
34360
|
+
harCaptures.delete(sid);
|
|
33909
34361
|
let download_id;
|
|
33910
34362
|
try {
|
|
33911
34363
|
const harBuf = Buffer.from(JSON.stringify(har, null, 2));
|
|
33912
|
-
const dl = saveToDownloads(harBuf, `capture-${Date.now()}.har`, { sessionId:
|
|
34364
|
+
const dl = saveToDownloads(harBuf, `capture-${Date.now()}.har`, { sessionId: sid, type: "har" });
|
|
33913
34365
|
download_id = dl.id;
|
|
33914
34366
|
} catch {}
|
|
33915
34367
|
return json({ har, entry_count: har.log.entries.length, download_id });
|
|
@@ -33917,32 +34369,35 @@ var init_mcp = __esm(async () => {
|
|
|
33917
34369
|
return err(e);
|
|
33918
34370
|
}
|
|
33919
34371
|
});
|
|
33920
|
-
server.tool("browser_performance", "Get performance metrics for the current page", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34372
|
+
server.tool("browser_performance", "Get performance metrics for the current page", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
33921
34373
|
try {
|
|
33922
|
-
const
|
|
34374
|
+
const sid = resolveSessionId(session_id);
|
|
34375
|
+
const page = getSessionPage(sid);
|
|
33923
34376
|
const metrics = await getPerformanceMetrics(page);
|
|
33924
34377
|
return json({ metrics });
|
|
33925
34378
|
} catch (e) {
|
|
33926
34379
|
return err(e);
|
|
33927
34380
|
}
|
|
33928
34381
|
});
|
|
33929
|
-
server.tool("browser_console_log", "Get captured console messages for a session", { session_id: exports_external.string(), level: exports_external.enum(["log", "warn", "error", "debug", "info"]).optional() }, async ({ session_id, level }) => {
|
|
34382
|
+
server.tool("browser_console_log", "Get captured console messages for a session", { session_id: exports_external.string().optional(), level: exports_external.enum(["log", "warn", "error", "debug", "info"]).optional() }, async ({ session_id, level }) => {
|
|
33930
34383
|
try {
|
|
33931
|
-
|
|
33932
|
-
|
|
33933
|
-
const
|
|
33934
|
-
|
|
34384
|
+
const sid = resolveSessionId(session_id);
|
|
34385
|
+
if (!consoleCaptureCleanup.has(sid)) {
|
|
34386
|
+
const page = getSessionPage(sid);
|
|
34387
|
+
const cleanup = enableConsoleCapture(page, sid);
|
|
34388
|
+
consoleCaptureCleanup.set(sid, cleanup);
|
|
33935
34389
|
}
|
|
33936
|
-
const messages = getConsoleLog(
|
|
34390
|
+
const messages = getConsoleLog(sid, level);
|
|
33937
34391
|
return json({ messages, count: messages.length });
|
|
33938
34392
|
} catch (e) {
|
|
33939
34393
|
return err(e);
|
|
33940
34394
|
}
|
|
33941
34395
|
});
|
|
33942
|
-
server.tool("browser_record_start", "Start recording actions in a session", { session_id: exports_external.string(), name: exports_external.string(), project_id: exports_external.string().optional() }, async ({ session_id, name }) => {
|
|
34396
|
+
server.tool("browser_record_start", "Start recording actions in a session", { session_id: exports_external.string().optional(), name: exports_external.string(), project_id: exports_external.string().optional() }, async ({ session_id, name }) => {
|
|
33943
34397
|
try {
|
|
33944
|
-
const
|
|
33945
|
-
const
|
|
34398
|
+
const sid = resolveSessionId(session_id);
|
|
34399
|
+
const page = getSessionPage(sid);
|
|
34400
|
+
const recording = startRecording(sid, name, page.url());
|
|
33946
34401
|
return json({ recording_id: recording.id, name: recording.name });
|
|
33947
34402
|
} catch (e) {
|
|
33948
34403
|
return err(e);
|
|
@@ -33970,9 +34425,10 @@ var init_mcp = __esm(async () => {
|
|
|
33970
34425
|
return err(e);
|
|
33971
34426
|
}
|
|
33972
34427
|
});
|
|
33973
|
-
server.tool("browser_record_replay", "Replay a recorded sequence in a session", { session_id: exports_external.string(), recording_id: exports_external.string() }, async ({ session_id, recording_id }) => {
|
|
34428
|
+
server.tool("browser_record_replay", "Replay a recorded sequence in a session", { session_id: exports_external.string().optional(), recording_id: exports_external.string() }, async ({ session_id, recording_id }) => {
|
|
33974
34429
|
try {
|
|
33975
|
-
const
|
|
34430
|
+
const sid = resolveSessionId(session_id);
|
|
34431
|
+
const page = getSessionPage(sid);
|
|
33976
34432
|
const result = await replayRecording(recording_id, page);
|
|
33977
34433
|
return json(result);
|
|
33978
34434
|
} catch (e) {
|
|
@@ -34010,7 +34466,7 @@ var init_mcp = __esm(async () => {
|
|
|
34010
34466
|
server.tool("browser_register_agent", "Register an agent with the browser service", {
|
|
34011
34467
|
name: exports_external.string(),
|
|
34012
34468
|
description: exports_external.string().optional(),
|
|
34013
|
-
session_id: exports_external.string().optional(),
|
|
34469
|
+
session_id: exports_external.string().optional().optional(),
|
|
34014
34470
|
project_id: exports_external.string().optional(),
|
|
34015
34471
|
working_dir: exports_external.string().optional()
|
|
34016
34472
|
}, async ({ name, description, session_id, project_id, working_dir }) => {
|
|
@@ -34051,9 +34507,10 @@ var init_mcp = __esm(async () => {
|
|
|
34051
34507
|
return err(e);
|
|
34052
34508
|
}
|
|
34053
34509
|
});
|
|
34054
|
-
server.tool("browser_scroll_and_screenshot", "Scroll the page and take a screenshot in one call. Saves 3 separate tool calls.", { session_id: exports_external.string(), direction: exports_external.enum(["up", "down", "left", "right"]).optional().default("down"), amount: exports_external.number().optional().default(500), wait_ms: exports_external.number().optional().default(300) }, async ({ session_id, direction, amount, wait_ms }) => {
|
|
34510
|
+
server.tool("browser_scroll_and_screenshot", "Scroll the page and take a screenshot in one call. Saves 3 separate tool calls.", { session_id: exports_external.string().optional(), direction: exports_external.enum(["up", "down", "left", "right"]).optional().default("down"), amount: exports_external.number().optional().default(500), wait_ms: exports_external.number().optional().default(300) }, async ({ session_id, direction, amount, wait_ms }) => {
|
|
34055
34511
|
try {
|
|
34056
|
-
const
|
|
34512
|
+
const sid = resolveSessionId(session_id);
|
|
34513
|
+
const page = getSessionPage(sid);
|
|
34057
34514
|
await scroll(page, direction, amount);
|
|
34058
34515
|
await new Promise((r) => setTimeout(r, wait_ms));
|
|
34059
34516
|
const result = await takeScreenshot(page, { maxWidth: 1280, track: true });
|
|
@@ -34068,9 +34525,10 @@ var init_mcp = __esm(async () => {
|
|
|
34068
34525
|
return err(e);
|
|
34069
34526
|
}
|
|
34070
34527
|
});
|
|
34071
|
-
server.tool("browser_wait_for_navigation", "Wait for URL change after a click or action. Returns the new URL and title.", { session_id: exports_external.string(), timeout: exports_external.number().optional().default(30000), url_pattern: exports_external.string().optional() }, async ({ session_id, timeout, url_pattern }) => {
|
|
34528
|
+
server.tool("browser_wait_for_navigation", "Wait for URL change after a click or action. Returns the new URL and title.", { session_id: exports_external.string().optional(), timeout: exports_external.number().optional().default(30000), url_pattern: exports_external.string().optional() }, async ({ session_id, timeout, url_pattern }) => {
|
|
34072
34529
|
try {
|
|
34073
|
-
const
|
|
34530
|
+
const sid = resolveSessionId(session_id);
|
|
34531
|
+
const page = getSessionPage(sid);
|
|
34074
34532
|
const start = Date.now();
|
|
34075
34533
|
if (url_pattern) {
|
|
34076
34534
|
await page.waitForURL(url_pattern, { timeout });
|
|
@@ -34092,16 +34550,63 @@ var init_mcp = __esm(async () => {
|
|
|
34092
34550
|
return err(e);
|
|
34093
34551
|
}
|
|
34094
34552
|
});
|
|
34095
|
-
server.tool("browser_session_rename", "Rename a browser session", { session_id: exports_external.string(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34553
|
+
server.tool("browser_session_rename", "Rename a browser session", { session_id: exports_external.string().optional(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34554
|
+
try {
|
|
34555
|
+
const sid = resolveSessionId(session_id);
|
|
34556
|
+
return json({ session: renameSession2(sid, name) });
|
|
34557
|
+
} catch (e) {
|
|
34558
|
+
return err(e);
|
|
34559
|
+
}
|
|
34560
|
+
});
|
|
34561
|
+
server.tool("browser_session_lock", "Lock a session so only the specified agent can use it", { session_id: exports_external.string().optional(), agent_id: exports_external.string() }, async ({ session_id, agent_id }) => {
|
|
34562
|
+
try {
|
|
34563
|
+
const sid = resolveSessionId(session_id);
|
|
34564
|
+
const { lockSession: lockSession2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
34565
|
+
return json({ session: lockSession2(sid, agent_id) });
|
|
34566
|
+
} catch (e) {
|
|
34567
|
+
return err(e);
|
|
34568
|
+
}
|
|
34569
|
+
});
|
|
34570
|
+
server.tool("browser_session_unlock", "Unlock a session", { session_id: exports_external.string().optional(), agent_id: exports_external.string().optional() }, async ({ session_id, agent_id }) => {
|
|
34571
|
+
try {
|
|
34572
|
+
const sid = resolveSessionId(session_id);
|
|
34573
|
+
const { unlockSession: unlockSession2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
34574
|
+
return json({ session: unlockSession2(sid, agent_id) });
|
|
34575
|
+
} catch (e) {
|
|
34576
|
+
return err(e);
|
|
34577
|
+
}
|
|
34578
|
+
});
|
|
34579
|
+
server.tool("browser_session_transfer", "Transfer session ownership to another agent", { session_id: exports_external.string().optional(), to_agent_id: exports_external.string() }, async ({ session_id, to_agent_id }) => {
|
|
34096
34580
|
try {
|
|
34097
|
-
|
|
34581
|
+
const sid = resolveSessionId(session_id);
|
|
34582
|
+
const { transferSession: transferSession2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
34583
|
+
return json({ session: transferSession2(sid, to_agent_id) });
|
|
34098
34584
|
} catch (e) {
|
|
34099
34585
|
return err(e);
|
|
34100
34586
|
}
|
|
34101
34587
|
});
|
|
34102
|
-
server.tool("
|
|
34588
|
+
server.tool("browser_session_tag", "Add a tag to a session for categorization (e.g. qa, scraping, monitoring)", { session_id: exports_external.string().optional(), tag: exports_external.string() }, async ({ session_id, tag }) => {
|
|
34103
34589
|
try {
|
|
34104
|
-
const
|
|
34590
|
+
const sid = resolveSessionId(session_id);
|
|
34591
|
+
const { addSessionTag: addSessionTag2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
34592
|
+
return json({ tags: addSessionTag2(sid, tag) });
|
|
34593
|
+
} catch (e) {
|
|
34594
|
+
return err(e);
|
|
34595
|
+
}
|
|
34596
|
+
});
|
|
34597
|
+
server.tool("browser_session_untag", "Remove a tag from a session", { session_id: exports_external.string().optional(), tag: exports_external.string() }, async ({ session_id, tag }) => {
|
|
34598
|
+
try {
|
|
34599
|
+
const sid = resolveSessionId(session_id);
|
|
34600
|
+
const { removeSessionTag: removeSessionTag2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
34601
|
+
return json({ tags: removeSessionTag2(sid, tag) });
|
|
34602
|
+
} catch (e) {
|
|
34603
|
+
return err(e);
|
|
34604
|
+
}
|
|
34605
|
+
});
|
|
34606
|
+
server.tool("browser_click_text", "Click an element by its visible text content", { session_id: exports_external.string().optional(), text: exports_external.string(), exact: exports_external.boolean().optional().default(false), timeout: exports_external.number().optional() }, async ({ session_id, text, exact, timeout }) => {
|
|
34607
|
+
try {
|
|
34608
|
+
const sid = resolveSessionId(session_id);
|
|
34609
|
+
const page = getSessionPage(sid);
|
|
34105
34610
|
await clickText(page, text, { exact, timeout });
|
|
34106
34611
|
return json({ clicked: text });
|
|
34107
34612
|
} catch (e) {
|
|
@@ -34109,21 +34614,23 @@ var init_mcp = __esm(async () => {
|
|
|
34109
34614
|
}
|
|
34110
34615
|
});
|
|
34111
34616
|
server.tool("browser_fill_form", "Fill multiple form fields in one call. Fields map: { selector: value }. Handles text, checkboxes, selects.", {
|
|
34112
|
-
session_id: exports_external.string(),
|
|
34617
|
+
session_id: exports_external.string().optional(),
|
|
34113
34618
|
fields: exports_external.record(exports_external.union([exports_external.string(), exports_external.boolean()])),
|
|
34114
34619
|
submit_selector: exports_external.string().optional()
|
|
34115
34620
|
}, async ({ session_id, fields, submit_selector }) => {
|
|
34116
34621
|
try {
|
|
34117
|
-
const
|
|
34622
|
+
const sid = resolveSessionId(session_id);
|
|
34623
|
+
const page = getSessionPage(sid);
|
|
34118
34624
|
const result = await fillForm(page, fields, submit_selector);
|
|
34119
34625
|
return json(result);
|
|
34120
34626
|
} catch (e) {
|
|
34121
|
-
return
|
|
34627
|
+
return errWithScreenshot(e, session_id);
|
|
34122
34628
|
}
|
|
34123
34629
|
});
|
|
34124
|
-
server.tool("browser_wait_for_text", "Wait until specific text appears on the page", { session_id: exports_external.string(), text: exports_external.string(), timeout: exports_external.number().optional().default(1e4), exact: exports_external.boolean().optional().default(false) }, async ({ session_id, text, timeout, exact }) => {
|
|
34630
|
+
server.tool("browser_wait_for_text", "Wait until specific text appears on the page", { session_id: exports_external.string().optional(), text: exports_external.string(), timeout: exports_external.number().optional().default(1e4), exact: exports_external.boolean().optional().default(false) }, async ({ session_id, text, timeout, exact }) => {
|
|
34125
34631
|
try {
|
|
34126
|
-
const
|
|
34632
|
+
const sid = resolveSessionId(session_id);
|
|
34633
|
+
const page = getSessionPage(sid);
|
|
34127
34634
|
const start = Date.now();
|
|
34128
34635
|
await waitForText(page, text, { timeout, exact });
|
|
34129
34636
|
return json({ found: true, elapsed_ms: Date.now() - start });
|
|
@@ -34131,46 +34638,51 @@ var init_mcp = __esm(async () => {
|
|
|
34131
34638
|
return err(e);
|
|
34132
34639
|
}
|
|
34133
34640
|
});
|
|
34134
|
-
server.tool("browser_element_exists", "Check if a selector exists on the page (no throw, returns boolean)", { session_id: exports_external.string(), selector: exports_external.string(), check_visible: exports_external.boolean().optional().default(false) }, async ({ session_id, selector, check_visible }) => {
|
|
34641
|
+
server.tool("browser_element_exists", "Check if a selector exists on the page (no throw, returns boolean)", { session_id: exports_external.string().optional(), selector: exports_external.string(), check_visible: exports_external.boolean().optional().default(false) }, async ({ session_id, selector, check_visible }) => {
|
|
34135
34642
|
try {
|
|
34136
|
-
const
|
|
34643
|
+
const sid = resolveSessionId(session_id);
|
|
34644
|
+
const page = getSessionPage(sid);
|
|
34137
34645
|
return json(await elementExists(page, selector, { visible: check_visible }));
|
|
34138
34646
|
} catch (e) {
|
|
34139
34647
|
return err(e);
|
|
34140
34648
|
}
|
|
34141
34649
|
});
|
|
34142
|
-
server.tool("browser_get_page_info", "Get a full page summary in one call: url, title, meta tags, link/image/form counts, text length", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34650
|
+
server.tool("browser_get_page_info", "Get a full page summary in one call: url, title, meta tags, link/image/form counts, text length", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34143
34651
|
try {
|
|
34144
|
-
const
|
|
34652
|
+
const sid = resolveSessionId(session_id);
|
|
34653
|
+
const page = getSessionPage(sid);
|
|
34145
34654
|
const info = await getPageInfo(page);
|
|
34146
|
-
const errors2 = getConsoleLog(
|
|
34655
|
+
const errors2 = getConsoleLog(sid, "error");
|
|
34147
34656
|
info.has_console_errors = errors2.length > 0;
|
|
34148
34657
|
return json(info);
|
|
34149
34658
|
} catch (e) {
|
|
34150
34659
|
return err(e);
|
|
34151
34660
|
}
|
|
34152
34661
|
});
|
|
34153
|
-
server.tool("browser_has_errors", "Quick check: does the session have any console errors?", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34662
|
+
server.tool("browser_has_errors", "Quick check: does the session have any console errors?", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34154
34663
|
try {
|
|
34155
|
-
const
|
|
34664
|
+
const sid = resolveSessionId(session_id);
|
|
34665
|
+
const errors2 = getConsoleLog(sid, "error");
|
|
34156
34666
|
return json({ has_errors: errors2.length > 0, error_count: errors2.length, errors: errors2 });
|
|
34157
34667
|
} catch (e) {
|
|
34158
34668
|
return err(e);
|
|
34159
34669
|
}
|
|
34160
34670
|
});
|
|
34161
|
-
server.tool("browser_clear_errors", "Clear console error log for a session", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34671
|
+
server.tool("browser_clear_errors", "Clear console error log for a session", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34162
34672
|
try {
|
|
34673
|
+
const sid = resolveSessionId(session_id);
|
|
34163
34674
|
const { clearConsoleLog: clearConsoleLog2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
|
|
34164
|
-
clearConsoleLog2(
|
|
34675
|
+
clearConsoleLog2(sid);
|
|
34165
34676
|
return json({ cleared: true });
|
|
34166
34677
|
} catch (e) {
|
|
34167
34678
|
return err(e);
|
|
34168
34679
|
}
|
|
34169
34680
|
});
|
|
34170
34681
|
activeWatchHandles2 = new Map;
|
|
34171
|
-
server.tool("browser_watch_start", "Start watching a page for DOM changes", { session_id: exports_external.string(), selector: exports_external.string().optional(), interval_ms: exports_external.number().optional().default(500), max_changes: exports_external.number().optional().default(50) }, async ({ session_id, selector, interval_ms, max_changes }) => {
|
|
34682
|
+
server.tool("browser_watch_start", "Start watching a page for DOM changes", { session_id: exports_external.string().optional(), selector: exports_external.string().optional(), interval_ms: exports_external.number().optional().default(500), max_changes: exports_external.number().optional().default(50) }, async ({ session_id, selector, interval_ms, max_changes }) => {
|
|
34172
34683
|
try {
|
|
34173
|
-
const
|
|
34684
|
+
const sid = resolveSessionId(session_id);
|
|
34685
|
+
const page = getSessionPage(sid);
|
|
34174
34686
|
const handle = watchPage(page, { selector, intervalMs: interval_ms, maxChanges: max_changes });
|
|
34175
34687
|
activeWatchHandles2.set(handle.id, handle);
|
|
34176
34688
|
return json({ watch_id: handle.id });
|
|
@@ -34197,7 +34709,7 @@ var init_mcp = __esm(async () => {
|
|
|
34197
34709
|
});
|
|
34198
34710
|
server.tool("browser_gallery_list", "List screenshot gallery entries with optional filters", {
|
|
34199
34711
|
project_id: exports_external.string().optional(),
|
|
34200
|
-
session_id: exports_external.string().optional(),
|
|
34712
|
+
session_id: exports_external.string().optional().optional(),
|
|
34201
34713
|
tag: exports_external.string().optional(),
|
|
34202
34714
|
is_favorite: exports_external.boolean().optional(),
|
|
34203
34715
|
date_from: exports_external.string().optional(),
|
|
@@ -34285,14 +34797,14 @@ var init_mcp = __esm(async () => {
|
|
|
34285
34797
|
return err(e);
|
|
34286
34798
|
}
|
|
34287
34799
|
});
|
|
34288
|
-
server.tool("browser_downloads_list", "List all files in the downloads folder", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34800
|
+
server.tool("browser_downloads_list", "List all files in the downloads folder", { session_id: exports_external.string().optional().optional() }, async ({ session_id }) => {
|
|
34289
34801
|
try {
|
|
34290
34802
|
return json({ downloads: listDownloads(session_id), count: listDownloads(session_id).length });
|
|
34291
34803
|
} catch (e) {
|
|
34292
34804
|
return err(e);
|
|
34293
34805
|
}
|
|
34294
34806
|
});
|
|
34295
|
-
server.tool("browser_downloads_get", "Get a downloaded file by id, returning base64 content and metadata", { id: exports_external.string(), session_id: exports_external.string().optional() }, async ({ id, session_id }) => {
|
|
34807
|
+
server.tool("browser_downloads_get", "Get a downloaded file by id, returning base64 content and metadata", { id: exports_external.string(), session_id: exports_external.string().optional().optional() }, async ({ id, session_id }) => {
|
|
34296
34808
|
try {
|
|
34297
34809
|
const file = getDownload(id, session_id);
|
|
34298
34810
|
if (!file)
|
|
@@ -34303,7 +34815,7 @@ var init_mcp = __esm(async () => {
|
|
|
34303
34815
|
return err(e);
|
|
34304
34816
|
}
|
|
34305
34817
|
});
|
|
34306
|
-
server.tool("browser_downloads_delete", "Delete a downloaded file by id", { id: exports_external.string(), session_id: exports_external.string().optional() }, async ({ id, session_id }) => {
|
|
34818
|
+
server.tool("browser_downloads_delete", "Delete a downloaded file by id", { id: exports_external.string(), session_id: exports_external.string().optional().optional() }, async ({ id, session_id }) => {
|
|
34307
34819
|
try {
|
|
34308
34820
|
const deleted = deleteDownload(id, session_id);
|
|
34309
34821
|
return json({ deleted });
|
|
@@ -34318,7 +34830,7 @@ var init_mcp = __esm(async () => {
|
|
|
34318
34830
|
return err(e);
|
|
34319
34831
|
}
|
|
34320
34832
|
});
|
|
34321
|
-
server.tool("browser_downloads_export", "Copy a downloaded file to a target path", { id: exports_external.string(), target_path: exports_external.string(), session_id: exports_external.string().optional() }, async ({ id, target_path, session_id }) => {
|
|
34833
|
+
server.tool("browser_downloads_export", "Copy a downloaded file to a target path", { id: exports_external.string(), target_path: exports_external.string(), session_id: exports_external.string().optional().optional() }, async ({ id, target_path, session_id }) => {
|
|
34322
34834
|
try {
|
|
34323
34835
|
const finalPath = exportToPath(id, target_path, session_id);
|
|
34324
34836
|
return json({ path: finalPath });
|
|
@@ -34343,12 +34855,13 @@ var init_mcp = __esm(async () => {
|
|
|
34343
34855
|
return err(e);
|
|
34344
34856
|
}
|
|
34345
34857
|
});
|
|
34346
|
-
server.tool("browser_snapshot_diff", "Take a new accessibility snapshot and diff it against the last snapshot for this session. Shows added/removed/modified interactive elements.", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34858
|
+
server.tool("browser_snapshot_diff", "Take a new accessibility snapshot and diff it against the last snapshot for this session. Shows added/removed/modified interactive elements.", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34347
34859
|
try {
|
|
34348
|
-
const
|
|
34349
|
-
const
|
|
34350
|
-
const
|
|
34351
|
-
|
|
34860
|
+
const sid = resolveSessionId(session_id);
|
|
34861
|
+
const page = getSessionPage(sid);
|
|
34862
|
+
const before = getLastSnapshot(sid);
|
|
34863
|
+
const after = await takeSnapshot(page, sid);
|
|
34864
|
+
setLastSnapshot(sid, after);
|
|
34352
34865
|
if (!before) {
|
|
34353
34866
|
return json({
|
|
34354
34867
|
message: "No previous snapshot \u2014 returning current snapshot only.",
|
|
@@ -34371,12 +34884,13 @@ var init_mcp = __esm(async () => {
|
|
|
34371
34884
|
return err(e);
|
|
34372
34885
|
}
|
|
34373
34886
|
});
|
|
34374
|
-
server.tool("browser_session_stats", "Get session info and estimated token usage (based on network log, console log, and gallery entry sizes).", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34887
|
+
server.tool("browser_session_stats", "Get session info and estimated token usage (based on network log, console log, and gallery entry sizes).", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34375
34888
|
try {
|
|
34376
|
-
const
|
|
34377
|
-
const
|
|
34378
|
-
const
|
|
34379
|
-
const
|
|
34889
|
+
const sid = resolveSessionId(session_id);
|
|
34890
|
+
const session = getSession2(sid);
|
|
34891
|
+
const networkLog = getNetworkLog(sid);
|
|
34892
|
+
const consoleLog = getConsoleLog(sid);
|
|
34893
|
+
const galleryEntries = listEntries({ sessionId: sid, limit: 1000 });
|
|
34380
34894
|
let totalChars = 0;
|
|
34381
34895
|
for (const req of networkLog) {
|
|
34382
34896
|
totalChars += (req.url?.length ?? 0) + (req.request_headers?.length ?? 0) + (req.response_headers?.length ?? 0) + (req.request_body?.length ?? 0);
|
|
@@ -34388,7 +34902,7 @@ var init_mcp = __esm(async () => {
|
|
|
34388
34902
|
totalChars += (entry.url?.length ?? 0) + (entry.title?.length ?? 0) + (entry.notes?.length ?? 0) + (entry.tags?.join(",").length ?? 0);
|
|
34389
34903
|
}
|
|
34390
34904
|
const estimatedTokens = Math.ceil(totalChars / 4);
|
|
34391
|
-
const tokenBudget = getTokenBudget(
|
|
34905
|
+
const tokenBudget = getTokenBudget(sid);
|
|
34392
34906
|
return json({
|
|
34393
34907
|
session,
|
|
34394
34908
|
network_request_count: networkLog.length,
|
|
@@ -34402,52 +34916,57 @@ var init_mcp = __esm(async () => {
|
|
|
34402
34916
|
return err(e);
|
|
34403
34917
|
}
|
|
34404
34918
|
});
|
|
34405
|
-
server.tool("browser_tab_new", "Open a new tab in the session's browser context, optionally navigating to a URL", { session_id: exports_external.string(), url: exports_external.string().optional() }, async ({ session_id, url }) => {
|
|
34919
|
+
server.tool("browser_tab_new", "Open a new tab in the session's browser context, optionally navigating to a URL", { session_id: exports_external.string().optional(), url: exports_external.string().optional() }, async ({ session_id, url }) => {
|
|
34406
34920
|
try {
|
|
34407
|
-
const
|
|
34921
|
+
const sid = resolveSessionId(session_id);
|
|
34922
|
+
const page = getSessionPage(sid);
|
|
34408
34923
|
const tab = await newTab(page, url);
|
|
34409
34924
|
return json(tab);
|
|
34410
34925
|
} catch (e) {
|
|
34411
34926
|
return err(e);
|
|
34412
34927
|
}
|
|
34413
34928
|
});
|
|
34414
|
-
server.tool("browser_tab_list", "List all open tabs in the session's browser context", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34929
|
+
server.tool("browser_tab_list", "List all open tabs in the session's browser context", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34415
34930
|
try {
|
|
34416
|
-
const
|
|
34931
|
+
const sid = resolveSessionId(session_id);
|
|
34932
|
+
const page = getSessionPage(sid);
|
|
34417
34933
|
const tabs = await listTabs(page);
|
|
34418
34934
|
return json({ tabs, count: tabs.length });
|
|
34419
34935
|
} catch (e) {
|
|
34420
34936
|
return err(e);
|
|
34421
34937
|
}
|
|
34422
34938
|
});
|
|
34423
|
-
server.tool("browser_tab_switch", "Switch to a different tab by index. Updates the session's active page.", { session_id: exports_external.string(), tab_id: exports_external.number() }, async ({ session_id, tab_id }) => {
|
|
34939
|
+
server.tool("browser_tab_switch", "Switch to a different tab by index. Updates the session's active page.", { session_id: exports_external.string().optional(), tab_id: exports_external.number() }, async ({ session_id, tab_id }) => {
|
|
34424
34940
|
try {
|
|
34425
|
-
const
|
|
34941
|
+
const sid = resolveSessionId(session_id);
|
|
34942
|
+
const page = getSessionPage(sid);
|
|
34426
34943
|
const result = await switchTab(page, tab_id);
|
|
34427
|
-
setSessionPage(
|
|
34944
|
+
setSessionPage(sid, result.page);
|
|
34428
34945
|
return json(result.tab);
|
|
34429
34946
|
} catch (e) {
|
|
34430
34947
|
return err(e);
|
|
34431
34948
|
}
|
|
34432
34949
|
});
|
|
34433
|
-
server.tool("browser_tab_close", "Close a tab by index. Cannot close the last tab.", { session_id: exports_external.string(), tab_id: exports_external.number() }, async ({ session_id, tab_id }) => {
|
|
34950
|
+
server.tool("browser_tab_close", "Close a tab by index. Cannot close the last tab.", { session_id: exports_external.string().optional(), tab_id: exports_external.number() }, async ({ session_id, tab_id }) => {
|
|
34434
34951
|
try {
|
|
34435
|
-
const
|
|
34952
|
+
const sid = resolveSessionId(session_id);
|
|
34953
|
+
const page = getSessionPage(sid);
|
|
34436
34954
|
const context = page.context();
|
|
34437
34955
|
const result = await closeTab(page, tab_id);
|
|
34438
34956
|
const remainingPages = context.pages();
|
|
34439
34957
|
const newActivePage = remainingPages[result.active_tab.index];
|
|
34440
34958
|
if (newActivePage) {
|
|
34441
|
-
setSessionPage(
|
|
34959
|
+
setSessionPage(sid, newActivePage);
|
|
34442
34960
|
}
|
|
34443
34961
|
return json(result);
|
|
34444
34962
|
} catch (e) {
|
|
34445
34963
|
return err(e);
|
|
34446
34964
|
}
|
|
34447
34965
|
});
|
|
34448
|
-
server.tool("browser_handle_dialog", "Accept or dismiss a pending dialog (alert, confirm, prompt). Handles the oldest pending dialog.", { session_id: exports_external.string(), action: exports_external.enum(["accept", "dismiss"]), prompt_text: exports_external.string().optional() }, async ({ session_id, action, prompt_text }) => {
|
|
34966
|
+
server.tool("browser_handle_dialog", "Accept or dismiss a pending dialog (alert, confirm, prompt). Handles the oldest pending dialog.", { session_id: exports_external.string().optional(), action: exports_external.enum(["accept", "dismiss"]), prompt_text: exports_external.string().optional() }, async ({ session_id, action, prompt_text }) => {
|
|
34449
34967
|
try {
|
|
34450
|
-
const
|
|
34968
|
+
const sid = resolveSessionId(session_id);
|
|
34969
|
+
const result = await handleDialog(sid, action, prompt_text);
|
|
34451
34970
|
if (!result.handled)
|
|
34452
34971
|
return err(new Error("No pending dialogs for this session"));
|
|
34453
34972
|
return json(result);
|
|
@@ -34455,28 +34974,31 @@ var init_mcp = __esm(async () => {
|
|
|
34455
34974
|
return err(e);
|
|
34456
34975
|
}
|
|
34457
34976
|
});
|
|
34458
|
-
server.tool("browser_get_dialogs", "Get all pending dialogs for a session", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
34977
|
+
server.tool("browser_get_dialogs", "Get all pending dialogs for a session", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34459
34978
|
try {
|
|
34460
|
-
const
|
|
34979
|
+
const sid = resolveSessionId(session_id);
|
|
34980
|
+
const dialogs = getDialogs(sid);
|
|
34461
34981
|
return json({ dialogs, count: dialogs.length });
|
|
34462
34982
|
} catch (e) {
|
|
34463
34983
|
return err(e);
|
|
34464
34984
|
}
|
|
34465
34985
|
});
|
|
34466
|
-
server.tool("browser_profile_save", "Save cookies + localStorage from the current session as a named profile", { session_id: exports_external.string(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34986
|
+
server.tool("browser_profile_save", "Save cookies + localStorage from the current session as a named profile", { session_id: exports_external.string().optional(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34467
34987
|
try {
|
|
34468
|
-
const
|
|
34988
|
+
const sid = resolveSessionId(session_id);
|
|
34989
|
+
const page = getSessionPage(sid);
|
|
34469
34990
|
const info = await saveProfile(page, name);
|
|
34470
34991
|
return json(info);
|
|
34471
34992
|
} catch (e) {
|
|
34472
34993
|
return err(e);
|
|
34473
34994
|
}
|
|
34474
34995
|
});
|
|
34475
|
-
server.tool("browser_profile_load", "Load a saved profile and apply cookies + localStorage to the current session", { session_id: exports_external.string().optional(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34996
|
+
server.tool("browser_profile_load", "Load a saved profile and apply cookies + localStorage to the current session", { session_id: exports_external.string().optional().optional(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
34476
34997
|
try {
|
|
34477
34998
|
const profileData = loadProfile(name);
|
|
34478
34999
|
if (session_id) {
|
|
34479
|
-
const
|
|
35000
|
+
const sid = resolveSessionId(session_id);
|
|
35001
|
+
const page = getSessionPage(sid);
|
|
34480
35002
|
const applied = await applyProfile(page, profileData);
|
|
34481
35003
|
return json({ ...applied, profile: name });
|
|
34482
35004
|
}
|
|
@@ -34614,7 +35136,13 @@ var init_mcp = __esm(async () => {
|
|
|
34614
35136
|
{ tool: "browser_session_close", description: "Close a session" },
|
|
34615
35137
|
{ tool: "browser_session_get_by_name", description: "Get session by name" },
|
|
34616
35138
|
{ tool: "browser_session_rename", description: "Rename a session" },
|
|
35139
|
+
{ tool: "browser_session_lock", description: "Lock a session for an agent" },
|
|
35140
|
+
{ tool: "browser_session_unlock", description: "Unlock a session" },
|
|
35141
|
+
{ tool: "browser_session_transfer", description: "Transfer session to another agent" },
|
|
35142
|
+
{ tool: "browser_session_tag", description: "Add a tag to a session" },
|
|
35143
|
+
{ tool: "browser_session_untag", description: "Remove a tag from a session" },
|
|
34617
35144
|
{ tool: "browser_session_stats", description: "Get session stats and token usage" },
|
|
35145
|
+
{ tool: "browser_session_timeline", description: "Get chronological action log" },
|
|
34618
35146
|
{ tool: "browser_tab_new", description: "Open a new tab" },
|
|
34619
35147
|
{ tool: "browser_tab_list", description: "List all open tabs" },
|
|
34620
35148
|
{ tool: "browser_tab_switch", description: "Switch to a tab by index" },
|
|
@@ -34652,18 +35180,19 @@ var init_mcp = __esm(async () => {
|
|
|
34652
35180
|
}
|
|
34653
35181
|
});
|
|
34654
35182
|
server.tool("browser_scroll_to_element", "Scroll an element into view (by ref or selector) then optionally take a screenshot of it. Replaces scroll + wait + screenshot pattern.", {
|
|
34655
|
-
session_id: exports_external.string(),
|
|
35183
|
+
session_id: exports_external.string().optional(),
|
|
34656
35184
|
selector: exports_external.string().optional(),
|
|
34657
35185
|
ref: exports_external.string().optional(),
|
|
34658
35186
|
screenshot: exports_external.boolean().optional().default(true),
|
|
34659
35187
|
wait_ms: exports_external.number().optional().default(200)
|
|
34660
35188
|
}, async ({ session_id, selector, ref, screenshot: doScreenshot, wait_ms }) => {
|
|
34661
35189
|
try {
|
|
34662
|
-
const
|
|
35190
|
+
const sid = resolveSessionId(session_id);
|
|
35191
|
+
const page = getSessionPage(sid);
|
|
34663
35192
|
let locator;
|
|
34664
35193
|
if (ref) {
|
|
34665
35194
|
const { getRefLocator: getRefLocator2 } = await Promise.resolve().then(() => (init_snapshot(), exports_snapshot));
|
|
34666
|
-
locator = getRefLocator2(page,
|
|
35195
|
+
locator = getRefLocator2(page, sid, ref);
|
|
34667
35196
|
} else if (selector) {
|
|
34668
35197
|
locator = page.locator(selector).first();
|
|
34669
35198
|
} else {
|
|
@@ -34688,11 +35217,12 @@ var init_mcp = __esm(async () => {
|
|
|
34688
35217
|
return err(e);
|
|
34689
35218
|
}
|
|
34690
35219
|
});
|
|
34691
|
-
server.tool("browser_check", "RECOMMENDED FIRST CALL: one-shot page summary \u2014 url, title, errors, performance, thumbnail, refs. Replaces 4+ separate tool calls.", { session_id: exports_external.string() }, async ({ session_id }) => {
|
|
35220
|
+
server.tool("browser_check", "RECOMMENDED FIRST CALL: one-shot page summary \u2014 url, title, errors, performance, thumbnail, refs. Replaces 4+ separate tool calls.", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
34692
35221
|
try {
|
|
34693
|
-
const
|
|
35222
|
+
const sid = resolveSessionId(session_id);
|
|
35223
|
+
const page = getSessionPage(sid);
|
|
34694
35224
|
const info = await getPageInfo(page);
|
|
34695
|
-
const errors2 = getConsoleLog(
|
|
35225
|
+
const errors2 = getConsoleLog(sid, "error");
|
|
34696
35226
|
info.has_console_errors = errors2.length > 0;
|
|
34697
35227
|
let perf = {};
|
|
34698
35228
|
try {
|
|
@@ -34706,8 +35236,8 @@ var init_mcp = __esm(async () => {
|
|
|
34706
35236
|
let snapshot_refs = "";
|
|
34707
35237
|
let interactive_count = 0;
|
|
34708
35238
|
try {
|
|
34709
|
-
const snap = await takeSnapshot(page,
|
|
34710
|
-
setLastSnapshot(
|
|
35239
|
+
const snap = await takeSnapshot(page, sid);
|
|
35240
|
+
setLastSnapshot(sid, snap);
|
|
34711
35241
|
interactive_count = snap.interactive_count;
|
|
34712
35242
|
snapshot_refs = Object.entries(snap.refs).slice(0, 30).map(([ref, i]) => `${i.role}:${i.name.slice(0, 50)} [${ref}]`).join(", ");
|
|
34713
35243
|
} catch {}
|
|
@@ -34716,9 +35246,10 @@ var init_mcp = __esm(async () => {
|
|
|
34716
35246
|
return err(e);
|
|
34717
35247
|
}
|
|
34718
35248
|
});
|
|
34719
|
-
server.tool("browser_secrets_login", "Login to a service using credentials from open-secrets vault or ~/.secrets. One call replaces 10+ tool calls.", { session_id: exports_external.string(), service: exports_external.string(), login_url: exports_external.string().optional(), save_profile: exports_external.boolean().optional().default(true) }, async ({ session_id, service, login_url, save_profile }) => {
|
|
35249
|
+
server.tool("browser_secrets_login", "Login to a service using credentials from open-secrets vault or ~/.secrets. One call replaces 10+ tool calls.", { session_id: exports_external.string().optional(), service: exports_external.string(), login_url: exports_external.string().optional(), save_profile: exports_external.boolean().optional().default(true) }, async ({ session_id, service, login_url, save_profile }) => {
|
|
34720
35250
|
try {
|
|
34721
|
-
const
|
|
35251
|
+
const sid = resolveSessionId(session_id);
|
|
35252
|
+
const page = getSessionPage(sid);
|
|
34722
35253
|
const { getCredentials: getCredentials2, loginWithCredentials: loginWithCredentials2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
|
|
34723
35254
|
const creds = await getCredentials2(service);
|
|
34724
35255
|
if (!creds)
|
|
@@ -34732,9 +35263,10 @@ var init_mcp = __esm(async () => {
|
|
|
34732
35263
|
return err(e);
|
|
34733
35264
|
}
|
|
34734
35265
|
});
|
|
34735
|
-
server.tool("browser_remember", "Store page facts in open-mementos for future recall. Agents skip re-scraping on repeat visits.", { session_id: exports_external.string(), facts: exports_external.record(exports_external.unknown()), tags: exports_external.array(exports_external.string()).optional() }, async ({ session_id, facts, tags }) => {
|
|
35266
|
+
server.tool("browser_remember", "Store page facts in open-mementos for future recall. Agents skip re-scraping on repeat visits.", { session_id: exports_external.string().optional(), facts: exports_external.record(exports_external.unknown()), tags: exports_external.array(exports_external.string()).optional() }, async ({ session_id, facts, tags }) => {
|
|
34736
35267
|
try {
|
|
34737
|
-
const
|
|
35268
|
+
const sid = resolveSessionId(session_id);
|
|
35269
|
+
const page = getSessionPage(sid);
|
|
34738
35270
|
const { rememberPage: rememberPage2 } = await Promise.resolve().then(() => (init_page_memory(), exports_page_memory));
|
|
34739
35271
|
const url = page.url();
|
|
34740
35272
|
await rememberPage2(url, facts, tags);
|
|
@@ -34752,12 +35284,13 @@ var init_mcp = __esm(async () => {
|
|
|
34752
35284
|
return err(e);
|
|
34753
35285
|
}
|
|
34754
35286
|
});
|
|
34755
|
-
server.tool("browser_session_announce", "Announce to other agents via open-conversations what this session is browsing.", { session_id: exports_external.string(), message: exports_external.string().optional() }, async ({ session_id, message }) => {
|
|
35287
|
+
server.tool("browser_session_announce", "Announce to other agents via open-conversations what this session is browsing.", { session_id: exports_external.string().optional(), message: exports_external.string().optional() }, async ({ session_id, message }) => {
|
|
34756
35288
|
try {
|
|
34757
|
-
const
|
|
35289
|
+
const sid = resolveSessionId(session_id);
|
|
35290
|
+
const page = getSessionPage(sid);
|
|
34758
35291
|
const { announceNavigation: announceNavigation2 } = await Promise.resolve().then(() => (init_coordination(), exports_coordination));
|
|
34759
35292
|
const url = page.url();
|
|
34760
|
-
await announceNavigation2(url,
|
|
35293
|
+
await announceNavigation2(url, sid);
|
|
34761
35294
|
return json({ announced: true, url, message });
|
|
34762
35295
|
} catch (e) {
|
|
34763
35296
|
return err(e);
|
|
@@ -34797,9 +35330,10 @@ var init_mcp = __esm(async () => {
|
|
|
34797
35330
|
return err(e);
|
|
34798
35331
|
}
|
|
34799
35332
|
});
|
|
34800
|
-
server.tool("browser_skill_run", "Run a pre-built browser skill (login, extract-pricing, extract-nav-links, monitor-price, get-metadata). One call replaces 5\u201315 tool calls.", { session_id: exports_external.string(), skill: exports_external.string(), params: exports_external.record(exports_external.unknown()).optional().default({}) }, async ({ session_id, skill, params }) => {
|
|
35333
|
+
server.tool("browser_skill_run", "Run a pre-built browser skill (login, extract-pricing, extract-nav-links, monitor-price, get-metadata). One call replaces 5\u201315 tool calls.", { session_id: exports_external.string().optional(), skill: exports_external.string(), params: exports_external.record(exports_external.unknown()).optional().default({}) }, async ({ session_id, skill, params }) => {
|
|
34801
35334
|
try {
|
|
34802
|
-
const
|
|
35335
|
+
const sid = resolveSessionId(session_id);
|
|
35336
|
+
const page = getSessionPage(sid);
|
|
34803
35337
|
const { runBrowserSkill: runBrowserSkill2 } = await Promise.resolve().then(() => (init_skills_runner(), exports_skills_runner));
|
|
34804
35338
|
return json(await runBrowserSkill2(skill, params, page));
|
|
34805
35339
|
} catch (e) {
|
|
@@ -34815,7 +35349,7 @@ var init_mcp = __esm(async () => {
|
|
|
34815
35349
|
}
|
|
34816
35350
|
});
|
|
34817
35351
|
server.tool("browser_batch", "Execute multiple browser actions in one call. Returns final snapshot. Eliminates 80% of round trips for multi-step flows.", {
|
|
34818
|
-
session_id: exports_external.string(),
|
|
35352
|
+
session_id: exports_external.string().optional(),
|
|
34819
35353
|
actions: exports_external.array(exports_external.object({
|
|
34820
35354
|
tool: exports_external.string(),
|
|
34821
35355
|
args: exports_external.record(exports_external.unknown()).optional().default({})
|
|
@@ -34823,12 +35357,13 @@ var init_mcp = __esm(async () => {
|
|
|
34823
35357
|
}, async ({ session_id, actions }) => {
|
|
34824
35358
|
try {
|
|
34825
35359
|
const results = [];
|
|
34826
|
-
const
|
|
35360
|
+
const sid = resolveSessionId(session_id);
|
|
35361
|
+
const page = getSessionPage(sid);
|
|
34827
35362
|
const t0 = Date.now();
|
|
34828
35363
|
for (const action of actions) {
|
|
34829
35364
|
try {
|
|
34830
35365
|
const toolName = action.tool.replace(/^browser_/, "");
|
|
34831
|
-
const args = { session_id, ...action.args };
|
|
35366
|
+
const args = { session_id: sid, ...action.args };
|
|
34832
35367
|
switch (toolName) {
|
|
34833
35368
|
case "navigate":
|
|
34834
35369
|
await navigate(page, action.args.url);
|
|
@@ -34837,7 +35372,7 @@ var init_mcp = __esm(async () => {
|
|
|
34837
35372
|
case "click":
|
|
34838
35373
|
if (args.ref) {
|
|
34839
35374
|
const { clickRef: clickRef2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
|
|
34840
|
-
await clickRef2(page,
|
|
35375
|
+
await clickRef2(page, sid, args.ref);
|
|
34841
35376
|
} else if (args.selector)
|
|
34842
35377
|
await page.click(args.selector);
|
|
34843
35378
|
results.push({ tool: action.tool, success: true });
|
|
@@ -34845,7 +35380,7 @@ var init_mcp = __esm(async () => {
|
|
|
34845
35380
|
case "type":
|
|
34846
35381
|
if (args.ref && args.text) {
|
|
34847
35382
|
const { typeRef: typeRef2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
|
|
34848
|
-
await typeRef2(page,
|
|
35383
|
+
await typeRef2(page, sid, args.ref, args.text);
|
|
34849
35384
|
} else if (args.selector && args.text)
|
|
34850
35385
|
await page.fill(args.selector, args.text);
|
|
34851
35386
|
results.push({ tool: action.tool, success: true });
|
|
@@ -34885,7 +35420,7 @@ var init_mcp = __esm(async () => {
|
|
|
34885
35420
|
}
|
|
34886
35421
|
let final_snapshot = {};
|
|
34887
35422
|
try {
|
|
34888
|
-
const snap = await takeSnapshot(page,
|
|
35423
|
+
const snap = await takeSnapshot(page, sid);
|
|
34889
35424
|
final_snapshot = {
|
|
34890
35425
|
refs: Object.fromEntries(Object.entries(snap.refs).slice(0, 20)),
|
|
34891
35426
|
interactive_count: snap.interactive_count
|
|
@@ -34982,18 +35517,20 @@ var init_mcp = __esm(async () => {
|
|
|
34982
35517
|
return err(e);
|
|
34983
35518
|
}
|
|
34984
35519
|
});
|
|
34985
|
-
server.tool("browser_task", "Execute a natural language browser task autonomously using Claude Haiku. Returns result + steps taken.", { session_id: exports_external.string(), task: exports_external.string(), max_steps: exports_external.number().optional().default(10), model: exports_external.string().optional() }, async ({ session_id, task, max_steps, model }) => {
|
|
35520
|
+
server.tool("browser_task", "Execute a natural language browser task autonomously using Claude Haiku. Returns result + steps taken.", { session_id: exports_external.string().optional(), task: exports_external.string(), max_steps: exports_external.number().optional().default(10), model: exports_external.string().optional() }, async ({ session_id, task, max_steps, model }) => {
|
|
34986
35521
|
try {
|
|
34987
|
-
const
|
|
35522
|
+
const sid = resolveSessionId(session_id);
|
|
35523
|
+
const page = getSessionPage(sid);
|
|
34988
35524
|
const { executeBrowserTask: executeBrowserTask2 } = await Promise.resolve().then(() => (init_ai_task(), exports_ai_task));
|
|
34989
|
-
return json(await executeBrowserTask2(page, task, { maxSteps: max_steps, model, sessionId:
|
|
35525
|
+
return json(await executeBrowserTask2(page, task, { maxSteps: max_steps, model, sessionId: sid }));
|
|
34990
35526
|
} catch (e) {
|
|
34991
35527
|
return err(e);
|
|
34992
35528
|
}
|
|
34993
35529
|
});
|
|
34994
|
-
server.tool("browser_assert", `Assert page conditions in one call. Conditions: 'url contains X', 'text:"Y" is visible', 'element:"#id" exists', 'count:"a" > 10', 'title contains Z'. Chain with AND.`, { session_id: exports_external.string(), condition: exports_external.string() }, async ({ session_id, condition }) => {
|
|
35530
|
+
server.tool("browser_assert", `Assert page conditions in one call. Conditions: 'url contains X', 'text:"Y" is visible', 'element:"#id" exists', 'count:"a" > 10', 'title contains Z'. Chain with AND.`, { session_id: exports_external.string().optional(), condition: exports_external.string() }, async ({ session_id, condition }) => {
|
|
34995
35531
|
try {
|
|
34996
|
-
const
|
|
35532
|
+
const sid = resolveSessionId(session_id);
|
|
35533
|
+
const page = getSessionPage(sid);
|
|
34997
35534
|
const checks = [];
|
|
34998
35535
|
let passed = true;
|
|
34999
35536
|
for (const part of condition.split(/\s+AND\s+/i)) {
|
|
@@ -35432,26 +35969,66 @@ import chalk from "chalk";
|
|
|
35432
35969
|
var pkg = JSON.parse(readFileSync9(join15(import.meta.dir, "../../package.json"), "utf8"));
|
|
35433
35970
|
var program2 = new Command;
|
|
35434
35971
|
program2.name("browser").description("@hasna/browser \u2014 general-purpose browser agent CLI").version(pkg.version);
|
|
35435
|
-
program2.command("navigate <url>").description("Navigate to a URL and optionally take a screenshot").option("--engine <engine>", "Browser engine: playwright|cdp|lightpanda|auto", "auto").option("--screenshot", "Take a screenshot after navigation").option("--extract", "Extract page text after navigation").option("--
|
|
35436
|
-
const { session, page } = await createSession2({ engine: opts.engine, headless:
|
|
35437
|
-
console.log(chalk.gray(`Session: ${session.id} (${session.engine})`));
|
|
35972
|
+
program2.command("navigate <url>").description("Navigate to a URL and optionally take a screenshot").option("--engine <engine>", "Browser engine: playwright|cdp|lightpanda|auto", "auto").option("--screenshot", "Take a screenshot after navigation").option("--extract", "Extract page text after navigation").option("--headed", "Run in headed (visible) mode").option("--json", "Output as JSON").action(async (url, opts) => {
|
|
35973
|
+
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
35438
35974
|
await navigate(page, url);
|
|
35439
35975
|
const title = await page.title();
|
|
35440
|
-
|
|
35441
|
-
console.log(chalk.blue(` Title: ${title}`));
|
|
35976
|
+
let screenshotPath;
|
|
35442
35977
|
if (opts.screenshot) {
|
|
35443
35978
|
const result = await takeScreenshot(page);
|
|
35444
|
-
|
|
35979
|
+
screenshotPath = result.path;
|
|
35445
35980
|
}
|
|
35981
|
+
let text;
|
|
35446
35982
|
if (opts.extract) {
|
|
35447
|
-
|
|
35448
|
-
|
|
35983
|
+
text = await getText(page);
|
|
35984
|
+
}
|
|
35985
|
+
if (opts.json) {
|
|
35986
|
+
const output = { session_id: session.id, engine: session.engine, url, title };
|
|
35987
|
+
if (screenshotPath)
|
|
35988
|
+
output.screenshot = screenshotPath;
|
|
35989
|
+
if (text)
|
|
35990
|
+
output.text = text.slice(0, 500);
|
|
35991
|
+
console.log(JSON.stringify(output, null, 2));
|
|
35992
|
+
} else {
|
|
35993
|
+
console.log(chalk.gray(`Session: ${session.id} (${session.engine})`));
|
|
35994
|
+
console.log(chalk.green(`\u2713 Navigated to: ${url}`));
|
|
35995
|
+
console.log(chalk.blue(` Title: ${title}`));
|
|
35996
|
+
if (screenshotPath)
|
|
35997
|
+
console.log(chalk.blue(` Screenshot: ${screenshotPath}`));
|
|
35998
|
+
if (text)
|
|
35999
|
+
console.log(chalk.white(`
|
|
35449
36000
|
${text.slice(0, 500)}...`));
|
|
35450
36001
|
}
|
|
35451
36002
|
await closeSession2(session.id);
|
|
35452
36003
|
});
|
|
35453
|
-
program2.command("
|
|
35454
|
-
const { session, page } = await createSession2({ engine: opts.engine, headless:
|
|
36004
|
+
program2.command("check <url>").description("One-liner page health check: navigate, screenshot, extract info, check errors").option("--engine <engine>", "Browser engine", "auto").option("--headed", "Run in headed (visible) mode").option("--json", "Output as JSON").action(async (url, opts) => {
|
|
36005
|
+
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
36006
|
+
await navigate(page, url);
|
|
36007
|
+
const title = await page.title();
|
|
36008
|
+
const currentUrl = page.url();
|
|
36009
|
+
const text = await getText(page);
|
|
36010
|
+
const links = await getLinks(page);
|
|
36011
|
+
const result = await takeScreenshot(page);
|
|
36012
|
+
const summary = {
|
|
36013
|
+
url: currentUrl,
|
|
36014
|
+
title,
|
|
36015
|
+
text_length: text.length,
|
|
36016
|
+
links_count: links.length,
|
|
36017
|
+
screenshot: result.path,
|
|
36018
|
+
screenshot_size_kb: +(result.size_bytes / 1024).toFixed(1)
|
|
36019
|
+
};
|
|
36020
|
+
if (opts.json) {
|
|
36021
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
36022
|
+
} else {
|
|
36023
|
+
console.log(chalk.green(`\u2713 ${title}`));
|
|
36024
|
+
console.log(chalk.blue(` URL: ${currentUrl}`));
|
|
36025
|
+
console.log(chalk.gray(` Text: ${text.length} chars, Links: ${links.length}`));
|
|
36026
|
+
console.log(chalk.gray(` Screenshot: ${result.path} (${summary.screenshot_size_kb} KB)`));
|
|
36027
|
+
}
|
|
36028
|
+
await closeSession2(session.id);
|
|
36029
|
+
});
|
|
36030
|
+
program2.command("screenshot <url>").description("Navigate to a URL and take a screenshot").option("--engine <engine>", "Browser engine", "auto").option("--selector <selector>", "CSS selector for element screenshot").option("--full-page", "Capture full page").option("--format <format>", "Image format: png|jpeg|webp", "png").option("--headed", "Run in headed (visible) mode").action(async (url, opts) => {
|
|
36031
|
+
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
35455
36032
|
await navigate(page, url);
|
|
35456
36033
|
const result = await takeScreenshot(page, {
|
|
35457
36034
|
selector: opts.selector,
|
|
@@ -35462,11 +36039,13 @@ program2.command("screenshot <url>").description("Navigate to a URL and take a s
|
|
|
35462
36039
|
console.log(chalk.gray(` Size: ${(result.size_bytes / 1024).toFixed(1)} KB`));
|
|
35463
36040
|
await closeSession2(session.id);
|
|
35464
36041
|
});
|
|
35465
|
-
program2.command("extract <url>").description("Extract content from a URL").option("--engine <engine>", "Browser engine", "auto").option("--selector <selector>", "CSS selector").option("--format <format>", "Format: text|html|links|table|structured", "text").action(async (url, opts) => {
|
|
35466
|
-
const { session, page } = await createSession2({ engine: opts.engine, headless:
|
|
36042
|
+
program2.command("extract <url>").description("Extract content from a URL").option("--engine <engine>", "Browser engine", "auto").option("--selector <selector>", "CSS selector").option("--format <format>", "Format: text|html|links|table|structured", "text").option("--headed", "Run in headed (visible) mode").option("--json", "Output as JSON").action(async (url, opts) => {
|
|
36043
|
+
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
35467
36044
|
await navigate(page, url);
|
|
35468
36045
|
const result = await extract(page, { format: opts.format, selector: opts.selector });
|
|
35469
|
-
if (opts.
|
|
36046
|
+
if (opts.json) {
|
|
36047
|
+
console.log(JSON.stringify(result, null, 2));
|
|
36048
|
+
} else if (opts.format === "links" && result.links) {
|
|
35470
36049
|
result.links.forEach((l) => console.log(l));
|
|
35471
36050
|
} else if (opts.format === "table" && result.table) {
|
|
35472
36051
|
result.table.forEach((row) => console.log(row.join("\t")));
|
|
@@ -35475,8 +36054,8 @@ program2.command("extract <url>").description("Extract content from a URL").opti
|
|
|
35475
36054
|
}
|
|
35476
36055
|
await closeSession2(session.id);
|
|
35477
36056
|
});
|
|
35478
|
-
program2.command("eval <url> <script>").description("Run JavaScript in a page context").option("--engine <engine>", "Browser engine", "auto").action(async (url, script, opts) => {
|
|
35479
|
-
const { session, page } = await createSession2({ engine: opts.engine, headless:
|
|
36057
|
+
program2.command("eval <url> <script>").description("Run JavaScript in a page context").option("--engine <engine>", "Browser engine", "auto").option("--headed", "Run in headed (visible) mode").action(async (url, script, opts) => {
|
|
36058
|
+
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
35480
36059
|
await navigate(page, url);
|
|
35481
36060
|
const result = await page.evaluate(script);
|
|
35482
36061
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -35502,14 +36081,16 @@ ${result.errors.length} errors:`));
|
|
|
35502
36081
|
}
|
|
35503
36082
|
});
|
|
35504
36083
|
var sessionCmd = program2.command("session").description("Manage browser sessions");
|
|
35505
|
-
sessionCmd.command("create").description("Create a new browser session").option("--engine <engine>", "Browser engine", "auto").option("--url <url>", "Start URL").action(async (opts) => {
|
|
35506
|
-
const { session } = await createSession2({ engine: opts.engine, startUrl: opts.url });
|
|
36084
|
+
sessionCmd.command("create").description("Create a new browser session").option("--engine <engine>", "Browser engine", "auto").option("--url <url>", "Start URL").option("--headed", "Run in headed (visible) mode").action(async (opts) => {
|
|
36085
|
+
const { session } = await createSession2({ engine: opts.engine, startUrl: opts.url, headless: !opts.headed });
|
|
35507
36086
|
console.log(chalk.green(`\u2713 Session created`));
|
|
35508
36087
|
console.log(JSON.stringify(session, null, 2));
|
|
35509
36088
|
});
|
|
35510
|
-
sessionCmd.command("list").description("List all sessions").option("--status <status>", "Filter by status").action((opts) => {
|
|
36089
|
+
sessionCmd.command("list").description("List all sessions").option("--status <status>", "Filter by status").option("--json", "Output as JSON").action((opts) => {
|
|
35511
36090
|
const sessions = listSessions2(opts.status ? { status: opts.status } : undefined);
|
|
35512
|
-
if (
|
|
36091
|
+
if (opts.json) {
|
|
36092
|
+
console.log(JSON.stringify(sessions, null, 2));
|
|
36093
|
+
} else if (sessions.length === 0) {
|
|
35513
36094
|
console.log(chalk.gray("No sessions found"));
|
|
35514
36095
|
} else {
|
|
35515
36096
|
sessions.forEach((s) => console.log(`${s.id} [${s.status}] ${s.engine} ${s.start_url ?? ""}`));
|
|
@@ -35520,8 +36101,8 @@ sessionCmd.command("close <id>").description("Close a session").action(async (id
|
|
|
35520
36101
|
console.log(chalk.green(`\u2713 Session closed: ${id}`));
|
|
35521
36102
|
});
|
|
35522
36103
|
var recordCmd = program2.command("record").description("Manage action recordings");
|
|
35523
|
-
recordCmd.command("start <name>").description("Start recording actions in a new session").option("--url <url>", "Start URL").option("--engine <engine>", "Browser engine", "auto").action(async (name, opts) => {
|
|
35524
|
-
const { session } = await createSession2({ engine: opts.engine, startUrl: opts.url });
|
|
36104
|
+
recordCmd.command("start <name>").description("Start recording actions in a new session").option("--url <url>", "Start URL").option("--engine <engine>", "Browser engine", "auto").option("--headed", "Run in headed (visible) mode").action(async (name, opts) => {
|
|
36105
|
+
const { session } = await createSession2({ engine: opts.engine, startUrl: opts.url, headless: !opts.headed });
|
|
35525
36106
|
const recording = startRecording(session.id, name, opts.url);
|
|
35526
36107
|
console.log(chalk.green(`\u2713 Recording started`));
|
|
35527
36108
|
console.log(` Recording ID: ${recording.id}`);
|
|
@@ -35532,8 +36113,8 @@ recordCmd.command("stop <recording_id>").description("Stop an active recording")
|
|
|
35532
36113
|
console.log(chalk.green(`\u2713 Recording stopped: ${recording.name}`));
|
|
35533
36114
|
console.log(` Steps: ${recording.steps.length}`);
|
|
35534
36115
|
});
|
|
35535
|
-
recordCmd.command("replay <recording_id>").description("Replay a recording in a new session").option("--url <url>", "Override start URL").option("--engine <engine>", "Browser engine", "auto").action(async (id, opts) => {
|
|
35536
|
-
const { session, page } = await createSession2({ engine: opts.engine, startUrl: opts.url });
|
|
36116
|
+
recordCmd.command("replay <recording_id>").description("Replay a recording in a new session").option("--url <url>", "Override start URL").option("--engine <engine>", "Browser engine", "auto").option("--headed", "Run in headed (visible) mode").action(async (id, opts) => {
|
|
36117
|
+
const { session, page } = await createSession2({ engine: opts.engine, startUrl: opts.url, headless: !opts.headed });
|
|
35537
36118
|
const result = await replayRecording(id, page);
|
|
35538
36119
|
console.log(result.success ? chalk.green("\u2713 Replay complete") : chalk.red("\u2717 Replay had errors"));
|
|
35539
36120
|
console.log(` Steps: ${result.steps_executed} executed, ${result.steps_failed} failed`);
|