@parall/daemon 1.33.0 → 1.34.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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.33.0",
3
- "built_at": "2026-06-08T13:37:00.737Z",
2
+ "version": "1.34.0",
3
+ "built_at": "2026-06-11T05:16:27.458Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,21 +16,21 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-claude-agent.js": {
19
- "sha256": "fd2395bb9ba5bc2912175d42419c4073ae581bd9382be6447d9169ce177e915c",
20
- "size": 1544348
19
+ "sha256": "57d99d5cef75b55e1449f3f830f018ac421720583e5cb5c802727bd679e57095",
20
+ "size": 1544820
21
21
  },
22
22
  "parall-codex-agent.js": {
23
- "sha256": "fa93fb3b2aab23b6f3ecd218a0c0124bbe323af11c0ced898c2d057ad293f03b",
24
- "size": 1575437
23
+ "sha256": "602db4f45264dd2859011744d5abdb0c115fb1ca74e722d1e4b72270ef7b48fb",
24
+ "size": 1575909
25
25
  },
26
26
  "parall-daemon.js": {
27
- "sha256": "83d37677018aa1bf0a37df14baaade7738bec2f907fa5d1f66b4bcad77430535",
28
- "size": 1565851
27
+ "sha256": "2be2ea545d7eb22366f09818e1efeb1dbc0c9c1ee23312a279738e24ad817de6",
28
+ "size": 1566501
29
29
  },
30
30
  "parall-openclaw-agent.js": {
31
31
  "sha256": "505433b4bf3a4b8dbb7b96978d8693d6c48079f4513282ff1ee59a6d13b3eddf",
32
32
  "size": 9801
33
33
  }
34
34
  },
35
- "signature": "z7sT/o8aV5gfuQpr9TYQ+IkY3+IfdC5EiKrFKoQ4kz3TO1zXBC1RQ4xh1M0cFdB9KCDbaGeti2smdYAAczoCAQ=="
35
+ "signature": "0Q0fz/iU46D4TzErmojSn08ldPZNYgVquvZS730veaqKBmWwqzeG5R1kx7zPF4L8mrh/EQwMEiir77n89NyTBw=="
36
36
  }
@@ -27297,6 +27297,8 @@ var ENDPOINTS = {
27297
27297
  PUSH_VAPID_KEY: `${API_BASE}/push/vapid-key`,
27298
27298
  // Notification preferences
27299
27299
  NOTIFICATION_PREFERENCES: `${API_BASE}/notification-preferences`,
27300
+ // Unified search (messages + tasks + wiki, org-scoped)
27301
+ SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
27300
27302
  // Feature flags (org-scoped, server-evaluated)
27301
27303
  FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
27302
27304
  // Billing & Credits (org-scoped)
@@ -27430,8 +27432,13 @@ var ParallClient = class _ParallClient {
27430
27432
  /** Proactive refresh when token expires within this window (seconds). */
27431
27433
  static REFRESH_THRESHOLD_S = 5 * 60;
27432
27434
  static normalizeFetchError(err) {
27433
- if (typeof DOMException !== "undefined" && err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError")) {
27434
- return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27435
+ if (typeof DOMException !== "undefined" && err instanceof DOMException) {
27436
+ if (err.name === "TimeoutError") {
27437
+ return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27438
+ }
27439
+ if (err.name === "AbortError") {
27440
+ return new ApiError(0, "Request aborted", "REQUEST_ABORTED");
27441
+ }
27435
27442
  }
27436
27443
  const apiError = new ApiError(0, "Network request failed", "NETWORK_ERROR");
27437
27444
  if (err instanceof Error && err.message && err.message !== "Failed to fetch") {
@@ -27538,13 +27545,15 @@ var ParallClient = class _ParallClient {
27538
27545
  url += `?${qs}`;
27539
27546
  }
27540
27547
  const headers = this.buildHeaders();
27548
+ const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
27549
+ const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
27541
27550
  let res;
27542
27551
  try {
27543
27552
  res = await fetch(url, {
27544
27553
  method,
27545
27554
  headers,
27546
27555
  body: body ? JSON.stringify(body) : void 0,
27547
- signal: AbortSignal.timeout(opts?.timeoutMs ?? 15e3)
27556
+ signal
27548
27557
  });
27549
27558
  } catch (err) {
27550
27559
  throw _ParallClient.normalizeFetchError(err);
@@ -28492,6 +28501,9 @@ var ParallClient = class _ParallClient {
28492
28501
  async getWikiNodeSections(orgId, wikiId, params) {
28493
28502
  return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
28494
28503
  }
28504
+ async search(orgId, params, opts) {
28505
+ return this.request("GET", ENDPOINTS.SEARCH(orgId), void 0, params, false, opts);
28506
+ }
28495
28507
  async searchWiki(orgId, wikiId, params) {
28496
28508
  return this.request("GET", ENDPOINTS.WIKI_SEARCH(orgId, wikiId), void 0, params);
28497
28509
  }
@@ -27297,6 +27297,8 @@ var ENDPOINTS = {
27297
27297
  PUSH_VAPID_KEY: `${API_BASE}/push/vapid-key`,
27298
27298
  // Notification preferences
27299
27299
  NOTIFICATION_PREFERENCES: `${API_BASE}/notification-preferences`,
27300
+ // Unified search (messages + tasks + wiki, org-scoped)
27301
+ SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
27300
27302
  // Feature flags (org-scoped, server-evaluated)
27301
27303
  FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
27302
27304
  // Billing & Credits (org-scoped)
@@ -27430,8 +27432,13 @@ var ParallClient = class _ParallClient {
27430
27432
  /** Proactive refresh when token expires within this window (seconds). */
27431
27433
  static REFRESH_THRESHOLD_S = 5 * 60;
27432
27434
  static normalizeFetchError(err) {
27433
- if (typeof DOMException !== "undefined" && err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError")) {
27434
- return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27435
+ if (typeof DOMException !== "undefined" && err instanceof DOMException) {
27436
+ if (err.name === "TimeoutError") {
27437
+ return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27438
+ }
27439
+ if (err.name === "AbortError") {
27440
+ return new ApiError(0, "Request aborted", "REQUEST_ABORTED");
27441
+ }
27435
27442
  }
27436
27443
  const apiError = new ApiError(0, "Network request failed", "NETWORK_ERROR");
27437
27444
  if (err instanceof Error && err.message && err.message !== "Failed to fetch") {
@@ -27538,13 +27545,15 @@ var ParallClient = class _ParallClient {
27538
27545
  url += `?${qs}`;
27539
27546
  }
27540
27547
  const headers = this.buildHeaders();
27548
+ const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
27549
+ const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
27541
27550
  let res;
27542
27551
  try {
27543
27552
  res = await fetch(url, {
27544
27553
  method,
27545
27554
  headers,
27546
27555
  body: body ? JSON.stringify(body) : void 0,
27547
- signal: AbortSignal.timeout(opts?.timeoutMs ?? 15e3)
27556
+ signal
27548
27557
  });
27549
27558
  } catch (err) {
27550
27559
  throw _ParallClient.normalizeFetchError(err);
@@ -28492,6 +28501,9 @@ var ParallClient = class _ParallClient {
28492
28501
  async getWikiNodeSections(orgId, wikiId, params) {
28493
28502
  return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
28494
28503
  }
28504
+ async search(orgId, params, opts) {
28505
+ return this.request("GET", ENDPOINTS.SEARCH(orgId), void 0, params, false, opts);
28506
+ }
28495
28507
  async searchWiki(orgId, wikiId, params) {
28496
28508
  return this.request("GET", ENDPOINTS.WIKI_SEARCH(orgId, wikiId), void 0, params);
28497
28509
  }
@@ -27334,6 +27334,8 @@ var ENDPOINTS = {
27334
27334
  PUSH_VAPID_KEY: `${API_BASE}/push/vapid-key`,
27335
27335
  // Notification preferences
27336
27336
  NOTIFICATION_PREFERENCES: `${API_BASE}/notification-preferences`,
27337
+ // Unified search (messages + tasks + wiki, org-scoped)
27338
+ SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
27337
27339
  // Feature flags (org-scoped, server-evaluated)
27338
27340
  FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
27339
27341
  // Billing & Credits (org-scoped)
@@ -27467,8 +27469,13 @@ var ParallClient = class _ParallClient {
27467
27469
  /** Proactive refresh when token expires within this window (seconds). */
27468
27470
  static REFRESH_THRESHOLD_S = 5 * 60;
27469
27471
  static normalizeFetchError(err) {
27470
- if (typeof DOMException !== "undefined" && err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError")) {
27471
- return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27472
+ if (typeof DOMException !== "undefined" && err instanceof DOMException) {
27473
+ if (err.name === "TimeoutError") {
27474
+ return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27475
+ }
27476
+ if (err.name === "AbortError") {
27477
+ return new ApiError(0, "Request aborted", "REQUEST_ABORTED");
27478
+ }
27472
27479
  }
27473
27480
  const apiError = new ApiError(0, "Network request failed", "NETWORK_ERROR");
27474
27481
  if (err instanceof Error && err.message && err.message !== "Failed to fetch") {
@@ -27575,13 +27582,15 @@ var ParallClient = class _ParallClient {
27575
27582
  url += `?${qs}`;
27576
27583
  }
27577
27584
  const headers = this.buildHeaders();
27585
+ const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
27586
+ const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
27578
27587
  let res;
27579
27588
  try {
27580
27589
  res = await fetch(url, {
27581
27590
  method,
27582
27591
  headers,
27583
27592
  body: body ? JSON.stringify(body) : void 0,
27584
- signal: AbortSignal.timeout(opts?.timeoutMs ?? 15e3)
27593
+ signal
27585
27594
  });
27586
27595
  } catch (err) {
27587
27596
  throw _ParallClient.normalizeFetchError(err);
@@ -28529,6 +28538,9 @@ var ParallClient = class _ParallClient {
28529
28538
  async getWikiNodeSections(orgId, wikiId, params) {
28530
28539
  return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
28531
28540
  }
28541
+ async search(orgId, params, opts) {
28542
+ return this.request("GET", ENDPOINTS.SEARCH(orgId), void 0, params, false, opts);
28543
+ }
28532
28544
  async searchWiki(orgId, wikiId, params) {
28533
28545
  return this.request("GET", ENDPOINTS.WIKI_SEARCH(orgId, wikiId), void 0, params);
28534
28546
  }
@@ -32068,30 +32080,13 @@ var BrowserProfileManager = class {
32068
32080
  throw new Error("browser profile id is required");
32069
32081
  this.reportedStatuses.delete(profileId);
32070
32082
  const url = normalizeStartUrl(startUrl);
32071
- let prepareResult = {
32072
- accountCreated: false,
32073
- accountExistedBeforePrepare: true
32074
- };
32075
- let firstAccountExistedBeforePrepare;
32076
- let recoveredDuringPrepare = false;
32077
- try {
32078
- prepareResult = await this.withDaemonRecovery(() => this.prepareOpenProfile(profileId, url, (exists) => {
32079
- firstAccountExistedBeforePrepare ??= exists;
32080
- }), `prepare open profile ${profileId}`, () => {
32081
- recoveredDuringPrepare = true;
32082
- });
32083
- } catch (err) {
32084
- const message = err instanceof Error ? err.message : String(err);
32085
- this.reportStatus(profileId, "error", message);
32086
- throw err;
32087
- }
32088
32083
  try {
32089
- await this.openProfileTabOnce(profileId, url, prepareResult.accountCreated, recoveredDuringPrepare, firstAccountExistedBeforePrepare ?? prepareResult.accountExistedBeforePrepare);
32084
+ await this.withDaemonRecovery(async () => {
32085
+ const prepareResult = await this.prepareOpenProfile(profileId, url);
32086
+ await this.openProfileTabOnce(profileId, url, prepareResult.accountCreated);
32087
+ }, `open profile ${profileId}`);
32090
32088
  this.reportStatus(profileId, "running");
32091
32089
  } catch (err) {
32092
- if (isRecoverableBrowserDaemonError(err)) {
32093
- await this.restartDaemon(`open profile ${profileId}`, err);
32094
- }
32095
32090
  const message = err instanceof Error ? err.message : String(err);
32096
32091
  this.reportStatus(profileId, "error", message);
32097
32092
  throw err;
@@ -32133,23 +32128,16 @@ var BrowserProfileManager = class {
32133
32128
  daemon.child.kill("SIGTERM");
32134
32129
  }
32135
32130
  }
32136
- async prepareOpenProfile(profileId, url, recordAccountExists) {
32137
- const accountExistedBeforePrepare = await this.accountExists(profileId);
32138
- recordAccountExists?.(accountExistedBeforePrepare);
32131
+ async prepareOpenProfile(profileId, url) {
32139
32132
  const accountCreated = await this.ensureAccount(profileId, url !== "about:blank" ? url : void 0);
32140
32133
  await this.sendCommand({ method: "tab_list", account: profileId });
32141
- return { accountCreated, accountExistedBeforePrepare };
32134
+ return { accountCreated };
32142
32135
  }
32143
- async openProfileTabOnce(profileId, url, accountCreated, recoveredDuringPrepare, accountExistedBeforePrepare) {
32136
+ async openProfileTabOnce(profileId, url, accountCreated) {
32144
32137
  if (accountCreated)
32145
32138
  return;
32146
- if (recoveredDuringPrepare && !accountExistedBeforePrepare) {
32147
- if (url === "about:blank")
32148
- return;
32149
- const host = safeUrlHost(url);
32150
- if (host && await this.findAccountTabOnHost(profileId, host) !== void 0)
32151
- return;
32152
- }
32139
+ if (await this.profileAlreadyHasOpenTab(profileId, url))
32140
+ return;
32153
32141
  await this.sendCommand({ method: "tab_new", account: profileId, url });
32154
32142
  }
32155
32143
  async resetProfileOnce(profileId) {
@@ -32291,12 +32279,40 @@ var BrowserProfileManager = class {
32291
32279
  }
32292
32280
  }
32293
32281
  async ensureAccountOwnedTab(account) {
32294
- const list = await this.sendCommand({ method: "tab_list", account });
32295
- const tabs = Array.isArray(list.tabs) ? list.tabs : [];
32296
- if (tabs.some((tab) => tab?.account === account))
32282
+ if (await this.hasAccountOwnedTab(account))
32297
32283
  return;
32298
32284
  await this.sendCommand({ method: "tab_new", account, url: "about:blank" });
32299
32285
  }
32286
+ async profileAlreadyHasOpenTab(profileId, url) {
32287
+ if (url === "about:blank")
32288
+ return this.hasAccountOwnedTab(profileId);
32289
+ return this.findAccountTabMatchingUrl(profileId, url);
32290
+ }
32291
+ async hasAccountOwnedTab(account) {
32292
+ const list = await this.sendCommand({ method: "tab_list", account });
32293
+ const tabs = Array.isArray(list.tabs) ? list.tabs : [];
32294
+ return tabs.some((tab) => tab?.account === account);
32295
+ }
32296
+ async findAccountTabMatchingUrl(account, url) {
32297
+ const expected = comparableUrl(url);
32298
+ if (!expected)
32299
+ return false;
32300
+ const list = await this.sendCommand({ method: "tab_list", account });
32301
+ const tabs = Array.isArray(list.tabs) ? list.tabs : [];
32302
+ for (const tab of tabs) {
32303
+ if (tab?.account !== account || typeof tab.url !== "string")
32304
+ continue;
32305
+ const actual = comparableUrl(tab.url);
32306
+ if (!actual)
32307
+ continue;
32308
+ const sameResource = hostsMatch(actual.host, expected.host) && actual.target === expected.target;
32309
+ const sameProtocol = actual.protocol === expected.protocol;
32310
+ const redirectedUpgrade = expected.protocol === "http:" && actual.protocol === "https:";
32311
+ if (sameResource && (sameProtocol || redirectedUpgrade))
32312
+ return true;
32313
+ }
32314
+ return false;
32315
+ }
32300
32316
  reportStatus(profileId, status, errorMsg) {
32301
32317
  const marker = `${status}\0${errorMsg ?? ""}`;
32302
32318
  if (this.reportedStatuses.get(profileId) === marker)
@@ -32328,14 +32344,13 @@ var BrowserProfileManager = class {
32328
32344
  this.starting = null;
32329
32345
  }
32330
32346
  }
32331
- async withDaemonRecovery(operation, label, onRecovered) {
32347
+ async withDaemonRecovery(operation, label) {
32332
32348
  try {
32333
32349
  return await operation();
32334
32350
  } catch (err) {
32335
32351
  if (!isRecoverableBrowserDaemonError(err))
32336
32352
  throw err;
32337
32353
  await this.restartDaemon(label, err);
32338
- onRecovered?.();
32339
32354
  return operation();
32340
32355
  }
32341
32356
  }
@@ -32355,7 +32370,7 @@ var BrowserProfileManager = class {
32355
32370
  this.starting = null;
32356
32371
  this.ensuredAccounts.clear();
32357
32372
  this.ensuringAccounts.clear();
32358
- this.opts.log.warn(`[bb-browser] ${label} hit a disconnected Chrome/CDP session; restarting bb-browser-daemon (${formatErrorForLog(err)})`);
32373
+ this.opts.log.warn(`[bb-browser] ${label} hit an unavailable Chrome/CDP page target; restarting bb-browser-daemon (${formatErrorForLog(err)})`);
32359
32374
  if (!daemon)
32360
32375
  return;
32361
32376
  try {
@@ -32471,13 +32486,18 @@ function isBrowserAccountInfoUnauthenticatedError(err) {
32471
32486
  function isRecoverableBrowserDaemonError(err) {
32472
32487
  const message = err instanceof Error ? err.message : String(err);
32473
32488
  const lower = message.toLowerCase();
32474
- return lower.includes("chrome not connected") || lower.includes("cdp at 127.0.0.1") || lower.includes("bb-browser /command returned 503") && (lower.includes("chrome") || lower.includes("cdp"));
32489
+ return lower.includes("chrome not connected") || lower.includes("cdp at 127.0.0.1") || lower.includes("no page target found") || lower.includes("bb-browser /command returned 503") && (lower.includes("chrome") || lower.includes("cdp"));
32475
32490
  }
32476
- function safeUrlHost(url) {
32491
+ function comparableUrl(url) {
32477
32492
  try {
32478
- return new URL(url).host;
32493
+ const parsed = new URL(url);
32494
+ return {
32495
+ protocol: parsed.protocol,
32496
+ host: parsed.host,
32497
+ target: `${parsed.pathname}${parsed.search}${parsed.hash}`
32498
+ };
32479
32499
  } catch {
32480
- return "";
32500
+ return null;
32481
32501
  }
32482
32502
  }
32483
32503
  function formatErrorForLog(err) {
@@ -54,6 +54,9 @@ export declare class BrowserProfileManager {
54
54
  private findAccountTabOnHost;
55
55
  private ensureAccount;
56
56
  private ensureAccountOwnedTab;
57
+ private profileAlreadyHasOpenTab;
58
+ private hasAccountOwnedTab;
59
+ private findAccountTabMatchingUrl;
57
60
  private reportStatus;
58
61
  private sendCommand;
59
62
  private ensureDaemon;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"AAQA,KAAK,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7F;AASD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AA2BD,qBAAa,qBAAqB;IAQpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;gBAEjC,IAAI,EAAE,4BAA4B;IAIzD,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CtF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmDhE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBb,kBAAkB;YAelB,kBAAkB;YAgBlB,gBAAgB;YAOhB,oBAAoB;YAKpB,yBAAyB;YAezB,aAAa;YAWb,mBAAmB;IAqBjC;;;;;;;;;;;;OAYG;YACW,uBAAuB;IAiCrC,oFAAoF;YACtE,oBAAoB;YAuBpB,aAAa;YAiCb,qBAAqB;IASnC,OAAO,CAAC,YAAY;YAON,WAAW;YAaX,YAAY;YAmBZ,kBAAkB;YAelB,aAAa;YAUb,iBAAiB;YAsBjB,WAAW;YAmDX,IAAI;CA2BnB"}
1
+ {"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"AAQA,KAAK,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7F;AASD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AA0BD,qBAAa,qBAAqB;IAQpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;gBAEjC,IAAI,EAAE,4BAA4B;IAIzD,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CtF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBb,kBAAkB;YAYlB,kBAAkB;YAUlB,gBAAgB;YAOhB,oBAAoB;YAKpB,yBAAyB;YAezB,aAAa;YAWb,mBAAmB;IAqBjC;;;;;;;;;;;;OAYG;YACW,uBAAuB;IAiCrC,oFAAoF;YACtE,oBAAoB;YAuBpB,aAAa;YAiCb,qBAAqB;YAKrB,wBAAwB;YAQxB,kBAAkB;YAQlB,yBAAyB;IAqBvC,OAAO,CAAC,YAAY;YAON,WAAW;YAaX,YAAY;YAmBZ,kBAAkB;YAUlB,aAAa;YAUb,iBAAiB;YAsBjB,WAAW;YAmDX,IAAI;CA2BnB"}
@@ -88,32 +88,14 @@ export class BrowserProfileManager {
88
88
  // chatty invoke path while making lifecycle outcomes authoritative.
89
89
  this.reportedStatuses.delete(profileId);
90
90
  const url = normalizeStartUrl(startUrl);
91
- let prepareResult = {
92
- accountCreated: false,
93
- accountExistedBeforePrepare: true,
94
- };
95
- let firstAccountExistedBeforePrepare;
96
- let recoveredDuringPrepare = false;
97
- try {
98
- prepareResult = await this.withDaemonRecovery(() => this.prepareOpenProfile(profileId, url, (exists) => {
99
- firstAccountExistedBeforePrepare ??= exists;
100
- }), `prepare open profile ${profileId}`, () => {
101
- recoveredDuringPrepare = true;
102
- });
103
- }
104
- catch (err) {
105
- const message = err instanceof Error ? err.message : String(err);
106
- this.reportStatus(profileId, 'error', message);
107
- throw err;
108
- }
109
91
  try {
110
- await this.openProfileTabOnce(profileId, url, prepareResult.accountCreated, recoveredDuringPrepare, firstAccountExistedBeforePrepare ?? prepareResult.accountExistedBeforePrepare);
92
+ await this.withDaemonRecovery(async () => {
93
+ const prepareResult = await this.prepareOpenProfile(profileId, url);
94
+ await this.openProfileTabOnce(profileId, url, prepareResult.accountCreated);
95
+ }, `open profile ${profileId}`);
111
96
  this.reportStatus(profileId, 'running');
112
97
  }
113
98
  catch (err) {
114
- if (isRecoverableBrowserDaemonError(err)) {
115
- await this.restartDaemon(`open profile ${profileId}`, err);
116
- }
117
99
  const message = err instanceof Error ? err.message : String(err);
118
100
  this.reportStatus(profileId, 'error', message);
119
101
  throw err;
@@ -160,23 +142,16 @@ export class BrowserProfileManager {
160
142
  daemon.child.kill('SIGTERM');
161
143
  }
162
144
  }
163
- async prepareOpenProfile(profileId, url, recordAccountExists) {
164
- const accountExistedBeforePrepare = await this.accountExists(profileId);
165
- recordAccountExists?.(accountExistedBeforePrepare);
145
+ async prepareOpenProfile(profileId, url) {
166
146
  const accountCreated = await this.ensureAccount(profileId, url !== 'about:blank' ? url : undefined);
167
147
  await this.sendCommand({ method: 'tab_list', account: profileId });
168
- return { accountCreated, accountExistedBeforePrepare };
148
+ return { accountCreated };
169
149
  }
170
- async openProfileTabOnce(profileId, url, accountCreated, recoveredDuringPrepare, accountExistedBeforePrepare) {
150
+ async openProfileTabOnce(profileId, url, accountCreated) {
171
151
  if (accountCreated)
172
152
  return;
173
- if (recoveredDuringPrepare && !accountExistedBeforePrepare) {
174
- if (url === 'about:blank')
175
- return;
176
- const host = safeUrlHost(url);
177
- if (host && (await this.findAccountTabOnHost(profileId, host)) !== undefined)
178
- return;
179
- }
153
+ if (await this.profileAlreadyHasOpenTab(profileId, url))
154
+ return;
180
155
  await this.sendCommand({ method: 'tab_new', account: profileId, url });
181
156
  }
182
157
  async resetProfileOnce(profileId) {
@@ -332,13 +307,46 @@ export class BrowserProfileManager {
332
307
  }
333
308
  }
334
309
  async ensureAccountOwnedTab(account) {
310
+ if (await this.hasAccountOwnedTab(account))
311
+ return;
312
+ await this.sendCommand({ method: 'tab_new', account, url: 'about:blank' });
313
+ }
314
+ async profileAlreadyHasOpenTab(profileId, url) {
315
+ // Lifecycle `open` can be delivered more than once (WS + pending reconcile,
316
+ // retry, or duplicate user action). Treat only the requested target URL as
317
+ // already-open; a same-host different path is still a distinct user intent.
318
+ if (url === 'about:blank')
319
+ return this.hasAccountOwnedTab(profileId);
320
+ return this.findAccountTabMatchingUrl(profileId, url);
321
+ }
322
+ async hasAccountOwnedTab(account) {
335
323
  const list = await this.sendCommand({ method: 'tab_list', account });
336
324
  const tabs = Array.isArray(list.tabs)
337
325
  ? list.tabs
338
326
  : [];
339
- if (tabs.some((tab) => tab?.account === account))
340
- return;
341
- await this.sendCommand({ method: 'tab_new', account, url: 'about:blank' });
327
+ return tabs.some((tab) => tab?.account === account);
328
+ }
329
+ async findAccountTabMatchingUrl(account, url) {
330
+ const expected = comparableUrl(url);
331
+ if (!expected)
332
+ return false;
333
+ const list = await this.sendCommand({ method: 'tab_list', account });
334
+ const tabs = Array.isArray(list.tabs)
335
+ ? list.tabs
336
+ : [];
337
+ for (const tab of tabs) {
338
+ if (tab?.account !== account || typeof tab.url !== 'string')
339
+ continue;
340
+ const actual = comparableUrl(tab.url);
341
+ if (!actual)
342
+ continue;
343
+ const sameResource = hostsMatch(actual.host, expected.host) && actual.target === expected.target;
344
+ const sameProtocol = actual.protocol === expected.protocol;
345
+ const redirectedUpgrade = expected.protocol === 'http:' && actual.protocol === 'https:';
346
+ if (sameResource && (sameProtocol || redirectedUpgrade))
347
+ return true;
348
+ }
349
+ return false;
342
350
  }
343
351
  reportStatus(profileId, status, errorMsg) {
344
352
  const marker = `${status}\u0000${errorMsg ?? ''}`;
@@ -374,7 +382,7 @@ export class BrowserProfileManager {
374
382
  this.starting = null;
375
383
  }
376
384
  }
377
- async withDaemonRecovery(operation, label, onRecovered) {
385
+ async withDaemonRecovery(operation, label) {
378
386
  try {
379
387
  return await operation();
380
388
  }
@@ -382,7 +390,6 @@ export class BrowserProfileManager {
382
390
  if (!isRecoverableBrowserDaemonError(err))
383
391
  throw err;
384
392
  await this.restartDaemon(label, err);
385
- onRecovered?.();
386
393
  return operation();
387
394
  }
388
395
  }
@@ -403,7 +410,7 @@ export class BrowserProfileManager {
403
410
  this.starting = null;
404
411
  this.ensuredAccounts.clear();
405
412
  this.ensuringAccounts.clear();
406
- this.opts.log.warn(`[bb-browser] ${label} hit a disconnected Chrome/CDP session; restarting bb-browser-daemon (${formatErrorForLog(err)})`);
413
+ this.opts.log.warn(`[bb-browser] ${label} hit an unavailable Chrome/CDP page target; restarting bb-browser-daemon (${formatErrorForLog(err)})`);
407
414
  if (!daemon)
408
415
  return;
409
416
  try {
@@ -534,15 +541,21 @@ function isRecoverableBrowserDaemonError(err) {
534
541
  const lower = message.toLowerCase();
535
542
  return (lower.includes('chrome not connected') ||
536
543
  lower.includes('cdp at 127.0.0.1') ||
544
+ lower.includes('no page target found') ||
537
545
  (lower.includes('bb-browser /command returned 503') &&
538
546
  (lower.includes('chrome') || lower.includes('cdp'))));
539
547
  }
540
- function safeUrlHost(url) {
548
+ function comparableUrl(url) {
541
549
  try {
542
- return new URL(url).host;
550
+ const parsed = new URL(url);
551
+ return {
552
+ protocol: parsed.protocol,
553
+ host: parsed.host,
554
+ target: `${parsed.pathname}${parsed.search}${parsed.hash}`,
555
+ };
543
556
  }
544
557
  catch {
545
- return '';
558
+ return null;
546
559
  }
547
560
  }
548
561
  function formatErrorForLog(err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.33.0",
3
+ "version": "1.34.0",
4
4
  "description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,11 +30,11 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@pinixai/bb-browser-pro": "0.15.0",
33
- "@parall/sdk": "1.33.0",
34
- "@parall/claude-agent": "1.33.0",
35
- "@parall/codex-agent": "1.33.0",
36
- "@parall/openclaw-agent": "1.33.0",
37
- "@parall/agent-core": "1.33.0"
33
+ "@parall/agent-core": "1.34.0",
34
+ "@parall/claude-agent": "1.34.0",
35
+ "@parall/codex-agent": "1.34.0",
36
+ "@parall/openclaw-agent": "1.34.0",
37
+ "@parall/sdk": "1.34.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.0.0",