@askjo/camoufox-browser 1.0.5 → 1.0.6
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/package.json +2 -2
- package/plugin.ts +94 -11
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askjo/camoufox-browser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Headless browser automation server and OpenClaw plugin for AI agents - anti-detection, element refs, and session isolation",
|
|
5
5
|
"main": "server-camoufox.js",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author": "Jo Inc <
|
|
7
|
+
"author": "Jo Inc <oss@askjo.ai>",
|
|
8
8
|
"homepage": "https://github.com/jo-inc/camoufox-browser#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
package/plugin.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Camoufox Browser - OpenClaw Plugin
|
|
3
3
|
*
|
|
4
4
|
* Provides browser automation tools using the Camoufox anti-detection browser.
|
|
5
|
-
*
|
|
5
|
+
* Server auto-starts when plugin loads (configurable via autoStart: false).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { spawn, ChildProcess } from "child_process";
|
|
@@ -11,6 +11,7 @@ import { join } from "path";
|
|
|
11
11
|
interface PluginConfig {
|
|
12
12
|
url?: string;
|
|
13
13
|
autoStart?: boolean;
|
|
14
|
+
port?: number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
interface ToolResult {
|
|
@@ -41,6 +42,67 @@ interface PluginApi {
|
|
|
41
42
|
|
|
42
43
|
let serverProcess: ChildProcess | null = null;
|
|
43
44
|
|
|
45
|
+
async function startServer(
|
|
46
|
+
pluginDir: string,
|
|
47
|
+
port: number,
|
|
48
|
+
log: PluginApi["log"]
|
|
49
|
+
): Promise<ChildProcess> {
|
|
50
|
+
const serverPath = join(pluginDir, "server-camoufox.js");
|
|
51
|
+
const proc = spawn("node", [serverPath], {
|
|
52
|
+
cwd: pluginDir,
|
|
53
|
+
env: { ...process.env, PORT: String(port) },
|
|
54
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
55
|
+
detached: false,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
proc.stdout?.on("data", (data: Buffer) => {
|
|
59
|
+
const msg = data.toString().trim();
|
|
60
|
+
if (msg) log.info(`[server] ${msg}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
proc.stderr?.on("data", (data: Buffer) => {
|
|
64
|
+
const msg = data.toString().trim();
|
|
65
|
+
if (msg) log.error(`[server] ${msg}`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
proc.on("error", (err) => {
|
|
69
|
+
log.error(`Server process error: ${err.message}`);
|
|
70
|
+
serverProcess = null;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
proc.on("exit", (code) => {
|
|
74
|
+
if (code !== 0 && code !== null) {
|
|
75
|
+
log.error(`Server exited with code ${code}`);
|
|
76
|
+
}
|
|
77
|
+
serverProcess = null;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Wait for server to be ready
|
|
81
|
+
const baseUrl = `http://localhost:${port}`;
|
|
82
|
+
for (let i = 0; i < 30; i++) {
|
|
83
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(`${baseUrl}/health`);
|
|
86
|
+
if (res.ok) {
|
|
87
|
+
log.info(`Camoufox server ready on port ${port}`);
|
|
88
|
+
return proc;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Server not ready yet
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw new Error("Server failed to start within 15 seconds");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function checkServerRunning(baseUrl: string): Promise<boolean> {
|
|
98
|
+
try {
|
|
99
|
+
const res = await fetch(`${baseUrl}/health`);
|
|
100
|
+
return res.ok;
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
44
106
|
async function fetchApi(
|
|
45
107
|
baseUrl: string,
|
|
46
108
|
path: string,
|
|
@@ -68,7 +130,26 @@ function toToolResult(data: unknown): ToolResult {
|
|
|
68
130
|
}
|
|
69
131
|
|
|
70
132
|
export default function register(api: PluginApi) {
|
|
71
|
-
const
|
|
133
|
+
const port = api.config.port || 9377;
|
|
134
|
+
const baseUrl = api.config.url || `http://localhost:${port}`;
|
|
135
|
+
const autoStart = api.config.autoStart !== false; // default true
|
|
136
|
+
const pluginDir = __dirname;
|
|
137
|
+
|
|
138
|
+
// Auto-start server if configured (default: true)
|
|
139
|
+
if (autoStart) {
|
|
140
|
+
(async () => {
|
|
141
|
+
const alreadyRunning = await checkServerRunning(baseUrl);
|
|
142
|
+
if (alreadyRunning) {
|
|
143
|
+
api.log.info(`Camoufox server already running at ${baseUrl}`);
|
|
144
|
+
} else {
|
|
145
|
+
try {
|
|
146
|
+
serverProcess = await startServer(pluginDir, port, api.log);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
api.log.error(`Failed to auto-start server: ${(err as Error).message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
})();
|
|
152
|
+
}
|
|
72
153
|
|
|
73
154
|
api.registerTool(
|
|
74
155
|
{
|
|
@@ -317,22 +398,24 @@ export default function register(api: PluginApi) {
|
|
|
317
398
|
try {
|
|
318
399
|
const health = await fetchApi(baseUrl, "/health");
|
|
319
400
|
api.log.info(`Camoufox server at ${baseUrl}: ${JSON.stringify(health)}`);
|
|
320
|
-
} catch
|
|
401
|
+
} catch {
|
|
321
402
|
api.log.error(`Camoufox server at ${baseUrl}: not reachable`);
|
|
322
403
|
}
|
|
323
404
|
break;
|
|
324
405
|
case "start":
|
|
325
406
|
if (serverProcess) {
|
|
326
|
-
api.log.info("Camoufox server already running");
|
|
407
|
+
api.log.info("Camoufox server already running (managed)");
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (await checkServerRunning(baseUrl)) {
|
|
411
|
+
api.log.info(`Camoufox server already running at ${baseUrl}`);
|
|
327
412
|
return;
|
|
328
413
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
});
|
|
335
|
-
api.log.info(`Started camoufox-browser server (pid: ${serverProcess.pid})`);
|
|
414
|
+
try {
|
|
415
|
+
serverProcess = await startServer(pluginDir, port, api.log);
|
|
416
|
+
} catch (err) {
|
|
417
|
+
api.log.error(`Failed to start server: ${(err as Error).message}`);
|
|
418
|
+
}
|
|
336
419
|
break;
|
|
337
420
|
case "stop":
|
|
338
421
|
if (serverProcess) {
|