chrome-ai-bridge 2.3.1 → 2.3.2
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/build/src/config.js +1 -1
- package/build/src/main.js +54 -20
- package/build/src/process-lock.js +24 -0
- package/package.json +1 -1
package/build/src/config.js
CHANGED
|
@@ -58,7 +58,7 @@ export function getSessionConfig() {
|
|
|
58
58
|
};
|
|
59
59
|
return {
|
|
60
60
|
sessionTtlMinutes: raw.ttl > 0 ? raw.ttl : 30,
|
|
61
|
-
maxAgents: raw.max > 0 ? Math.floor(raw.max) :
|
|
61
|
+
maxAgents: raw.max > 0 ? Math.floor(raw.max) : 20,
|
|
62
62
|
cleanupIntervalMinutes: raw.interval > 0 ? raw.interval : 5,
|
|
63
63
|
};
|
|
64
64
|
}
|
package/build/src/main.js
CHANGED
|
@@ -32,7 +32,7 @@ import { cleanupAllConnections } from './fast-cdp/fast-chat.js';
|
|
|
32
32
|
import { generateAgentId, setAgentId } from './fast-cdp/agent-context.js';
|
|
33
33
|
import { cleanupStaleSessions } from './fast-cdp/session-manager.js';
|
|
34
34
|
import { getSessionConfig, IPC_CONFIG } from './config.js';
|
|
35
|
-
import {
|
|
35
|
+
import { releaseLock, tryAcquireLockSafe, checkExistingPrimary, updateLockPort } from './process-lock.js';
|
|
36
36
|
import { checkPrimaryHealth, startProxyMode } from './stdio-http-proxy.js';
|
|
37
37
|
function readPackageJson() {
|
|
38
38
|
const currentDir = import.meta.dirname;
|
|
@@ -56,28 +56,62 @@ logger(`Starting Chrome AI Bridge v${version} (Extension-only mode)`);
|
|
|
56
56
|
// Initialize agent ID for Agent Teams support
|
|
57
57
|
const agentId = generateAgentId();
|
|
58
58
|
setAgentId(agentId);
|
|
59
|
-
// ─── Multi-client routing ───
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// ─── Multi-client routing with retry ───
|
|
60
|
+
// Handles concurrent startup of many processes (e.g. tproj 16-pane scenario).
|
|
61
|
+
// Each process tries to become Primary or fall back to Proxy mode,
|
|
62
|
+
// with exponential backoff + jitter to avoid thundering herd.
|
|
63
|
+
const MAX_STARTUP_ATTEMPTS = 5;
|
|
64
|
+
const BASE_DELAY_MS = 300;
|
|
65
|
+
const HEALTH_CHECK_RETRIES = 3;
|
|
66
|
+
const HEALTH_CHECK_INTERVAL_MS = 500;
|
|
67
|
+
const instanceId = randomUUID();
|
|
68
|
+
let becamePrimary = false;
|
|
69
|
+
for (let attempt = 0; attempt < MAX_STARTUP_ATTEMPTS; attempt++) {
|
|
70
|
+
// 1. Try to become Primary (non-throwing)
|
|
71
|
+
const lockAcquired = await tryAcquireLockSafe(IPC_CONFIG.port, instanceId);
|
|
72
|
+
if (lockAcquired) {
|
|
73
|
+
becamePrimary = true;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// 2. Lock held by another process — try to connect as Proxy
|
|
77
|
+
const existingPrimary = checkExistingPrimary();
|
|
78
|
+
if (existingPrimary && existingPrimary.port > 0) {
|
|
79
|
+
for (let hc = 0; hc < HEALTH_CHECK_RETRIES; hc++) {
|
|
80
|
+
const healthy = await checkPrimaryHealth(existingPrimary.port);
|
|
81
|
+
if (healthy) {
|
|
82
|
+
logger(`[main] Primary is healthy (port=${existingPrimary.port}). Entering proxy mode.`);
|
|
83
|
+
await startProxyMode(existingPrimary.port); // never returns
|
|
84
|
+
}
|
|
85
|
+
if (hc < HEALTH_CHECK_RETRIES - 1) {
|
|
86
|
+
const jitter = Math.random() * HEALTH_CHECK_INTERVAL_MS;
|
|
87
|
+
await new Promise(r => setTimeout(r, HEALTH_CHECK_INTERVAL_MS + jitter));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
logger(`[main] Primary (port=${existingPrimary.port}) not healthy after ${HEALTH_CHECK_RETRIES} retries.`);
|
|
91
|
+
}
|
|
92
|
+
// 3. Neither Primary nor Proxy — backoff with jitter and retry
|
|
93
|
+
if (attempt < MAX_STARTUP_ATTEMPTS - 1) {
|
|
94
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
95
|
+
const jitter = Math.random() * BASE_DELAY_MS;
|
|
96
|
+
const delay = backoff + jitter;
|
|
97
|
+
logger(`[main] Startup attempt ${attempt + 1}/${MAX_STARTUP_ATTEMPTS} failed. Retrying in ${Math.round(delay)}ms...`);
|
|
98
|
+
await new Promise(r => setTimeout(r, delay));
|
|
68
99
|
}
|
|
69
|
-
logger(`[main] Primary (port=${existingPrimary.port}) not healthy. Starting as Primary.`);
|
|
70
100
|
}
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
|
|
101
|
+
if (!becamePrimary) {
|
|
102
|
+
// Final fallback: one last proxy attempt before giving up
|
|
103
|
+
const existingPrimary = checkExistingPrimary();
|
|
104
|
+
if (existingPrimary && existingPrimary.port > 0) {
|
|
105
|
+
const healthy = await checkPrimaryHealth(existingPrimary.port);
|
|
106
|
+
if (healthy) {
|
|
107
|
+
logger(`[main] Final fallback: entering proxy mode (port=${existingPrimary.port}).`);
|
|
108
|
+
await startProxyMode(existingPrimary.port); // never returns
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
logger('[main] Failed to start as Primary or Proxy after all retries. Exiting.');
|
|
112
|
+
process.exit(1);
|
|
76
113
|
}
|
|
77
|
-
//
|
|
78
|
-
const instanceId = randomUUID();
|
|
79
|
-
// Acquire exclusive process lock (writes port + instanceId to lock file)
|
|
80
|
-
await acquireLock(IPC_CONFIG.port, instanceId);
|
|
114
|
+
// ─── Primary mode ───
|
|
81
115
|
// Start session cleanup timer
|
|
82
116
|
const sessionConfig = getSessionConfig();
|
|
83
117
|
const cleanupTimer = setInterval(async () => {
|
|
@@ -133,6 +133,30 @@ async function handleExistingLock() {
|
|
|
133
133
|
logger(`[process-lock] Primary is alive (pid=${info.pid}, port=${info.port}). Cannot acquire lock.`);
|
|
134
134
|
return false;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Try to acquire lock without throwing on failure.
|
|
138
|
+
* Returns true if lock acquired, false if another process holds it.
|
|
139
|
+
* Used by the retry-based startup loop in main.ts.
|
|
140
|
+
*/
|
|
141
|
+
export async function tryAcquireLockSafe(port, instanceId) {
|
|
142
|
+
const fd = tryCreateLock(port, instanceId);
|
|
143
|
+
if (fd !== null) {
|
|
144
|
+
lockFd = fd;
|
|
145
|
+
logger(`[process-lock] Lock acquired (pid=${process.pid}, port=${port}, instanceId=${instanceId.slice(0, 8)})`);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
const canRetry = await handleExistingLock();
|
|
149
|
+
if (!canRetry) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const fd2 = tryCreateLock(port, instanceId);
|
|
153
|
+
if (fd2 !== null) {
|
|
154
|
+
lockFd = fd2;
|
|
155
|
+
logger(`[process-lock] Lock acquired after cleanup (pid=${process.pid}, port=${port})`);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
136
160
|
/**
|
|
137
161
|
* Acquire an exclusive process lock. Call once at startup for Primary mode.
|
|
138
162
|
*
|
package/package.json
CHANGED