unbrowse 3.2.1 → 3.2.2-experiments.128ff09
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 +37 -35
- package/dist/mcp.js +5 -5
- package/dist/server.js +546 -259
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -4210,7 +4210,7 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4210
4210
|
return "";
|
|
4211
4211
|
}
|
|
4212
4212
|
})();
|
|
4213
|
-
const isApiUrl = /\/(api|graphql)\b/i.test(urlPath) || /\.(json)(\?|$)/.test(req.url);
|
|
4213
|
+
const isApiUrl = /\/(api|graphql|youtubei|__ssr_data__)\b/i.test(urlPath) || /\.(json)(\?|$)/.test(req.url);
|
|
4214
4214
|
let graphqlOpName;
|
|
4215
4215
|
if (/graphql/i.test(req.url)) {
|
|
4216
4216
|
if (req.request_body) {
|
|
@@ -4299,7 +4299,12 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4299
4299
|
const sanitizedQParams = isGet ? sanitizeQueryParams(extractQueryParams(req.url)) : undefined;
|
|
4300
4300
|
let pathTemplate = sanitizeUrlTemplate(normalized);
|
|
4301
4301
|
const qBindings = sanitizedQParams ? buildQueryBindingMap(Object.keys(sanitizedQParams)) : {};
|
|
4302
|
-
const
|
|
4302
|
+
const existingQKeys = new Set;
|
|
4303
|
+
try {
|
|
4304
|
+
for (const k of new URL(pathTemplate).searchParams.keys())
|
|
4305
|
+
existingQKeys.add(k.toLowerCase());
|
|
4306
|
+
} catch {}
|
|
4307
|
+
const qTemplateStr = sanitizedQParams && Object.keys(sanitizedQParams).length > 0 ? Object.keys(sanitizedQParams).filter((k) => !existingQKeys.has(k.toLowerCase())).map((k) => `${encodeURIComponent(k)}={${qBindings[k] ?? k}}`).join("&") || null : null;
|
|
4303
4308
|
const { url: templatizedPath, pathParams, pathBindingCandidates } = templatizePathSegments(pathTemplate, req.url, context);
|
|
4304
4309
|
pathTemplate = templatizedPath;
|
|
4305
4310
|
const parsedRequestBody = !isGet && req.request_body ? tryParseBody(req.request_body) : undefined;
|
|
@@ -4315,7 +4320,7 @@ function extractEndpoints(requests, wsMessages, context) {
|
|
|
4315
4320
|
let endpoint = {
|
|
4316
4321
|
endpoint_id: nanoid2(),
|
|
4317
4322
|
method: req.method,
|
|
4318
|
-
url_template: qTemplateStr ? `${pathTemplate}
|
|
4323
|
+
url_template: qTemplateStr ? `${pathTemplate}${pathTemplate.includes("?") ? "&" : "?"}${qTemplateStr}` : pathTemplate,
|
|
4319
4324
|
description: buildEndpointDescription(req, sampleRequest, sampleResponse),
|
|
4320
4325
|
headers_template: sanitizeHeaders(req.request_headers),
|
|
4321
4326
|
query: sanitizedQParams,
|
|
@@ -4946,7 +4951,7 @@ var init_reverse_engineer = __esm(() => {
|
|
|
4946
4951
|
SKIP_HOSTS = /(cloudflare\.com|google-analytics\.com|doubleclick\.net|gstatic\.com|accounts\.google\.com|login\.microsoftonline\.com|auth0\.com|cognito-idp\.|appleid\.apple\.com|github\.com\/login|facebook\.com\/login|protechts\.net|demdex\.net|litms|platform-telemetry|datadoghq\.com|fullstory\.com|launchdarkly\.com|intercom\.io|privy\.io|mypinata\.cloud|sentry\.io|segment\.io|amplitude\.com|mixpanel\.com|hotjar\.com|clarity\.ms|googletagmanager\.com|walletconnect\.com|imagedelivery\.net|cloudflareinsights\.com)/i;
|
|
4947
4952
|
SKIP_TELEMETRY_HOSTS = /(waa-pa\.|signaler-pa\.|appsgrowthpromo-pa\.|ogads-pa\.|peoplestackwebexperiments-pa\.)/i;
|
|
4948
4953
|
SKIP_TELEMETRY_PATHS = /\/(log|logging|telemetry|analytics|beacon|ping|heartbeat|metrics)(\/|$)/i;
|
|
4949
|
-
RPC_HINTS = /(\/$rpc\/|\/rpc\/|graphql|trending|search|feed|results|batchexecute|\/api
|
|
4954
|
+
RPC_HINTS = /(\/$rpc\/|\/rpc\/|graphql|trending|search|feed|results|batchexecute|\/api\/|youtubei|__ssr_data__)/i;
|
|
4950
4955
|
ALLOWED_METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
4951
4956
|
STRIP_HEADERS = new Set([
|
|
4952
4957
|
"cookie",
|
|
@@ -5024,7 +5029,180 @@ var init_reverse_engineer = __esm(() => {
|
|
|
5024
5029
|
"adsize",
|
|
5025
5030
|
"lineitemid"
|
|
5026
5031
|
]);
|
|
5027
|
-
ON_DOMAIN_NOISE = /\/(recaptcha|captcha|update-recaptcha|csrf|consent|data-protection|badge|drawer|header-action|geolocation|onboarding|wana\/bids|prebid|bids\/request|ads\/|pixel|beacon|collect|impression|click-tracking|heartbeat|webConfig|config\.json|manifest\.json|service-worker|sw\.js|favicon|robots\.txt|sitemap|opensearch|partial\/[a-zA-Z]+\/mod-|logging|csp-report|gen_204|generate_204|sodar|
|
|
5032
|
+
ON_DOMAIN_NOISE = /\/(recaptcha|captcha|update-recaptcha|csrf|consent|data-protection|badge|drawer|header-action|geolocation|onboarding|wana\/bids|prebid|bids\/request|ads\/|pixel|beacon|collect|impression|click-tracking|heartbeat|webConfig|config\.json|manifest\.json|service-worker|sw\.js|favicon|robots\.txt|sitemap|opensearch|partial\/[a-zA-Z]+\/mod-|logging|csp-report|gen_204|generate_204|sodar|__webpack|__next|devvit-|user-drawer|action-item)/i;
|
|
5033
|
+
});
|
|
5034
|
+
|
|
5035
|
+
// ../../src/vault/index.ts
|
|
5036
|
+
var exports_vault = {};
|
|
5037
|
+
__export(exports_vault, {
|
|
5038
|
+
storeCredential: () => storeCredential,
|
|
5039
|
+
setKeytarClientForTests: () => setKeytarClientForTests,
|
|
5040
|
+
resetKeytarClientForTests: () => resetKeytarClientForTests,
|
|
5041
|
+
normalizeKeytarModule: () => normalizeKeytarModule,
|
|
5042
|
+
getCredential: () => getCredential,
|
|
5043
|
+
deleteCredential: () => deleteCredential
|
|
5044
|
+
});
|
|
5045
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
5046
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
5047
|
+
import { join as join2 } from "path";
|
|
5048
|
+
import { homedir } from "os";
|
|
5049
|
+
function normalizeKeytarModule(mod) {
|
|
5050
|
+
let candidate = mod;
|
|
5051
|
+
for (let depth = 0;depth < 3; depth++) {
|
|
5052
|
+
if (!candidate || typeof candidate !== "object" || !("default" in candidate))
|
|
5053
|
+
break;
|
|
5054
|
+
candidate = candidate.default;
|
|
5055
|
+
}
|
|
5056
|
+
if (!candidate)
|
|
5057
|
+
return null;
|
|
5058
|
+
if (typeof candidate.setPassword === "function" && typeof candidate.getPassword === "function" && typeof candidate.deletePassword === "function") {
|
|
5059
|
+
return candidate;
|
|
5060
|
+
}
|
|
5061
|
+
return null;
|
|
5062
|
+
}
|
|
5063
|
+
function isKeytarBindingError(error) {
|
|
5064
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
5065
|
+
return KEYTAR_BINDING_ERROR_RE.test(message);
|
|
5066
|
+
}
|
|
5067
|
+
function disableKeytar(error) {
|
|
5068
|
+
keytar = null;
|
|
5069
|
+
if (keytarFallbackLogged)
|
|
5070
|
+
return;
|
|
5071
|
+
const summary = error instanceof Error ? error.message : String(error);
|
|
5072
|
+
log("vault", `keytar runtime unavailable (${summary}); using encrypted file fallback`);
|
|
5073
|
+
keytarFallbackLogged = true;
|
|
5074
|
+
}
|
|
5075
|
+
async function callKeytar(op) {
|
|
5076
|
+
if (!keytar)
|
|
5077
|
+
return KEYTAR_UNAVAILABLE;
|
|
5078
|
+
try {
|
|
5079
|
+
return await op(keytar);
|
|
5080
|
+
} catch (error) {
|
|
5081
|
+
if (!isKeytarBindingError(error))
|
|
5082
|
+
throw error;
|
|
5083
|
+
disableKeytar(error);
|
|
5084
|
+
return KEYTAR_UNAVAILABLE;
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
function setKeytarClientForTests(client) {
|
|
5088
|
+
keytar = client;
|
|
5089
|
+
keytarFallbackLogged = false;
|
|
5090
|
+
}
|
|
5091
|
+
function resetKeytarClientForTests() {
|
|
5092
|
+
keytar = importedKeytar;
|
|
5093
|
+
keytarFallbackLogged = false;
|
|
5094
|
+
}
|
|
5095
|
+
function getOrCreateKey() {
|
|
5096
|
+
if (!existsSync3(VAULT_DIR))
|
|
5097
|
+
mkdirSync3(VAULT_DIR, { recursive: true, mode: 448 });
|
|
5098
|
+
if (existsSync3(KEY_FILE))
|
|
5099
|
+
return readFileSync(KEY_FILE);
|
|
5100
|
+
const key = randomBytes(32);
|
|
5101
|
+
writeFileSync2(KEY_FILE, key, { mode: 384 });
|
|
5102
|
+
return key;
|
|
5103
|
+
}
|
|
5104
|
+
function withVaultLock(fn) {
|
|
5105
|
+
const prev = vaultLock;
|
|
5106
|
+
let release;
|
|
5107
|
+
vaultLock = new Promise((r) => {
|
|
5108
|
+
release = r;
|
|
5109
|
+
});
|
|
5110
|
+
return prev.then(fn).finally(() => release());
|
|
5111
|
+
}
|
|
5112
|
+
function readVaultFile() {
|
|
5113
|
+
if (!existsSync3(VAULT_FILE))
|
|
5114
|
+
return {};
|
|
5115
|
+
try {
|
|
5116
|
+
const key = getOrCreateKey();
|
|
5117
|
+
const raw = readFileSync(VAULT_FILE);
|
|
5118
|
+
const iv = raw.subarray(0, 16);
|
|
5119
|
+
const enc = raw.subarray(16);
|
|
5120
|
+
const decipher = createDecipheriv("aes-256-cbc", key, iv);
|
|
5121
|
+
const dec = Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
5122
|
+
return JSON.parse(dec.toString("utf8"));
|
|
5123
|
+
} catch {
|
|
5124
|
+
return {};
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
function writeVaultFile(data) {
|
|
5128
|
+
const key = getOrCreateKey();
|
|
5129
|
+
const iv = randomBytes(16);
|
|
5130
|
+
const cipher = createCipheriv("aes-256-cbc", key, iv);
|
|
5131
|
+
const enc = Buffer.concat([cipher.update(JSON.stringify(data), "utf8"), cipher.final()]);
|
|
5132
|
+
writeFileSync2(VAULT_FILE, Buffer.concat([iv, enc]), { mode: 384 });
|
|
5133
|
+
}
|
|
5134
|
+
async function storeCredential(account, value, opts) {
|
|
5135
|
+
const wrapped = {
|
|
5136
|
+
value,
|
|
5137
|
+
stored_at: new Date().toISOString(),
|
|
5138
|
+
expires_at: opts?.expires_at,
|
|
5139
|
+
max_age_ms: opts?.max_age_ms
|
|
5140
|
+
};
|
|
5141
|
+
const serialized = JSON.stringify(wrapped);
|
|
5142
|
+
const keytarResult = await callKeytar((client) => client.setPassword(SERVICE, account, serialized));
|
|
5143
|
+
if (keytarResult !== KEYTAR_UNAVAILABLE)
|
|
5144
|
+
return;
|
|
5145
|
+
await withVaultLock(() => {
|
|
5146
|
+
const data = readVaultFile();
|
|
5147
|
+
data[account] = serialized;
|
|
5148
|
+
writeVaultFile(data);
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
function isExpired(cred) {
|
|
5152
|
+
if (cred.expires_at) {
|
|
5153
|
+
return new Date(cred.expires_at).getTime() <= Date.now();
|
|
5154
|
+
}
|
|
5155
|
+
if (cred.max_age_ms) {
|
|
5156
|
+
return new Date(cred.stored_at).getTime() + cred.max_age_ms <= Date.now();
|
|
5157
|
+
}
|
|
5158
|
+
return false;
|
|
5159
|
+
}
|
|
5160
|
+
async function getCredential(account) {
|
|
5161
|
+
let raw;
|
|
5162
|
+
const keytarResult = await callKeytar((client) => client.getPassword(SERVICE, account));
|
|
5163
|
+
if (keytarResult !== KEYTAR_UNAVAILABLE) {
|
|
5164
|
+
raw = keytarResult;
|
|
5165
|
+
} else {
|
|
5166
|
+
const data = readVaultFile();
|
|
5167
|
+
raw = data[account] ?? null;
|
|
5168
|
+
}
|
|
5169
|
+
if (!raw)
|
|
5170
|
+
return null;
|
|
5171
|
+
try {
|
|
5172
|
+
const parsed = JSON.parse(raw);
|
|
5173
|
+
if (parsed.value && parsed.stored_at) {
|
|
5174
|
+
if (isExpired(parsed)) {
|
|
5175
|
+
await deleteCredential(account);
|
|
5176
|
+
return null;
|
|
5177
|
+
}
|
|
5178
|
+
return parsed.value;
|
|
5179
|
+
}
|
|
5180
|
+
} catch {}
|
|
5181
|
+
return raw;
|
|
5182
|
+
}
|
|
5183
|
+
async function deleteCredential(account) {
|
|
5184
|
+
const keytarResult = await callKeytar((client) => client.deletePassword(SERVICE, account));
|
|
5185
|
+
if (keytarResult !== KEYTAR_UNAVAILABLE)
|
|
5186
|
+
return;
|
|
5187
|
+
await withVaultLock(() => {
|
|
5188
|
+
const data = readVaultFile();
|
|
5189
|
+
delete data[account];
|
|
5190
|
+
writeVaultFile(data);
|
|
5191
|
+
});
|
|
5192
|
+
}
|
|
5193
|
+
var KEYTAR_UNAVAILABLE, KEYTAR_BINDING_ERROR_RE, keytar = null, importedKeytar, keytarFallbackLogged = false, SERVICE = "unbrowse", VAULT_DIR, VAULT_FILE, KEY_FILE, vaultLock;
|
|
5194
|
+
var init_vault = __esm(async () => {
|
|
5195
|
+
init_logger();
|
|
5196
|
+
KEYTAR_UNAVAILABLE = Symbol("KEYTAR_UNAVAILABLE");
|
|
5197
|
+
KEYTAR_BINDING_ERROR_RE = /(keytar(?:\.node)?|native bindings?|bindings file|no native build was found|could not locate the bindings file|module did not self-register|err_dlopen_failed|dlopen\(|compiled against a different node\.js version|cannot find module .*keytar|wasm is not supported on this platform|(set|get|delete)password is not a function)/i;
|
|
5198
|
+
try {
|
|
5199
|
+
keytar = normalizeKeytarModule(await import("keytar"));
|
|
5200
|
+
} catch {}
|
|
5201
|
+
importedKeytar = keytar;
|
|
5202
|
+
VAULT_DIR = join2(homedir(), ".unbrowse", "vault");
|
|
5203
|
+
VAULT_FILE = join2(VAULT_DIR, "credentials.enc");
|
|
5204
|
+
KEY_FILE = join2(VAULT_DIR, ".key");
|
|
5205
|
+
vaultLock = Promise.resolve();
|
|
5028
5206
|
});
|
|
5029
5207
|
|
|
5030
5208
|
// ../../src/runtime/browser-access.ts
|
|
@@ -5477,7 +5655,12 @@ function normalizeCapturedUrl(url, baseUrl) {
|
|
|
5477
5655
|
if (!url)
|
|
5478
5656
|
return url;
|
|
5479
5657
|
try {
|
|
5480
|
-
|
|
5658
|
+
const parsed = new URL(url);
|
|
5659
|
+
if (baseUrl && (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost")) {
|
|
5660
|
+
const pageOrigin = new URL(baseUrl).origin;
|
|
5661
|
+
return `${pageOrigin}${parsed.pathname}${parsed.search}`;
|
|
5662
|
+
}
|
|
5663
|
+
return parsed.toString();
|
|
5481
5664
|
} catch {
|
|
5482
5665
|
if (!baseUrl)
|
|
5483
5666
|
return url;
|
|
@@ -5878,7 +6061,76 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
|
|
|
5878
6061
|
log("capture", "scriptInject unavailable — falling back to evaluate injection");
|
|
5879
6062
|
}
|
|
5880
6063
|
}
|
|
5881
|
-
|
|
6064
|
+
try {
|
|
6065
|
+
await phase("harStart", () => harStart(tabId));
|
|
6066
|
+
} catch {}
|
|
6067
|
+
const cdpNetworkEntries = [];
|
|
6068
|
+
const cdpRequestMap = new Map;
|
|
6069
|
+
const cdpResponseMeta = new Map;
|
|
6070
|
+
const cdpFinishedRequests = new Set;
|
|
6071
|
+
let cdpWs = null;
|
|
6072
|
+
let cdpMsgId = 10;
|
|
6073
|
+
const cdpPendingBodies = new Map;
|
|
6074
|
+
const cdpResolvedBodies = new Map;
|
|
6075
|
+
const API_URL_PATTERN = /\/api\/|\/graphql|voyager|youtubei|\/v\d+\//i;
|
|
6076
|
+
try {
|
|
6077
|
+
const cdpPort = getCdpPort();
|
|
6078
|
+
if (cdpPort) {
|
|
6079
|
+
const tabsResp = await fetch(`http://127.0.0.1:${cdpPort}/json`);
|
|
6080
|
+
const tabs = await tabsResp.json();
|
|
6081
|
+
const cdpTab = tabs.find((t) => t.id === tabId) ?? tabs.find((t) => t.type === "page");
|
|
6082
|
+
if (cdpTab?.webSocketDebuggerUrl) {
|
|
6083
|
+
cdpWs = new WebSocket(cdpTab.webSocketDebuggerUrl);
|
|
6084
|
+
await new Promise((resolve, reject) => {
|
|
6085
|
+
cdpWs.onopen = () => resolve();
|
|
6086
|
+
cdpWs.onerror = () => reject(new Error("CDP WS failed"));
|
|
6087
|
+
setTimeout(() => reject(new Error("CDP WS timeout")), 3000);
|
|
6088
|
+
});
|
|
6089
|
+
cdpWs.onmessage = (ev) => {
|
|
6090
|
+
try {
|
|
6091
|
+
const msg = JSON.parse(String(ev.data));
|
|
6092
|
+
if (msg.method === "Network.requestWillBeSent") {
|
|
6093
|
+
const p = msg.params;
|
|
6094
|
+
const reqHeaders = Object.entries(p.request?.headers ?? {}).map(([name, value]) => ({ name, value: String(value) }));
|
|
6095
|
+
cdpRequestMap.set(p.requestId, {
|
|
6096
|
+
method: p.request?.method ?? "GET",
|
|
6097
|
+
url: p.request?.url ?? "",
|
|
6098
|
+
headers: reqHeaders,
|
|
6099
|
+
postData: p.request?.postData
|
|
6100
|
+
});
|
|
6101
|
+
} else if (msg.method === "Network.responseReceived") {
|
|
6102
|
+
const p = msg.params;
|
|
6103
|
+
const respHeaders = Object.entries(p.response?.headers ?? {}).map(([name, value]) => ({ name, value: String(value) }));
|
|
6104
|
+
cdpResponseMeta.set(p.requestId, {
|
|
6105
|
+
status: p.response?.status ?? 0,
|
|
6106
|
+
headers: respHeaders,
|
|
6107
|
+
mimeType: p.response?.mimeType ?? ""
|
|
6108
|
+
});
|
|
6109
|
+
} else if (msg.method === "Network.loadingFinished") {
|
|
6110
|
+
const requestId = msg.params?.requestId;
|
|
6111
|
+
cdpFinishedRequests.add(requestId);
|
|
6112
|
+
const req = cdpRequestMap.get(requestId);
|
|
6113
|
+
const resp = cdpResponseMeta.get(requestId);
|
|
6114
|
+
if (req && resp && API_URL_PATTERN.test(req.url) && (resp.mimeType.includes("json") || resp.mimeType.includes("text"))) {
|
|
6115
|
+
const id = ++cdpMsgId;
|
|
6116
|
+
cdpPendingBodies.set(id, req.url);
|
|
6117
|
+
cdpWs.send(JSON.stringify({ id, method: "Network.getResponseBody", params: { requestId } }));
|
|
6118
|
+
}
|
|
6119
|
+
} else if (msg.id && cdpPendingBodies.has(msg.id)) {
|
|
6120
|
+
const reqUrl = cdpPendingBodies.get(msg.id);
|
|
6121
|
+
cdpPendingBodies.delete(msg.id);
|
|
6122
|
+
if (msg.result?.body) {
|
|
6123
|
+
cdpResolvedBodies.set(reqUrl, msg.result.body);
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
} catch {}
|
|
6127
|
+
};
|
|
6128
|
+
cdpWs.send(JSON.stringify({ id: 1, method: "Network.enable", params: {} }));
|
|
6129
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
6130
|
+
log("capture", "CDP network capture enabled (direct websocket)");
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
} catch {}
|
|
5882
6134
|
let pageDomain;
|
|
5883
6135
|
try {
|
|
5884
6136
|
pageDomain = getRegistrableDomain(new URL(url).hostname);
|
|
@@ -5932,11 +6184,85 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
|
|
|
5932
6184
|
const perfEntries = await phase("evaluate:perf", () => collectPerformanceResourceEntries(tabId));
|
|
5933
6185
|
performanceUrls = await phase("replay-fetch", () => replayPerformanceApiResponses(tabId, perfEntries, responseBodies, url, intent));
|
|
5934
6186
|
} catch {}
|
|
6187
|
+
if (cdpRequestMap.size > 0) {
|
|
6188
|
+
try {
|
|
6189
|
+
const cdpRawReqs = [...cdpRequestMap.values()].map((r) => ({
|
|
6190
|
+
url: r.url,
|
|
6191
|
+
method: r.method,
|
|
6192
|
+
request_headers: Object.fromEntries(r.headers.map((h) => [h.name.toLowerCase(), h.value])),
|
|
6193
|
+
response_status: 200,
|
|
6194
|
+
response_headers: {},
|
|
6195
|
+
response_body: undefined,
|
|
6196
|
+
timestamp: ""
|
|
6197
|
+
}));
|
|
6198
|
+
const authHeaders2 = extractAuthHeaders(cdpRawReqs);
|
|
6199
|
+
if (Object.keys(authHeaders2).length > 0) {
|
|
6200
|
+
const rawKey = `${domain}-session`;
|
|
6201
|
+
const { getCredential: getCredential2 } = await init_vault().then(() => exports_vault);
|
|
6202
|
+
let existing = {};
|
|
6203
|
+
try {
|
|
6204
|
+
const stored = await getCredential2(rawKey);
|
|
6205
|
+
if (stored)
|
|
6206
|
+
existing = JSON.parse(stored);
|
|
6207
|
+
} catch {}
|
|
6208
|
+
if (!existing.cookies || existing.cookies.length === 0) {
|
|
6209
|
+
try {
|
|
6210
|
+
const loginKey = `auth:${getRegistrableDomain(domain)}`;
|
|
6211
|
+
const loginStored = await getCredential2(loginKey);
|
|
6212
|
+
if (loginStored) {
|
|
6213
|
+
const loginData = JSON.parse(loginStored);
|
|
6214
|
+
if (loginData.cookies?.length)
|
|
6215
|
+
existing.cookies = loginData.cookies;
|
|
6216
|
+
}
|
|
6217
|
+
} catch {}
|
|
6218
|
+
}
|
|
6219
|
+
await storeCredential(rawKey, JSON.stringify({
|
|
6220
|
+
...existing,
|
|
6221
|
+
headers: { ...existing.headers ?? {}, ...authHeaders2 }
|
|
6222
|
+
}));
|
|
6223
|
+
log("capture", `early auth store: ${Object.keys(authHeaders2).join(", ")} for ${domain}`);
|
|
6224
|
+
}
|
|
6225
|
+
} catch (e) {
|
|
6226
|
+
log("capture", `early auth store failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
5935
6229
|
let harEntries = [];
|
|
5936
6230
|
try {
|
|
5937
|
-
const
|
|
5938
|
-
|
|
6231
|
+
const harPromise = harStop(tabId);
|
|
6232
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve({ entries: [] }), 5000));
|
|
6233
|
+
const harResult = await Promise.race([harPromise, timeoutPromise]);
|
|
6234
|
+
harEntries = harResult.entries ?? [];
|
|
5939
6235
|
} catch {}
|
|
6236
|
+
if (cdpPendingBodies.size > 0) {
|
|
6237
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
6238
|
+
}
|
|
6239
|
+
if (cdpWs) {
|
|
6240
|
+
try {
|
|
6241
|
+
cdpWs.close();
|
|
6242
|
+
} catch {}
|
|
6243
|
+
}
|
|
6244
|
+
const harUrls = new Set(harEntries.map((e) => e.request?.url).filter(Boolean));
|
|
6245
|
+
let cdpAdded = 0;
|
|
6246
|
+
for (const [requestId, req] of cdpRequestMap) {
|
|
6247
|
+
if (harUrls.has(req.url))
|
|
6248
|
+
continue;
|
|
6249
|
+
const resp = cdpResponseMeta.get(requestId);
|
|
6250
|
+
if (!resp)
|
|
6251
|
+
continue;
|
|
6252
|
+
const body = cdpResolvedBodies.get(req.url);
|
|
6253
|
+
harEntries.push({
|
|
6254
|
+
startedDateTime: new Date().toISOString(),
|
|
6255
|
+
request: { method: req.method, url: req.url, headers: req.headers, postData: req.postData ? { text: req.postData } : undefined },
|
|
6256
|
+
response: { status: resp.status, headers: resp.headers, content: body ? { text: body, mimeType: resp.mimeType } : {} }
|
|
6257
|
+
});
|
|
6258
|
+
if (body && body.length > 0 && !responseBodies.has(req.url)) {
|
|
6259
|
+
responseBodies.set(req.url, body);
|
|
6260
|
+
}
|
|
6261
|
+
cdpAdded++;
|
|
6262
|
+
}
|
|
6263
|
+
if (cdpAdded > 0) {
|
|
6264
|
+
log("capture", `CDP direct capture added ${cdpAdded} entries (${cdpResolvedBodies.size} with bodies, ${harEntries.length} total HAR)`);
|
|
6265
|
+
}
|
|
5940
6266
|
const HAR_REPLAY_CT2 = /application\/json|text\/plain|\+json/i;
|
|
5941
6267
|
let harReplayCount = 0;
|
|
5942
6268
|
for (const entry of harEntries) {
|
|
@@ -5952,7 +6278,7 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
|
|
|
5952
6278
|
if (status < 200 || status >= 400)
|
|
5953
6279
|
continue;
|
|
5954
6280
|
const ct = (entry.response?.headers ?? []).find((h) => h.name.toLowerCase() === "content-type")?.value ?? "";
|
|
5955
|
-
if (!HAR_REPLAY_CT2.test(ct) && !harUrl.includes("/api/") && !harUrl.includes("_search") && !harUrl.includes("graphql"))
|
|
6281
|
+
if (!HAR_REPLAY_CT2.test(ct) && !harUrl.includes("/api/") && !harUrl.includes("_search") && !harUrl.includes("graphql") && !harUrl.includes("youtubei"))
|
|
5956
6282
|
continue;
|
|
5957
6283
|
if (harReplayCount >= 20)
|
|
5958
6284
|
break;
|
|
@@ -5960,7 +6286,7 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
|
|
|
5960
6286
|
const postData = method !== "GET" ? entry.request?.postData?.text : undefined;
|
|
5961
6287
|
const replayScript = method === "GET" || !postData ? `(function(){var x=new XMLHttpRequest();x.open('GET','${harUrl.replace(/'/g, "\\'")}',false);x.send();return x.status>=200&&x.status<400?x.responseText:''})()` : `(function(){var x=new XMLHttpRequest();x.open('${method}','${harUrl.replace(/'/g, "\\'")}',false);x.setRequestHeader('Content-Type','application/json');x.send(${JSON.stringify(postData)});return x.status>=200&&x.status<400?x.responseText:''})()`;
|
|
5962
6288
|
const body = await phase("har-replay", () => evaluate(tabId, replayScript));
|
|
5963
|
-
if (typeof body === "string" && body.length > 0 && body.length <
|
|
6289
|
+
if (typeof body === "string" && body.length > 0 && body.length < 524288) {
|
|
5964
6290
|
responseBodies.set(harUrl, body);
|
|
5965
6291
|
harReplayCount++;
|
|
5966
6292
|
log("capture", `har-replay-fetched ${harUrl.substring(0, 80)} (${body.length}B)`);
|
|
@@ -5987,6 +6313,77 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
|
|
|
5987
6313
|
}
|
|
5988
6314
|
html = await phase("getPageHtml", () => getPageHtml(tabId));
|
|
5989
6315
|
} catch {}
|
|
6316
|
+
const SSR_DATA_EXTRACTORS = [
|
|
6317
|
+
{ name: "ytmusic", script: `(function(){try{var d=ytcfg.get('YTMUSIC_INITIAL_DATA');if(!d||!d.length)return null;var out={};d.forEach(function(x){if(x.path&&x.data)out[x.path]=x.data});out.__apiKey=ytcfg.get('INNERTUBE_API_KEY')||'';out.__context=ytcfg.get('INNERTUBE_CONTEXT')||null;return JSON.stringify(out)}catch(e){return null}})()` },
|
|
6318
|
+
{ name: "youtube", script: `(function(){try{return typeof ytInitialData!=='undefined'?JSON.stringify(ytInitialData):null}catch(e){return null}})()` },
|
|
6319
|
+
{ name: "nextjs", script: `(function(){try{return window.__NEXT_DATA__?JSON.stringify(window.__NEXT_DATA__):null}catch(e){return null}})()` },
|
|
6320
|
+
{ name: "nuxt", script: `(function(){try{return window.__NUXT__?JSON.stringify(window.__NUXT__):null}catch(e){return null}})()` }
|
|
6321
|
+
];
|
|
6322
|
+
let embeddedDataCount = 0;
|
|
6323
|
+
for (const extractor of SSR_DATA_EXTRACTORS) {
|
|
6324
|
+
try {
|
|
6325
|
+
const raw = await phase(`ssr:${extractor.name}`, () => evaluate(tabId, extractor.script));
|
|
6326
|
+
if (typeof raw !== "string" || !raw || raw === "null")
|
|
6327
|
+
continue;
|
|
6328
|
+
if (extractor.name === "ytmusic") {
|
|
6329
|
+
const parsed = JSON.parse(raw);
|
|
6330
|
+
const apiKey = parsed.__apiKey || "";
|
|
6331
|
+
const innertubeContext = parsed.__context;
|
|
6332
|
+
delete parsed.__apiKey;
|
|
6333
|
+
delete parsed.__context;
|
|
6334
|
+
for (const [path4, data] of Object.entries(parsed)) {
|
|
6335
|
+
if (!data || typeof data !== "object")
|
|
6336
|
+
continue;
|
|
6337
|
+
const cleaned = { ...data };
|
|
6338
|
+
delete cleaned.responseContext;
|
|
6339
|
+
delete cleaned.trackingParams;
|
|
6340
|
+
delete cleaned.header;
|
|
6341
|
+
delete cleaned.background;
|
|
6342
|
+
let bodyStr = JSON.stringify(cleaned);
|
|
6343
|
+
if (bodyStr.length > 1e4) {
|
|
6344
|
+
bodyStr = bodyStr.substring(0, 1e4) + '"}]}';
|
|
6345
|
+
try {
|
|
6346
|
+
JSON.parse(bodyStr);
|
|
6347
|
+
} catch {
|
|
6348
|
+
bodyStr = JSON.stringify(cleaned).substring(0, 1e4);
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
if (bodyStr.length < 100)
|
|
6352
|
+
continue;
|
|
6353
|
+
const origin = new URL(url).origin;
|
|
6354
|
+
const contextParams = new URL(url).searchParams;
|
|
6355
|
+
const keyParam = apiKey ? `key=${apiKey}&` : "";
|
|
6356
|
+
const queryStr = path4.includes("search") && contextParams.toString() ? `?${keyParam}${contextParams.toString()}&prettyPrint=false` : `?${keyParam}prettyPrint=false`;
|
|
6357
|
+
const syntheticUrl = `${origin}/youtubei/v1${path4}${queryStr}`;
|
|
6358
|
+
responseBodies.set(syntheticUrl, bodyStr);
|
|
6359
|
+
const queryValue = contextParams.get("q") ?? "";
|
|
6360
|
+
const postBody = path4.includes("search") && innertubeContext ? JSON.stringify({ context: innertubeContext, query: queryValue }) : path4.includes("browse") && innertubeContext ? JSON.stringify({ context: innertubeContext, browseId: "FEmusic_liked_videos" }) : JSON.stringify({ context: innertubeContext ?? {} });
|
|
6361
|
+
harEntries.push({
|
|
6362
|
+
startedDateTime: new Date().toISOString(),
|
|
6363
|
+
request: { method: "POST", url: syntheticUrl, headers: [{ name: "content-type", value: "application/json" }], postData: { text: postBody } },
|
|
6364
|
+
response: { status: 200, headers: [{ name: "content-type", value: "application/json" }], content: { text: bodyStr, mimeType: "application/json" } }
|
|
6365
|
+
});
|
|
6366
|
+
embeddedDataCount++;
|
|
6367
|
+
log("capture", `ssr:${extractor.name} extracted ${path4} (${bodyStr.length}B)`);
|
|
6368
|
+
}
|
|
6369
|
+
} else {
|
|
6370
|
+
if (raw.length < 100)
|
|
6371
|
+
continue;
|
|
6372
|
+
const syntheticUrl = `${new URL(url).origin}/__ssr_data__/${extractor.name}`;
|
|
6373
|
+
responseBodies.set(syntheticUrl, raw);
|
|
6374
|
+
harEntries.push({
|
|
6375
|
+
startedDateTime: new Date().toISOString(),
|
|
6376
|
+
request: { method: "GET", url: syntheticUrl, headers: [] },
|
|
6377
|
+
response: { status: 200, headers: [{ name: "content-type", value: "application/json" }], content: { text: raw, mimeType: "application/json" } }
|
|
6378
|
+
});
|
|
6379
|
+
embeddedDataCount++;
|
|
6380
|
+
log("capture", `ssr:${extractor.name} extracted (${raw.length}B)`);
|
|
6381
|
+
}
|
|
6382
|
+
} catch {}
|
|
6383
|
+
}
|
|
6384
|
+
if (embeddedDataCount > 0) {
|
|
6385
|
+
log("capture", `embedded SSR data: ${embeddedDataCount} synthetic endpoints injected`);
|
|
6386
|
+
}
|
|
5990
6387
|
const requests = mergePassiveCaptureData(intercepted, harEntries, extensionEntries, responseBodies, performanceUrls);
|
|
5991
6388
|
log("capture", `tracked ${harEntries.length} HAR, ${intercepted.length} intercepted, ${extensionEntries.length} extension, ${responseBodies.size} bodies → ${requests.length} merged`);
|
|
5992
6389
|
const rawCookies = await phase("extractCookies", () => extractCookiesFromPage(tabId, url));
|
|
@@ -6385,7 +6782,8 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6385
6782
|
var isData = ct.indexOf('json') !== -1 || ct.indexOf('application/x-protobuf') !== -1 ||
|
|
6386
6783
|
ct.indexOf('text/plain') !== -1 ||
|
|
6387
6784
|
url.indexOf('batchexecute') !== -1 || url.indexOf('/api/') !== -1 ||
|
|
6388
|
-
url.indexOf('graphql') !== -1 || url.indexOf('voyager') !== -1
|
|
6785
|
+
url.indexOf('graphql') !== -1 || url.indexOf('voyager') !== -1 ||
|
|
6786
|
+
url.indexOf('youtubei') !== -1;
|
|
6389
6787
|
if (!isJs && !isData) return response;
|
|
6390
6788
|
if (/\\.(css|woff2?|png|jpg|svg|ico)(\\?|$)/.test(url)) return response;
|
|
6391
6789
|
var clone = response.clone();
|
|
@@ -6435,7 +6833,8 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6435
6833
|
var isData = ct.indexOf('json') !== -1 || ct.indexOf('application/x-protobuf') !== -1 ||
|
|
6436
6834
|
ct.indexOf('text/plain') !== -1 ||
|
|
6437
6835
|
url.indexOf('batchexecute') !== -1 || url.indexOf('/api/') !== -1 ||
|
|
6438
|
-
url.indexOf('graphql') !== -1 || url.indexOf('voyager') !== -1
|
|
6836
|
+
url.indexOf('graphql') !== -1 || url.indexOf('voyager') !== -1 ||
|
|
6837
|
+
url.indexOf('youtubei') !== -1;
|
|
6439
6838
|
if (!isJs && !isData) return;
|
|
6440
6839
|
if (/\\.(css|woff2?|png|jpg|svg|ico)(\\?|$)/.test(url)) return;
|
|
6441
6840
|
var respBody = xhr.responseText || '';
|
|
@@ -6457,11 +6856,13 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
|
|
|
6457
6856
|
return origSend.apply(this, arguments);
|
|
6458
6857
|
};
|
|
6459
6858
|
})()`, REPLAY_SKIP, HAR_REPLAY_CT;
|
|
6460
|
-
var init_capture = __esm(() => {
|
|
6859
|
+
var init_capture = __esm(async () => {
|
|
6461
6860
|
init_client();
|
|
6462
6861
|
init_domain();
|
|
6463
6862
|
init_logger();
|
|
6863
|
+
init_reverse_engineer();
|
|
6464
6864
|
init_browser_access();
|
|
6865
|
+
await init_vault();
|
|
6465
6866
|
waitQueue = [];
|
|
6466
6867
|
activeTabRegistry = new Set;
|
|
6467
6868
|
interceptorInjectedTabs = new Set;
|
|
@@ -6478,17 +6879,17 @@ var init_capture = __esm(() => {
|
|
|
6478
6879
|
});
|
|
6479
6880
|
|
|
6480
6881
|
// ../../src/build-info.generated.ts
|
|
6481
|
-
var BUILD_RELEASE_VERSION = "3.2.
|
|
6882
|
+
var BUILD_RELEASE_VERSION = "3.2.2-experiments.128ff09", BUILD_GIT_SHA = "128ff09f51b3", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjItZXhwZXJpbWVudHMuMTI4ZmYwOSIsImdpdF9zaGEiOiIxMjhmZjA5ZjUxYjMiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDEyOGZmMDlmNTFiMyIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMTE6NTk6NDYuOTQyWiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "M80x5zV9aWn-teZrpEFDyzwSU53Hwfj6rf19dl9m-jM", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
|
|
6482
6883
|
|
|
6483
6884
|
// ../../src/version.ts
|
|
6484
6885
|
import { createHash } from "crypto";
|
|
6485
|
-
import { existsSync as
|
|
6486
|
-
import { dirname, join as
|
|
6886
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
6887
|
+
import { dirname, join as join3, parse } from "path";
|
|
6487
6888
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6488
6889
|
function collectTsFiles(dir) {
|
|
6489
6890
|
const results = [];
|
|
6490
6891
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
6491
|
-
const full =
|
|
6892
|
+
const full = join3(dir, entry.name);
|
|
6492
6893
|
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
6493
6894
|
results.push(...collectTsFiles(full));
|
|
6494
6895
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -6501,21 +6902,21 @@ function hashFiles(srcDir, files) {
|
|
|
6501
6902
|
const hash = createHash("sha256");
|
|
6502
6903
|
for (const file of files) {
|
|
6503
6904
|
hash.update(file.slice(srcDir.length));
|
|
6504
|
-
hash.update(
|
|
6905
|
+
hash.update(readFileSync2(file, "utf-8"));
|
|
6505
6906
|
}
|
|
6506
6907
|
return hash.digest("hex").slice(0, 12);
|
|
6507
6908
|
}
|
|
6508
6909
|
function resolveCodeHashSourceDir(moduleDir) {
|
|
6509
6910
|
const candidates = [
|
|
6510
6911
|
moduleDir,
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6912
|
+
join3(moduleDir, "runtime-src"),
|
|
6913
|
+
join3(moduleDir, "..", "runtime-src"),
|
|
6914
|
+
join3(moduleDir, "src"),
|
|
6915
|
+
join3(moduleDir, "..", "src")
|
|
6515
6916
|
];
|
|
6516
6917
|
for (const candidate of candidates) {
|
|
6517
6918
|
try {
|
|
6518
|
-
if (!
|
|
6919
|
+
if (!existsSync4(candidate))
|
|
6519
6920
|
continue;
|
|
6520
6921
|
const files = collectTsFiles(candidate);
|
|
6521
6922
|
if (files.length > 0)
|
|
@@ -6565,7 +6966,7 @@ function getPackageVersionForModuleDir(moduleDir) {
|
|
|
6565
6966
|
const root = parse(dir).root;
|
|
6566
6967
|
while (true) {
|
|
6567
6968
|
try {
|
|
6568
|
-
const pkg = JSON.parse(
|
|
6969
|
+
const pkg = JSON.parse(readFileSync2(join3(dir, "package.json"), "utf-8"));
|
|
6569
6970
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
6570
6971
|
} catch {}
|
|
6571
6972
|
if (dir === root)
|
|
@@ -6669,18 +7070,18 @@ async function ensureCascadeSplitForSkill(skill, deps = {}) {
|
|
|
6669
7070
|
var init_cascade = () => {};
|
|
6670
7071
|
|
|
6671
7072
|
// ../../src/payments/wallet.ts
|
|
6672
|
-
import { existsSync as
|
|
6673
|
-
import { homedir } from "node:os";
|
|
6674
|
-
import { join as
|
|
7073
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
7074
|
+
import { homedir as homedir2 } from "node:os";
|
|
7075
|
+
import { join as join4 } from "node:path";
|
|
6675
7076
|
function asNonEmptyString(value) {
|
|
6676
7077
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
6677
7078
|
}
|
|
6678
7079
|
function getLobsterWalletFromLocalConfig() {
|
|
6679
|
-
const agentsPath =
|
|
6680
|
-
if (!
|
|
7080
|
+
const agentsPath = join4(process.env.HOME || homedir2(), ".lobster", "agents.json");
|
|
7081
|
+
if (!existsSync5(agentsPath))
|
|
6681
7082
|
return;
|
|
6682
7083
|
try {
|
|
6683
|
-
const raw = JSON.parse(
|
|
7084
|
+
const raw = JSON.parse(readFileSync3(agentsPath, "utf8"));
|
|
6684
7085
|
const activeAgentId = asNonEmptyString(raw.activeAgentId);
|
|
6685
7086
|
const activeAgent = Array.isArray(raw.agents) ? raw.agents.find((agent) => asNonEmptyString(agent.id) === activeAgentId) : activeAgentId ? raw.agents?.[activeAgentId] : undefined;
|
|
6686
7087
|
return asNonEmptyString(activeAgent?.authorizedWallets?.solana) ?? asNonEmptyString(activeAgent?.walletAddress) ?? asNonEmptyString(activeAgent?.wallet_address);
|
|
@@ -6831,9 +7232,9 @@ __export(exports_lobster_pay, {
|
|
|
6831
7232
|
isLobsterAvailable: () => isLobsterAvailable
|
|
6832
7233
|
});
|
|
6833
7234
|
import { execFile, execFileSync as execFileSync2 } from "node:child_process";
|
|
6834
|
-
import { existsSync as
|
|
6835
|
-
import { homedir as
|
|
6836
|
-
import { join as
|
|
7235
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
7236
|
+
import { homedir as homedir3 } from "node:os";
|
|
7237
|
+
import { join as join5 } from "node:path";
|
|
6837
7238
|
function getLobsterCommand() {
|
|
6838
7239
|
try {
|
|
6839
7240
|
execFileSync2("lobstercash", ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
@@ -6841,8 +7242,8 @@ function getLobsterCommand() {
|
|
|
6841
7242
|
} catch (_e) {}
|
|
6842
7243
|
try {
|
|
6843
7244
|
const npmPrefix = execFileSync2("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
|
|
6844
|
-
const lobsterPath =
|
|
6845
|
-
if (
|
|
7245
|
+
const lobsterPath = join5(npmPrefix, "bin", "lobstercash");
|
|
7246
|
+
if (existsSync6(lobsterPath)) {
|
|
6846
7247
|
execFileSync2(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
|
|
6847
7248
|
return { cmd: lobsterPath, prefix: [] };
|
|
6848
7249
|
}
|
|
@@ -6855,8 +7256,8 @@ function lobsterCmd() {
|
|
|
6855
7256
|
return cachedCommand;
|
|
6856
7257
|
}
|
|
6857
7258
|
function isLobsterAvailable() {
|
|
6858
|
-
const agentsPath =
|
|
6859
|
-
return
|
|
7259
|
+
const agentsPath = join5(process.env.HOME || homedir3(), ".lobster", "agents.json");
|
|
7260
|
+
return existsSync6(agentsPath);
|
|
6860
7261
|
}
|
|
6861
7262
|
function lobsterX402Fetch(url, options) {
|
|
6862
7263
|
return new Promise((resolve) => {
|
|
@@ -6988,10 +7389,10 @@ __export(exports_client2, {
|
|
|
6988
7389
|
buildDefaultAgentName: () => buildDefaultAgentName,
|
|
6989
7390
|
autoFileIssue: () => autoFileIssue
|
|
6990
7391
|
});
|
|
6991
|
-
import { readFileSync as
|
|
6992
|
-
import { join as
|
|
6993
|
-
import { homedir as
|
|
6994
|
-
import { randomBytes, createHash as createHash3 } from "crypto";
|
|
7392
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
|
|
7393
|
+
import { join as join6 } from "path";
|
|
7394
|
+
import { homedir as homedir4, hostname, release as osRelease } from "os";
|
|
7395
|
+
import { randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
|
|
6995
7396
|
import { createInterface } from "readline";
|
|
6996
7397
|
function buildReleaseAttestationHeaders(manifestBase64, signature) {
|
|
6997
7398
|
const manifest = manifestBase64.trim();
|
|
@@ -7025,18 +7426,18 @@ function scopedSkillKey(skillId, scopeId) {
|
|
|
7025
7426
|
return scopeId ? `${scopeId}:${skillId}` : skillId;
|
|
7026
7427
|
}
|
|
7027
7428
|
function getSkillCacheDir() {
|
|
7028
|
-
return process.env.UNBROWSE_SKILL_CACHE_DIR ||
|
|
7429
|
+
return process.env.UNBROWSE_SKILL_CACHE_DIR || join6(getConfigDir(), "skill-cache");
|
|
7029
7430
|
}
|
|
7030
7431
|
function getConfigDir() {
|
|
7031
7432
|
if (process.env.UNBROWSE_CONFIG_DIR)
|
|
7032
7433
|
return process.env.UNBROWSE_CONFIG_DIR;
|
|
7033
|
-
return PROFILE_NAME ?
|
|
7434
|
+
return PROFILE_NAME ? join6(homedir4(), ".unbrowse", "profiles", PROFILE_NAME) : join6(homedir4(), ".unbrowse");
|
|
7034
7435
|
}
|
|
7035
7436
|
function getConfigPath() {
|
|
7036
|
-
return
|
|
7437
|
+
return join6(getConfigDir(), "config.json");
|
|
7037
7438
|
}
|
|
7038
7439
|
function getInstallTelemetryPath() {
|
|
7039
|
-
return
|
|
7440
|
+
return join6(getConfigDir(), "install-state.json");
|
|
7040
7441
|
}
|
|
7041
7442
|
function getLandingToken() {
|
|
7042
7443
|
const token = process.env.UNBROWSE_LANDING_TOKEN?.trim();
|
|
@@ -7054,8 +7455,8 @@ function isLocalOnlyMode() {
|
|
|
7054
7455
|
function loadConfig() {
|
|
7055
7456
|
try {
|
|
7056
7457
|
const configPath = getConfigPath();
|
|
7057
|
-
if (
|
|
7058
|
-
return JSON.parse(
|
|
7458
|
+
if (existsSync7(configPath)) {
|
|
7459
|
+
return JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
7059
7460
|
}
|
|
7060
7461
|
} catch {}
|
|
7061
7462
|
return null;
|
|
@@ -7063,15 +7464,15 @@ function loadConfig() {
|
|
|
7063
7464
|
function saveConfig(config) {
|
|
7064
7465
|
const configDir = getConfigDir();
|
|
7065
7466
|
const configPath = getConfigPath();
|
|
7066
|
-
if (!
|
|
7067
|
-
|
|
7068
|
-
|
|
7467
|
+
if (!existsSync7(configDir))
|
|
7468
|
+
mkdirSync4(configDir, { recursive: true });
|
|
7469
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
7069
7470
|
}
|
|
7070
7471
|
function loadInstallTelemetryState() {
|
|
7071
7472
|
try {
|
|
7072
7473
|
const statePath = getInstallTelemetryPath();
|
|
7073
|
-
if (
|
|
7074
|
-
return JSON.parse(
|
|
7474
|
+
if (existsSync7(statePath)) {
|
|
7475
|
+
return JSON.parse(readFileSync4(statePath, "utf-8"));
|
|
7075
7476
|
}
|
|
7076
7477
|
} catch {}
|
|
7077
7478
|
return null;
|
|
@@ -7079,13 +7480,13 @@ function loadInstallTelemetryState() {
|
|
|
7079
7480
|
function saveInstallTelemetryState(state) {
|
|
7080
7481
|
const configDir = getConfigDir();
|
|
7081
7482
|
const statePath = getInstallTelemetryPath();
|
|
7082
|
-
if (!
|
|
7083
|
-
|
|
7084
|
-
|
|
7483
|
+
if (!existsSync7(configDir))
|
|
7484
|
+
mkdirSync4(configDir, { recursive: true });
|
|
7485
|
+
writeFileSync3(statePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
7085
7486
|
}
|
|
7086
7487
|
function createInstallTelemetryState() {
|
|
7087
7488
|
return {
|
|
7088
|
-
install_id: `install_${
|
|
7489
|
+
install_id: `install_${randomBytes2(8).toString("hex")}`,
|
|
7089
7490
|
first_seen_at: new Date().toISOString()
|
|
7090
7491
|
};
|
|
7091
7492
|
}
|
|
@@ -7225,7 +7626,7 @@ function isValidAgentEmail(value) {
|
|
|
7225
7626
|
return EMAIL_RE.test(normalizeAgentEmail(value));
|
|
7226
7627
|
}
|
|
7227
7628
|
function buildDefaultAgentName() {
|
|
7228
|
-
return `${hostname()}-${
|
|
7629
|
+
return `${hostname()}-${randomBytes2(3).toString("hex")}`;
|
|
7229
7630
|
}
|
|
7230
7631
|
function resolveAgentName(preferredEmail, fallbackName) {
|
|
7231
7632
|
const normalized = normalizeAgentEmail(preferredEmail ?? "");
|
|
@@ -7580,11 +7981,11 @@ async function waitForBackgroundRegistration(timeoutMs = 0) {
|
|
|
7580
7981
|
]);
|
|
7581
7982
|
}
|
|
7582
7983
|
function skillCachePath(skillId) {
|
|
7583
|
-
return
|
|
7984
|
+
return join6(getSkillCacheDir(), `${skillId}.json`);
|
|
7584
7985
|
}
|
|
7585
7986
|
function readSkillCache(skillId) {
|
|
7586
7987
|
try {
|
|
7587
|
-
const raw =
|
|
7988
|
+
const raw = readFileSync4(skillCachePath(skillId), "utf-8");
|
|
7588
7989
|
return JSON.parse(raw);
|
|
7589
7990
|
} catch {
|
|
7590
7991
|
return null;
|
|
@@ -7594,8 +7995,8 @@ function writeSkillCache(skill, scopeId) {
|
|
|
7594
7995
|
try {
|
|
7595
7996
|
recentLocalSkills.set(scopedSkillKey(skill.skill_id, scopeId), skill);
|
|
7596
7997
|
const skillCacheDir = getSkillCacheDir();
|
|
7597
|
-
if (!
|
|
7598
|
-
|
|
7998
|
+
if (!existsSync7(skillCacheDir))
|
|
7999
|
+
mkdirSync4(skillCacheDir, { recursive: true });
|
|
7599
8000
|
const existing = readSkillCache(skill.skill_id);
|
|
7600
8001
|
if (existing) {
|
|
7601
8002
|
for (const ep of skill.endpoints) {
|
|
@@ -7611,7 +8012,7 @@ function writeSkillCache(skill, scopeId) {
|
|
|
7611
8012
|
const hasStrategy = skill.endpoints.some((e) => e.exec_strategy);
|
|
7612
8013
|
if (hasStrategy)
|
|
7613
8014
|
console.log(`[cache] writing skill ${skill.skill_id} with exec_strategy`);
|
|
7614
|
-
|
|
8015
|
+
writeFileSync3(skillCachePath(skill.skill_id), JSON.stringify(skill), "utf-8");
|
|
7615
8016
|
} catch {}
|
|
7616
8017
|
}
|
|
7617
8018
|
function cachePublishedSkill(skill, scopeId) {
|
|
@@ -7646,7 +8047,7 @@ function isIntentCompatible(lhs, rhs) {
|
|
|
7646
8047
|
function findExistingSkillForDomain(domain, intent) {
|
|
7647
8048
|
try {
|
|
7648
8049
|
const skillCacheDir = getSkillCacheDir();
|
|
7649
|
-
if (!
|
|
8050
|
+
if (!existsSync7(skillCacheDir))
|
|
7650
8051
|
return null;
|
|
7651
8052
|
const files = readdirSync2(skillCacheDir);
|
|
7652
8053
|
let compatible = null;
|
|
@@ -7655,7 +8056,7 @@ function findExistingSkillForDomain(domain, intent) {
|
|
|
7655
8056
|
if (!f.endsWith(".json") || f === "browser-capture.json")
|
|
7656
8057
|
continue;
|
|
7657
8058
|
try {
|
|
7658
|
-
const raw =
|
|
8059
|
+
const raw = readFileSync4(join6(skillCacheDir, f), "utf-8");
|
|
7659
8060
|
const skill = JSON.parse(raw);
|
|
7660
8061
|
if (skill.domain === domain && skill.execution_type === "http") {
|
|
7661
8062
|
if (!fallback)
|
|
@@ -7705,11 +8106,11 @@ async function getSkillChunk2(skillId, opts) {
|
|
|
7705
8106
|
async function listSkills() {
|
|
7706
8107
|
if (LOCAL_ONLY) {
|
|
7707
8108
|
try {
|
|
7708
|
-
if (!
|
|
8109
|
+
if (!existsSync7(SKILL_CACHE_DIR))
|
|
7709
8110
|
return [];
|
|
7710
8111
|
return readdirSync2(SKILL_CACHE_DIR).filter((file) => file.endsWith(".json")).map((file) => {
|
|
7711
8112
|
try {
|
|
7712
|
-
return JSON.parse(
|
|
8113
|
+
return JSON.parse(readFileSync4(join6(SKILL_CACHE_DIR, file), "utf-8"));
|
|
7713
8114
|
} catch {
|
|
7714
8115
|
return null;
|
|
7715
8116
|
}
|
|
@@ -8401,10 +8802,10 @@ var init_publish_admission = __esm(() => {
|
|
|
8401
8802
|
|
|
8402
8803
|
// ../../src/telemetry.ts
|
|
8403
8804
|
import { createHash as createHash4 } from "node:crypto";
|
|
8404
|
-
import { existsSync as
|
|
8405
|
-
import { join as
|
|
8805
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8806
|
+
import { join as join7 } from "node:path";
|
|
8406
8807
|
function getTraceDir() {
|
|
8407
|
-
return process.env.UNBROWSE_TRACES_DIR ??
|
|
8808
|
+
return process.env.UNBROWSE_TRACES_DIR ?? join7(process.env.HOME ?? "/tmp", ".unbrowse", "traces");
|
|
8408
8809
|
}
|
|
8409
8810
|
function isTracingEnabled() {
|
|
8410
8811
|
return process.env.UNBROWSE_DISABLE_TRACES !== "1";
|
|
@@ -8478,11 +8879,11 @@ function emitRouteTrace(params) {
|
|
|
8478
8879
|
};
|
|
8479
8880
|
try {
|
|
8480
8881
|
const traceDir = getTraceDir();
|
|
8481
|
-
if (!
|
|
8482
|
-
|
|
8882
|
+
if (!existsSync8(traceDir))
|
|
8883
|
+
mkdirSync5(traceDir, { recursive: true });
|
|
8483
8884
|
const stamp = params.started_at.replace(/[:.]/g, "-");
|
|
8484
|
-
const file =
|
|
8485
|
-
|
|
8885
|
+
const file = join7(traceDir, `${stamp}-${params.outcome}-${params.trace_id}.json`);
|
|
8886
|
+
writeFileSync4(file, JSON.stringify(artifact, null, 2), "utf-8");
|
|
8486
8887
|
return file;
|
|
8487
8888
|
} catch {
|
|
8488
8889
|
return null;
|
|
@@ -9118,161 +9519,6 @@ var init_token_resolver = __esm(() => {
|
|
|
9118
9519
|
init_token_sources();
|
|
9119
9520
|
});
|
|
9120
9521
|
|
|
9121
|
-
// ../../src/vault/index.ts
|
|
9122
|
-
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
|
|
9123
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
9124
|
-
import { join as join7 } from "path";
|
|
9125
|
-
import { homedir as homedir4 } from "os";
|
|
9126
|
-
function normalizeKeytarModule(mod) {
|
|
9127
|
-
let candidate = mod;
|
|
9128
|
-
for (let depth = 0;depth < 3; depth++) {
|
|
9129
|
-
if (!candidate || typeof candidate !== "object" || !("default" in candidate))
|
|
9130
|
-
break;
|
|
9131
|
-
candidate = candidate.default;
|
|
9132
|
-
}
|
|
9133
|
-
if (!candidate)
|
|
9134
|
-
return null;
|
|
9135
|
-
if (typeof candidate.setPassword === "function" && typeof candidate.getPassword === "function" && typeof candidate.deletePassword === "function") {
|
|
9136
|
-
return candidate;
|
|
9137
|
-
}
|
|
9138
|
-
return null;
|
|
9139
|
-
}
|
|
9140
|
-
function isKeytarBindingError(error) {
|
|
9141
|
-
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
9142
|
-
return KEYTAR_BINDING_ERROR_RE.test(message);
|
|
9143
|
-
}
|
|
9144
|
-
function disableKeytar(error) {
|
|
9145
|
-
keytar = null;
|
|
9146
|
-
if (keytarFallbackLogged)
|
|
9147
|
-
return;
|
|
9148
|
-
const summary = error instanceof Error ? error.message : String(error);
|
|
9149
|
-
log("vault", `keytar runtime unavailable (${summary}); using encrypted file fallback`);
|
|
9150
|
-
keytarFallbackLogged = true;
|
|
9151
|
-
}
|
|
9152
|
-
async function callKeytar(op) {
|
|
9153
|
-
if (!keytar)
|
|
9154
|
-
return KEYTAR_UNAVAILABLE;
|
|
9155
|
-
try {
|
|
9156
|
-
return await op(keytar);
|
|
9157
|
-
} catch (error) {
|
|
9158
|
-
if (!isKeytarBindingError(error))
|
|
9159
|
-
throw error;
|
|
9160
|
-
disableKeytar(error);
|
|
9161
|
-
return KEYTAR_UNAVAILABLE;
|
|
9162
|
-
}
|
|
9163
|
-
}
|
|
9164
|
-
function getOrCreateKey() {
|
|
9165
|
-
if (!existsSync8(VAULT_DIR))
|
|
9166
|
-
mkdirSync5(VAULT_DIR, { recursive: true, mode: 448 });
|
|
9167
|
-
if (existsSync8(KEY_FILE))
|
|
9168
|
-
return readFileSync4(KEY_FILE);
|
|
9169
|
-
const key = randomBytes2(32);
|
|
9170
|
-
writeFileSync4(KEY_FILE, key, { mode: 384 });
|
|
9171
|
-
return key;
|
|
9172
|
-
}
|
|
9173
|
-
function withVaultLock(fn) {
|
|
9174
|
-
const prev = vaultLock;
|
|
9175
|
-
let release;
|
|
9176
|
-
vaultLock = new Promise((r) => {
|
|
9177
|
-
release = r;
|
|
9178
|
-
});
|
|
9179
|
-
return prev.then(fn).finally(() => release());
|
|
9180
|
-
}
|
|
9181
|
-
function readVaultFile() {
|
|
9182
|
-
if (!existsSync8(VAULT_FILE))
|
|
9183
|
-
return {};
|
|
9184
|
-
try {
|
|
9185
|
-
const key = getOrCreateKey();
|
|
9186
|
-
const raw = readFileSync4(VAULT_FILE);
|
|
9187
|
-
const iv = raw.subarray(0, 16);
|
|
9188
|
-
const enc = raw.subarray(16);
|
|
9189
|
-
const decipher = createDecipheriv("aes-256-cbc", key, iv);
|
|
9190
|
-
const dec = Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
9191
|
-
return JSON.parse(dec.toString("utf8"));
|
|
9192
|
-
} catch {
|
|
9193
|
-
return {};
|
|
9194
|
-
}
|
|
9195
|
-
}
|
|
9196
|
-
function writeVaultFile(data) {
|
|
9197
|
-
const key = getOrCreateKey();
|
|
9198
|
-
const iv = randomBytes2(16);
|
|
9199
|
-
const cipher = createCipheriv("aes-256-cbc", key, iv);
|
|
9200
|
-
const enc = Buffer.concat([cipher.update(JSON.stringify(data), "utf8"), cipher.final()]);
|
|
9201
|
-
writeFileSync4(VAULT_FILE, Buffer.concat([iv, enc]), { mode: 384 });
|
|
9202
|
-
}
|
|
9203
|
-
async function storeCredential(account, value, opts) {
|
|
9204
|
-
const wrapped = {
|
|
9205
|
-
value,
|
|
9206
|
-
stored_at: new Date().toISOString(),
|
|
9207
|
-
expires_at: opts?.expires_at,
|
|
9208
|
-
max_age_ms: opts?.max_age_ms
|
|
9209
|
-
};
|
|
9210
|
-
const serialized = JSON.stringify(wrapped);
|
|
9211
|
-
const keytarResult = await callKeytar((client) => client.setPassword(SERVICE, account, serialized));
|
|
9212
|
-
if (keytarResult !== KEYTAR_UNAVAILABLE)
|
|
9213
|
-
return;
|
|
9214
|
-
await withVaultLock(() => {
|
|
9215
|
-
const data = readVaultFile();
|
|
9216
|
-
data[account] = serialized;
|
|
9217
|
-
writeVaultFile(data);
|
|
9218
|
-
});
|
|
9219
|
-
}
|
|
9220
|
-
function isExpired(cred) {
|
|
9221
|
-
if (cred.expires_at) {
|
|
9222
|
-
return new Date(cred.expires_at).getTime() <= Date.now();
|
|
9223
|
-
}
|
|
9224
|
-
if (cred.max_age_ms) {
|
|
9225
|
-
return new Date(cred.stored_at).getTime() + cred.max_age_ms <= Date.now();
|
|
9226
|
-
}
|
|
9227
|
-
return false;
|
|
9228
|
-
}
|
|
9229
|
-
async function getCredential(account) {
|
|
9230
|
-
let raw;
|
|
9231
|
-
const keytarResult = await callKeytar((client) => client.getPassword(SERVICE, account));
|
|
9232
|
-
if (keytarResult !== KEYTAR_UNAVAILABLE) {
|
|
9233
|
-
raw = keytarResult;
|
|
9234
|
-
} else {
|
|
9235
|
-
const data = readVaultFile();
|
|
9236
|
-
raw = data[account] ?? null;
|
|
9237
|
-
}
|
|
9238
|
-
if (!raw)
|
|
9239
|
-
return null;
|
|
9240
|
-
try {
|
|
9241
|
-
const parsed = JSON.parse(raw);
|
|
9242
|
-
if (parsed.value && parsed.stored_at) {
|
|
9243
|
-
if (isExpired(parsed)) {
|
|
9244
|
-
await deleteCredential(account);
|
|
9245
|
-
return null;
|
|
9246
|
-
}
|
|
9247
|
-
return parsed.value;
|
|
9248
|
-
}
|
|
9249
|
-
} catch {}
|
|
9250
|
-
return raw;
|
|
9251
|
-
}
|
|
9252
|
-
async function deleteCredential(account) {
|
|
9253
|
-
const keytarResult = await callKeytar((client) => client.deletePassword(SERVICE, account));
|
|
9254
|
-
if (keytarResult !== KEYTAR_UNAVAILABLE)
|
|
9255
|
-
return;
|
|
9256
|
-
await withVaultLock(() => {
|
|
9257
|
-
const data = readVaultFile();
|
|
9258
|
-
delete data[account];
|
|
9259
|
-
writeVaultFile(data);
|
|
9260
|
-
});
|
|
9261
|
-
}
|
|
9262
|
-
var KEYTAR_UNAVAILABLE, KEYTAR_BINDING_ERROR_RE, keytar = null, keytarFallbackLogged = false, SERVICE = "unbrowse", VAULT_DIR, VAULT_FILE, KEY_FILE, vaultLock;
|
|
9263
|
-
var init_vault = __esm(async () => {
|
|
9264
|
-
init_logger();
|
|
9265
|
-
KEYTAR_UNAVAILABLE = Symbol("KEYTAR_UNAVAILABLE");
|
|
9266
|
-
KEYTAR_BINDING_ERROR_RE = /(keytar(?:\.node)?|native bindings?|bindings file|no native build was found|could not locate the bindings file|module did not self-register|err_dlopen_failed|dlopen\(|compiled against a different node\.js version|cannot find module .*keytar|wasm is not supported on this platform|(set|get|delete)password is not a function)/i;
|
|
9267
|
-
try {
|
|
9268
|
-
keytar = normalizeKeytarModule(await import("keytar"));
|
|
9269
|
-
} catch {}
|
|
9270
|
-
VAULT_DIR = join7(homedir4(), ".unbrowse", "vault");
|
|
9271
|
-
VAULT_FILE = join7(VAULT_DIR, "credentials.enc");
|
|
9272
|
-
KEY_FILE = join7(VAULT_DIR, ".key");
|
|
9273
|
-
vaultLock = Promise.resolve();
|
|
9274
|
-
});
|
|
9275
|
-
|
|
9276
9522
|
// ../../src/runtime/supervisor.ts
|
|
9277
9523
|
function getDefaultLoginConfig(headless) {
|
|
9278
9524
|
return {
|
|
@@ -13944,6 +14190,10 @@ function validateWorkflowReplayParams(recipe, params) {
|
|
|
13944
14190
|
continue;
|
|
13945
14191
|
const value = valueForSpec(spec, params);
|
|
13946
14192
|
if (spec.required && (value == null || value === "")) {
|
|
14193
|
+
if (spec.default_value != null && spec.default_value !== "")
|
|
14194
|
+
continue;
|
|
14195
|
+
if (spec.example_value != null && spec.example_value !== "")
|
|
14196
|
+
continue;
|
|
13947
14197
|
errors.push({ name: spec.name, reason: "required" });
|
|
13948
14198
|
continue;
|
|
13949
14199
|
}
|
|
@@ -14238,18 +14488,27 @@ async function reloadExecutionAuthState(skill, epDomain, authHeaders, cookies) {
|
|
|
14238
14488
|
cookies.push(...resolved);
|
|
14239
14489
|
} catch {}
|
|
14240
14490
|
}
|
|
14241
|
-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
|
|
14245
|
-
|
|
14246
|
-
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14491
|
+
{
|
|
14492
|
+
for (const sessionKey of [`${epDomain}-session`, `${getRegistrableDomain(epDomain)}-session`]) {
|
|
14493
|
+
try {
|
|
14494
|
+
const sessionData = await getCredential(sessionKey);
|
|
14495
|
+
if (sessionData) {
|
|
14496
|
+
const parsed = JSON.parse(sessionData);
|
|
14497
|
+
if (parsed.headers)
|
|
14498
|
+
Object.assign(authHeaders, parsed.headers);
|
|
14499
|
+
if (parsed.cookies && cookies.length === 0)
|
|
14500
|
+
cookies.push(...parsed.cookies);
|
|
14501
|
+
if (Object.keys(authHeaders).length > 0)
|
|
14502
|
+
break;
|
|
14503
|
+
}
|
|
14504
|
+
} catch {}
|
|
14505
|
+
}
|
|
14506
|
+
}
|
|
14507
|
+
if (cookies.length > 0 && authHeaders["csrf-token"]) {
|
|
14508
|
+
const jsessionId = cookies.find((c) => c.name === "JSESSIONID");
|
|
14509
|
+
if (jsessionId) {
|
|
14510
|
+
authHeaders["csrf-token"] = jsessionId.value.replace(/"/g, "");
|
|
14511
|
+
}
|
|
14253
14512
|
}
|
|
14254
14513
|
}
|
|
14255
14514
|
function persistWorkflowArtifactForCapture(artifactSkill, captured, capturedAuthHeaders) {
|
|
@@ -15270,10 +15529,13 @@ async function executeBrowserCapture(skill, params, options) {
|
|
|
15270
15529
|
}));
|
|
15271
15530
|
}
|
|
15272
15531
|
if (!auth_profile_ref) {
|
|
15273
|
-
const vaultKey
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15532
|
+
for (const vaultKey of [`auth:${targetDomain}`, `${domain}-session`, `${targetDomain}-session`]) {
|
|
15533
|
+
const hasStoredAuth = await getCredential(vaultKey) != null;
|
|
15534
|
+
if (hasStoredAuth) {
|
|
15535
|
+
auth_profile_ref = vaultKey;
|
|
15536
|
+
break;
|
|
15537
|
+
}
|
|
15538
|
+
}
|
|
15277
15539
|
}
|
|
15278
15540
|
const authBackedCapture = usedStoredAuth || !!auth_profile_ref;
|
|
15279
15541
|
if (authBackedCapture) {
|
|
@@ -15854,7 +16116,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
15854
16116
|
u.searchParams.set(k, String(v));
|
|
15855
16117
|
}
|
|
15856
16118
|
}
|
|
15857
|
-
urlTemplate = restoreTemplatePlaceholderEncoding(u.toString());
|
|
16119
|
+
urlTemplate = restoreTemplatePlaceholderEncoding(u.toString()).replace(/%28/gi, "(").replace(/%29/gi, ")").replace(/%2C/gi, ",").replace(/%3A/gi, ":");
|
|
15858
16120
|
} catch {}
|
|
15859
16121
|
}
|
|
15860
16122
|
let url = interpolate(urlTemplate, mergedParams);
|
|
@@ -15990,7 +16252,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
15990
16252
|
let last = { data: null, status: 0 };
|
|
15991
16253
|
for (const replayUrl of replayUrls) {
|
|
15992
16254
|
const replayHeaders = buildStructuredReplayHeaders(url, replayUrl, headers);
|
|
15993
|
-
log("exec", `server-fetch: ${endpoint.method} ${replayUrl.substring(0,
|
|
16255
|
+
log("exec", `server-fetch: ${endpoint.method} ${replayUrl.substring(0, 200)} csrf-token=${(replayHeaders["csrf-token"] || "none").substring(0, 20)}... hdrs=${Object.keys(replayHeaders).length} cookies=${replayHeaders["cookie"]?.length ?? 0}chars`);
|
|
15994
16256
|
const res = await fetch(replayUrl, {
|
|
15995
16257
|
method: endpoint.method,
|
|
15996
16258
|
headers: replayHeaders,
|
|
@@ -16394,7 +16656,12 @@ function interpolate(template, params) {
|
|
|
16394
16656
|
const base = template.substring(0, qIdx);
|
|
16395
16657
|
const query = template.substring(qIdx + 1);
|
|
16396
16658
|
const interpolatedBase = base.replace(/\{(\w+)\}/g, (_, k) => params[k] != null ? String(params[k]) : `{${k}}`);
|
|
16397
|
-
const interpolatedQuery = query.replace(/\{(\w+)\}/g, (_, k) =>
|
|
16659
|
+
const interpolatedQuery = query.replace(/\{(\w+)\}/g, (_, k) => {
|
|
16660
|
+
if (params[k] == null)
|
|
16661
|
+
return `{${k}}`;
|
|
16662
|
+
const val = String(params[k]);
|
|
16663
|
+
return val.replace(/[#&=\s]/g, (ch) => encodeURIComponent(ch));
|
|
16664
|
+
});
|
|
16398
16665
|
return `${interpolatedBase}?${interpolatedQuery}`;
|
|
16399
16666
|
}
|
|
16400
16667
|
function interpolateObj(obj, params) {
|
|
@@ -16709,6 +16976,17 @@ function rankEndpoints(endpoints, intent, skillDomain, contextUrl) {
|
|
|
16709
16976
|
}
|
|
16710
16977
|
score += matches * 100;
|
|
16711
16978
|
}
|
|
16979
|
+
if (rawTokens.length > 0 && pathname) {
|
|
16980
|
+
const pathLower = pathname.toLowerCase();
|
|
16981
|
+
const pathSegs = pathLower.split("/").filter(Boolean);
|
|
16982
|
+
for (const token of rawTokens) {
|
|
16983
|
+
const stemmed = stem(token);
|
|
16984
|
+
if (pathSegs.some((seg) => seg === stemmed || seg === token || seg.includes(token))) {
|
|
16985
|
+
score += 150;
|
|
16986
|
+
break;
|
|
16987
|
+
}
|
|
16988
|
+
}
|
|
16989
|
+
}
|
|
16712
16990
|
if (ep.dom_extraction)
|
|
16713
16991
|
score += 25;
|
|
16714
16992
|
if (descriptionMeta.needs_review && ep.dom_extraction)
|
|
@@ -16943,8 +17221,6 @@ function isSpaShell(html) {
|
|
|
16943
17221
|
}
|
|
16944
17222
|
var DEFAULT_BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36", VALID_VERIFICATION_STATUSES, BM25_K1 = 1.2, BM25_B = 0.75, STOPWORDS, SYNONYMS;
|
|
16945
17223
|
var init_execution = __esm(async () => {
|
|
16946
|
-
init_capture();
|
|
16947
|
-
init_capture();
|
|
16948
17224
|
init_reverse_engineer();
|
|
16949
17225
|
init_bundle_scanner();
|
|
16950
17226
|
init_token_resolver();
|
|
@@ -16968,6 +17244,8 @@ var init_execution = __esm(async () => {
|
|
|
16968
17244
|
init_compile();
|
|
16969
17245
|
init_publish();
|
|
16970
17246
|
await __promiseAll([
|
|
17247
|
+
init_capture(),
|
|
17248
|
+
init_capture(),
|
|
16971
17249
|
init_vault(),
|
|
16972
17250
|
init_auth(),
|
|
16973
17251
|
init_indexer(),
|
|
@@ -18568,6 +18846,13 @@ function isCachedSkillRelevantForIntent(skill, intent, contextUrl) {
|
|
|
18568
18846
|
const hasStructuredSearchEndpoint = candidateSkill.endpoints.some((endpoint) => endpointHasSearchBindings(endpoint) && (!!endpoint.dom_extraction || !!endpoint.response_schema) && endpointMatchesContextOrigin(endpoint, contextUrl) && endpointMatchesExplicitSearchContext(endpoint, contextUrl));
|
|
18569
18847
|
if (hasStructuredSearchEndpoint)
|
|
18570
18848
|
return true;
|
|
18849
|
+
if (top && top.score >= 0) {
|
|
18850
|
+
try {
|
|
18851
|
+
const topPath = new URL(top.endpoint.url_template).pathname.toLowerCase();
|
|
18852
|
+
if (/\/(search|find|query|browse|explore)\b/.test(topPath))
|
|
18853
|
+
return true;
|
|
18854
|
+
} catch {}
|
|
18855
|
+
}
|
|
18571
18856
|
if (collectExplicitSearchContextBindingKeys(contextUrl).size > 0)
|
|
18572
18857
|
return false;
|
|
18573
18858
|
}
|
|
@@ -19610,7 +19895,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
19610
19895
|
for (const cookie of cookies)
|
|
19611
19896
|
await setCookie(handoffTabId, cookie).catch(() => {});
|
|
19612
19897
|
} catch {}
|
|
19613
|
-
await evaluate(handoffTabId, (await
|
|
19898
|
+
await evaluate(handoffTabId, (await init_capture().then(() => exports_capture)).INTERCEPTOR_SCRIPT).catch(() => {});
|
|
19614
19899
|
await harStart(handoffTabId).catch(() => {});
|
|
19615
19900
|
try {
|
|
19616
19901
|
const routesModule = await init_routes().then(() => exports_routes);
|
|
@@ -24111,13 +24396,13 @@ function schedulePeriodicVerification() {
|
|
|
24111
24396
|
}, VERIFICATION_INTERVAL_MS);
|
|
24112
24397
|
}
|
|
24113
24398
|
var VERIFICATION_INTERVAL_MS, VERIFY_ENDPOINT_BATCH_SIZE;
|
|
24114
|
-
var init_verification = __esm(() => {
|
|
24115
|
-
init_capture();
|
|
24399
|
+
var init_verification = __esm(async () => {
|
|
24116
24400
|
init_marketplace();
|
|
24117
24401
|
init_marketplace();
|
|
24118
24402
|
init_drift();
|
|
24119
24403
|
init_matrix();
|
|
24120
24404
|
init_candidates();
|
|
24405
|
+
await init_capture();
|
|
24121
24406
|
VERIFICATION_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
24122
24407
|
VERIFY_ENDPOINT_BATCH_SIZE = Math.max(1, Number(process.env.UNBROWSE_VERIFY_ENDPOINT_BATCH_SIZE ?? 3));
|
|
24123
24408
|
});
|
|
@@ -24224,9 +24509,11 @@ function schedulePeriodicStaleCleanup() {
|
|
|
24224
24509
|
var VERIFY_TIMEOUT_MS, CLEANUP_INTERVAL_MS, CLEANUP_BATCH_SIZE, CLEANUP_VERIFY_ENDPOINT_LIMIT;
|
|
24225
24510
|
var init_stale_cleanup_runner = __esm(async () => {
|
|
24226
24511
|
init_marketplace();
|
|
24227
|
-
init_verification();
|
|
24228
24512
|
init_candidates();
|
|
24229
|
-
await
|
|
24513
|
+
await __promiseAll([
|
|
24514
|
+
init_verification(),
|
|
24515
|
+
init_orchestrator()
|
|
24516
|
+
]);
|
|
24230
24517
|
VERIFY_TIMEOUT_MS = Math.max(5000, Number(process.env.UNBROWSE_STALE_VERIFY_TIMEOUT_MS ?? 15000));
|
|
24231
24518
|
CLEANUP_INTERVAL_MS = Math.max(30 * 60 * 1000, Number(process.env.UNBROWSE_STALE_CLEANUP_INTERVAL_MS ?? 6 * 60 * 60 * 1000));
|
|
24232
24519
|
CLEANUP_BATCH_SIZE = Math.max(1, Number(process.env.UNBROWSE_STALE_CLEANUP_BATCH_SIZE ?? 25));
|
|
@@ -25268,7 +25555,7 @@ async function registerRoutes(app) {
|
|
|
25268
25555
|
if (!skill)
|
|
25269
25556
|
return reply.code(404).send({ error: "Skill not found" });
|
|
25270
25557
|
try {
|
|
25271
|
-
const { verifySkill: verifySkill2 } = await
|
|
25558
|
+
const { verifySkill: verifySkill2 } = await init_verification().then(() => exports_verification);
|
|
25272
25559
|
const results = await verifySkill2(skill);
|
|
25273
25560
|
return reply.send({ skill_id, verification: results });
|
|
25274
25561
|
} catch (err) {
|
|
@@ -25861,7 +26148,6 @@ var BETA_API_URL, TRACES_DIR, BROWSE_BROKER_MAX, BROWSE_BROKER_BASE_PORT, browse
|
|
|
25861
26148
|
var init_routes = __esm(async () => {
|
|
25862
26149
|
init_client();
|
|
25863
26150
|
init_reverse_engineer();
|
|
25864
|
-
init_capture();
|
|
25865
26151
|
init_marketplace();
|
|
25866
26152
|
init_graph();
|
|
25867
26153
|
init_client2();
|
|
@@ -25880,6 +26166,7 @@ var init_routes = __esm(async () => {
|
|
|
25880
26166
|
init_schema_review();
|
|
25881
26167
|
init_artifact();
|
|
25882
26168
|
await __promiseAll([
|
|
26169
|
+
init_capture(),
|
|
25883
26170
|
init_indexer(),
|
|
25884
26171
|
init_vault(),
|
|
25885
26172
|
init_orchestrator(),
|
|
@@ -25902,12 +26189,12 @@ import { config as loadEnv } from "dotenv";
|
|
|
25902
26189
|
|
|
25903
26190
|
// ../../src/server.ts
|
|
25904
26191
|
init_ratelimit();
|
|
25905
|
-
init_verification();
|
|
25906
26192
|
init_client2();
|
|
25907
|
-
init_capture();
|
|
25908
26193
|
init_version();
|
|
25909
26194
|
await __promiseAll([
|
|
25910
26195
|
init_routes(),
|
|
26196
|
+
init_verification(),
|
|
26197
|
+
init_capture(),
|
|
25911
26198
|
init_stale_cleanup_runner()
|
|
25912
26199
|
]);
|
|
25913
26200
|
import { execSync as execSync2 } from "node:child_process";
|