unbrowse 3.2.2 → 3.2.3-experiments.5c5b5cc

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 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 = "3.2.2", BUILD_GIT_SHA = "150cce0d751e", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjIiLCJnaXRfc2hhIjoiMTUwY2NlMGQ3NTFlIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0AxNTBjY2UwZDc1MWUiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA4OjQ2OjM5LjIwNVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "ZS7C10DGuwyn1m02shMHyz6cN5UbTHfa8yqnkELg_L8", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "3.2.3-experiments.5c5b5cc", BUILD_GIT_SHA = "5c5b5cc8b8c6", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjMtZXhwZXJpbWVudHMuNWM1YjVjYyIsImdpdF9zaGEiOiI1YzViNWNjOGI4YzYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDVjNWI1Y2M4YjhjNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMTU6MjQ6NDkuMjg4WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "Jq9skS2755Nby4cAonNUHdfnsTUBZmnZv-s6DPuISRo", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -679,23 +679,6 @@ var init_telemetry = __esm(() => {
679
679
  autoFiledKeys = new Set;
680
680
  });
681
681
 
682
- // ../../src/runtime/browser-access.ts
683
- var init_browser_access = () => {};
684
-
685
- // ../../src/capture/index.ts
686
- import { nanoid as nanoid2 } from "nanoid";
687
- var activeTabRegistry, interceptorInjectedTabs, cdpDocStartTabs, cdpCapturedHeaders;
688
- var init_capture = __esm(() => {
689
- init_client2();
690
- init_domain();
691
- init_logger();
692
- init_browser_access();
693
- activeTabRegistry = new Set;
694
- interceptorInjectedTabs = new Set;
695
- cdpDocStartTabs = new Set;
696
- cdpCapturedHeaders = new Map;
697
- });
698
-
699
682
  // ../../src/transform/index.ts
700
683
  var EPHEMERAL_KEYS;
701
684
  var init_transform = __esm(() => {
@@ -704,7 +687,7 @@ var init_transform = __esm(() => {
704
687
 
705
688
  // ../../src/debug-trace.ts
706
689
  import { join as join6 } from "node:path";
707
- import { nanoid as nanoid3 } from "nanoid";
690
+ import { nanoid as nanoid2 } from "nanoid";
708
691
  var TRACE_DIR;
709
692
  var init_debug_trace = __esm(() => {
710
693
  TRACE_DIR = process.env.TRACES_DIR ?? join6(process.cwd(), "traces");
@@ -719,7 +702,7 @@ var init_description_prompt = __esm(() => {
719
702
  init_sanitize();
720
703
  });
721
704
  // ../../src/reverse-engineer/index.ts
722
- import { nanoid as nanoid4 } from "nanoid";
705
+ import { nanoid as nanoid3 } from "nanoid";
723
706
  var ALLOWED_METHODS, STRIP_HEADERS, REPLAY_HEADER_EXACT, SAFE_HEADERS, AD_SCHEMA_KEYS;
724
707
  var init_reverse_engineer = __esm(() => {
725
708
  init_transform();
@@ -791,19 +774,6 @@ var init_reverse_engineer = __esm(() => {
791
774
  ]);
792
775
  });
793
776
 
794
- // ../../src/reverse-engineer/bundle-scanner.ts
795
- var init_bundle_scanner = __esm(() => {
796
- init_logger();
797
- });
798
-
799
- // ../../src/reverse-engineer/token-sources.ts
800
- var init_token_sources = () => {};
801
-
802
- // ../../src/execution/token-resolver.ts
803
- var init_token_resolver = __esm(() => {
804
- init_token_sources();
805
- });
806
-
807
777
  // ../../src/vault/index.ts
808
778
  import { join as join7 } from "path";
809
779
  import { homedir as homedir5 } from "os";
@@ -834,6 +804,38 @@ var init_vault = __esm(async () => {
834
804
  vaultLock = Promise.resolve();
835
805
  });
836
806
 
807
+ // ../../src/runtime/browser-access.ts
808
+ var init_browser_access = () => {};
809
+
810
+ // ../../src/capture/index.ts
811
+ import { nanoid as nanoid4 } from "nanoid";
812
+ var activeTabRegistry, interceptorInjectedTabs, cdpDocStartTabs, cdpCapturedHeaders;
813
+ var init_capture = __esm(async () => {
814
+ init_client2();
815
+ init_domain();
816
+ init_logger();
817
+ init_reverse_engineer();
818
+ init_browser_access();
819
+ await init_vault();
820
+ activeTabRegistry = new Set;
821
+ interceptorInjectedTabs = new Set;
822
+ cdpDocStartTabs = new Set;
823
+ cdpCapturedHeaders = new Map;
824
+ });
825
+
826
+ // ../../src/reverse-engineer/bundle-scanner.ts
827
+ var init_bundle_scanner = __esm(() => {
828
+ init_logger();
829
+ });
830
+
831
+ // ../../src/reverse-engineer/token-sources.ts
832
+ var init_token_sources = () => {};
833
+
834
+ // ../../src/execution/token-resolver.ts
835
+ var init_token_resolver = __esm(() => {
836
+ init_token_sources();
837
+ });
838
+
837
839
  // ../../src/auth/index.ts
838
840
  var init_auth = __esm(async () => {
839
841
  init_client2();
@@ -1018,8 +1020,6 @@ var init_compile = () => {};
1018
1020
  import { nanoid as nanoid6 } from "nanoid";
1019
1021
  var VALID_VERIFICATION_STATUSES, STOPWORDS;
1020
1022
  var init_execution = __esm(async () => {
1021
- init_capture();
1022
- init_capture();
1023
1023
  init_reverse_engineer();
1024
1024
  init_bundle_scanner();
1025
1025
  init_token_resolver();
@@ -1043,6 +1043,8 @@ var init_execution = __esm(async () => {
1043
1043
  init_compile();
1044
1044
  init_publish();
1045
1045
  await __promiseAll([
1046
+ init_capture(),
1047
+ init_capture(),
1046
1048
  init_vault(),
1047
1049
  init_auth(),
1048
1050
  init_indexer(),
package/dist/mcp.js CHANGED
@@ -225,12 +225,12 @@ import { dirname, join, parse } from "path";
225
225
  import { fileURLToPath as fileURLToPath2 } from "url";
226
226
 
227
227
  // ../../src/build-info.generated.ts
228
- var BUILD_RELEASE_VERSION = "3.2.2";
229
- var BUILD_GIT_SHA = "150cce0d751e";
228
+ var BUILD_RELEASE_VERSION = "3.2.3-experiments.5c5b5cc";
229
+ var BUILD_GIT_SHA = "5c5b5cc8b8c6";
230
230
  var BUILD_CODE_HASH = "1488fc1d92b7";
231
- var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjIiLCJnaXRfc2hhIjoiMTUwY2NlMGQ3NTFlIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0AxNTBjY2UwZDc1MWUiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA4OjQ2OjM5LjIwNVoifQ";
232
- var BUILD_RELEASE_MANIFEST_SIGNATURE = "ZS7C10DGuwyn1m02shMHyz6cN5UbTHfa8yqnkELg_L8";
233
- var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
231
+ var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjMtZXhwZXJpbWVudHMuNWM1YjVjYyIsImdpdF9zaGEiOiI1YzViNWNjOGI4YzYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDVjNWI1Y2M4YjhjNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMTU6MjQ6NDkuMjg4WiJ9";
232
+ var BUILD_RELEASE_MANIFEST_SIGNATURE = "Jq9skS2755Nby4cAonNUHdfnsTUBZmnZv-s6DPuISRo";
233
+ var BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
234
234
 
235
235
  // ../../src/version.ts
236
236
  var MODULE_DIR = dirname(fileURLToPath2(import.meta.url));
package/dist/server.js CHANGED
@@ -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 qTemplateStr = sanitizedQParams && Object.keys(sanitizedQParams).length > 0 ? Object.keys(sanitizedQParams).map((k) => `${encodeURIComponent(k)}={${qBindings[k] ?? k}}`).join("&") : null;
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}?${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,
@@ -5027,6 +5032,179 @@ var init_reverse_engineer = __esm(() => {
5027
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;
5028
5033
  });
5029
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();
5206
+ });
5207
+
5030
5208
  // ../../src/runtime/browser-access.ts
5031
5209
  var DEFAULT_BROWSER_ACCESS;
5032
5210
  var init_browser_access = __esm(() => {
@@ -5477,7 +5655,12 @@ function normalizeCapturedUrl(url, baseUrl) {
5477
5655
  if (!url)
5478
5656
  return url;
5479
5657
  try {
5480
- return new URL(url).toString();
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
- await phase("harStart", () => harStart(tabId));
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,90 @@ 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
- try {
5937
- const harResult = await phase("harStop", () => harStop(tabId));
5938
- harEntries = harResult.entries;
5939
- } catch {}
6230
+ if (cdpRequestMap.size === 0) {
6231
+ try {
6232
+ const harResult = await Promise.race([
6233
+ harStop(tabId),
6234
+ new Promise((r) => setTimeout(() => r({ entries: [] }), 5000))
6235
+ ]);
6236
+ harEntries = harResult.entries ?? [];
6237
+ } catch {}
6238
+ } else {
6239
+ harStop(tabId).catch(() => {});
6240
+ }
6241
+ if (cdpPendingBodies.size > 0) {
6242
+ await new Promise((r) => setTimeout(r, 1500));
6243
+ }
6244
+ if (cdpWs) {
6245
+ try {
6246
+ cdpWs.close();
6247
+ } catch {}
6248
+ }
6249
+ const harUrls = new Set(harEntries.map((e) => e.request?.url).filter(Boolean));
6250
+ let cdpAdded = 0;
6251
+ for (const [requestId, req] of cdpRequestMap) {
6252
+ if (harUrls.has(req.url))
6253
+ continue;
6254
+ const resp = cdpResponseMeta.get(requestId);
6255
+ if (!resp)
6256
+ continue;
6257
+ const body = cdpResolvedBodies.get(req.url);
6258
+ harEntries.push({
6259
+ startedDateTime: new Date().toISOString(),
6260
+ request: { method: req.method, url: req.url, headers: req.headers, postData: req.postData ? { text: req.postData } : undefined },
6261
+ response: { status: resp.status, headers: resp.headers, content: body ? { text: body, mimeType: resp.mimeType } : {} }
6262
+ });
6263
+ if (body && body.length > 0 && !responseBodies.has(req.url)) {
6264
+ responseBodies.set(req.url, body);
6265
+ }
6266
+ cdpAdded++;
6267
+ }
6268
+ if (cdpAdded > 0) {
6269
+ log("capture", `CDP direct capture added ${cdpAdded} entries (${cdpResolvedBodies.size} with bodies, ${harEntries.length} total HAR)`);
6270
+ }
5940
6271
  const HAR_REPLAY_CT2 = /application\/json|text\/plain|\+json/i;
5941
6272
  let harReplayCount = 0;
5942
6273
  for (const entry of harEntries) {
@@ -5960,7 +6291,7 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
5960
6291
  const postData = method !== "GET" ? entry.request?.postData?.text : undefined;
5961
6292
  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
6293
  const body = await phase("har-replay", () => evaluate(tabId, replayScript));
5963
- if (typeof body === "string" && body.length > 0 && body.length < 512 * 1024) {
6294
+ if (typeof body === "string" && body.length > 0 && body.length < 524288) {
5964
6295
  responseBodies.set(harUrl, body);
5965
6296
  harReplayCount++;
5966
6297
  log("capture", `har-replay-fetched ${harUrl.substring(0, 80)} (${body.length}B)`);
@@ -5988,7 +6319,7 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
5988
6319
  html = await phase("getPageHtml", () => getPageHtml(tabId));
5989
6320
  } catch {}
5990
6321
  const SSR_DATA_EXTRACTORS = [
5991
- { 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});return JSON.stringify(out)}catch(e){return null}})()` },
6322
+ { 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}})()` },
5992
6323
  { name: "youtube", script: `(function(){try{return typeof ytInitialData!=='undefined'?JSON.stringify(ytInitialData):null}catch(e){return null}})()` },
5993
6324
  { name: "nextjs", script: `(function(){try{return window.__NEXT_DATA__?JSON.stringify(window.__NEXT_DATA__):null}catch(e){return null}})()` },
5994
6325
  { name: "nuxt", script: `(function(){try{return window.__NUXT__?JSON.stringify(window.__NUXT__):null}catch(e){return null}})()` }
@@ -6000,8 +6331,12 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
6000
6331
  if (typeof raw !== "string" || !raw || raw === "null")
6001
6332
  continue;
6002
6333
  if (extractor.name === "ytmusic") {
6003
- const paths = JSON.parse(raw);
6004
- for (const [path4, data] of Object.entries(paths)) {
6334
+ const parsed = JSON.parse(raw);
6335
+ const apiKey = parsed.__apiKey || "";
6336
+ const innertubeContext = parsed.__context;
6337
+ delete parsed.__apiKey;
6338
+ delete parsed.__context;
6339
+ for (const [path4, data] of Object.entries(parsed)) {
6005
6340
  if (!data || typeof data !== "object")
6006
6341
  continue;
6007
6342
  const cleaned = { ...data };
@@ -6022,12 +6357,15 @@ async function captureSession(url, authHeaders, cookies, intent, options) {
6022
6357
  continue;
6023
6358
  const origin = new URL(url).origin;
6024
6359
  const contextParams = new URL(url).searchParams;
6025
- const queryStr = path4.includes("search") && contextParams.toString() ? `?${contextParams.toString()}&prettyPrint=false` : "?prettyPrint=false";
6360
+ const keyParam = apiKey ? `key=${apiKey}&` : "";
6361
+ const queryStr = path4.includes("search") && contextParams.toString() ? `?${keyParam}${contextParams.toString()}&prettyPrint=false` : `?${keyParam}prettyPrint=false`;
6026
6362
  const syntheticUrl = `${origin}/youtubei/v1${path4}${queryStr}`;
6027
6363
  responseBodies.set(syntheticUrl, bodyStr);
6364
+ const queryValue = contextParams.get("q") ?? "";
6365
+ 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 ?? {} });
6028
6366
  harEntries.push({
6029
6367
  startedDateTime: new Date().toISOString(),
6030
- request: { method: "POST", url: syntheticUrl, headers: [{ name: "content-type", value: "application/json" }], postData: { text: "{}" } },
6368
+ request: { method: "POST", url: syntheticUrl, headers: [{ name: "content-type", value: "application/json" }], postData: { text: postBody } },
6031
6369
  response: { status: 200, headers: [{ name: "content-type", value: "application/json" }], content: { text: bodyStr, mimeType: "application/json" } }
6032
6370
  });
6033
6371
  embeddedDataCount++;
@@ -6523,11 +6861,13 @@ var MAX_CONCURRENT_TABS = 3, activeTabs = 0, waitQueue, activeTabRegistry, inter
6523
6861
  return origSend.apply(this, arguments);
6524
6862
  };
6525
6863
  })()`, REPLAY_SKIP, HAR_REPLAY_CT;
6526
- var init_capture = __esm(() => {
6864
+ var init_capture = __esm(async () => {
6527
6865
  init_client();
6528
6866
  init_domain();
6529
6867
  init_logger();
6868
+ init_reverse_engineer();
6530
6869
  init_browser_access();
6870
+ await init_vault();
6531
6871
  waitQueue = [];
6532
6872
  activeTabRegistry = new Set;
6533
6873
  interceptorInjectedTabs = new Set;
@@ -6544,17 +6884,17 @@ var init_capture = __esm(() => {
6544
6884
  });
6545
6885
 
6546
6886
  // ../../src/build-info.generated.ts
6547
- var BUILD_RELEASE_VERSION = "3.2.2", BUILD_GIT_SHA = "150cce0d751e", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjIiLCJnaXRfc2hhIjoiMTUwY2NlMGQ3NTFlIiwiY29kZV9oYXNoIjoiMTQ4OGZjMWQ5MmI3IiwidHJhY2VfdmVyc2lvbiI6IjE0ODhmYzFkOTJiN0AxNTBjY2UwZDc1MWUiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTA2VDA4OjQ2OjM5LjIwNVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "ZS7C10DGuwyn1m02shMHyz6cN5UbTHfa8yqnkELg_L8", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
6887
+ var BUILD_RELEASE_VERSION = "3.2.3-experiments.5c5b5cc", BUILD_GIT_SHA = "5c5b5cc8b8c6", BUILD_CODE_HASH = "1488fc1d92b7", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy4yLjMtZXhwZXJpbWVudHMuNWM1YjVjYyIsImdpdF9zaGEiOiI1YzViNWNjOGI4YzYiLCJjb2RlX2hhc2giOiIxNDg4ZmMxZDkyYjciLCJ0cmFjZV92ZXJzaW9uIjoiMTQ4OGZjMWQ5MmI3QDVjNWI1Y2M4YjhjNiIsImlzc3VlZF9hdCI6IjIwMjYtMDQtMDZUMTU6MjQ6NDkuMjg4WiJ9", BUILD_RELEASE_MANIFEST_SIGNATURE = "Jq9skS2755Nby4cAonNUHdfnsTUBZmnZv-s6DPuISRo", BUILD_DEFAULT_BACKEND_URL = "https://unbrowse-backend-experiments.lewis-6d8.workers.dev";
6548
6888
 
6549
6889
  // ../../src/version.ts
6550
6890
  import { createHash } from "crypto";
6551
- import { existsSync as existsSync3, readFileSync, readdirSync } from "fs";
6552
- import { dirname, join as join2, parse } from "path";
6891
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync } from "fs";
6892
+ import { dirname, join as join3, parse } from "path";
6553
6893
  import { fileURLToPath as fileURLToPath2 } from "url";
6554
6894
  function collectTsFiles(dir) {
6555
6895
  const results = [];
6556
6896
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
6557
- const full = join2(dir, entry.name);
6897
+ const full = join3(dir, entry.name);
6558
6898
  if (entry.isDirectory() && entry.name !== "node_modules") {
6559
6899
  results.push(...collectTsFiles(full));
6560
6900
  } else if (entry.name.endsWith(".ts")) {
@@ -6567,21 +6907,21 @@ function hashFiles(srcDir, files) {
6567
6907
  const hash = createHash("sha256");
6568
6908
  for (const file of files) {
6569
6909
  hash.update(file.slice(srcDir.length));
6570
- hash.update(readFileSync(file, "utf-8"));
6910
+ hash.update(readFileSync2(file, "utf-8"));
6571
6911
  }
6572
6912
  return hash.digest("hex").slice(0, 12);
6573
6913
  }
6574
6914
  function resolveCodeHashSourceDir(moduleDir) {
6575
6915
  const candidates = [
6576
6916
  moduleDir,
6577
- join2(moduleDir, "runtime-src"),
6578
- join2(moduleDir, "..", "runtime-src"),
6579
- join2(moduleDir, "src"),
6580
- join2(moduleDir, "..", "src")
6917
+ join3(moduleDir, "runtime-src"),
6918
+ join3(moduleDir, "..", "runtime-src"),
6919
+ join3(moduleDir, "src"),
6920
+ join3(moduleDir, "..", "src")
6581
6921
  ];
6582
6922
  for (const candidate of candidates) {
6583
6923
  try {
6584
- if (!existsSync3(candidate))
6924
+ if (!existsSync4(candidate))
6585
6925
  continue;
6586
6926
  const files = collectTsFiles(candidate);
6587
6927
  if (files.length > 0)
@@ -6631,7 +6971,7 @@ function getPackageVersionForModuleDir(moduleDir) {
6631
6971
  const root = parse(dir).root;
6632
6972
  while (true) {
6633
6973
  try {
6634
- const pkg = JSON.parse(readFileSync(join2(dir, "package.json"), "utf-8"));
6974
+ const pkg = JSON.parse(readFileSync2(join3(dir, "package.json"), "utf-8"));
6635
6975
  return typeof pkg.version === "string" ? pkg.version : "unknown";
6636
6976
  } catch {}
6637
6977
  if (dir === root)
@@ -6735,18 +7075,18 @@ async function ensureCascadeSplitForSkill(skill, deps = {}) {
6735
7075
  var init_cascade = () => {};
6736
7076
 
6737
7077
  // ../../src/payments/wallet.ts
6738
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "node:fs";
6739
- import { homedir } from "node:os";
6740
- import { join as join3 } from "node:path";
7078
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
7079
+ import { homedir as homedir2 } from "node:os";
7080
+ import { join as join4 } from "node:path";
6741
7081
  function asNonEmptyString(value) {
6742
7082
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
6743
7083
  }
6744
7084
  function getLobsterWalletFromLocalConfig() {
6745
- const agentsPath = join3(process.env.HOME || homedir(), ".lobster", "agents.json");
6746
- if (!existsSync4(agentsPath))
7085
+ const agentsPath = join4(process.env.HOME || homedir2(), ".lobster", "agents.json");
7086
+ if (!existsSync5(agentsPath))
6747
7087
  return;
6748
7088
  try {
6749
- const raw = JSON.parse(readFileSync2(agentsPath, "utf8"));
7089
+ const raw = JSON.parse(readFileSync3(agentsPath, "utf8"));
6750
7090
  const activeAgentId = asNonEmptyString(raw.activeAgentId);
6751
7091
  const activeAgent = Array.isArray(raw.agents) ? raw.agents.find((agent) => asNonEmptyString(agent.id) === activeAgentId) : activeAgentId ? raw.agents?.[activeAgentId] : undefined;
6752
7092
  return asNonEmptyString(activeAgent?.authorizedWallets?.solana) ?? asNonEmptyString(activeAgent?.walletAddress) ?? asNonEmptyString(activeAgent?.wallet_address);
@@ -6897,9 +7237,9 @@ __export(exports_lobster_pay, {
6897
7237
  isLobsterAvailable: () => isLobsterAvailable
6898
7238
  });
6899
7239
  import { execFile, execFileSync as execFileSync2 } from "node:child_process";
6900
- import { existsSync as existsSync5 } from "node:fs";
6901
- import { homedir as homedir2 } from "node:os";
6902
- import { join as join4 } from "node:path";
7240
+ import { existsSync as existsSync6 } from "node:fs";
7241
+ import { homedir as homedir3 } from "node:os";
7242
+ import { join as join5 } from "node:path";
6903
7243
  function getLobsterCommand() {
6904
7244
  try {
6905
7245
  execFileSync2("lobstercash", ["--version"], { stdio: "ignore", timeout: 3000 });
@@ -6907,8 +7247,8 @@ function getLobsterCommand() {
6907
7247
  } catch (_e) {}
6908
7248
  try {
6909
7249
  const npmPrefix = execFileSync2("npm", ["config", "get", "prefix"], { encoding: "utf8", timeout: 5000 }).trim();
6910
- const lobsterPath = join4(npmPrefix, "bin", "lobstercash");
6911
- if (existsSync5(lobsterPath)) {
7250
+ const lobsterPath = join5(npmPrefix, "bin", "lobstercash");
7251
+ if (existsSync6(lobsterPath)) {
6912
7252
  execFileSync2(lobsterPath, ["--version"], { stdio: "ignore", timeout: 3000 });
6913
7253
  return { cmd: lobsterPath, prefix: [] };
6914
7254
  }
@@ -6921,8 +7261,8 @@ function lobsterCmd() {
6921
7261
  return cachedCommand;
6922
7262
  }
6923
7263
  function isLobsterAvailable() {
6924
- const agentsPath = join4(process.env.HOME || homedir2(), ".lobster", "agents.json");
6925
- return existsSync5(agentsPath);
7264
+ const agentsPath = join5(process.env.HOME || homedir3(), ".lobster", "agents.json");
7265
+ return existsSync6(agentsPath);
6926
7266
  }
6927
7267
  function lobsterX402Fetch(url, options) {
6928
7268
  return new Promise((resolve) => {
@@ -7054,10 +7394,10 @@ __export(exports_client2, {
7054
7394
  buildDefaultAgentName: () => buildDefaultAgentName,
7055
7395
  autoFileIssue: () => autoFileIssue
7056
7396
  });
7057
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "fs";
7058
- import { join as join5 } from "path";
7059
- import { homedir as homedir3, hostname, release as osRelease } from "os";
7060
- import { randomBytes, createHash as createHash3 } from "crypto";
7397
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
7398
+ import { join as join6 } from "path";
7399
+ import { homedir as homedir4, hostname, release as osRelease } from "os";
7400
+ import { randomBytes as randomBytes2, createHash as createHash3 } from "crypto";
7061
7401
  import { createInterface } from "readline";
7062
7402
  function buildReleaseAttestationHeaders(manifestBase64, signature) {
7063
7403
  const manifest = manifestBase64.trim();
@@ -7091,18 +7431,18 @@ function scopedSkillKey(skillId, scopeId) {
7091
7431
  return scopeId ? `${scopeId}:${skillId}` : skillId;
7092
7432
  }
7093
7433
  function getSkillCacheDir() {
7094
- return process.env.UNBROWSE_SKILL_CACHE_DIR || join5(getConfigDir(), "skill-cache");
7434
+ return process.env.UNBROWSE_SKILL_CACHE_DIR || join6(getConfigDir(), "skill-cache");
7095
7435
  }
7096
7436
  function getConfigDir() {
7097
7437
  if (process.env.UNBROWSE_CONFIG_DIR)
7098
7438
  return process.env.UNBROWSE_CONFIG_DIR;
7099
- return PROFILE_NAME ? join5(homedir3(), ".unbrowse", "profiles", PROFILE_NAME) : join5(homedir3(), ".unbrowse");
7439
+ return PROFILE_NAME ? join6(homedir4(), ".unbrowse", "profiles", PROFILE_NAME) : join6(homedir4(), ".unbrowse");
7100
7440
  }
7101
7441
  function getConfigPath() {
7102
- return join5(getConfigDir(), "config.json");
7442
+ return join6(getConfigDir(), "config.json");
7103
7443
  }
7104
7444
  function getInstallTelemetryPath() {
7105
- return join5(getConfigDir(), "install-state.json");
7445
+ return join6(getConfigDir(), "install-state.json");
7106
7446
  }
7107
7447
  function getLandingToken() {
7108
7448
  const token = process.env.UNBROWSE_LANDING_TOKEN?.trim();
@@ -7120,8 +7460,8 @@ function isLocalOnlyMode() {
7120
7460
  function loadConfig() {
7121
7461
  try {
7122
7462
  const configPath = getConfigPath();
7123
- if (existsSync6(configPath)) {
7124
- return JSON.parse(readFileSync3(configPath, "utf-8"));
7463
+ if (existsSync7(configPath)) {
7464
+ return JSON.parse(readFileSync4(configPath, "utf-8"));
7125
7465
  }
7126
7466
  } catch {}
7127
7467
  return null;
@@ -7129,15 +7469,15 @@ function loadConfig() {
7129
7469
  function saveConfig(config) {
7130
7470
  const configDir = getConfigDir();
7131
7471
  const configPath = getConfigPath();
7132
- if (!existsSync6(configDir))
7133
- mkdirSync3(configDir, { recursive: true });
7134
- writeFileSync2(configPath, JSON.stringify(config, null, 2), { mode: 384 });
7472
+ if (!existsSync7(configDir))
7473
+ mkdirSync4(configDir, { recursive: true });
7474
+ writeFileSync3(configPath, JSON.stringify(config, null, 2), { mode: 384 });
7135
7475
  }
7136
7476
  function loadInstallTelemetryState() {
7137
7477
  try {
7138
7478
  const statePath = getInstallTelemetryPath();
7139
- if (existsSync6(statePath)) {
7140
- return JSON.parse(readFileSync3(statePath, "utf-8"));
7479
+ if (existsSync7(statePath)) {
7480
+ return JSON.parse(readFileSync4(statePath, "utf-8"));
7141
7481
  }
7142
7482
  } catch {}
7143
7483
  return null;
@@ -7145,13 +7485,13 @@ function loadInstallTelemetryState() {
7145
7485
  function saveInstallTelemetryState(state) {
7146
7486
  const configDir = getConfigDir();
7147
7487
  const statePath = getInstallTelemetryPath();
7148
- if (!existsSync6(configDir))
7149
- mkdirSync3(configDir, { recursive: true });
7150
- writeFileSync2(statePath, JSON.stringify(state, null, 2), { mode: 384 });
7488
+ if (!existsSync7(configDir))
7489
+ mkdirSync4(configDir, { recursive: true });
7490
+ writeFileSync3(statePath, JSON.stringify(state, null, 2), { mode: 384 });
7151
7491
  }
7152
7492
  function createInstallTelemetryState() {
7153
7493
  return {
7154
- install_id: `install_${randomBytes(8).toString("hex")}`,
7494
+ install_id: `install_${randomBytes2(8).toString("hex")}`,
7155
7495
  first_seen_at: new Date().toISOString()
7156
7496
  };
7157
7497
  }
@@ -7291,7 +7631,7 @@ function isValidAgentEmail(value) {
7291
7631
  return EMAIL_RE.test(normalizeAgentEmail(value));
7292
7632
  }
7293
7633
  function buildDefaultAgentName() {
7294
- return `${hostname()}-${randomBytes(3).toString("hex")}`;
7634
+ return `${hostname()}-${randomBytes2(3).toString("hex")}`;
7295
7635
  }
7296
7636
  function resolveAgentName(preferredEmail, fallbackName) {
7297
7637
  const normalized = normalizeAgentEmail(preferredEmail ?? "");
@@ -7646,11 +7986,11 @@ async function waitForBackgroundRegistration(timeoutMs = 0) {
7646
7986
  ]);
7647
7987
  }
7648
7988
  function skillCachePath(skillId) {
7649
- return join5(getSkillCacheDir(), `${skillId}.json`);
7989
+ return join6(getSkillCacheDir(), `${skillId}.json`);
7650
7990
  }
7651
7991
  function readSkillCache(skillId) {
7652
7992
  try {
7653
- const raw = readFileSync3(skillCachePath(skillId), "utf-8");
7993
+ const raw = readFileSync4(skillCachePath(skillId), "utf-8");
7654
7994
  return JSON.parse(raw);
7655
7995
  } catch {
7656
7996
  return null;
@@ -7660,8 +8000,8 @@ function writeSkillCache(skill, scopeId) {
7660
8000
  try {
7661
8001
  recentLocalSkills.set(scopedSkillKey(skill.skill_id, scopeId), skill);
7662
8002
  const skillCacheDir = getSkillCacheDir();
7663
- if (!existsSync6(skillCacheDir))
7664
- mkdirSync3(skillCacheDir, { recursive: true });
8003
+ if (!existsSync7(skillCacheDir))
8004
+ mkdirSync4(skillCacheDir, { recursive: true });
7665
8005
  const existing = readSkillCache(skill.skill_id);
7666
8006
  if (existing) {
7667
8007
  for (const ep of skill.endpoints) {
@@ -7677,7 +8017,7 @@ function writeSkillCache(skill, scopeId) {
7677
8017
  const hasStrategy = skill.endpoints.some((e) => e.exec_strategy);
7678
8018
  if (hasStrategy)
7679
8019
  console.log(`[cache] writing skill ${skill.skill_id} with exec_strategy`);
7680
- writeFileSync2(skillCachePath(skill.skill_id), JSON.stringify(skill), "utf-8");
8020
+ writeFileSync3(skillCachePath(skill.skill_id), JSON.stringify(skill), "utf-8");
7681
8021
  } catch {}
7682
8022
  }
7683
8023
  function cachePublishedSkill(skill, scopeId) {
@@ -7712,7 +8052,7 @@ function isIntentCompatible(lhs, rhs) {
7712
8052
  function findExistingSkillForDomain(domain, intent) {
7713
8053
  try {
7714
8054
  const skillCacheDir = getSkillCacheDir();
7715
- if (!existsSync6(skillCacheDir))
8055
+ if (!existsSync7(skillCacheDir))
7716
8056
  return null;
7717
8057
  const files = readdirSync2(skillCacheDir);
7718
8058
  let compatible = null;
@@ -7721,7 +8061,7 @@ function findExistingSkillForDomain(domain, intent) {
7721
8061
  if (!f.endsWith(".json") || f === "browser-capture.json")
7722
8062
  continue;
7723
8063
  try {
7724
- const raw = readFileSync3(join5(skillCacheDir, f), "utf-8");
8064
+ const raw = readFileSync4(join6(skillCacheDir, f), "utf-8");
7725
8065
  const skill = JSON.parse(raw);
7726
8066
  if (skill.domain === domain && skill.execution_type === "http") {
7727
8067
  if (!fallback)
@@ -7771,11 +8111,11 @@ async function getSkillChunk2(skillId, opts) {
7771
8111
  async function listSkills() {
7772
8112
  if (LOCAL_ONLY) {
7773
8113
  try {
7774
- if (!existsSync6(SKILL_CACHE_DIR))
8114
+ if (!existsSync7(SKILL_CACHE_DIR))
7775
8115
  return [];
7776
8116
  return readdirSync2(SKILL_CACHE_DIR).filter((file) => file.endsWith(".json")).map((file) => {
7777
8117
  try {
7778
- return JSON.parse(readFileSync3(join5(SKILL_CACHE_DIR, file), "utf-8"));
8118
+ return JSON.parse(readFileSync4(join6(SKILL_CACHE_DIR, file), "utf-8"));
7779
8119
  } catch {
7780
8120
  return null;
7781
8121
  }
@@ -8467,10 +8807,10 @@ var init_publish_admission = __esm(() => {
8467
8807
 
8468
8808
  // ../../src/telemetry.ts
8469
8809
  import { createHash as createHash4 } from "node:crypto";
8470
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "node:fs";
8471
- import { join as join6 } from "node:path";
8810
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
8811
+ import { join as join7 } from "node:path";
8472
8812
  function getTraceDir() {
8473
- return process.env.UNBROWSE_TRACES_DIR ?? join6(process.env.HOME ?? "/tmp", ".unbrowse", "traces");
8813
+ return process.env.UNBROWSE_TRACES_DIR ?? join7(process.env.HOME ?? "/tmp", ".unbrowse", "traces");
8474
8814
  }
8475
8815
  function isTracingEnabled() {
8476
8816
  return process.env.UNBROWSE_DISABLE_TRACES !== "1";
@@ -8544,11 +8884,11 @@ function emitRouteTrace(params) {
8544
8884
  };
8545
8885
  try {
8546
8886
  const traceDir = getTraceDir();
8547
- if (!existsSync7(traceDir))
8548
- mkdirSync4(traceDir, { recursive: true });
8887
+ if (!existsSync8(traceDir))
8888
+ mkdirSync5(traceDir, { recursive: true });
8549
8889
  const stamp = params.started_at.replace(/[:.]/g, "-");
8550
- const file = join6(traceDir, `${stamp}-${params.outcome}-${params.trace_id}.json`);
8551
- writeFileSync3(file, JSON.stringify(artifact, null, 2), "utf-8");
8890
+ const file = join7(traceDir, `${stamp}-${params.outcome}-${params.trace_id}.json`);
8891
+ writeFileSync4(file, JSON.stringify(artifact, null, 2), "utf-8");
8552
8892
  return file;
8553
8893
  } catch {
8554
8894
  return null;
@@ -9184,161 +9524,6 @@ var init_token_resolver = __esm(() => {
9184
9524
  init_token_sources();
9185
9525
  });
9186
9526
 
9187
- // ../../src/vault/index.ts
9188
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
9189
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
9190
- import { join as join7 } from "path";
9191
- import { homedir as homedir4 } from "os";
9192
- function normalizeKeytarModule(mod) {
9193
- let candidate = mod;
9194
- for (let depth = 0;depth < 3; depth++) {
9195
- if (!candidate || typeof candidate !== "object" || !("default" in candidate))
9196
- break;
9197
- candidate = candidate.default;
9198
- }
9199
- if (!candidate)
9200
- return null;
9201
- if (typeof candidate.setPassword === "function" && typeof candidate.getPassword === "function" && typeof candidate.deletePassword === "function") {
9202
- return candidate;
9203
- }
9204
- return null;
9205
- }
9206
- function isKeytarBindingError(error) {
9207
- const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
9208
- return KEYTAR_BINDING_ERROR_RE.test(message);
9209
- }
9210
- function disableKeytar(error) {
9211
- keytar = null;
9212
- if (keytarFallbackLogged)
9213
- return;
9214
- const summary = error instanceof Error ? error.message : String(error);
9215
- log("vault", `keytar runtime unavailable (${summary}); using encrypted file fallback`);
9216
- keytarFallbackLogged = true;
9217
- }
9218
- async function callKeytar(op) {
9219
- if (!keytar)
9220
- return KEYTAR_UNAVAILABLE;
9221
- try {
9222
- return await op(keytar);
9223
- } catch (error) {
9224
- if (!isKeytarBindingError(error))
9225
- throw error;
9226
- disableKeytar(error);
9227
- return KEYTAR_UNAVAILABLE;
9228
- }
9229
- }
9230
- function getOrCreateKey() {
9231
- if (!existsSync8(VAULT_DIR))
9232
- mkdirSync5(VAULT_DIR, { recursive: true, mode: 448 });
9233
- if (existsSync8(KEY_FILE))
9234
- return readFileSync4(KEY_FILE);
9235
- const key = randomBytes2(32);
9236
- writeFileSync4(KEY_FILE, key, { mode: 384 });
9237
- return key;
9238
- }
9239
- function withVaultLock(fn) {
9240
- const prev = vaultLock;
9241
- let release;
9242
- vaultLock = new Promise((r) => {
9243
- release = r;
9244
- });
9245
- return prev.then(fn).finally(() => release());
9246
- }
9247
- function readVaultFile() {
9248
- if (!existsSync8(VAULT_FILE))
9249
- return {};
9250
- try {
9251
- const key = getOrCreateKey();
9252
- const raw = readFileSync4(VAULT_FILE);
9253
- const iv = raw.subarray(0, 16);
9254
- const enc = raw.subarray(16);
9255
- const decipher = createDecipheriv("aes-256-cbc", key, iv);
9256
- const dec = Buffer.concat([decipher.update(enc), decipher.final()]);
9257
- return JSON.parse(dec.toString("utf8"));
9258
- } catch {
9259
- return {};
9260
- }
9261
- }
9262
- function writeVaultFile(data) {
9263
- const key = getOrCreateKey();
9264
- const iv = randomBytes2(16);
9265
- const cipher = createCipheriv("aes-256-cbc", key, iv);
9266
- const enc = Buffer.concat([cipher.update(JSON.stringify(data), "utf8"), cipher.final()]);
9267
- writeFileSync4(VAULT_FILE, Buffer.concat([iv, enc]), { mode: 384 });
9268
- }
9269
- async function storeCredential(account, value, opts) {
9270
- const wrapped = {
9271
- value,
9272
- stored_at: new Date().toISOString(),
9273
- expires_at: opts?.expires_at,
9274
- max_age_ms: opts?.max_age_ms
9275
- };
9276
- const serialized = JSON.stringify(wrapped);
9277
- const keytarResult = await callKeytar((client) => client.setPassword(SERVICE, account, serialized));
9278
- if (keytarResult !== KEYTAR_UNAVAILABLE)
9279
- return;
9280
- await withVaultLock(() => {
9281
- const data = readVaultFile();
9282
- data[account] = serialized;
9283
- writeVaultFile(data);
9284
- });
9285
- }
9286
- function isExpired(cred) {
9287
- if (cred.expires_at) {
9288
- return new Date(cred.expires_at).getTime() <= Date.now();
9289
- }
9290
- if (cred.max_age_ms) {
9291
- return new Date(cred.stored_at).getTime() + cred.max_age_ms <= Date.now();
9292
- }
9293
- return false;
9294
- }
9295
- async function getCredential(account) {
9296
- let raw;
9297
- const keytarResult = await callKeytar((client) => client.getPassword(SERVICE, account));
9298
- if (keytarResult !== KEYTAR_UNAVAILABLE) {
9299
- raw = keytarResult;
9300
- } else {
9301
- const data = readVaultFile();
9302
- raw = data[account] ?? null;
9303
- }
9304
- if (!raw)
9305
- return null;
9306
- try {
9307
- const parsed = JSON.parse(raw);
9308
- if (parsed.value && parsed.stored_at) {
9309
- if (isExpired(parsed)) {
9310
- await deleteCredential(account);
9311
- return null;
9312
- }
9313
- return parsed.value;
9314
- }
9315
- } catch {}
9316
- return raw;
9317
- }
9318
- async function deleteCredential(account) {
9319
- const keytarResult = await callKeytar((client) => client.deletePassword(SERVICE, account));
9320
- if (keytarResult !== KEYTAR_UNAVAILABLE)
9321
- return;
9322
- await withVaultLock(() => {
9323
- const data = readVaultFile();
9324
- delete data[account];
9325
- writeVaultFile(data);
9326
- });
9327
- }
9328
- var KEYTAR_UNAVAILABLE, KEYTAR_BINDING_ERROR_RE, keytar = null, keytarFallbackLogged = false, SERVICE = "unbrowse", VAULT_DIR, VAULT_FILE, KEY_FILE, vaultLock;
9329
- var init_vault = __esm(async () => {
9330
- init_logger();
9331
- KEYTAR_UNAVAILABLE = Symbol("KEYTAR_UNAVAILABLE");
9332
- 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;
9333
- try {
9334
- keytar = normalizeKeytarModule(await import("keytar"));
9335
- } catch {}
9336
- VAULT_DIR = join7(homedir4(), ".unbrowse", "vault");
9337
- VAULT_FILE = join7(VAULT_DIR, "credentials.enc");
9338
- KEY_FILE = join7(VAULT_DIR, ".key");
9339
- vaultLock = Promise.resolve();
9340
- });
9341
-
9342
9527
  // ../../src/runtime/supervisor.ts
9343
9528
  function getDefaultLoginConfig(headless) {
9344
9529
  return {
@@ -14010,6 +14195,10 @@ function validateWorkflowReplayParams(recipe, params) {
14010
14195
  continue;
14011
14196
  const value = valueForSpec(spec, params);
14012
14197
  if (spec.required && (value == null || value === "")) {
14198
+ if (spec.default_value != null && spec.default_value !== "")
14199
+ continue;
14200
+ if (spec.example_value != null && spec.example_value !== "")
14201
+ continue;
14013
14202
  errors.push({ name: spec.name, reason: "required" });
14014
14203
  continue;
14015
14204
  }
@@ -14304,18 +14493,27 @@ async function reloadExecutionAuthState(skill, epDomain, authHeaders, cookies) {
14304
14493
  cookies.push(...resolved);
14305
14494
  } catch {}
14306
14495
  }
14307
- if (Object.keys(authHeaders).length === 0) {
14308
- try {
14309
- const sessionKey = `${getRegistrableDomain(epDomain)}-session`;
14310
- const sessionData = await getCredential(sessionKey);
14311
- if (sessionData) {
14312
- const parsed = JSON.parse(sessionData);
14313
- if (parsed.headers)
14314
- Object.assign(authHeaders, parsed.headers);
14315
- if (parsed.cookies && cookies.length === 0)
14316
- cookies.push(...parsed.cookies);
14317
- }
14318
- } catch {}
14496
+ {
14497
+ for (const sessionKey of [`${epDomain}-session`, `${getRegistrableDomain(epDomain)}-session`]) {
14498
+ try {
14499
+ const sessionData = await getCredential(sessionKey);
14500
+ if (sessionData) {
14501
+ const parsed = JSON.parse(sessionData);
14502
+ if (parsed.headers)
14503
+ Object.assign(authHeaders, parsed.headers);
14504
+ if (parsed.cookies && cookies.length === 0)
14505
+ cookies.push(...parsed.cookies);
14506
+ if (Object.keys(authHeaders).length > 0)
14507
+ break;
14508
+ }
14509
+ } catch {}
14510
+ }
14511
+ }
14512
+ if (cookies.length > 0 && authHeaders["csrf-token"]) {
14513
+ const jsessionId = cookies.find((c) => c.name === "JSESSIONID");
14514
+ if (jsessionId) {
14515
+ authHeaders["csrf-token"] = jsessionId.value.replace(/"/g, "");
14516
+ }
14319
14517
  }
14320
14518
  }
14321
14519
  function persistWorkflowArtifactForCapture(artifactSkill, captured, capturedAuthHeaders) {
@@ -15336,10 +15534,13 @@ async function executeBrowserCapture(skill, params, options) {
15336
15534
  }));
15337
15535
  }
15338
15536
  if (!auth_profile_ref) {
15339
- const vaultKey = `auth:${targetDomain}`;
15340
- const hasStoredAuth = await getCredential(vaultKey) != null;
15341
- if (hasStoredAuth)
15342
- auth_profile_ref = vaultKey;
15537
+ for (const vaultKey of [`auth:${targetDomain}`, `${domain}-session`, `${targetDomain}-session`]) {
15538
+ const hasStoredAuth = await getCredential(vaultKey) != null;
15539
+ if (hasStoredAuth) {
15540
+ auth_profile_ref = vaultKey;
15541
+ break;
15542
+ }
15543
+ }
15343
15544
  }
15344
15545
  const authBackedCapture = usedStoredAuth || !!auth_profile_ref;
15345
15546
  if (authBackedCapture) {
@@ -15920,7 +16121,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
15920
16121
  u.searchParams.set(k, String(v));
15921
16122
  }
15922
16123
  }
15923
- urlTemplate = restoreTemplatePlaceholderEncoding(u.toString());
16124
+ urlTemplate = restoreTemplatePlaceholderEncoding(u.toString()).replace(/%28/gi, "(").replace(/%29/gi, ")").replace(/%2C/gi, ",").replace(/%3A/gi, ":");
15924
16125
  } catch {}
15925
16126
  }
15926
16127
  let url = interpolate(urlTemplate, mergedParams);
@@ -16056,7 +16257,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
16056
16257
  let last = { data: null, status: 0 };
16057
16258
  for (const replayUrl of replayUrls) {
16058
16259
  const replayHeaders = buildStructuredReplayHeaders(url, replayUrl, headers);
16059
- log("exec", `server-fetch: ${endpoint.method} ${replayUrl.substring(0, 80)} auth=${(replayHeaders["authorization"] || "none").substring(0, 50)} csrf=${replayHeaders["x-csrf-token"]?.substring(0, 10)}... cookies=${replayHeaders["cookie"]?.length ?? 0}chars`);
16260
+ 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`);
16060
16261
  const res = await fetch(replayUrl, {
16061
16262
  method: endpoint.method,
16062
16263
  headers: replayHeaders,
@@ -16460,7 +16661,12 @@ function interpolate(template, params) {
16460
16661
  const base = template.substring(0, qIdx);
16461
16662
  const query = template.substring(qIdx + 1);
16462
16663
  const interpolatedBase = base.replace(/\{(\w+)\}/g, (_, k) => params[k] != null ? String(params[k]) : `{${k}}`);
16463
- const interpolatedQuery = query.replace(/\{(\w+)\}/g, (_, k) => params[k] != null ? encodeURIComponent(String(params[k])) : `{${k}}`);
16664
+ const interpolatedQuery = query.replace(/\{(\w+)\}/g, (_, k) => {
16665
+ if (params[k] == null)
16666
+ return `{${k}}`;
16667
+ const val = String(params[k]);
16668
+ return val.replace(/[#&=\s]/g, (ch) => encodeURIComponent(ch));
16669
+ });
16464
16670
  return `${interpolatedBase}?${interpolatedQuery}`;
16465
16671
  }
16466
16672
  function interpolateObj(obj, params) {
@@ -17020,8 +17226,6 @@ function isSpaShell(html) {
17020
17226
  }
17021
17227
  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;
17022
17228
  var init_execution = __esm(async () => {
17023
- init_capture();
17024
- init_capture();
17025
17229
  init_reverse_engineer();
17026
17230
  init_bundle_scanner();
17027
17231
  init_token_resolver();
@@ -17045,6 +17249,8 @@ var init_execution = __esm(async () => {
17045
17249
  init_compile();
17046
17250
  init_publish();
17047
17251
  await __promiseAll([
17252
+ init_capture(),
17253
+ init_capture(),
17048
17254
  init_vault(),
17049
17255
  init_auth(),
17050
17256
  init_indexer(),
@@ -19694,7 +19900,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
19694
19900
  for (const cookie of cookies)
19695
19901
  await setCookie(handoffTabId, cookie).catch(() => {});
19696
19902
  } catch {}
19697
- await evaluate(handoffTabId, (await Promise.resolve().then(() => (init_capture(), exports_capture))).INTERCEPTOR_SCRIPT).catch(() => {});
19903
+ await evaluate(handoffTabId, (await init_capture().then(() => exports_capture)).INTERCEPTOR_SCRIPT).catch(() => {});
19698
19904
  await harStart(handoffTabId).catch(() => {});
19699
19905
  try {
19700
19906
  const routesModule = await init_routes().then(() => exports_routes);
@@ -24195,13 +24401,13 @@ function schedulePeriodicVerification() {
24195
24401
  }, VERIFICATION_INTERVAL_MS);
24196
24402
  }
24197
24403
  var VERIFICATION_INTERVAL_MS, VERIFY_ENDPOINT_BATCH_SIZE;
24198
- var init_verification = __esm(() => {
24199
- init_capture();
24404
+ var init_verification = __esm(async () => {
24200
24405
  init_marketplace();
24201
24406
  init_marketplace();
24202
24407
  init_drift();
24203
24408
  init_matrix();
24204
24409
  init_candidates();
24410
+ await init_capture();
24205
24411
  VERIFICATION_INTERVAL_MS = 6 * 60 * 60 * 1000;
24206
24412
  VERIFY_ENDPOINT_BATCH_SIZE = Math.max(1, Number(process.env.UNBROWSE_VERIFY_ENDPOINT_BATCH_SIZE ?? 3));
24207
24413
  });
@@ -24308,9 +24514,11 @@ function schedulePeriodicStaleCleanup() {
24308
24514
  var VERIFY_TIMEOUT_MS, CLEANUP_INTERVAL_MS, CLEANUP_BATCH_SIZE, CLEANUP_VERIFY_ENDPOINT_LIMIT;
24309
24515
  var init_stale_cleanup_runner = __esm(async () => {
24310
24516
  init_marketplace();
24311
- init_verification();
24312
24517
  init_candidates();
24313
- await init_orchestrator();
24518
+ await __promiseAll([
24519
+ init_verification(),
24520
+ init_orchestrator()
24521
+ ]);
24314
24522
  VERIFY_TIMEOUT_MS = Math.max(5000, Number(process.env.UNBROWSE_STALE_VERIFY_TIMEOUT_MS ?? 15000));
24315
24523
  CLEANUP_INTERVAL_MS = Math.max(30 * 60 * 1000, Number(process.env.UNBROWSE_STALE_CLEANUP_INTERVAL_MS ?? 6 * 60 * 60 * 1000));
24316
24524
  CLEANUP_BATCH_SIZE = Math.max(1, Number(process.env.UNBROWSE_STALE_CLEANUP_BATCH_SIZE ?? 25));
@@ -25352,7 +25560,7 @@ async function registerRoutes(app) {
25352
25560
  if (!skill)
25353
25561
  return reply.code(404).send({ error: "Skill not found" });
25354
25562
  try {
25355
- const { verifySkill: verifySkill2 } = await Promise.resolve().then(() => (init_verification(), exports_verification));
25563
+ const { verifySkill: verifySkill2 } = await init_verification().then(() => exports_verification);
25356
25564
  const results = await verifySkill2(skill);
25357
25565
  return reply.send({ skill_id, verification: results });
25358
25566
  } catch (err) {
@@ -25945,7 +26153,6 @@ var BETA_API_URL, TRACES_DIR, BROWSE_BROKER_MAX, BROWSE_BROKER_BASE_PORT, browse
25945
26153
  var init_routes = __esm(async () => {
25946
26154
  init_client();
25947
26155
  init_reverse_engineer();
25948
- init_capture();
25949
26156
  init_marketplace();
25950
26157
  init_graph();
25951
26158
  init_client2();
@@ -25964,6 +26171,7 @@ var init_routes = __esm(async () => {
25964
26171
  init_schema_review();
25965
26172
  init_artifact();
25966
26173
  await __promiseAll([
26174
+ init_capture(),
25967
26175
  init_indexer(),
25968
26176
  init_vault(),
25969
26177
  init_orchestrator(),
@@ -25986,12 +26194,12 @@ import { config as loadEnv } from "dotenv";
25986
26194
 
25987
26195
  // ../../src/server.ts
25988
26196
  init_ratelimit();
25989
- init_verification();
25990
26197
  init_client2();
25991
- init_capture();
25992
26198
  init_version();
25993
26199
  await __promiseAll([
25994
26200
  init_routes(),
26201
+ init_verification(),
26202
+ init_capture(),
25995
26203
  init_stale_cleanup_runner()
25996
26204
  ]);
25997
26205
  import { execSync as execSync2 } from "node:child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbrowse",
3
- "version": "3.2.2",
3
+ "version": "3.2.3-experiments.5c5b5cc",
4
4
  "description": "Reverse-engineer any website into reusable API skills. Zero-dep single binary with embedded browser engine.",
5
5
  "type": "module",
6
6
  "bin": {