mcp-scraper 0.3.18 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/api-server.cjs +169 -18
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +1 -1
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/{chunk-2XCNU22L.js → chunk-6N6USFMH.js} +2 -2
- package/dist/{chunk-5H22TOXQ.js → chunk-IORA5BFI.js} +107 -6
- package/dist/chunk-IORA5BFI.js.map +1 -0
- package/dist/chunk-XLHBBA3U.js +7 -0
- package/dist/chunk-XLHBBA3U.js.map +1 -0
- package/dist/{chunk-HDPWLYK7.js → chunk-YZAYOFJV.js} +4 -4
- package/dist/chunk-YZAYOFJV.js.map +1 -0
- package/dist/{db-P76GVIFN.js → db-ZWV4Y43C.js} +10 -2
- package/dist/{server-3M3XCVOT.js → server-NKZW2SEV.js} +64 -15
- package/dist/server-NKZW2SEV.js.map +1 -0
- package/dist/{worker-J6I5XP2F.js → worker-PR7GODIV.js} +3 -3
- package/docs/mcp-tool-manifest.generated.json +1 -1
- package/package.json +1 -1
- package/dist/chunk-5H22TOXQ.js.map +0 -1
- package/dist/chunk-C7ZNIXOA.js +0 -7
- package/dist/chunk-C7ZNIXOA.js.map +0 -1
- package/dist/chunk-HDPWLYK7.js.map +0 -1
- package/dist/server-3M3XCVOT.js.map +0 -1
- /package/dist/{chunk-2XCNU22L.js.map → chunk-6N6USFMH.js.map} +0 -0
- /package/dist/{db-P76GVIFN.js.map → db-ZWV4Y43C.js.map} +0 -0
- /package/dist/{worker-J6I5XP2F.js.map → worker-PR7GODIV.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -7322,6 +7322,7 @@ var init_site_audit_service = __esm({
|
|
|
7322
7322
|
// src/api/db.ts
|
|
7323
7323
|
var db_exports = {};
|
|
7324
7324
|
__export(db_exports, {
|
|
7325
|
+
CREDIT_LOT_TTL: () => CREDIT_LOT_TTL,
|
|
7325
7326
|
SiteAuditJobRowSchema: () => SiteAuditJobRowSchema,
|
|
7326
7327
|
SiteAuditPhaseLogRowSchema: () => SiteAuditPhaseLogRowSchema,
|
|
7327
7328
|
advanceWorkflowRunStep: () => advanceWorkflowRunStep,
|
|
@@ -7348,6 +7349,7 @@ __export(db_exports, {
|
|
|
7348
7349
|
deactivateUser: () => deactivateUser,
|
|
7349
7350
|
debitMc: () => debitMc,
|
|
7350
7351
|
deleteWorkflowSchedule: () => deleteWorkflowSchedule,
|
|
7352
|
+
expireOldLots: () => expireOldLots,
|
|
7351
7353
|
failJob: () => failJob,
|
|
7352
7354
|
failKpoJob: () => failKpoJob,
|
|
7353
7355
|
failWorkflowRunRecord: () => failWorkflowRunRecord,
|
|
@@ -7385,6 +7387,7 @@ __export(db_exports, {
|
|
|
7385
7387
|
markWorkflowRunRunning: () => markWorkflowRunRunning,
|
|
7386
7388
|
markWorkflowScheduleRan: () => markWorkflowScheduleRan,
|
|
7387
7389
|
migrate: () => migrate,
|
|
7390
|
+
migrateExistingBalancesToLots: () => migrateExistingBalancesToLots,
|
|
7388
7391
|
patchWorkflowSchedule: () => patchWorkflowSchedule,
|
|
7389
7392
|
reconcileBalanceMc: () => reconcileBalanceMc,
|
|
7390
7393
|
recordStripeEvent: () => recordStripeEvent,
|
|
@@ -7393,6 +7396,7 @@ __export(db_exports, {
|
|
|
7393
7396
|
revokeApiKey: () => revokeApiKey,
|
|
7394
7397
|
rotateApiKey: () => rotateApiKey,
|
|
7395
7398
|
setConcurrencySubId: () => setConcurrencySubId,
|
|
7399
|
+
setDbForTesting: () => setDbForTesting,
|
|
7396
7400
|
setExtraConcurrencySlots: () => setExtraConcurrencySlots,
|
|
7397
7401
|
setPassword: () => setPassword,
|
|
7398
7402
|
setStripeCustomerId: () => setStripeCustomerId,
|
|
@@ -7405,6 +7409,9 @@ function getDb() {
|
|
|
7405
7409
|
if (!_db) _db = (0, import_http.createClient)({ url: DB_URL, authToken: DB_TOKEN });
|
|
7406
7410
|
return _db;
|
|
7407
7411
|
}
|
|
7412
|
+
function setDbForTesting(client2) {
|
|
7413
|
+
_db = client2;
|
|
7414
|
+
}
|
|
7408
7415
|
async function migrate() {
|
|
7409
7416
|
const db = getDb();
|
|
7410
7417
|
await db.execute(`
|
|
@@ -7546,6 +7553,19 @@ async function migrate() {
|
|
|
7546
7553
|
)
|
|
7547
7554
|
`);
|
|
7548
7555
|
await db.execute(`CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)`);
|
|
7556
|
+
await db.execute(`
|
|
7557
|
+
CREATE TABLE IF NOT EXISTS credit_lots (
|
|
7558
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7559
|
+
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
7560
|
+
amount_mc INTEGER NOT NULL,
|
|
7561
|
+
remaining_mc INTEGER NOT NULL,
|
|
7562
|
+
source TEXT NOT NULL,
|
|
7563
|
+
stripe_pi TEXT,
|
|
7564
|
+
granted_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
7565
|
+
expires_at TEXT NOT NULL
|
|
7566
|
+
)
|
|
7567
|
+
`);
|
|
7568
|
+
await db.execute(`CREATE INDEX IF NOT EXISTS credit_lots_user_active ON credit_lots(user_id, expires_at, remaining_mc)`);
|
|
7549
7569
|
await db.execute(`
|
|
7550
7570
|
CREATE TABLE IF NOT EXISTS free_credit_refreshes (
|
|
7551
7571
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
@@ -8574,12 +8594,89 @@ async function setConcurrencySubId(userId, subId) {
|
|
|
8574
8594
|
args: [subId, userId]
|
|
8575
8595
|
});
|
|
8576
8596
|
}
|
|
8597
|
+
async function ensureMigrated(userId) {
|
|
8598
|
+
const db = getDb();
|
|
8599
|
+
const hasLots = await db.execute({ sql: "SELECT 1 FROM credit_lots WHERE user_id = ? LIMIT 1", args: [userId] });
|
|
8600
|
+
if (hasLots.rows.length) return;
|
|
8601
|
+
const legacy = await db.execute({ sql: "SELECT COALESCE(SUM(amount_mc), 0) AS bal FROM ledger WHERE user_id = ?", args: [userId] });
|
|
8602
|
+
const bal = Number(legacy.rows[0]?.bal ?? 0);
|
|
8603
|
+
if (bal > 0) {
|
|
8604
|
+
await db.execute({
|
|
8605
|
+
sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at) VALUES (?, ?, ?, 'migration', datetime('now', ?))`,
|
|
8606
|
+
args: [userId, bal, bal, CREDIT_LOT_TTL]
|
|
8607
|
+
});
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
async function drainLots(userId, mc) {
|
|
8611
|
+
const db = getDb();
|
|
8612
|
+
const lots = await db.execute({
|
|
8613
|
+
sql: `SELECT id, remaining_mc FROM credit_lots WHERE user_id = ? AND remaining_mc > 0 AND expires_at > datetime('now') ORDER BY expires_at ASC, granted_at ASC, id ASC`,
|
|
8614
|
+
args: [userId]
|
|
8615
|
+
});
|
|
8616
|
+
let need = mc;
|
|
8617
|
+
const updates = [];
|
|
8618
|
+
for (const lot of lots.rows) {
|
|
8619
|
+
if (need <= 0) break;
|
|
8620
|
+
const take = Math.min(Number(lot.remaining_mc), need);
|
|
8621
|
+
updates.push({ sql: "UPDATE credit_lots SET remaining_mc = remaining_mc - ? WHERE id = ?", args: [take, Number(lot.id)] });
|
|
8622
|
+
need -= take;
|
|
8623
|
+
}
|
|
8624
|
+
if (updates.length) await db.batch(updates, "write");
|
|
8625
|
+
}
|
|
8626
|
+
async function expireOldLots() {
|
|
8627
|
+
const db = getDb();
|
|
8628
|
+
const lots = await db.execute({
|
|
8629
|
+
sql: `SELECT id, user_id, remaining_mc FROM credit_lots WHERE remaining_mc > 0 AND expires_at <= datetime('now') LIMIT 500`
|
|
8630
|
+
});
|
|
8631
|
+
if (lots.rows.length === 0) return { expired_lots: 0, expired_mc: 0 };
|
|
8632
|
+
let totalMc = 0;
|
|
8633
|
+
const users = /* @__PURE__ */ new Set();
|
|
8634
|
+
const stmts = [];
|
|
8635
|
+
for (const lot of lots.rows) {
|
|
8636
|
+
const rem = Number(lot.remaining_mc);
|
|
8637
|
+
const uid = Number(lot.user_id);
|
|
8638
|
+
totalMc += rem;
|
|
8639
|
+
users.add(uid);
|
|
8640
|
+
stmts.push({ sql: "UPDATE credit_lots SET remaining_mc = 0 WHERE id = ?", args: [Number(lot.id)] });
|
|
8641
|
+
stmts.push({ sql: `INSERT INTO ledger (user_id, amount_mc, operation, description) VALUES (?, ?, 'credit_expired', 'credits expired')`, args: [uid, -rem] });
|
|
8642
|
+
}
|
|
8643
|
+
await db.batch(stmts, "write");
|
|
8644
|
+
for (const uid of users) await reconcileBalanceMc(uid);
|
|
8645
|
+
return { expired_lots: lots.rows.length, expired_mc: totalMc };
|
|
8646
|
+
}
|
|
8647
|
+
async function migrateExistingBalancesToLots() {
|
|
8648
|
+
const db = getDb();
|
|
8649
|
+
const res = await db.execute({
|
|
8650
|
+
sql: `SELECT u.id AS user_id, COALESCE(SUM(l.amount_mc), 0) AS bal
|
|
8651
|
+
FROM users u LEFT JOIN ledger l ON l.user_id = u.id
|
|
8652
|
+
WHERE u.id NOT IN (SELECT DISTINCT user_id FROM credit_lots)
|
|
8653
|
+
GROUP BY u.id HAVING bal > 0`
|
|
8654
|
+
});
|
|
8655
|
+
let migrated = 0;
|
|
8656
|
+
let totalMc = 0;
|
|
8657
|
+
for (const row of res.rows) {
|
|
8658
|
+
const uid = Number(row.user_id);
|
|
8659
|
+
const bal = Number(row.bal);
|
|
8660
|
+
await db.execute({
|
|
8661
|
+
sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at) VALUES (?, ?, ?, 'migration', datetime('now', ?))`,
|
|
8662
|
+
args: [uid, bal, bal, CREDIT_LOT_TTL]
|
|
8663
|
+
});
|
|
8664
|
+
await reconcileBalanceMc(uid);
|
|
8665
|
+
migrated++;
|
|
8666
|
+
totalMc += bal;
|
|
8667
|
+
}
|
|
8668
|
+
return { migrated, total_mc: totalMc };
|
|
8669
|
+
}
|
|
8577
8670
|
async function creditMc(userId, mc, operation, description, stripePaymentIntent) {
|
|
8578
8671
|
const db = getDb();
|
|
8579
|
-
await
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8672
|
+
await ensureMigrated(userId);
|
|
8673
|
+
const stmts = [];
|
|
8674
|
+
if (mc > 0) {
|
|
8675
|
+
stmts.push({ sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at) VALUES (?, ?, ?, ?, ?, datetime('now', ?))`, args: [userId, mc, mc, operation, stripePaymentIntent ?? null, CREDIT_LOT_TTL] });
|
|
8676
|
+
}
|
|
8677
|
+
stmts.push({ sql: "UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?", args: [mc, userId] });
|
|
8678
|
+
stmts.push({ sql: "INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)", args: [userId, mc, operation, description ?? null, stripePaymentIntent ?? null] });
|
|
8679
|
+
await db.batch(stmts, "write");
|
|
8583
8680
|
const res = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
|
|
8584
8681
|
return Number(res.rows[0]?.balance_mc ?? 0);
|
|
8585
8682
|
}
|
|
@@ -8599,6 +8696,7 @@ async function claimMonthlyFreeRefresh(userId, month) {
|
|
|
8599
8696
|
}
|
|
8600
8697
|
async function debitMc(userId, mc, operation, description) {
|
|
8601
8698
|
const db = getDb();
|
|
8699
|
+
await ensureMigrated(userId);
|
|
8602
8700
|
const upd = await db.execute({
|
|
8603
8701
|
sql: "UPDATE users SET balance_mc = balance_mc - ? WHERE id = ? AND balance_mc >= ?",
|
|
8604
8702
|
args: [mc, userId, mc]
|
|
@@ -8607,6 +8705,7 @@ async function debitMc(userId, mc, operation, description) {
|
|
|
8607
8705
|
const res2 = await db.execute({ sql: "SELECT balance_mc FROM users WHERE id = ?", args: [userId] });
|
|
8608
8706
|
return { ok: false, balance_mc: Number(res2.rows[0]?.balance_mc ?? 0) };
|
|
8609
8707
|
}
|
|
8708
|
+
await drainLots(userId, mc);
|
|
8610
8709
|
await db.execute({
|
|
8611
8710
|
sql: "INSERT INTO ledger (user_id, amount_mc, operation, description) VALUES (?, ?, ?, ?)",
|
|
8612
8711
|
args: [userId, -mc, operation, description ?? null]
|
|
@@ -8638,8 +8737,9 @@ async function ledgerExistsForStripePI(stripePI) {
|
|
|
8638
8737
|
}
|
|
8639
8738
|
async function reconcileBalanceMc(userId) {
|
|
8640
8739
|
const db = getDb();
|
|
8740
|
+
await ensureMigrated(Number(userId));
|
|
8641
8741
|
const res = await db.execute({
|
|
8642
|
-
sql:
|
|
8742
|
+
sql: `SELECT COALESCE(SUM(remaining_mc), 0) AS balance_mc FROM credit_lots WHERE user_id = ? AND remaining_mc > 0 AND expires_at > datetime('now')`,
|
|
8643
8743
|
args: [userId]
|
|
8644
8744
|
});
|
|
8645
8745
|
const balanceMc = Number(res.rows[0]?.balance_mc ?? 0);
|
|
@@ -8649,7 +8749,7 @@ async function reconcileBalanceMc(userId) {
|
|
|
8649
8749
|
});
|
|
8650
8750
|
return balanceMc;
|
|
8651
8751
|
}
|
|
8652
|
-
var import_http, import_node_crypto2, import_zod10, DB_URL, DB_TOKEN, _db, _rateLimitSchemaReady, SiteAuditJobRowSchema, SiteAuditPhaseLogRowSchema;
|
|
8752
|
+
var import_http, import_node_crypto2, import_zod10, DB_URL, DB_TOKEN, _db, _rateLimitSchemaReady, CREDIT_LOT_TTL, SiteAuditJobRowSchema, SiteAuditPhaseLogRowSchema;
|
|
8653
8753
|
var init_db = __esm({
|
|
8654
8754
|
"src/api/db.ts"() {
|
|
8655
8755
|
"use strict";
|
|
@@ -8660,6 +8760,7 @@ var init_db = __esm({
|
|
|
8660
8760
|
DB_TOKEN = process.env.TURSO_AUTH_TOKEN;
|
|
8661
8761
|
_db = null;
|
|
8662
8762
|
_rateLimitSchemaReady = false;
|
|
8763
|
+
CREDIT_LOT_TTL = "+3 months";
|
|
8663
8764
|
SiteAuditJobRowSchema = import_zod10.z.object({
|
|
8664
8765
|
id: import_zod10.z.string(),
|
|
8665
8766
|
user_id: import_zod10.z.number(),
|
|
@@ -9661,8 +9762,8 @@ var init_rates = __esm({
|
|
|
9661
9762
|
label: "Interactive browser session",
|
|
9662
9763
|
aliases: ["browser_open", "browser agent", "browser_agent", "live browser", "browse", "browser control", "interactive browser"],
|
|
9663
9764
|
credits: mcToCredits(MC_COSTS.browser_minute),
|
|
9664
|
-
unit: "per minute
|
|
9665
|
-
notes: "Metered
|
|
9765
|
+
unit: "per minute the browser is open",
|
|
9766
|
+
notes: "Metered for the full time the browser session is open (active or idle) \u2014 every minute the session stays alive is billed. Close the session to stop the meter; abandoned sessions are auto-closed after a short idle window."
|
|
9666
9767
|
}
|
|
9667
9768
|
];
|
|
9668
9769
|
CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
@@ -12478,6 +12579,8 @@ var init_screenshot_routes = __esm({
|
|
|
12478
12579
|
init_api_auth();
|
|
12479
12580
|
init_url_utils();
|
|
12480
12581
|
init_concurrency_gates();
|
|
12582
|
+
init_db();
|
|
12583
|
+
init_rates();
|
|
12481
12584
|
ScreenshotBodySchema = import_zod14.z.object({
|
|
12482
12585
|
url: import_zod14.z.string().trim().min(1, "url is required"),
|
|
12483
12586
|
device: import_zod14.z.string().trim().optional(),
|
|
@@ -12516,7 +12619,11 @@ var init_screenshot_routes = __esm({
|
|
|
12516
12619
|
metadata: { url: targetUrl, device }
|
|
12517
12620
|
});
|
|
12518
12621
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
12622
|
+
let debited = false;
|
|
12519
12623
|
try {
|
|
12624
|
+
const { ok, balance_mc } = await debitMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, targetUrl);
|
|
12625
|
+
if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.page_scrape), 402);
|
|
12626
|
+
debited = true;
|
|
12520
12627
|
const buf = await captureScreenshot(targetUrl, browserServiceApiKey(), device);
|
|
12521
12628
|
return new Response(new Uint8Array(buf), {
|
|
12522
12629
|
status: 200,
|
|
@@ -12527,6 +12634,7 @@ var init_screenshot_routes = __esm({
|
|
|
12527
12634
|
}
|
|
12528
12635
|
});
|
|
12529
12636
|
} catch (err) {
|
|
12637
|
+
if (debited) await creditMc(user.id, MC_COSTS.page_scrape, LedgerOperation.EXTRACT_URL, "failed screenshot refund");
|
|
12530
12638
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12531
12639
|
return c.json({ error: msg }, 502);
|
|
12532
12640
|
} finally {
|
|
@@ -23755,7 +23863,7 @@ var PACKAGE_VERSION;
|
|
|
23755
23863
|
var init_version = __esm({
|
|
23756
23864
|
"src/version.ts"() {
|
|
23757
23865
|
"use strict";
|
|
23758
|
-
PACKAGE_VERSION = "0.3.
|
|
23866
|
+
PACKAGE_VERSION = "0.3.20";
|
|
23759
23867
|
}
|
|
23760
23868
|
});
|
|
23761
23869
|
|
|
@@ -25670,6 +25778,14 @@ async function listSessionRows(userId, includeClosed = false) {
|
|
|
25670
25778
|
});
|
|
25671
25779
|
return res.rows;
|
|
25672
25780
|
}
|
|
25781
|
+
async function listIdleOpenSessionRows(idleSeconds) {
|
|
25782
|
+
const db = getDb();
|
|
25783
|
+
const res = await db.execute({
|
|
25784
|
+
sql: `SELECT * FROM browser_agent_sessions WHERE status = 'open' AND COALESCE(last_action_at, created_at) <= datetime('now', ?) ORDER BY created_at LIMIT 200`,
|
|
25785
|
+
args: [`-${Math.max(1, Math.round(idleSeconds))} seconds`]
|
|
25786
|
+
});
|
|
25787
|
+
return res.rows;
|
|
25788
|
+
}
|
|
25673
25789
|
async function addActiveMs(id, deltaMs) {
|
|
25674
25790
|
const db = getDb();
|
|
25675
25791
|
await db.execute({
|
|
@@ -27062,16 +27178,42 @@ function browserSessionLockTtlSeconds(timeoutSeconds) {
|
|
|
27062
27178
|
}
|
|
27063
27179
|
return DEFAULT_BROWSER_SESSION_LOCK_TTL_SECONDS;
|
|
27064
27180
|
}
|
|
27065
|
-
async function charge(sessionId, userId,
|
|
27066
|
-
const
|
|
27067
|
-
|
|
27068
|
-
const
|
|
27069
|
-
|
|
27181
|
+
async function charge(sessionId, userId, _startedAtMs) {
|
|
27182
|
+
const row = await getSessionRow(sessionId);
|
|
27183
|
+
if (!row) return;
|
|
27184
|
+
const createdMs = parseAgentDate(row.created_at);
|
|
27185
|
+
if (createdMs == null) return;
|
|
27186
|
+
const uptimeMs = Math.max(0, Date.now() - createdMs);
|
|
27187
|
+
const owed = browserActiveCostMc(uptimeMs);
|
|
27188
|
+
await addActiveMs(sessionId, 0);
|
|
27189
|
+
const delta = owed - Number(row.billed_mc ?? 0);
|
|
27070
27190
|
if (delta > 0) {
|
|
27071
27191
|
const res = await debitMc(userId, delta, LedgerOperation.BROWSER_SESSION, sessionId);
|
|
27072
27192
|
if (res.ok) await setBilledMc(sessionId, owed);
|
|
27073
27193
|
}
|
|
27074
27194
|
}
|
|
27195
|
+
async function reapIdleBrowserSessions(idleSeconds = 120) {
|
|
27196
|
+
const rows = await listIdleOpenSessionRows(idleSeconds);
|
|
27197
|
+
let reaped = 0;
|
|
27198
|
+
for (const row of rows) {
|
|
27199
|
+
if (row.user_id != null) {
|
|
27200
|
+
try {
|
|
27201
|
+
await charge(row.id, row.user_id);
|
|
27202
|
+
} catch {
|
|
27203
|
+
}
|
|
27204
|
+
}
|
|
27205
|
+
try {
|
|
27206
|
+
await closeSession(row.runtime_session_id);
|
|
27207
|
+
} catch {
|
|
27208
|
+
}
|
|
27209
|
+
await markSessionClosed(row.id).catch(() => {
|
|
27210
|
+
});
|
|
27211
|
+
if (row.concurrency_lock_id) await releaseConcurrencyGate(row.concurrency_lock_id).catch(() => {
|
|
27212
|
+
});
|
|
27213
|
+
reaped++;
|
|
27214
|
+
}
|
|
27215
|
+
return { reaped };
|
|
27216
|
+
}
|
|
27075
27217
|
function publicSession(row) {
|
|
27076
27218
|
return {
|
|
27077
27219
|
session_id: row.id,
|
|
@@ -27081,10 +27223,16 @@ function publicSession(row) {
|
|
|
27081
27223
|
created_at: row.created_at,
|
|
27082
27224
|
last_action_at: row.last_action_at,
|
|
27083
27225
|
closed_at: row.closed_at,
|
|
27084
|
-
|
|
27226
|
+
uptime_seconds: uptimeSeconds(row),
|
|
27085
27227
|
credits_used: Math.round((row.billed_mc ?? 0) / 10) / 100
|
|
27086
27228
|
};
|
|
27087
27229
|
}
|
|
27230
|
+
function uptimeSeconds(row) {
|
|
27231
|
+
const createdMs = parseAgentDate(row.created_at);
|
|
27232
|
+
const endMs = row.closed_at ? parseAgentDate(row.closed_at) : Date.now();
|
|
27233
|
+
if (createdMs == null || endMs == null) return 0;
|
|
27234
|
+
return Math.max(0, Math.round((endMs - createdMs) / 1e3));
|
|
27235
|
+
}
|
|
27088
27236
|
function failure(err) {
|
|
27089
27237
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27090
27238
|
return { error: sanitizeVendorName(msg) };
|
|
@@ -27296,6 +27444,7 @@ function buildBrowserAgentRoutes() {
|
|
|
27296
27444
|
const user = c.get("user");
|
|
27297
27445
|
const row = await loadOpenSession(c.req.param("id"), user.id);
|
|
27298
27446
|
if (!row) return c.json({ error: "not found" }, 404);
|
|
27447
|
+
await charge(row.id, user.id);
|
|
27299
27448
|
try {
|
|
27300
27449
|
await closeSession(row.runtime_session_id);
|
|
27301
27450
|
} catch {
|
|
@@ -29240,11 +29389,13 @@ var init_server = __esm({
|
|
|
29240
29389
|
const { drainQueue: drainQueue2 } = await Promise.resolve().then(() => (init_worker(), worker_exports));
|
|
29241
29390
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
29242
29391
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
29243
|
-
const [results, sweepResult] = await Promise.all([
|
|
29392
|
+
const [results, sweepResult, reapResult, expiredResult] = await Promise.all([
|
|
29244
29393
|
drainQueue2(budget),
|
|
29245
|
-
runMonthlyRefreshSweep()
|
|
29394
|
+
runMonthlyRefreshSweep(),
|
|
29395
|
+
reapIdleBrowserSessions(120).catch(() => ({ reaped: 0 })),
|
|
29396
|
+
expireOldLots().catch(() => ({ expired_lots: 0, expired_mc: 0 }))
|
|
29246
29397
|
]);
|
|
29247
|
-
return c.json({ drained: results.length, results, sweepResult, workflowDispatch: workflowDispatchResult });
|
|
29398
|
+
return c.json({ drained: results.length, results, sweepResult, reaped: reapResult, expired: expiredResult, workflowDispatch: workflowDispatchResult });
|
|
29248
29399
|
});
|
|
29249
29400
|
app.on(["GET", "POST", "PUT"], "/api/inngest", (0, import_hono14.serve)({ client: inngest, functions: [siteAuditFn, siteExtractFn] }));
|
|
29250
29401
|
app.route("/api/internal/site-architecture-auditor", siteAuditApp);
|