unbrowse 6.5.2 → 6.6.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "6.5.2", BUILD_GIT_SHA = "2bedcaef3363", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi41LjIiLCJnaXRfc2hhIjoiMmJlZGNhZWYzMzYzIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAyYmVkY2FlZjMzNjMiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTAzVDIzOjM2OjIyLjUxM1oifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "wNWNFYL91xCcb02bCfQvbVnHfmLAUCyHeX-oVLv2Wy4", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
34
+ var BUILD_RELEASE_VERSION = "6.6.0-preview.0", BUILD_GIT_SHA = "e74cd1481aa5", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAtcHJldmlldy4wIiwiZ2l0X3NoYSI6ImU3NGNkMTQ4MWFhNSIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFAZTc0Y2QxNDgxYWE1IiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wNFQwNTo1MTo1Mi4yNTNaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "_hrkIsHrYUpg_q-N3P2xraayyLNVbgdyB8-8cRupt6U", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -6609,10 +6609,25 @@ function printHelp() {
6609
6609
  async function cmdStatus(flags) {
6610
6610
  const healthy = await fetch(`${BASE_URL}/health`, { signal: AbortSignal.timeout(2000) }).then((r) => r.ok).catch(() => false);
6611
6611
  const versionInfo = checkServerVersion(BASE_URL, import.meta.url);
6612
+ let activeSessions = [];
6613
+ let sessionCount = 0;
6614
+ if (healthy) {
6615
+ try {
6616
+ const res = await fetch(`${BASE_URL}/v1/browse/sessions`, { signal: AbortSignal.timeout(2000) });
6617
+ if (res.ok) {
6618
+ const data = await res.json();
6619
+ activeSessions = data.sessions ?? [];
6620
+ sessionCount = data.count ?? activeSessions.length;
6621
+ }
6622
+ } catch {}
6623
+ }
6612
6624
  output({
6613
6625
  server: healthy ? "running" : "stopped",
6614
6626
  url: BASE_URL,
6615
- ...versionInfo ?? {}
6627
+ ...versionInfo ?? {},
6628
+ active_browse_sessions: sessionCount,
6629
+ sessions: activeSessions,
6630
+ chrome_debug_url: process.env.CHROME_DEBUG_URL ?? null
6616
6631
  }, !!flags.pretty);
6617
6632
  }
6618
6633
  async function cmdRestart(flags) {
package/dist/mcp.js CHANGED
@@ -226,11 +226,11 @@ import { dirname, join, parse } from "path";
226
226
  import { fileURLToPath as fileURLToPath2 } from "url";
227
227
 
228
228
  // ../../src/build-info.generated.ts
229
- var BUILD_RELEASE_VERSION = "6.5.2";
230
- var BUILD_GIT_SHA = "2bedcaef3363";
229
+ var BUILD_RELEASE_VERSION = "6.6.0-preview.0";
230
+ var BUILD_GIT_SHA = "e74cd1481aa5";
231
231
  var BUILD_CODE_HASH = "5d9ebf619c61";
232
- var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi41LjIiLCJnaXRfc2hhIjoiMmJlZGNhZWYzMzYzIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAyYmVkY2FlZjMzNjMiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTAzVDIzOjM2OjIyLjUxM1oifQ";
233
- var BUILD_RELEASE_MANIFEST_SIGNATURE = "wNWNFYL91xCcb02bCfQvbVnHfmLAUCyHeX-oVLv2Wy4";
232
+ var BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAtcHJldmlldy4wIiwiZ2l0X3NoYSI6ImU3NGNkMTQ4MWFhNSIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFAZTc0Y2QxNDgxYWE1IiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wNFQwNTo1MTo1Mi4yNTNaIn0";
233
+ var BUILD_RELEASE_MANIFEST_SIGNATURE = "_hrkIsHrYUpg_q-N3P2xraayyLNVbgdyB8-8cRupt6U";
234
234
  var BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
235
235
  var BUILD_DEFAULT_PROFILE = "";
236
236
 
package/dist/server.js CHANGED
@@ -211,13 +211,11 @@ function resolveKuriLaunchConfig(env = process.env) {
211
211
  headless = true;
212
212
  }
213
213
  const cleanRoom = envFlag(env.UNBROWSE_LOCAL_ONLY) || envFlag(env.KURI_CLEAN_ROOM);
214
- const browserCookieOptOut = falseyEnv(env.UNBROWSE_IMPORT_BROWSER_COOKIES);
215
- const explicitAttach = envFlag(env.KURI_ATTACH_EXISTING_CHROME ?? env.UNBROWSE_ATTACH_EXISTING_CHROME);
216
214
  const disableCdpAttach = envFlag(env.KURI_DISABLE_CDP_ATTACH);
217
- const canAttachToExistingChrome = !headless && !disableCdpAttach && !cleanRoom;
215
+ const attachToExistingChrome = !disableCdpAttach && !cleanRoom;
218
216
  return {
219
217
  headless,
220
- attachToExistingChrome: canAttachToExistingChrome && (explicitAttach || browserCookieOptOut)
218
+ attachToExistingChrome
221
219
  };
222
220
  }
223
221
  function kuriBinaryName() {
@@ -7344,7 +7342,7 @@ var init_capture = __esm(async () => {
7344
7342
  });
7345
7343
 
7346
7344
  // ../../src/build-info.generated.ts
7347
- var BUILD_RELEASE_VERSION = "6.5.2", BUILD_GIT_SHA = "2bedcaef3363", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi41LjIiLCJnaXRfc2hhIjoiMmJlZGNhZWYzMzYzIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAyYmVkY2FlZjMzNjMiLCJpc3N1ZWRfYXQiOiIyMDI2LTA1LTAzVDIzOjM2OjIyLjUxM1oifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "wNWNFYL91xCcb02bCfQvbVnHfmLAUCyHeX-oVLv2Wy4", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
7345
+ var BUILD_RELEASE_VERSION = "6.6.0-preview.0", BUILD_GIT_SHA = "e74cd1481aa5", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNi42LjAtcHJldmlldy4wIiwiZ2l0X3NoYSI6ImU3NGNkMTQ4MWFhNSIsImNvZGVfaGFzaCI6IjVkOWViZjYxOWM2MSIsInRyYWNlX3ZlcnNpb24iOiI1ZDllYmY2MTljNjFAZTc0Y2QxNDgxYWE1IiwiaXNzdWVkX2F0IjoiMjAyNi0wNS0wNFQwNTo1MTo1Mi4yNTNaIn0", BUILD_RELEASE_MANIFEST_SIGNATURE = "_hrkIsHrYUpg_q-N3P2xraayyLNVbgdyB8-8cRupt6U", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai", BUILD_DEFAULT_PROFILE = "";
7348
7346
 
7349
7347
  // ../../src/version.ts
7350
7348
  import { createHash as createHash2 } from "crypto";
@@ -28155,6 +28153,74 @@ async function registerRoutes(app) {
28155
28153
  next_step: settings.auto_publish_checkpoints ? "Auto-publish after sync/close is enabled unless a domain rule blocks it." : "Auto-publish after sync/close is disabled. Use index for local recompute and publish only when you explicitly want remote share."
28156
28154
  });
28157
28155
  });
28156
+ app.get("/v1/browse/sessions", async (_req, reply) => {
28157
+ const sessions = Array.from(browseSessions.values()).map((s) => ({
28158
+ session_id: s.sessionId,
28159
+ tab_id: s.tabId,
28160
+ url: s.url,
28161
+ domain: s.domain,
28162
+ har_active: s.harActive,
28163
+ broker_port: s.brokerPort ?? null,
28164
+ streaming_publish_active: streamingWatchers.has(s.sessionId)
28165
+ }));
28166
+ return reply.send({ sessions, count: sessions.length });
28167
+ });
28168
+ app.get("/v1/browse/sessions/:id/buffer", async (req, reply) => {
28169
+ const { id } = req.params;
28170
+ let session;
28171
+ for (const s of browseSessions.values()) {
28172
+ if (s.sessionId === id || s.tabId === id) {
28173
+ session = s;
28174
+ break;
28175
+ }
28176
+ }
28177
+ if (!session)
28178
+ return reply.code(404).send({ error: "session_not_found", id });
28179
+ let intercepted = [];
28180
+ let interceptError = null;
28181
+ try {
28182
+ intercepted = await collectInterceptedRequests(session.tabId);
28183
+ } catch (err) {
28184
+ interceptError = err instanceof Error ? err.message : String(err);
28185
+ }
28186
+ let harEntries = [];
28187
+ let harError = null;
28188
+ if (session.harActive) {
28189
+ try {
28190
+ const broker = brokerForSession(session);
28191
+ const stopResult = await broker.harStop(session.tabId);
28192
+ harEntries = stopResult.entries ?? [];
28193
+ try {
28194
+ await broker.harStart(session.tabId);
28195
+ } catch {}
28196
+ } catch (err) {
28197
+ harError = err instanceof Error ? err.message : String(err);
28198
+ }
28199
+ }
28200
+ return reply.send({
28201
+ session: {
28202
+ session_id: session.sessionId,
28203
+ tab_id: session.tabId,
28204
+ url: session.url,
28205
+ domain: session.domain,
28206
+ har_active: session.harActive,
28207
+ streaming_publish_active: streamingWatchers.has(session.sessionId)
28208
+ },
28209
+ intercepted_requests: intercepted,
28210
+ intercepted_count: Array.isArray(intercepted) ? intercepted.length : 0,
28211
+ intercept_error: interceptError,
28212
+ har_entries: harEntries.map((e) => ({
28213
+ url: e?.request?.url,
28214
+ method: e?.request?.method,
28215
+ status: e?.response?.status,
28216
+ mime_type: e?.response?.content?.mimeType,
28217
+ size: e?.response?.bodySize
28218
+ })),
28219
+ har_count: harEntries.length,
28220
+ har_error: harError,
28221
+ total_captured: (Array.isArray(intercepted) ? intercepted.length : 0) + harEntries.length
28222
+ });
28223
+ });
28158
28224
  app.get("/v1/trace/:trace_id", async (req, reply) => {
28159
28225
  const { trace_id } = req.params;
28160
28226
  const traceDir = path6.join(process.env.UNBROWSE_TRACE_DIR ?? path6.join(os4.homedir(), ".unbrowse", "traces"));
@@ -28181,6 +28247,25 @@ async function registerRoutes(app) {
28181
28247
  const { intent, params, context, projection, confirm_unsafe, confirm_third_party_terms, dry_run, force_capture, skip_robots_check, visual_context, budget_ms } = req.body;
28182
28248
  if (!intent)
28183
28249
  return reply.code(400).send({ error: "intent required" });
28250
+ let inflightFlushResult = null;
28251
+ if (context?.url && !force_capture) {
28252
+ try {
28253
+ const activeSession = findActiveSessionForDomain(context.url);
28254
+ if (activeSession) {
28255
+ const flushed = await lightFlushBrowseCapture(activeSession);
28256
+ if (flushed.request_count > 0) {
28257
+ inflightFlushResult = {
28258
+ skill_id: flushed.skill_id,
28259
+ request_count: flushed.request_count,
28260
+ endpoint_count: flushed.endpoint_count
28261
+ };
28262
+ console.log(`[in-flight-flush] session=${activeSession.sessionId.slice(0, 8)} domain=${flushed.domain} requests=${flushed.request_count} endpoints=${flushed.endpoint_count} skill=${flushed.skill_id?.slice(0, 15) ?? "none"}`);
28263
+ }
28264
+ }
28265
+ } catch (err) {
28266
+ console.warn(`[in-flight-flush] failed: ${err.message}`);
28267
+ }
28268
+ }
28184
28269
  try {
28185
28270
  const result = await resolveAndExecute(intent, params ?? {}, context, projection, { confirm_unsafe, confirm_third_party_terms, dry_run, force_capture, skip_robots_check, client_scope: clientScope, budget_ms });
28186
28271
  const res = attachAgentOutcomeHints({ ...result }, {
@@ -28209,6 +28294,9 @@ async function registerRoutes(app) {
28209
28294
  }
28210
28295
  } catch {}
28211
28296
  }
28297
+ if (inflightFlushResult) {
28298
+ res.inflight_flush = inflightFlushResult;
28299
+ }
28212
28300
  return reply.send(res);
28213
28301
  } catch (err) {
28214
28302
  return reply.code(500).send({ error: err.message });
@@ -28853,6 +28941,141 @@ async function registerRoutes(app) {
28853
28941
  session.harActive = true;
28854
28942
  await injectInterceptor(session.tabId).catch(() => {});
28855
28943
  }
28944
+ async function lightFlushBrowseCapture(session) {
28945
+ let harEntries = [];
28946
+ if (session.harActive) {
28947
+ try {
28948
+ const broker = brokerForSession(session);
28949
+ const stopResult = await broker.harStop(session.tabId);
28950
+ harEntries = stopResult.entries ?? [];
28951
+ try {
28952
+ await broker.harStart(session.tabId);
28953
+ } catch {}
28954
+ } catch {}
28955
+ }
28956
+ const allRequests = await enrichPassiveCaptureRequests({
28957
+ tabId: session.tabId,
28958
+ captureUrl: session.url,
28959
+ harEntries,
28960
+ intent: `browse ${session.domain || profileName(session.url)}`
28961
+ });
28962
+ if (allRequests.length === 0) {
28963
+ return { skill_id: null, domain: session.domain, request_count: 0, endpoint_count: 0 };
28964
+ }
28965
+ const jsBundles = new Map;
28966
+ try {
28967
+ const intercepted = await collectInterceptedRequests(session.tabId).catch(() => []);
28968
+ for (const entry of intercepted) {
28969
+ if (entry.is_js && entry.response_body && jsBundles.size < 20) {
28970
+ jsBundles.set(entry.url, entry.response_body);
28971
+ }
28972
+ }
28973
+ } catch {}
28974
+ const syncResult = await cacheBrowseRequests({
28975
+ sessionUrl: session.url,
28976
+ sessionDomain: session.domain,
28977
+ requests: allRequests,
28978
+ getPageHtml: () => brokerForSession(session).getPageHtml(session.tabId),
28979
+ jsBundles: jsBundles.size > 0 ? jsBundles : undefined,
28980
+ intent: `browse ${session.domain || profileName(session.url)}`
28981
+ });
28982
+ if (syncResult.skill && syncResult.domain) {
28983
+ const domainKey = getDomainReuseKey(session.url || syncResult.domain);
28984
+ if (domainKey) {
28985
+ const cacheKey2 = scopedCacheKey("local", `${domainKey}:${syncResult.skill.skill_id}`);
28986
+ writeSkillSnapshot(cacheKey2, syncResult.skill);
28987
+ domainSkillCache.set(domainKey, {
28988
+ skillId: syncResult.skill.skill_id,
28989
+ localSkillPath: snapshotPathForCacheKey(cacheKey2),
28990
+ ts: Date.now()
28991
+ });
28992
+ persistDomainCache();
28993
+ }
28994
+ }
28995
+ return {
28996
+ skill_id: syncResult.skill?.skill_id ?? null,
28997
+ domain: syncResult.domain,
28998
+ request_count: allRequests.length,
28999
+ endpoint_count: syncResult.skill?.endpoints.length ?? 0
29000
+ };
29001
+ }
29002
+ function findActiveSessionForDomain(targetUrl) {
29003
+ const targetDomain = getDomainReuseKey(targetUrl);
29004
+ if (!targetDomain)
29005
+ return null;
29006
+ let match = null;
29007
+ for (const session of browseSessions.values()) {
29008
+ if (!session.harActive)
29009
+ continue;
29010
+ const sessionDomain = getDomainReuseKey(session.url);
29011
+ if (sessionDomain === targetDomain)
29012
+ match = session;
29013
+ }
29014
+ return match;
29015
+ }
29016
+ const streamingWatchers = new Map;
29017
+ const STREAMING_INTERVAL_MS = parseInt(process.env.UNBROWSE_STREAMING_INTERVAL_MS ?? "10000", 10);
29018
+ const STREAMING_ENABLED = process.env.UNBROWSE_STREAMING_PUBLISH !== "0";
29019
+ const streamingState = new Map;
29020
+ function startStreamingWatcher(session) {
29021
+ if (!STREAMING_ENABLED)
29022
+ return;
29023
+ if (streamingWatchers.has(session.sessionId))
29024
+ return;
29025
+ streamingState.set(session.sessionId, { lastEndpointCount: 0, lastSkillId: null });
29026
+ const tick = async () => {
29027
+ if (!browseSessions.has(session.sessionId)) {
29028
+ stopStreamingWatcher(session.sessionId);
29029
+ return;
29030
+ }
29031
+ const live = browseSessions.get(session.sessionId);
29032
+ if (!live || !live.harActive)
29033
+ return;
29034
+ try {
29035
+ const flushed = await lightFlushBrowseCapture(live);
29036
+ const state = streamingState.get(session.sessionId);
29037
+ if (!state)
29038
+ return;
29039
+ const grew = flushed.endpoint_count > state.lastEndpointCount || flushed.skill_id && flushed.skill_id !== state.lastSkillId;
29040
+ if (grew && flushed.skill_id) {
29041
+ state.lastEndpointCount = flushed.endpoint_count;
29042
+ state.lastSkillId = flushed.skill_id;
29043
+ const domainKey = getDomainReuseKey(live.url || flushed.domain);
29044
+ const cacheEntry = domainKey ? domainSkillCache.get(domainKey) : null;
29045
+ if (cacheEntry?.localSkillPath) {
29046
+ const skill = readSkillSnapshot(cacheEntry.localSkillPath);
29047
+ if (skill) {
29048
+ const decision = decideCheckpointPublish(flushed.domain);
29049
+ queueBackgroundIndex({
29050
+ skill: { ...skill },
29051
+ domain: flushed.domain,
29052
+ intent: skill.intent_signature || `browse ${flushed.domain}`,
29053
+ contextUrl: live.url,
29054
+ cacheKey: `streaming:${flushed.domain}:${Date.now()}`,
29055
+ publishAfterIndex: decision.publishQueued
29056
+ });
29057
+ console.log(`[streaming-publish] session=${session.sessionId.slice(0, 8)} domain=${flushed.domain} endpoints=${flushed.endpoint_count} publish=${decision.publishQueued}`);
29058
+ }
29059
+ }
29060
+ }
29061
+ } catch (err) {
29062
+ console.warn(`[streaming-publish] tick failed: ${err.message}`);
29063
+ }
29064
+ };
29065
+ const timer = setInterval(() => {
29066
+ tick();
29067
+ }, STREAMING_INTERVAL_MS);
29068
+ streamingWatchers.set(session.sessionId, timer);
29069
+ console.log(`[streaming-publish] watcher started session=${session.sessionId.slice(0, 8)} interval=${STREAMING_INTERVAL_MS}ms`);
29070
+ }
29071
+ function stopStreamingWatcher(sessionId) {
29072
+ const timer = streamingWatchers.get(sessionId);
29073
+ if (timer) {
29074
+ clearInterval(timer);
29075
+ streamingWatchers.delete(sessionId);
29076
+ }
29077
+ streamingState.delete(sessionId);
29078
+ }
28856
29079
  async function flushBrowseCapture(session, options = {}) {
28857
29080
  let harEntries = [];
28858
29081
  if (session.harActive) {
@@ -29061,6 +29284,7 @@ async function registerRoutes(app) {
29061
29284
  }
29062
29285
  }
29063
29286
  } catch {}
29287
+ startStreamingWatcher(session);
29064
29288
  return reply.send({
29065
29289
  ok: true,
29066
29290
  session_id: session.sessionId,
@@ -29068,7 +29292,20 @@ async function registerRoutes(app) {
29068
29292
  tab_id: session.tabId,
29069
29293
  auth_profile: session.domain,
29070
29294
  ...result.cookiesInjected > 0 ? { cookies_injected: result.cookiesInjected } : {},
29071
- ...authRequired ? { auth_required: true, auth_hint: authHint } : {}
29295
+ ...authRequired ? { auth_required: true, auth_hint: authHint } : {},
29296
+ autonomy: (() => {
29297
+ const publishDecision = decideCheckpointPublish(session.domain);
29298
+ return {
29299
+ har_active: session.harActive,
29300
+ streaming_publish_active: streamingWatchers.has(session.sessionId),
29301
+ attached_existing_chrome: result.attachedExistingChrome ?? false,
29302
+ chrome_debug_url: process.env.CHROME_DEBUG_URL ?? null,
29303
+ inspect_buffer: `GET ${process.env.UNBROWSE_API_BASE ?? "http://127.0.0.1:6969"}/v1/browse/sessions/${session.sessionId}/buffer`,
29304
+ marketplace_publish_enabled: publishDecision.publishQueued,
29305
+ marketplace_publish_mode: publishDecision.mode,
29306
+ marketplace_publish_reason: publishDecision.reason
29307
+ };
29308
+ })()
29072
29309
  });
29073
29310
  } catch (error) {
29074
29311
  return sendBrowseSessionError(reply, error);
@@ -29110,6 +29347,7 @@ async function registerRoutes(app) {
29110
29347
  activeSession.domain = profileName(activeSession.url);
29111
29348
  const stillLive = await isBrowseSessionLive(activeSession, browseClient).catch(() => false);
29112
29349
  if (!stillLive) {
29350
+ stopStreamingWatcher(activeSession.sessionId);
29113
29351
  removeBrowseSession(browseSessions, activeSession.sessionId);
29114
29352
  throw new BrowseSessionError("session_expired");
29115
29353
  }
@@ -29337,6 +29575,7 @@ async function registerRoutes(app) {
29337
29575
  await saveAuthProfileBestEffort(session2.tabId, session2.domain, "browse_close");
29338
29576
  }
29339
29577
  const syncResult2 = await flushBrowseCapture(session2, { queueIndex: true, queuePublish: true });
29578
+ stopStreamingWatcher(session2.sessionId);
29340
29579
  await broker.closeTab(session2.tabId).catch(() => {});
29341
29580
  removeBrowseSession(browseSessions, session2.sessionId);
29342
29581
  return syncResult2;
@@ -29459,6 +29698,16 @@ async function startUnbrowseServer(options = {}) {
29459
29698
  process.env.UNBROWSE_SKIP_TOS_CHECK = "1";
29460
29699
  }
29461
29700
  startBackgroundRegistration();
29701
+ const UNBROWSE_CDP_PORT = Number(process.env.UNBROWSE_CDP_PORT ?? 9222);
29702
+ if (!process.env.CHROME_DEBUG_URL) {
29703
+ process.env.CHROME_DEBUG_URL = `http://127.0.0.1:${UNBROWSE_CDP_PORT}`;
29704
+ }
29705
+ if (!process.env.PUPPETEER_BROWSER_WS_ENDPOINT) {
29706
+ process.env.PUPPETEER_BROWSER_WS_ENDPOINT = `ws://127.0.0.1:${UNBROWSE_CDP_PORT}`;
29707
+ }
29708
+ if (!process.env.PLAYWRIGHT_CHROMIUM_REMOTE_DEBUGGING_URL) {
29709
+ process.env.PLAYWRIGHT_CHROMIUM_REMOTE_DEBUGGING_URL = `http://127.0.0.1:${UNBROWSE_CDP_PORT}`;
29710
+ }
29462
29711
  const app = Fastify({ logger: options.logger ?? true });
29463
29712
  await app.register(cors, { origin: true });
29464
29713
  await registerRateLimiter(app);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbrowse",
3
- "version": "6.5.2",
3
+ "version": "6.6.0-preview.0",
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": {