@inetafrica/open-claudia 2.2.19 → 2.2.20
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/Dockerfile +9 -0
- package/core/system-prompt.js +14 -0
- package/health.js +31 -127
- package/package.json +1 -1
package/Dockerfile
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
FROM node:20-slim
|
|
2
2
|
|
|
3
3
|
# Install system dependencies
|
|
4
|
+
# chromium is the browser engine driven by agent-browser (apt pulls its headless libs).
|
|
4
5
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
5
6
|
curl \
|
|
6
7
|
ffmpeg \
|
|
@@ -13,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
13
14
|
sudo \
|
|
14
15
|
openssh-client \
|
|
15
16
|
rsync \
|
|
17
|
+
chromium \
|
|
16
18
|
&& rm -rf /var/lib/apt/lists/*
|
|
17
19
|
|
|
18
20
|
# Install Claude Code CLI
|
|
@@ -22,6 +24,13 @@ RUN curl -fsSL https://claude.ai/install.sh | sh || \
|
|
|
22
24
|
# Install OpenAI Codex CLI for the /codex backend
|
|
23
25
|
RUN npm install -g @openai/codex
|
|
24
26
|
|
|
27
|
+
# Browser automation for the agent: vercel-labs agent-browser (Rust CLI, token-efficient,
|
|
28
|
+
# replaces Playwright MCP). Installed at build time as root so the non-root runtime user
|
|
29
|
+
# doesn't hit EACCES on `npm install -g`. Chrome is pinned to the apt chromium binary so it
|
|
30
|
+
# lives outside the /data VOLUME (build-time writes to /data are discarded at runtime).
|
|
31
|
+
RUN npm install -g agent-browser
|
|
32
|
+
ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium
|
|
33
|
+
|
|
25
34
|
# Create non-root user (Claude Code refuses --dangerously-skip-permissions as root)
|
|
26
35
|
# node:20-slim already has uid/gid 1000 (node user). Create claudia with different IDs.
|
|
27
36
|
RUN groupadd -g 1001 claudia && useradd -u 1001 -g 1001 -m -d /data claudia
|
package/core/system-prompt.js
CHANGED
|
@@ -182,6 +182,20 @@ Sub-agents (spawn a fresh throwaway Claude for focused research — output comes
|
|
|
182
182
|
- Use when a side question would pollute this conversation, or to fan out independent lookups.
|
|
183
183
|
- The sub-agent has Read/Glob/Grep/Bash but no access to your chat session or send-* tools.
|
|
184
184
|
|
|
185
|
+
## Browser automation
|
|
186
|
+
You can drive a real web browser via the \`agent-browser\` CLI (vercel-labs). It is a plain command you run through Bash — NOT an MCP tool, so there is nothing to enable; just call it. Chrome is bundled in the image and runs headless (it auto-adds --no-sandbox in this container). Use it to read pages, fill forms, click through flows, scrape content, or capture screenshots of how something renders.
|
|
187
|
+
|
|
188
|
+
Core workflow — open, snapshot to get element refs, then act on those refs:
|
|
189
|
+
- \`agent-browser open <url>\` — navigate (also: \`goto\`, \`back\`, \`forward\`, \`reload\`).
|
|
190
|
+
- \`agent-browser snapshot -i\` — list interactive elements as @refs (e.g. @e1, @e2). Do this before clicking/filling so you target the right element. Add \`--json\` for machine-readable output.
|
|
191
|
+
- \`agent-browser click @e1\` / \`fill @e2 "text"\` / \`type\`, \`press\`, \`select\`, \`hover\`, \`check\`/\`uncheck\`, \`upload\` — interact.
|
|
192
|
+
- \`agent-browser get text @e3\` / \`get html\` / \`get url\` / \`get title\` / \`is visible @e4\` — read state.
|
|
193
|
+
- \`agent-browser screenshot <path.png>\` — capture; then deliver with \`open-claudia send-photo <path.png>\`.
|
|
194
|
+
- \`agent-browser eval "<js>"\` — run JS in the page when no command fits.
|
|
195
|
+
- \`agent-browser close\` — end the session when done.
|
|
196
|
+
|
|
197
|
+
Notes: a background daemon persists between commands, so navigation state carries across calls in a turn. Prefer \`snapshot -i\` refs over guessing selectors. Keep output lean by acting on refs rather than dumping full HTML. This replaced the old Playwright MCP — do not look for a playwright tool.
|
|
198
|
+
|
|
185
199
|
If you tell the user "I'll check back in N minutes" or "I'll run this every morning", you MUST schedule it with one of the commands above in the same turn. Otherwise nothing happens.
|
|
186
200
|
|
|
187
201
|
## Guidelines
|
package/health.js
CHANGED
|
@@ -336,32 +336,15 @@ async function runHealthChecks(options = {}) {
|
|
|
336
336
|
results.warnings.push(`FFmpeg not found at: ${env.FFMPEG} (voice notes disabled)`);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
// 12. Check
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const pwMcpPkg = path.join(root, "@playwright", "mcp", "package.json");
|
|
343
|
-
if (!fs.existsSync(pwMcpPkg)) {
|
|
344
|
-
results.warnings.push("@playwright/mcp not installed globally (browser tools unavailable)");
|
|
345
|
-
} else {
|
|
346
|
-
results.checks.playwright_mcp = { ok: true };
|
|
347
|
-
}
|
|
348
|
-
} catch (e) {
|
|
349
|
-
results.warnings.push("Could not check @playwright/mcp installation");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (fs.existsSync(MCP_CONFIG_FILE)) {
|
|
353
|
-
try {
|
|
354
|
-
const mcpCfg = JSON.parse(fs.readFileSync(MCP_CONFIG_FILE, "utf-8"));
|
|
355
|
-
if (!mcpCfg.mcpServers || !mcpCfg.mcpServers.playwright) {
|
|
356
|
-
results.warnings.push("Playwright MCP not configured in ~/.claude/mcp.json");
|
|
357
|
-
} else {
|
|
358
|
-
results.checks.mcp_config = { ok: true };
|
|
359
|
-
}
|
|
360
|
-
} catch (e) {
|
|
361
|
-
results.warnings.push("~/.claude/mcp.json is malformed");
|
|
362
|
-
}
|
|
339
|
+
// 12. Check browser automation (agent-browser CLI + Chrome)
|
|
340
|
+
if (!binaryExists("agent-browser")) {
|
|
341
|
+
results.warnings.push("agent-browser CLI not found on PATH (browser tools unavailable)");
|
|
363
342
|
} else {
|
|
364
|
-
results.
|
|
343
|
+
results.checks.agent_browser = { ok: true };
|
|
344
|
+
const chrome = env.AGENT_BROWSER_EXECUTABLE_PATH;
|
|
345
|
+
if (chrome && !fs.existsSync(chrome)) {
|
|
346
|
+
results.warnings.push(`Chrome not found at AGENT_BROWSER_EXECUTABLE_PATH=${chrome} (browser tools may fail)`);
|
|
347
|
+
}
|
|
365
348
|
}
|
|
366
349
|
|
|
367
350
|
return results;
|
|
@@ -406,99 +389,27 @@ function formatResults(results, verbose = false) {
|
|
|
406
389
|
|
|
407
390
|
// ── Idempotent setup: ensures required tools and config are in place ──
|
|
408
391
|
|
|
409
|
-
const MCP_CONFIG_FILE = path.join(process.env.HOME || require("os").homedir(), ".claude", "mcp.json");
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Ensure @playwright/mcp is installed globally.
|
|
413
|
-
* Returns { installed: bool, alreadyInstalled: bool, error?: string }
|
|
414
|
-
*/
|
|
415
|
-
function ensurePlaywrightMcp() {
|
|
416
|
-
// Check if already installed
|
|
417
|
-
try {
|
|
418
|
-
const root = execSync("npm root -g", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
419
|
-
const pkgPath = path.join(root, "@playwright", "mcp", "package.json");
|
|
420
|
-
if (fs.existsSync(pkgPath)) {
|
|
421
|
-
return { installed: true, alreadyInstalled: true };
|
|
422
|
-
}
|
|
423
|
-
} catch (e) { /* not installed */ }
|
|
424
|
-
|
|
425
|
-
// Install it
|
|
426
|
-
try {
|
|
427
|
-
execSync("npm install -g @playwright/mcp@latest 2>&1", {
|
|
428
|
-
encoding: "utf-8",
|
|
429
|
-
timeout: 120000,
|
|
430
|
-
stdio: "pipe",
|
|
431
|
-
env: { ...process.env, PATH: process.env.PATH },
|
|
432
|
-
});
|
|
433
|
-
return { installed: true, alreadyInstalled: false };
|
|
434
|
-
} catch (e) {
|
|
435
|
-
return { installed: false, error: (e.stderr || e.message || "").slice(-300) };
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
392
|
/**
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
393
|
+
* Verify the agent-browser CLI is available for browser automation.
|
|
394
|
+
* The binary + Chrome are baked into the image at build time (see Dockerfile),
|
|
395
|
+
* so this is a presence check, not an installer — a non-root runtime user can't
|
|
396
|
+
* `npm install -g` anyway. Returns { installed, alreadyInstalled, error? }.
|
|
443
397
|
*/
|
|
444
|
-
function
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
try {
|
|
460
|
-
mcpConfig = JSON.parse(fs.readFileSync(MCP_CONFIG_FILE, "utf-8"));
|
|
461
|
-
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
462
|
-
} catch (e) {
|
|
463
|
-
// Corrupted config — back up and start fresh
|
|
464
|
-
const backup = MCP_CONFIG_FILE + ".bak." + Date.now();
|
|
465
|
-
try { fs.copyFileSync(MCP_CONFIG_FILE, backup); } catch (e2) {}
|
|
466
|
-
mcpConfig = { mcpServers: {} };
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Check if Playwright MCP is already configured
|
|
471
|
-
if (mcpConfig.mcpServers.playwright) {
|
|
472
|
-
return { configured: true, alreadyConfigured: true };
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Find the installed binary
|
|
476
|
-
let mcpBin = "playwright-mcp";
|
|
477
|
-
try {
|
|
478
|
-
const resolved = execSync("which playwright-mcp", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
479
|
-
if (resolved) mcpBin = resolved;
|
|
480
|
-
} catch (e) {
|
|
481
|
-
// Try npx path
|
|
482
|
-
try {
|
|
483
|
-
const root = execSync("npm root -g", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
484
|
-
const binPath = path.join(root, "@playwright", "mcp", "cli.js");
|
|
485
|
-
if (fs.existsSync(binPath)) mcpBin = binPath;
|
|
486
|
-
} catch (e2) {}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// Add Playwright MCP server config
|
|
490
|
-
mcpConfig.mcpServers.playwright = {
|
|
491
|
-
command: "npx",
|
|
492
|
-
args: ["@playwright/mcp@latest", "--headless"],
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
// Write config
|
|
496
|
-
try {
|
|
497
|
-
fs.writeFileSync(MCP_CONFIG_FILE, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
498
|
-
return { configured: true, alreadyConfigured: false };
|
|
499
|
-
} catch (e) {
|
|
500
|
-
return { configured: false, error: `Failed to write ${MCP_CONFIG_FILE}: ${e.message}` };
|
|
501
|
-
}
|
|
398
|
+
function ensureAgentBrowser() {
|
|
399
|
+
if (!binaryExists("agent-browser")) {
|
|
400
|
+
return {
|
|
401
|
+
installed: false,
|
|
402
|
+
error: "agent-browser CLI not on PATH (expected from image build; browser tools unavailable)",
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const chrome = process.env.AGENT_BROWSER_EXECUTABLE_PATH;
|
|
406
|
+
if (chrome && !fs.existsSync(chrome)) {
|
|
407
|
+
return {
|
|
408
|
+
installed: false,
|
|
409
|
+
error: `Chrome missing at AGENT_BROWSER_EXECUTABLE_PATH=${chrome}`,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
return { installed: true, alreadyInstalled: true };
|
|
502
413
|
}
|
|
503
414
|
|
|
504
415
|
/**
|
|
@@ -509,15 +420,9 @@ function ensureMcpConfig() {
|
|
|
509
420
|
function ensureSetup() {
|
|
510
421
|
const results = { ok: true, steps: {} };
|
|
511
422
|
|
|
512
|
-
//
|
|
513
|
-
results.steps.
|
|
514
|
-
if (!results.steps.
|
|
515
|
-
results.ok = false;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Step 2: Configure MCP in Claude Code config
|
|
519
|
-
results.steps.mcp_config = ensureMcpConfig();
|
|
520
|
-
if (!results.steps.mcp_config.configured) {
|
|
423
|
+
// Browser automation: verify agent-browser CLI + Chrome are present.
|
|
424
|
+
results.steps.agent_browser = ensureAgentBrowser();
|
|
425
|
+
if (!results.steps.agent_browser.installed) {
|
|
521
426
|
results.ok = false;
|
|
522
427
|
}
|
|
523
428
|
|
|
@@ -554,8 +459,7 @@ module.exports = {
|
|
|
554
459
|
binaryExists,
|
|
555
460
|
ensureSetup,
|
|
556
461
|
formatSetupResults,
|
|
557
|
-
|
|
558
|
-
ensureMcpConfig,
|
|
462
|
+
ensureAgentBrowser,
|
|
559
463
|
REQUIRED_ENV_KEYS,
|
|
560
464
|
OPTIONAL_ENV_KEYS,
|
|
561
465
|
};
|
package/package.json
CHANGED