@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.
- package/bundle/manifest.json +9 -9
- package/bundle/parall-claude-agent.js +15 -3
- package/bundle/parall-codex-agent.js +15 -3
- package/bundle/parall-daemon.js +66 -46
- package/dist/clip-runtime/browser-profile-manager.d.ts +3 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +56 -43
- package/package.json +6 -6
package/bundle/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"built_at": "2026-06-
|
|
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": "
|
|
20
|
-
"size":
|
|
19
|
+
"sha256": "57d99d5cef75b55e1449f3f830f018ac421720583e5cb5c802727bd679e57095",
|
|
20
|
+
"size": 1544820
|
|
21
21
|
},
|
|
22
22
|
"parall-codex-agent.js": {
|
|
23
|
-
"sha256": "
|
|
24
|
-
"size":
|
|
23
|
+
"sha256": "602db4f45264dd2859011744d5abdb0c115fb1ca74e722d1e4b72270ef7b48fb",
|
|
24
|
+
"size": 1575909
|
|
25
25
|
},
|
|
26
26
|
"parall-daemon.js": {
|
|
27
|
-
"sha256": "
|
|
28
|
-
"size":
|
|
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": "
|
|
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
|
|
27434
|
-
|
|
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
|
|
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
|
|
27434
|
-
|
|
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
|
|
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
|
}
|
package/bundle/parall-daemon.js
CHANGED
|
@@ -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
|
|
27471
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
32134
|
+
return { accountCreated };
|
|
32142
32135
|
}
|
|
32143
|
-
async openProfileTabOnce(profileId, url, accountCreated
|
|
32136
|
+
async openProfileTabOnce(profileId, url, accountCreated) {
|
|
32144
32137
|
if (accountCreated)
|
|
32145
32138
|
return;
|
|
32146
|
-
if (
|
|
32147
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
32491
|
+
function comparableUrl(url) {
|
|
32477
32492
|
try {
|
|
32478
|
-
|
|
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;
|
|
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.
|
|
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
|
|
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
|
|
148
|
+
return { accountCreated };
|
|
169
149
|
}
|
|
170
|
-
async openProfileTabOnce(profileId, url, accountCreated
|
|
150
|
+
async openProfileTabOnce(profileId, url, accountCreated) {
|
|
171
151
|
if (accountCreated)
|
|
172
152
|
return;
|
|
173
|
-
if (
|
|
174
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
|
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
|
|
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
|
|
548
|
+
function comparableUrl(url) {
|
|
541
549
|
try {
|
|
542
|
-
|
|
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.
|
|
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/
|
|
34
|
-
"@parall/claude-agent": "1.
|
|
35
|
-
"@parall/codex-agent": "1.
|
|
36
|
-
"@parall/openclaw-agent": "1.
|
|
37
|
-
"@parall/
|
|
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",
|