granclaw 0.0.1-beta.25 → 0.0.1-beta.26
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.
|
@@ -28,6 +28,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
28
28
|
exports.STEALTH_EXTENSION_DIR = void 0;
|
|
29
29
|
exports.stealthArgv = stealthArgv;
|
|
30
30
|
exports.injectStealthViaCdp = injectStealthViaCdp;
|
|
31
|
+
exports.prewarmStealthDaemon = prewarmStealthDaemon;
|
|
31
32
|
exports.__resetStealthCacheForTests = __resetStealthCacheForTests;
|
|
32
33
|
const fs_1 = __importDefault(require("fs"));
|
|
33
34
|
const path_1 = __importDefault(require("path"));
|
|
@@ -122,23 +123,26 @@ function stealthArgv() {
|
|
|
122
123
|
}
|
|
123
124
|
// ── CDP stealth injection ─────────────────────────────────────────────────────
|
|
124
125
|
/**
|
|
125
|
-
* Poll for the browser-level CDP WebSocket URL.
|
|
126
|
-
*
|
|
126
|
+
* Poll for the browser-level CDP WebSocket URL.
|
|
127
|
+
*
|
|
128
|
+
* @param retries How many attempts to make (default 8, ~2.4 s). Pass 1 for
|
|
129
|
+
* a fast single-shot check used by the background watcher.
|
|
127
130
|
*/
|
|
128
|
-
async function discoverCdpUrl(sessionId, workspaceDir) {
|
|
131
|
+
async function discoverCdpUrl(sessionId, workspaceDir, retries = 8) {
|
|
129
132
|
const bin = process.env.AGENT_BROWSER_BIN ?? 'agent-browser';
|
|
130
|
-
for (let i = 0; i <
|
|
133
|
+
for (let i = 0; i < retries; i++) {
|
|
131
134
|
try {
|
|
132
135
|
const { stdout } = await execFileAsync(bin, ['--session', sessionId, 'get', 'cdp-url'], {
|
|
133
136
|
cwd: workspaceDir,
|
|
134
|
-
timeout:
|
|
137
|
+
timeout: 2000,
|
|
135
138
|
});
|
|
136
139
|
const url = stdout.trim();
|
|
137
140
|
if (url.startsWith('ws://') || url.startsWith('wss://'))
|
|
138
141
|
return url;
|
|
139
142
|
}
|
|
140
143
|
catch { /* daemon not ready yet */ }
|
|
141
|
-
|
|
144
|
+
if (i < retries - 1)
|
|
145
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
142
146
|
}
|
|
143
147
|
return null;
|
|
144
148
|
}
|
|
@@ -233,6 +237,40 @@ async function injectStealthViaCdp(sessionId, workspaceDir) {
|
|
|
233
237
|
console.warn(`[stealth] CDP injection failed for "${sessionId}":`, err);
|
|
234
238
|
}
|
|
235
239
|
}
|
|
240
|
+
// ── Daemon pre-warm ───────────────────────────────────────────────────────────
|
|
241
|
+
/**
|
|
242
|
+
* Pre-warm the agent's browser daemon and register stealth before any
|
|
243
|
+
* agent navigation. Call this once at agent process startup for agents
|
|
244
|
+
* that have the browser tool enabled.
|
|
245
|
+
*
|
|
246
|
+
* Steps:
|
|
247
|
+
* 1. Start the daemon with `agent-browser open about:blank` (no-op if
|
|
248
|
+
* already running — agent-browser reuses the existing daemon).
|
|
249
|
+
* 2. Inject Page.addScriptToEvaluateOnNewDocument via CDP so every
|
|
250
|
+
* subsequent navigation the agent makes will have stealth running
|
|
251
|
+
* before the page's own scripts.
|
|
252
|
+
*
|
|
253
|
+
* This is a one-time setup. If the agent later kills its daemon via
|
|
254
|
+
* `browser close --all`, the next daemon start won't have stealth until
|
|
255
|
+
* the live view relay re-attaches (which also injects stealth).
|
|
256
|
+
*/
|
|
257
|
+
async function prewarmStealthDaemon(sessionId, workspaceDir) {
|
|
258
|
+
if (process.env.GRANCLAW_STEALTH_DISABLED === '1')
|
|
259
|
+
return;
|
|
260
|
+
try {
|
|
261
|
+
const bin = process.env.AGENT_BROWSER_BIN ?? 'agent-browser';
|
|
262
|
+
// Boot the daemon (or no-op if already running) and land on about:blank.
|
|
263
|
+
// Use execFileAsync with a short timeout — we don't care about the result,
|
|
264
|
+
// only that the daemon is now up.
|
|
265
|
+
await execFileAsync(bin, ['--session', sessionId, ...stealthArgv(), 'open', 'about:blank'], { cwd: workspaceDir, timeout: 15000 });
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Daemon failed to start (no Chrome, wrong env, etc.) — skip silently.
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// Daemon is up. Register stealth for all future navigations.
|
|
272
|
+
await injectStealthViaCdp(sessionId, workspaceDir);
|
|
273
|
+
}
|
|
236
274
|
// ── Test helpers ──────────────────────────────────────────────────────────────
|
|
237
275
|
/** @internal */
|
|
238
276
|
function __resetStealthCacheForTests() {
|
|
@@ -25,6 +25,7 @@ exports.stopAndRemoveAgent = stopAndRemoveAgent;
|
|
|
25
25
|
const child_process_1 = require("child_process");
|
|
26
26
|
const path_1 = __importDefault(require("path"));
|
|
27
27
|
const config_js_1 = require("../config.js");
|
|
28
|
+
const stealth_js_1 = require("../browser/stealth.js");
|
|
28
29
|
const secrets_vault_js_1 = require("../secrets-vault.js");
|
|
29
30
|
exports.BASE_AGENT_PORT = Number(process.env.AGENT_BASE_PORT ?? 3100);
|
|
30
31
|
/**
|
|
@@ -87,6 +88,13 @@ function startNewAgent(agent) {
|
|
|
87
88
|
const managed = { config: agent, wsPort, bbPort: null, pid: child.pid };
|
|
88
89
|
registry.set(agent.id, managed);
|
|
89
90
|
console.log(`[orchestrator] agent "${agent.id}" started on ws port ${wsPort} (pid ${child.pid})`);
|
|
91
|
+
// Pre-warm the browser daemon for browser-capable agents so stealth is
|
|
92
|
+
// registered via Page.addScriptToEvaluateOnNewDocument before the agent's
|
|
93
|
+
// first navigation. Fire-and-forget — never blocks agent startup.
|
|
94
|
+
if (agent.allowedTools?.includes('browser')) {
|
|
95
|
+
const workspaceDir = path_1.default.resolve(config_js_1.REPO_ROOT, agent.workspaceDir);
|
|
96
|
+
void (0, stealth_js_1.prewarmStealthDaemon)(agent.id, workspaceDir);
|
|
97
|
+
}
|
|
90
98
|
return managed;
|
|
91
99
|
}
|
|
92
100
|
/**
|