unbrowse 6.5.2 → 6.6.0
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.js +113 -17
- package/dist/mcp.js +4 -4
- package/dist/server.js +343 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
|
|
|
31
31
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
32
32
|
|
|
33
33
|
// ../../src/build-info.generated.ts
|
|
34
|
-
var BUILD_RELEASE_VERSION = "6.
|
|
34
|
+
var BUILD_RELEASE_VERSION = "6.6.0", BUILD_GIT_SHA = "7de260727d86", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAiLCJnaXRfc2hhIjoiN2RlMjYwNzI3ZDg2IiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUA3ZGUyNjA3MjdkODYiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTA0VDA2OjM2OjA0LjY1NVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "ZP-8gEii8oLih47bn-ZtfHZzY88cRU-K5rRbAl3oaSA", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
|
|
35
35
|
|
|
36
36
|
// ../../src/version.ts
|
|
37
37
|
import { createHash } from "crypto";
|
|
@@ -5050,7 +5050,25 @@ function parseArgs(argv) {
|
|
|
5050
5050
|
} else if (a.startsWith("--")) {
|
|
5051
5051
|
const key = a.slice(2);
|
|
5052
5052
|
const next = rest[i + 1];
|
|
5053
|
-
const valueExpectedFlags = new Set([
|
|
5053
|
+
const valueExpectedFlags = new Set([
|
|
5054
|
+
"skill",
|
|
5055
|
+
"endpoint",
|
|
5056
|
+
"intent",
|
|
5057
|
+
"url",
|
|
5058
|
+
"domain",
|
|
5059
|
+
"params",
|
|
5060
|
+
"path",
|
|
5061
|
+
"extract",
|
|
5062
|
+
"limit",
|
|
5063
|
+
"session",
|
|
5064
|
+
"ref",
|
|
5065
|
+
"text",
|
|
5066
|
+
"value",
|
|
5067
|
+
"form-selector",
|
|
5068
|
+
"submit-selector",
|
|
5069
|
+
"wait-for",
|
|
5070
|
+
"timeout-ms"
|
|
5071
|
+
]);
|
|
5054
5072
|
if (valueExpectedFlags.has(key)) {
|
|
5055
5073
|
if (next === undefined)
|
|
5056
5074
|
die(`--${key} requires a value`);
|
|
@@ -5379,14 +5397,29 @@ async function cmdResolve(flags) {
|
|
|
5379
5397
|
}, endpointNeedsThirdPartyTermsConfirmation = function(endpoint) {
|
|
5380
5398
|
return endpoint.requires_third_party_terms_confirmation === true;
|
|
5381
5399
|
}, resolveSkillId = function() {
|
|
5382
|
-
return result.skill?.skill_id ?? result.skill_id;
|
|
5400
|
+
return result.skill?.skill_id ?? result.skill_id ?? result.result?.skill_id;
|
|
5401
|
+
}, resolveAvailableEndpoints = function() {
|
|
5402
|
+
return Array.isArray(result.available_endpoints) ? result.available_endpoints : Array.isArray(result.result?.available_endpoints) ? result.result.available_endpoints : undefined;
|
|
5403
|
+
}, endpointIsSafeToAutoExecute = function(endpoint) {
|
|
5404
|
+
const method = String(endpoint.method ?? "GET").toUpperCase();
|
|
5405
|
+
if (method !== "GET" && method !== "HEAD")
|
|
5406
|
+
return false;
|
|
5407
|
+
if (endpoint.needs_params && Object.keys(extraParams).length === 0)
|
|
5408
|
+
return false;
|
|
5409
|
+
return true;
|
|
5383
5410
|
};
|
|
5384
5411
|
const body = { intent };
|
|
5385
5412
|
const url = flags.url;
|
|
5386
5413
|
const domain = flags.domain;
|
|
5387
|
-
const
|
|
5388
|
-
const
|
|
5389
|
-
const
|
|
5414
|
+
const endpointFlag = flags["endpoint-id"] ?? flags.endpoint;
|
|
5415
|
+
const explicitEndpointId = typeof endpointFlag === "string" ? endpointFlag : undefined;
|
|
5416
|
+
const noExecute = flags["no-execute"] === true;
|
|
5417
|
+
const autoExecute = !noExecute;
|
|
5418
|
+
const cliKv = flags._params;
|
|
5419
|
+
const extraParams = {
|
|
5420
|
+
...flags.params ? JSON.parse(flags.params) : {},
|
|
5421
|
+
...cliKv && Object.keys(cliKv).length > 0 ? cliKv : {}
|
|
5422
|
+
};
|
|
5390
5423
|
if (url) {
|
|
5391
5424
|
body.params = { url };
|
|
5392
5425
|
body.context = { url };
|
|
@@ -5429,20 +5462,27 @@ async function cmdResolve(flags) {
|
|
|
5429
5462
|
if (loginUrl)
|
|
5430
5463
|
info(`Authentication required. Run: unbrowse login --url "${loginUrl}"`);
|
|
5431
5464
|
}
|
|
5432
|
-
if (explicitEndpointId &&
|
|
5465
|
+
if (explicitEndpointId && resolveAvailableEndpoints()) {
|
|
5433
5466
|
const skillId = resolveSkillId();
|
|
5434
5467
|
if (skillId) {
|
|
5435
5468
|
result = await withPendingNotice(api2("POST", `/v1/skills/${skillId}/execute`, execBody(explicitEndpointId)), "Executing selected endpoint...");
|
|
5436
5469
|
}
|
|
5437
5470
|
}
|
|
5438
|
-
|
|
5439
|
-
|
|
5471
|
+
const endpointsForAutoExecute = resolveAvailableEndpoints();
|
|
5472
|
+
if (autoExecute && endpointsForAutoExecute) {
|
|
5473
|
+
const endpoints = endpointsForAutoExecute;
|
|
5440
5474
|
const skillId = resolveSkillId();
|
|
5441
5475
|
if (skillId && endpoints.length > 0) {
|
|
5442
5476
|
const bestEndpoint = endpoints[0];
|
|
5443
5477
|
if (endpointNeedsThirdPartyTermsConfirmation(bestEndpoint) && !flags["confirm-third-party-terms"]) {
|
|
5444
5478
|
process.stderr.write(`Skipping auto-execute: endpoint ${bestEndpoint.endpoint_id ?? "?"} ` + `requires explicit third-party terms confirmation. ` + `Re-run with --confirm-third-party-terms to proceed.
|
|
5445
5479
|
`);
|
|
5480
|
+
} else if (!endpointIsSafeToAutoExecute(bestEndpoint)) {
|
|
5481
|
+
result.next_action = {
|
|
5482
|
+
title: "Execute selected endpoint",
|
|
5483
|
+
command: `unbrowse execute --skill ${skillId} --endpoint ${bestEndpoint.endpoint_id}`,
|
|
5484
|
+
why: "Resolve found a candidate but did not auto-execute because the endpoint is not a safe ready GET."
|
|
5485
|
+
};
|
|
5446
5486
|
} else {
|
|
5447
5487
|
info(`Auto-executing endpoint: ${bestEndpoint.description ?? bestEndpoint.endpoint_id}`);
|
|
5448
5488
|
result = await withPendingNotice(api2("POST", `/v1/skills/${skillId}/execute`, execBody(bestEndpoint.endpoint_id)), "Executing best endpoint...");
|
|
@@ -6289,7 +6329,7 @@ var CLI_REFERENCE = {
|
|
|
6289
6329
|
{ name: "mcp", usage: "[--no-auto-start]", desc: "Run the stdio MCP server" },
|
|
6290
6330
|
{ name: "setup", usage: "[--opencode auto|global|project|off] [--no-start]", desc: "Bootstrap browser deps + Open Code command" },
|
|
6291
6331
|
{ name: "upgrade", usage: "", desc: "Check latest release and print the right upgrade command" },
|
|
6292
|
-
{ name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "
|
|
6332
|
+
{ name: "resolve", usage: '--intent "..." [--domain "..."] [--url "..."] [opts]', desc: "Resolve an intent, auto-executing the top safe GET endpoint by default; pass --no-execute for metadata only" },
|
|
6293
6333
|
{ name: "explain", usage: '--intent "..." --url "..." [--top N]', desc: "Emit top-N candidate endpoints + evidence for an LLM judge to pick from (no heuristic verdict \u2014 primitives + agent judgment)" },
|
|
6294
6334
|
{ name: "execute", usage: "--skill ID --endpoint ID [-p key=val ...] [--params '{json}'] [opts]", desc: "Execute a specific endpoint. Pass replay params via repeated -p key=val flags or --params with a JSON object" },
|
|
6295
6335
|
{ name: "feedback", usage: "--skill ID --endpoint ID --rating N", desc: "Submit feedback (mandatory after resolve)" },
|
|
@@ -6304,6 +6344,7 @@ var CLI_REFERENCE = {
|
|
|
6304
6344
|
{ name: "skill", usage: "<id>", desc: "Get skill details" },
|
|
6305
6345
|
{ name: "cleanup-stale", usage: "[--skill ID] [--domain host] [--limit N]", desc: "Verify skills and evict stale cached endpoints" },
|
|
6306
6346
|
{ name: "sessions", usage: '--domain "..." [--limit N]', desc: "Debug session logs" },
|
|
6347
|
+
{ name: "inspect", usage: "[--session id] [--all]", desc: "Inspect live browser capture evidence, candidate endpoints, and next actions" },
|
|
6307
6348
|
{ name: "go", usage: "<url> [--session id]", desc: "Open a fresh Kuri browser tab, or reuse explicit --session" },
|
|
6308
6349
|
{ name: "submit", usage: "[--session id] [--form-selector sel] [--submit-selector sel] [--wait-for hint] [--assist-site-state]", desc: "Submit current form. Thin browser-native proxy by default; site-state assist and same-origin rehydrate are explicit opt-ins" },
|
|
6309
6350
|
{ name: "snap", usage: "[--session id] [--filter interactive]", desc: "A11y snapshot with @eN refs" },
|
|
@@ -6342,7 +6383,8 @@ var CLI_REFERENCE = {
|
|
|
6342
6383
|
{ flag: "--opencode auto|global|project|off", desc: "setup: install /unbrowse command for Open Code" }
|
|
6343
6384
|
],
|
|
6344
6385
|
resolveExecuteFlags: [
|
|
6345
|
-
{ flag: "--execute", desc: "
|
|
6386
|
+
{ flag: "--no-execute", desc: "Resolve only; do not auto-execute safe GET endpoints" },
|
|
6387
|
+
{ flag: "--execute", desc: "Deprecated no-op alias; safe GET execution is now the default" },
|
|
6346
6388
|
{ flag: "--schema", desc: "Show response schema + extraction hints only (no data)" },
|
|
6347
6389
|
{ flag: '--path "data.items[]"', desc: "Drill into result before extract/output" },
|
|
6348
6390
|
{ flag: '--extract "field1,alias:deep.path.to.val"', desc: "Pick specific fields (no piping needed)" },
|
|
@@ -6609,12 +6651,53 @@ function printHelp() {
|
|
|
6609
6651
|
async function cmdStatus(flags) {
|
|
6610
6652
|
const healthy = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(2000) }).then((r) => r.ok).catch(() => false);
|
|
6611
6653
|
const versionInfo = checkServerVersion(BASE_URL, import.meta.url);
|
|
6654
|
+
let activeSessions = [];
|
|
6655
|
+
let sessionCount = 0;
|
|
6656
|
+
let latestSessionId = null;
|
|
6657
|
+
if (healthy) {
|
|
6658
|
+
try {
|
|
6659
|
+
const res = await fetch(`${BASE_URL}/v1/browse/sessions`, { signal: AbortSignal.timeout(2000) });
|
|
6660
|
+
if (res.ok) {
|
|
6661
|
+
const data = await res.json();
|
|
6662
|
+
activeSessions = data.sessions ?? [];
|
|
6663
|
+
sessionCount = data.count ?? activeSessions.length;
|
|
6664
|
+
latestSessionId = data.latest_session_id ?? null;
|
|
6665
|
+
}
|
|
6666
|
+
} catch {}
|
|
6667
|
+
}
|
|
6612
6668
|
output({
|
|
6613
6669
|
server: healthy ? "running" : "stopped",
|
|
6614
6670
|
url: BASE_URL,
|
|
6615
|
-
...versionInfo ?? {}
|
|
6671
|
+
...versionInfo ?? {},
|
|
6672
|
+
active_browse_sessions: sessionCount,
|
|
6673
|
+
latest_session_id: latestSessionId,
|
|
6674
|
+
sessions: activeSessions,
|
|
6675
|
+
chrome_debug_url: process.env.CHROME_DEBUG_URL ?? null
|
|
6616
6676
|
}, !!flags.pretty);
|
|
6617
6677
|
}
|
|
6678
|
+
async function cmdInspect(args, flags) {
|
|
6679
|
+
const explicitSession = typeof flags.session === "string" ? flags.session : typeof args[0] === "string" ? args[0] : undefined;
|
|
6680
|
+
const sessions = await api2("GET", "/v1/browse/sessions");
|
|
6681
|
+
if (flags.all) {
|
|
6682
|
+
output(sessions, !!flags.pretty);
|
|
6683
|
+
return;
|
|
6684
|
+
}
|
|
6685
|
+
const latestSessionId = sessions.latest_session_id ?? sessions.sessions?.at(-1)?.session_id;
|
|
6686
|
+
const sessionId = explicitSession ?? latestSessionId;
|
|
6687
|
+
if (!sessionId) {
|
|
6688
|
+
output({
|
|
6689
|
+
error: "no_active_session",
|
|
6690
|
+
message: "No active browse session to inspect.",
|
|
6691
|
+
next_action: {
|
|
6692
|
+
title: "Open a browser session",
|
|
6693
|
+
command: 'unbrowse go "https://example.com" --pretty',
|
|
6694
|
+
why: "Inspection reads live HAR/interceptor evidence from an active Kuri session."
|
|
6695
|
+
}
|
|
6696
|
+
}, !!flags.pretty);
|
|
6697
|
+
return;
|
|
6698
|
+
}
|
|
6699
|
+
output(await api2("GET", `/v1/browse/sessions/${encodeURIComponent(sessionId)}/buffer`), !!flags.pretty);
|
|
6700
|
+
}
|
|
6618
6701
|
async function cmdRestart(flags) {
|
|
6619
6702
|
info("Restarting server...");
|
|
6620
6703
|
await restartServer(BASE_URL, import.meta.url);
|
|
@@ -6828,7 +6911,7 @@ async function cmdSnap(flags) {
|
|
|
6828
6911
|
}
|
|
6829
6912
|
}
|
|
6830
6913
|
async function cmdClick(args, flags) {
|
|
6831
|
-
const ref = args[0];
|
|
6914
|
+
const ref = args[0] ?? (typeof flags.ref === "string" ? flags.ref : undefined);
|
|
6832
6915
|
if (!ref)
|
|
6833
6916
|
die("Usage: unbrowse click <ref>");
|
|
6834
6917
|
output(await api2("POST", "/v1/browse/click", {
|
|
@@ -6837,10 +6920,10 @@ async function cmdClick(args, flags) {
|
|
|
6837
6920
|
}), false);
|
|
6838
6921
|
}
|
|
6839
6922
|
async function cmdFill(args, flags) {
|
|
6840
|
-
const ref = args[0];
|
|
6841
|
-
const value = args.slice(1).join(" ");
|
|
6923
|
+
const ref = args[0] ?? (typeof flags.ref === "string" ? flags.ref : undefined);
|
|
6924
|
+
const value = args.length > 1 ? args.slice(1).join(" ") : typeof flags.text === "string" ? flags.text : typeof flags.value === "string" ? flags.value : "";
|
|
6842
6925
|
if (!ref || !value)
|
|
6843
|
-
die(
|
|
6926
|
+
die('Usage: unbrowse fill <ref> <value> (also: unbrowse fill --ref e5 --text "hello")');
|
|
6844
6927
|
output(await api2("POST", "/v1/browse/fill", {
|
|
6845
6928
|
ref,
|
|
6846
6929
|
value,
|
|
@@ -7275,7 +7358,17 @@ async function cmdConnectChrome() {
|
|
|
7275
7358
|
console.error("Could not connect to Chrome. Make sure all Chrome windows are closed and try again.");
|
|
7276
7359
|
}
|
|
7277
7360
|
async function main() {
|
|
7278
|
-
const
|
|
7361
|
+
const parsed = parseArgs(process.argv);
|
|
7362
|
+
let { command, args, flags } = parsed;
|
|
7363
|
+
const cliParams = parsed.params;
|
|
7364
|
+
if (command === "browse") {
|
|
7365
|
+
const subcommand = args.shift();
|
|
7366
|
+
if (!subcommand || subcommand === "help") {
|
|
7367
|
+
printHelp();
|
|
7368
|
+
process.exit(subcommand === "help" ? 0 : 1);
|
|
7369
|
+
}
|
|
7370
|
+
command = subcommand;
|
|
7371
|
+
}
|
|
7279
7372
|
if (Object.keys(cliParams).length > 0) {
|
|
7280
7373
|
flags._params = cliParams;
|
|
7281
7374
|
}
|
|
@@ -7344,6 +7437,7 @@ async function main() {
|
|
|
7344
7437
|
"search",
|
|
7345
7438
|
"sessions",
|
|
7346
7439
|
"status",
|
|
7440
|
+
"inspect",
|
|
7347
7441
|
"stop",
|
|
7348
7442
|
"restart",
|
|
7349
7443
|
"upgrade",
|
|
@@ -7440,6 +7534,8 @@ async function main() {
|
|
|
7440
7534
|
return cmdSearch(flags);
|
|
7441
7535
|
case "sessions":
|
|
7442
7536
|
return cmdSessions(flags);
|
|
7537
|
+
case "inspect":
|
|
7538
|
+
return cmdInspect(args, flags);
|
|
7443
7539
|
case "go":
|
|
7444
7540
|
return cmdGo(args, flags);
|
|
7445
7541
|
case "submit":
|
package/dist/mcp.js
CHANGED
|
@@ -226,11 +226,11 @@ import { dirname, join, parse } from "path";
|
|
|
226
226
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
227
227
|
|
|
228
228
|
// ../../src/build-info.generated.ts
|
|
229
|
-
var BUILD_RELEASE_VERSION = "6.
|
|
230
|
-
var BUILD_GIT_SHA = "
|
|
229
|
+
var BUILD_RELEASE_VERSION = "6.6.0";
|
|
230
|
+
var BUILD_GIT_SHA = "7de260727d86";
|
|
231
231
|
var BUILD_CODE_HASH = "5d9ebf619c61";
|
|
232
|
-
var BUILD_RELEASE_MANIFEST_BASE64 = "
|
|
233
|
-
var BUILD_RELEASE_MANIFEST_SIGNATURE = "
|
|
232
|
+
var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAiLCJnaXRfc2hhIjoiN2RlMjYwNzI3ZDg2IiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUA3ZGUyNjA3MjdkODYiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTA0VDA2OjM2OjA0LjY1NVoifQ";
|
|
233
|
+
var BUILD_RELEASE_MANIFEST_SIGNATURE = "ZP-8gEii8oLih47bn-ZtfHZzY88cRU-K5rRbAl3oaSA";
|
|
234
234
|
var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
|
|
235
235
|
var BUILD_DEFAULT_PROFILE = "";
|
|
236
236
|
|
package/dist/server.js
CHANGED
|
@@ -211,13 +211,11 @@ function resolveKuriLaunchConfig(env = process.env) {
|
|
|
211
211
|
headless = true;
|
|
212
212
|
}
|
|
213
213
|
const cleanRoom = envFlag(env.UNBROWSE_LOCAL_ONLY) || envFlag(env.KURI_CLEAN_ROOM);
|
|
214
|
-
const browserCookieOptOut = falseyEnv(env.UNBROWSE_IMPORT_BROWSER_COOKIES);
|
|
215
|
-
const explicitAttach = envFlag(env.KURI_ATTACH_EXISTING_CHROME ?? env.UNBROWSE_ATTACH_EXISTING_CHROME);
|
|
216
214
|
const disableCdpAttach = envFlag(env.KURI_DISABLE_CDP_ATTACH);
|
|
217
|
-
const
|
|
215
|
+
const attachToExistingChrome = !disableCdpAttach && !cleanRoom;
|
|
218
216
|
return {
|
|
219
217
|
headless,
|
|
220
|
-
attachToExistingChrome
|
|
218
|
+
attachToExistingChrome
|
|
221
219
|
};
|
|
222
220
|
}
|
|
223
221
|
function kuriBinaryName() {
|
|
@@ -7344,7 +7342,7 @@ var init_capture = __esm(async () => {
|
|
|
7344
7342
|
});
|
|
7345
7343
|
|
|
7346
7344
|
// ../../src/build-info.generated.ts
|
|
7347
|
-
var BUILD_RELEASE_VERSION = "6.
|
|
7345
|
+
var BUILD_RELEASE_VERSION = "6.6.0", BUILD_GIT_SHA = "7de260727d86", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAiLCJnaXRfc2hhIjoiN2RlMjYwNzI3ZDg2IiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUA3ZGUyNjA3MjdkODYiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTA0VDA2OjM2OjA0LjY1NVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "ZP-8gEii8oLih47bn-ZtfHZzY88cRU-K5rRbAl3oaSA", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
|
|
7348
7346
|
|
|
7349
7347
|
// ../../src/version.ts
|
|
7350
7348
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -25771,9 +25769,7 @@ async function resolveRequestedBrowseSession(sessions, client, requestedSessionI
|
|
|
25771
25769
|
const live = await listLiveBrowseSessions(sessions, client);
|
|
25772
25770
|
if (live.length === 0)
|
|
25773
25771
|
throw new BrowseSessionError("no_active_session");
|
|
25774
|
-
|
|
25775
|
-
throw new BrowseSessionError("session_id_required");
|
|
25776
|
-
return live[0];
|
|
25772
|
+
return live[live.length - 1];
|
|
25777
25773
|
}
|
|
25778
25774
|
async function withSessionQueue(sessionId, fn) {
|
|
25779
25775
|
const prev = sessionQueues.get(sessionId) ?? Promise.resolve();
|
|
@@ -27923,6 +27919,17 @@ function passiveIndexFromRequests(requests, pageUrl, options = {}) {
|
|
|
27923
27919
|
function passiveIndexHar(entries, pageUrl, options = {}) {
|
|
27924
27920
|
passiveIndexFromRequests(harEntriesToRawRequests(entries), pageUrl, options);
|
|
27925
27921
|
}
|
|
27922
|
+
function rememberInspectedHarEntries(sessionId, entries) {
|
|
27923
|
+
if (entries.length === 0)
|
|
27924
|
+
return;
|
|
27925
|
+
const existing = inspectedHarEntries.get(sessionId) ?? [];
|
|
27926
|
+
inspectedHarEntries.set(sessionId, [...existing, ...entries]);
|
|
27927
|
+
}
|
|
27928
|
+
function drainInspectedHarEntries(sessionId) {
|
|
27929
|
+
const entries = inspectedHarEntries.get(sessionId) ?? [];
|
|
27930
|
+
inspectedHarEntries.delete(sessionId);
|
|
27931
|
+
return entries;
|
|
27932
|
+
}
|
|
27926
27933
|
function browseBrokerPorts() {
|
|
27927
27934
|
return Array.from({ length: BROWSE_BROKER_MAX }, (_, index) => BROWSE_BROKER_BASE_PORT + index);
|
|
27928
27935
|
}
|
|
@@ -28155,6 +28162,130 @@ async function registerRoutes(app) {
|
|
|
28155
28162
|
next_step: settings.auto_publish_checkpoints ? "Auto-publish after sync/close is enabled unless a domain rule blocks it." : "Auto-publish after sync/close is disabled. Use index for local recompute and publish only when you explicitly want remote share."
|
|
28156
28163
|
});
|
|
28157
28164
|
});
|
|
28165
|
+
app.get("/v1/browse/sessions", async (_req, reply) => {
|
|
28166
|
+
const sessions = Array.from(browseSessions.values()).map((s) => ({
|
|
28167
|
+
session_id: s.sessionId,
|
|
28168
|
+
tab_id: s.tabId,
|
|
28169
|
+
url: s.url,
|
|
28170
|
+
domain: s.domain,
|
|
28171
|
+
har_active: s.harActive,
|
|
28172
|
+
broker_port: s.brokerPort ?? null,
|
|
28173
|
+
streaming_publish_active: streamingWatchers.has(s.sessionId),
|
|
28174
|
+
marketplace_publish: (() => {
|
|
28175
|
+
const decision = decideCheckpointPublish(s.domain || profileName(s.url));
|
|
28176
|
+
return {
|
|
28177
|
+
enabled: decision.publishQueued,
|
|
28178
|
+
mode: decision.mode,
|
|
28179
|
+
reason: decision.reason
|
|
28180
|
+
};
|
|
28181
|
+
})(),
|
|
28182
|
+
suggested_commands: [
|
|
28183
|
+
`unbrowse inspect --session ${s.sessionId} --pretty`,
|
|
28184
|
+
`unbrowse resolve --intent "<task>" --url "${s.url}" --pretty`,
|
|
28185
|
+
`unbrowse close --session ${s.sessionId}`
|
|
28186
|
+
]
|
|
28187
|
+
}));
|
|
28188
|
+
const latest = sessions[sessions.length - 1] ?? null;
|
|
28189
|
+
return reply.send({ sessions, count: sessions.length, latest_session_id: latest?.session_id ?? null });
|
|
28190
|
+
});
|
|
28191
|
+
app.get("/v1/browse/sessions/:id/buffer", async (req, reply) => {
|
|
28192
|
+
const { id } = req.params;
|
|
28193
|
+
let session;
|
|
28194
|
+
for (const s of browseSessions.values()) {
|
|
28195
|
+
if (s.sessionId === id || s.tabId === id) {
|
|
28196
|
+
session = s;
|
|
28197
|
+
break;
|
|
28198
|
+
}
|
|
28199
|
+
}
|
|
28200
|
+
if (!session)
|
|
28201
|
+
return reply.code(404).send({ error: "session_not_found", id });
|
|
28202
|
+
let intercepted = [];
|
|
28203
|
+
let interceptError = null;
|
|
28204
|
+
try {
|
|
28205
|
+
intercepted = await collectInterceptedRequests(session.tabId);
|
|
28206
|
+
} catch (err) {
|
|
28207
|
+
interceptError = err instanceof Error ? err.message : String(err);
|
|
28208
|
+
}
|
|
28209
|
+
let harEntries = [];
|
|
28210
|
+
let harError = null;
|
|
28211
|
+
if (session.harActive) {
|
|
28212
|
+
try {
|
|
28213
|
+
const broker = brokerForSession(session);
|
|
28214
|
+
const stopResult = await broker.harStop(session.tabId);
|
|
28215
|
+
harEntries = stopResult.entries ?? [];
|
|
28216
|
+
rememberInspectedHarEntries(session.sessionId, harEntries);
|
|
28217
|
+
try {
|
|
28218
|
+
await broker.harStart(session.tabId);
|
|
28219
|
+
} catch {}
|
|
28220
|
+
} catch (err) {
|
|
28221
|
+
harError = err instanceof Error ? err.message : String(err);
|
|
28222
|
+
}
|
|
28223
|
+
}
|
|
28224
|
+
const rawRequests = [
|
|
28225
|
+
...Array.isArray(intercepted) ? intercepted : [],
|
|
28226
|
+
...harEntriesToRawRequests(harEntries, session.url)
|
|
28227
|
+
];
|
|
28228
|
+
const candidateEndpoints = extractEndpoints(rawRequests, undefined, {
|
|
28229
|
+
pageUrl: session.url,
|
|
28230
|
+
finalUrl: session.url
|
|
28231
|
+
}).slice(0, 10).map((endpoint) => ({
|
|
28232
|
+
endpoint_id: endpoint.endpoint_id,
|
|
28233
|
+
method: endpoint.method,
|
|
28234
|
+
url_template: endpoint.url_template,
|
|
28235
|
+
description: endpoint.description ?? generateLocalDescription(endpoint),
|
|
28236
|
+
reliability_score: endpoint.reliability_score,
|
|
28237
|
+
idempotency: endpoint.idempotency,
|
|
28238
|
+
auth_token_count: endpoint.auth_tokens?.length ?? 0
|
|
28239
|
+
}));
|
|
28240
|
+
const publishDecision = decideCheckpointPublish(session.domain || profileName(session.url));
|
|
28241
|
+
return reply.send({
|
|
28242
|
+
session: {
|
|
28243
|
+
session_id: session.sessionId,
|
|
28244
|
+
tab_id: session.tabId,
|
|
28245
|
+
url: session.url,
|
|
28246
|
+
domain: session.domain,
|
|
28247
|
+
har_active: session.harActive,
|
|
28248
|
+
streaming_publish_active: streamingWatchers.has(session.sessionId),
|
|
28249
|
+
marketplace_publish: {
|
|
28250
|
+
enabled: publishDecision.publishQueued,
|
|
28251
|
+
mode: publishDecision.mode,
|
|
28252
|
+
reason: publishDecision.reason
|
|
28253
|
+
}
|
|
28254
|
+
},
|
|
28255
|
+
intercepted_requests: intercepted,
|
|
28256
|
+
intercepted_count: Array.isArray(intercepted) ? intercepted.length : 0,
|
|
28257
|
+
intercept_error: interceptError,
|
|
28258
|
+
har_entries: harEntries.map((e) => ({
|
|
28259
|
+
url: e?.request?.url,
|
|
28260
|
+
method: e?.request?.method,
|
|
28261
|
+
status: e?.response?.status,
|
|
28262
|
+
mime_type: e?.response?.content?.mimeType,
|
|
28263
|
+
size: e?.response?.bodySize
|
|
28264
|
+
})),
|
|
28265
|
+
har_count: harEntries.length,
|
|
28266
|
+
har_error: harError,
|
|
28267
|
+
total_captured: (Array.isArray(intercepted) ? intercepted.length : 0) + harEntries.length,
|
|
28268
|
+
candidate_endpoints: candidateEndpoints,
|
|
28269
|
+
candidate_endpoint_count: candidateEndpoints.length,
|
|
28270
|
+
next_actions: [
|
|
28271
|
+
{
|
|
28272
|
+
title: "Resolve from captured evidence",
|
|
28273
|
+
command: `unbrowse resolve --intent "<task>" --url "${session.url}" --pretty`,
|
|
28274
|
+
why: "Runs the normal resolver after the current in-flight capture has been exposed."
|
|
28275
|
+
},
|
|
28276
|
+
{
|
|
28277
|
+
title: "Checkpoint and publish review material",
|
|
28278
|
+
command: `unbrowse sync --session ${session.sessionId} --pretty`,
|
|
28279
|
+
why: "Keeps the tab open and compiles current browser traffic into local skill evidence."
|
|
28280
|
+
},
|
|
28281
|
+
{
|
|
28282
|
+
title: "Close after judging",
|
|
28283
|
+
command: `unbrowse close --session ${session.sessionId}`,
|
|
28284
|
+
why: "Flushes final capture, saves auth, and stops the live tab."
|
|
28285
|
+
}
|
|
28286
|
+
]
|
|
28287
|
+
});
|
|
28288
|
+
});
|
|
28158
28289
|
app.get("/v1/trace/:trace_id", async (req, reply) => {
|
|
28159
28290
|
const { trace_id } = req.params;
|
|
28160
28291
|
const traceDir = path6.join(process.env.UNBROWSE_TRACE_DIR ?? path6.join(os4.homedir(), ".unbrowse", "traces"));
|
|
@@ -28181,6 +28312,25 @@ async function registerRoutes(app) {
|
|
|
28181
28312
|
const { intent, params, context, projection, confirm_unsafe, confirm_third_party_terms, dry_run, force_capture, skip_robots_check, visual_context, budget_ms } = req.body;
|
|
28182
28313
|
if (!intent)
|
|
28183
28314
|
return reply.code(400).send({ error: "intent required" });
|
|
28315
|
+
let inflightFlushResult = null;
|
|
28316
|
+
if (context?.url && !force_capture) {
|
|
28317
|
+
try {
|
|
28318
|
+
const activeSession = findActiveSessionForDomain(context.url);
|
|
28319
|
+
if (activeSession) {
|
|
28320
|
+
const flushed = await lightFlushBrowseCapture(activeSession);
|
|
28321
|
+
if (flushed.request_count > 0) {
|
|
28322
|
+
inflightFlushResult = {
|
|
28323
|
+
skill_id: flushed.skill_id,
|
|
28324
|
+
request_count: flushed.request_count,
|
|
28325
|
+
endpoint_count: flushed.endpoint_count
|
|
28326
|
+
};
|
|
28327
|
+
console.log(`[in-flight-flush] session=${activeSession.sessionId.slice(0, 8)} domain=${flushed.domain} requests=${flushed.request_count} endpoints=${flushed.endpoint_count} skill=${flushed.skill_id?.slice(0, 15) ?? "none"}`);
|
|
28328
|
+
}
|
|
28329
|
+
}
|
|
28330
|
+
} catch (err) {
|
|
28331
|
+
console.warn(`[in-flight-flush] failed: ${err.message}`);
|
|
28332
|
+
}
|
|
28333
|
+
}
|
|
28184
28334
|
try {
|
|
28185
28335
|
const result = await resolveAndExecute(intent, params ?? {}, context, projection, { confirm_unsafe, confirm_third_party_terms, dry_run, force_capture, skip_robots_check, client_scope: clientScope, budget_ms });
|
|
28186
28336
|
const res = attachAgentOutcomeHints({ ...result }, {
|
|
@@ -28209,6 +28359,9 @@ async function registerRoutes(app) {
|
|
|
28209
28359
|
}
|
|
28210
28360
|
} catch {}
|
|
28211
28361
|
}
|
|
28362
|
+
if (inflightFlushResult) {
|
|
28363
|
+
res.inflight_flush = inflightFlushResult;
|
|
28364
|
+
}
|
|
28212
28365
|
return reply.send(res);
|
|
28213
28366
|
} catch (err) {
|
|
28214
28367
|
return reply.code(500).send({ error: err.message });
|
|
@@ -28809,7 +28962,20 @@ async function registerRoutes(app) {
|
|
|
28809
28962
|
}
|
|
28810
28963
|
function sendBrowseSessionError(reply, error) {
|
|
28811
28964
|
if (error instanceof BrowseSessionError) {
|
|
28812
|
-
|
|
28965
|
+
const latestSession = Array.from(browseSessions.values()).at(-1);
|
|
28966
|
+
return reply.code(error.statusCode).send({
|
|
28967
|
+
error: error.code,
|
|
28968
|
+
...latestSession ? { latest_session_id: latestSession.sessionId } : {},
|
|
28969
|
+
next_action: latestSession ? {
|
|
28970
|
+
title: "Retry with latest active session",
|
|
28971
|
+
command: `unbrowse inspect --session ${latestSession.sessionId} --pretty`,
|
|
28972
|
+
why: "A live session exists; inspect it or pass --session to the browser command."
|
|
28973
|
+
} : {
|
|
28974
|
+
title: "Open a browser session",
|
|
28975
|
+
command: 'unbrowse go "<url>" --pretty',
|
|
28976
|
+
why: "This command needs a live Kuri-backed browse session."
|
|
28977
|
+
}
|
|
28978
|
+
});
|
|
28813
28979
|
}
|
|
28814
28980
|
if (isRecoverableBrowseFailure(error)) {
|
|
28815
28981
|
return reply.code(502).send({
|
|
@@ -28853,6 +29019,143 @@ async function registerRoutes(app) {
|
|
|
28853
29019
|
session.harActive = true;
|
|
28854
29020
|
await injectInterceptor(session.tabId).catch(() => {});
|
|
28855
29021
|
}
|
|
29022
|
+
async function lightFlushBrowseCapture(session) {
|
|
29023
|
+
let harEntries = [];
|
|
29024
|
+
if (session.harActive) {
|
|
29025
|
+
try {
|
|
29026
|
+
const broker = brokerForSession(session);
|
|
29027
|
+
const stopResult = await broker.harStop(session.tabId);
|
|
29028
|
+
harEntries = stopResult.entries ?? [];
|
|
29029
|
+
try {
|
|
29030
|
+
await broker.harStart(session.tabId);
|
|
29031
|
+
} catch {}
|
|
29032
|
+
} catch {}
|
|
29033
|
+
}
|
|
29034
|
+
harEntries = [...drainInspectedHarEntries(session.sessionId), ...harEntries];
|
|
29035
|
+
const allRequests = await enrichPassiveCaptureRequests({
|
|
29036
|
+
tabId: session.tabId,
|
|
29037
|
+
captureUrl: session.url,
|
|
29038
|
+
harEntries,
|
|
29039
|
+
intent: `browse ${session.domain || profileName(session.url)}`
|
|
29040
|
+
});
|
|
29041
|
+
if (allRequests.length === 0) {
|
|
29042
|
+
return { skill_id: null, domain: session.domain, request_count: 0, endpoint_count: 0 };
|
|
29043
|
+
}
|
|
29044
|
+
const jsBundles = new Map;
|
|
29045
|
+
try {
|
|
29046
|
+
const intercepted = await collectInterceptedRequests(session.tabId).catch(() => []);
|
|
29047
|
+
for (const entry of intercepted) {
|
|
29048
|
+
if (entry.is_js && entry.response_body && jsBundles.size < 20) {
|
|
29049
|
+
jsBundles.set(entry.url, entry.response_body);
|
|
29050
|
+
}
|
|
29051
|
+
}
|
|
29052
|
+
} catch {}
|
|
29053
|
+
const syncResult = await cacheBrowseRequests({
|
|
29054
|
+
sessionUrl: session.url,
|
|
29055
|
+
sessionDomain: session.domain,
|
|
29056
|
+
requests: allRequests,
|
|
29057
|
+
getPageHtml: () => brokerForSession(session).getPageHtml(session.tabId),
|
|
29058
|
+
jsBundles: jsBundles.size > 0 ? jsBundles : undefined,
|
|
29059
|
+
intent: `browse ${session.domain || profileName(session.url)}`
|
|
29060
|
+
});
|
|
29061
|
+
if (syncResult.skill && syncResult.domain) {
|
|
29062
|
+
const domainKey = getDomainReuseKey(session.url || syncResult.domain);
|
|
29063
|
+
if (domainKey) {
|
|
29064
|
+
const cacheKey2 = scopedCacheKey("local", `${domainKey}:${syncResult.skill.skill_id}`);
|
|
29065
|
+
writeSkillSnapshot(cacheKey2, syncResult.skill);
|
|
29066
|
+
domainSkillCache.set(domainKey, {
|
|
29067
|
+
skillId: syncResult.skill.skill_id,
|
|
29068
|
+
localSkillPath: snapshotPathForCacheKey(cacheKey2),
|
|
29069
|
+
ts: Date.now()
|
|
29070
|
+
});
|
|
29071
|
+
persistDomainCache();
|
|
29072
|
+
}
|
|
29073
|
+
}
|
|
29074
|
+
return {
|
|
29075
|
+
skill_id: syncResult.skill?.skill_id ?? null,
|
|
29076
|
+
domain: syncResult.domain,
|
|
29077
|
+
request_count: allRequests.length,
|
|
29078
|
+
endpoint_count: syncResult.skill?.endpoints.length ?? 0
|
|
29079
|
+
};
|
|
29080
|
+
}
|
|
29081
|
+
function findActiveSessionForDomain(targetUrl) {
|
|
29082
|
+
const targetDomain = getDomainReuseKey(targetUrl);
|
|
29083
|
+
if (!targetDomain)
|
|
29084
|
+
return null;
|
|
29085
|
+
let match = null;
|
|
29086
|
+
for (const session of browseSessions.values()) {
|
|
29087
|
+
if (!session.harActive)
|
|
29088
|
+
continue;
|
|
29089
|
+
const sessionDomain = getDomainReuseKey(session.url);
|
|
29090
|
+
if (sessionDomain === targetDomain)
|
|
29091
|
+
match = session;
|
|
29092
|
+
}
|
|
29093
|
+
return match;
|
|
29094
|
+
}
|
|
29095
|
+
const streamingWatchers = new Map;
|
|
29096
|
+
const STREAMING_INTERVAL_MS = parseInt(process.env.UNBROWSE_STREAMING_INTERVAL_MS ?? "10000", 10);
|
|
29097
|
+
const STREAMING_ENABLED = process.env.UNBROWSE_STREAMING_PUBLISH !== "0";
|
|
29098
|
+
const streamingState = new Map;
|
|
29099
|
+
function startStreamingWatcher(session) {
|
|
29100
|
+
if (!STREAMING_ENABLED)
|
|
29101
|
+
return;
|
|
29102
|
+
if (streamingWatchers.has(session.sessionId))
|
|
29103
|
+
return;
|
|
29104
|
+
streamingState.set(session.sessionId, { lastEndpointCount: 0, lastSkillId: null });
|
|
29105
|
+
const tick = async () => {
|
|
29106
|
+
if (!browseSessions.has(session.sessionId)) {
|
|
29107
|
+
stopStreamingWatcher(session.sessionId);
|
|
29108
|
+
return;
|
|
29109
|
+
}
|
|
29110
|
+
const live = browseSessions.get(session.sessionId);
|
|
29111
|
+
if (!live || !live.harActive)
|
|
29112
|
+
return;
|
|
29113
|
+
try {
|
|
29114
|
+
const flushed = await lightFlushBrowseCapture(live);
|
|
29115
|
+
const state = streamingState.get(session.sessionId);
|
|
29116
|
+
if (!state)
|
|
29117
|
+
return;
|
|
29118
|
+
const grew = flushed.endpoint_count > state.lastEndpointCount || flushed.skill_id && flushed.skill_id !== state.lastSkillId;
|
|
29119
|
+
if (grew && flushed.skill_id) {
|
|
29120
|
+
state.lastEndpointCount = flushed.endpoint_count;
|
|
29121
|
+
state.lastSkillId = flushed.skill_id;
|
|
29122
|
+
const domainKey = getDomainReuseKey(live.url || flushed.domain);
|
|
29123
|
+
const cacheEntry = domainKey ? domainSkillCache.get(domainKey) : null;
|
|
29124
|
+
if (cacheEntry?.localSkillPath) {
|
|
29125
|
+
const skill = readSkillSnapshot(cacheEntry.localSkillPath);
|
|
29126
|
+
if (skill) {
|
|
29127
|
+
const decision = decideCheckpointPublish(flushed.domain);
|
|
29128
|
+
queueBackgroundIndex({
|
|
29129
|
+
skill: { ...skill },
|
|
29130
|
+
domain: flushed.domain,
|
|
29131
|
+
intent: skill.intent_signature || `browse ${flushed.domain}`,
|
|
29132
|
+
contextUrl: live.url,
|
|
29133
|
+
cacheKey: `streaming:${flushed.domain}:${Date.now()}`,
|
|
29134
|
+
publishAfterIndex: decision.publishQueued
|
|
29135
|
+
});
|
|
29136
|
+
console.log(`[streaming-publish] session=${session.sessionId.slice(0, 8)} domain=${flushed.domain} endpoints=${flushed.endpoint_count} publish=${decision.publishQueued}`);
|
|
29137
|
+
}
|
|
29138
|
+
}
|
|
29139
|
+
}
|
|
29140
|
+
} catch (err) {
|
|
29141
|
+
console.warn(`[streaming-publish] tick failed: ${err.message}`);
|
|
29142
|
+
}
|
|
29143
|
+
};
|
|
29144
|
+
const timer = setInterval(() => {
|
|
29145
|
+
tick();
|
|
29146
|
+
}, STREAMING_INTERVAL_MS);
|
|
29147
|
+
streamingWatchers.set(session.sessionId, timer);
|
|
29148
|
+
console.log(`[streaming-publish] watcher started session=${session.sessionId.slice(0, 8)} interval=${STREAMING_INTERVAL_MS}ms`);
|
|
29149
|
+
}
|
|
29150
|
+
function stopStreamingWatcher(sessionId) {
|
|
29151
|
+
const timer = streamingWatchers.get(sessionId);
|
|
29152
|
+
if (timer) {
|
|
29153
|
+
clearInterval(timer);
|
|
29154
|
+
streamingWatchers.delete(sessionId);
|
|
29155
|
+
}
|
|
29156
|
+
streamingState.delete(sessionId);
|
|
29157
|
+
inspectedHarEntries.delete(sessionId);
|
|
29158
|
+
}
|
|
28856
29159
|
async function flushBrowseCapture(session, options = {}) {
|
|
28857
29160
|
let harEntries = [];
|
|
28858
29161
|
if (session.harActive) {
|
|
@@ -28861,6 +29164,7 @@ async function registerRoutes(app) {
|
|
|
28861
29164
|
harEntries = entries;
|
|
28862
29165
|
} catch {}
|
|
28863
29166
|
}
|
|
29167
|
+
harEntries = [...drainInspectedHarEntries(session.sessionId), ...harEntries];
|
|
28864
29168
|
session.harActive = false;
|
|
28865
29169
|
const allRequests = await enrichPassiveCaptureRequests({
|
|
28866
29170
|
tabId: session.tabId,
|
|
@@ -29061,6 +29365,7 @@ async function registerRoutes(app) {
|
|
|
29061
29365
|
}
|
|
29062
29366
|
}
|
|
29063
29367
|
} catch {}
|
|
29368
|
+
startStreamingWatcher(session);
|
|
29064
29369
|
return reply.send({
|
|
29065
29370
|
ok: true,
|
|
29066
29371
|
session_id: session.sessionId,
|
|
@@ -29068,7 +29373,21 @@ async function registerRoutes(app) {
|
|
|
29068
29373
|
tab_id: session.tabId,
|
|
29069
29374
|
auth_profile: session.domain,
|
|
29070
29375
|
...result.cookiesInjected > 0 ? { cookies_injected: result.cookiesInjected } : {},
|
|
29071
|
-
...authRequired ? { auth_required: true, auth_hint: authHint } : {}
|
|
29376
|
+
...authRequired ? { auth_required: true, auth_hint: authHint } : {},
|
|
29377
|
+
autonomy: (() => {
|
|
29378
|
+
const publishDecision = decideCheckpointPublish(session.domain);
|
|
29379
|
+
return {
|
|
29380
|
+
har_active: session.harActive,
|
|
29381
|
+
streaming_publish_active: streamingWatchers.has(session.sessionId),
|
|
29382
|
+
attached_existing_chrome: result.attachedExistingChrome ?? false,
|
|
29383
|
+
chrome_debug_url: process.env.CHROME_DEBUG_URL ?? null,
|
|
29384
|
+
inspect_command: `unbrowse inspect --session ${session.sessionId} --pretty`,
|
|
29385
|
+
inspect_buffer: `GET ${process.env.UNBROWSE_API_BASE ?? "http://127.0.0.1:6969"}/v1/browse/sessions/${session.sessionId}/buffer`,
|
|
29386
|
+
marketplace_publish_enabled: publishDecision.publishQueued,
|
|
29387
|
+
marketplace_publish_mode: publishDecision.mode,
|
|
29388
|
+
marketplace_publish_reason: publishDecision.reason
|
|
29389
|
+
};
|
|
29390
|
+
})()
|
|
29072
29391
|
});
|
|
29073
29392
|
} catch (error) {
|
|
29074
29393
|
return sendBrowseSessionError(reply, error);
|
|
@@ -29110,6 +29429,7 @@ async function registerRoutes(app) {
|
|
|
29110
29429
|
activeSession.domain = profileName(activeSession.url);
|
|
29111
29430
|
const stillLive = await isBrowseSessionLive(activeSession, browseClient).catch(() => false);
|
|
29112
29431
|
if (!stillLive) {
|
|
29432
|
+
stopStreamingWatcher(activeSession.sessionId);
|
|
29113
29433
|
removeBrowseSession(browseSessions, activeSession.sessionId);
|
|
29114
29434
|
throw new BrowseSessionError("session_expired");
|
|
29115
29435
|
}
|
|
@@ -29337,6 +29657,7 @@ async function registerRoutes(app) {
|
|
|
29337
29657
|
await saveAuthProfileBestEffort(session2.tabId, session2.domain, "browse_close");
|
|
29338
29658
|
}
|
|
29339
29659
|
const syncResult2 = await flushBrowseCapture(session2, { queueIndex: true, queuePublish: true });
|
|
29660
|
+
stopStreamingWatcher(session2.sessionId);
|
|
29340
29661
|
await broker.closeTab(session2.tabId).catch(() => {});
|
|
29341
29662
|
removeBrowseSession(browseSessions, session2.sessionId);
|
|
29342
29663
|
return syncResult2;
|
|
@@ -29366,7 +29687,7 @@ function saveTrace(trace) {
|
|
|
29366
29687
|
const t = trace;
|
|
29367
29688
|
writeFileSync13(join19(TRACES_DIR, `${t.trace_id}.json`), JSON.stringify(trace, null, 2));
|
|
29368
29689
|
}
|
|
29369
|
-
var BETA_API_URL, TRACES_DIR, BROWSE_BROKER_MAX, BROWSE_BROKER_BASE_PORT, browseSessions, statsCache = null, STATS_CACHE_TTL;
|
|
29690
|
+
var BETA_API_URL, TRACES_DIR, BROWSE_BROKER_MAX, BROWSE_BROKER_BASE_PORT, browseSessions, inspectedHarEntries, statsCache = null, STATS_CACHE_TTL;
|
|
29370
29691
|
var init_routes = __esm(async () => {
|
|
29371
29692
|
init_client();
|
|
29372
29693
|
init_reverse_engineer();
|
|
@@ -29405,6 +29726,7 @@ var init_routes = __esm(async () => {
|
|
|
29405
29726
|
BROWSE_BROKER_MAX = Math.max(1, Number(process.env.KURI_MULTI_BROKER_MAX ?? "1"));
|
|
29406
29727
|
BROWSE_BROKER_BASE_PORT = Number(process.env.KURI_PORT ?? "7700");
|
|
29407
29728
|
browseSessions = new Map;
|
|
29729
|
+
inspectedHarEntries = new Map;
|
|
29408
29730
|
STATS_CACHE_TTL = 5 * 60 * 1000;
|
|
29409
29731
|
});
|
|
29410
29732
|
|
|
@@ -29459,6 +29781,16 @@ async function startUnbrowseServer(options = {}) {
|
|
|
29459
29781
|
process.env.UNBROWSE_SKIP_TOS_CHECK = "1";
|
|
29460
29782
|
}
|
|
29461
29783
|
startBackgroundRegistration();
|
|
29784
|
+
const UNBROWSE_CDP_PORT = Number(process.env.UNBROWSE_CDP_PORT ?? 9222);
|
|
29785
|
+
if (!process.env.CHROME_DEBUG_URL) {
|
|
29786
|
+
process.env.CHROME_DEBUG_URL = `http://127.0.0.1:${UNBROWSE_CDP_PORT}`;
|
|
29787
|
+
}
|
|
29788
|
+
if (!process.env.PUPPETEER_BROWSER_WS_ENDPOINT) {
|
|
29789
|
+
process.env.PUPPETEER_BROWSER_WS_ENDPOINT = `ws://127.0.0.1:${UNBROWSE_CDP_PORT}`;
|
|
29790
|
+
}
|
|
29791
|
+
if (!process.env.PLAYWRIGHT_CHROMIUM_REMOTE_DEBUGGING_URL) {
|
|
29792
|
+
process.env.PLAYWRIGHT_CHROMIUM_REMOTE_DEBUGGING_URL = `http://127.0.0.1:${UNBROWSE_CDP_PORT}`;
|
|
29793
|
+
}
|
|
29462
29794
|
const app = Fastify({ logger: options.logger ?? true });
|
|
29463
29795
|
await app.register(cors, { origin: true });
|
|
29464
29796
|
await registerRateLimiter(app);
|