@inetafrica/open-claudia 2.2.19 → 2.2.21
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/.env.example +1 -1
- package/CHANGELOG.md +3 -0
- package/Dockerfile +9 -0
- package/bot-agent.js +4 -4
- package/core/config.js +1 -1
- package/core/handlers.js +3 -0
- package/core/system-prompt.js +14 -0
- package/health.js +31 -127
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -17,7 +17,7 @@ KAZEE_DEBUG_EVENTS=
|
|
|
17
17
|
WORKSPACE=/path/to/your/workspace
|
|
18
18
|
CLAUDE_PATH=/path/to/claude
|
|
19
19
|
# Default Claude Code model when users haven't selected one with /model.
|
|
20
|
-
CLAUDE_MODEL=claude-
|
|
20
|
+
CLAUDE_MODEL=claude-fable-5
|
|
21
21
|
CURSOR_PATH=
|
|
22
22
|
CODEX_PATH=
|
|
23
23
|
AUTO_COMPACT_TOKENS=280000
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.2.21
|
|
4
|
+
- Add Claude Fable 5 (`claude-fable-5`) to the Claude model picker and make it the default Claude Code model used when no per-user model override is selected. The default can still be overridden with `CLAUDE_MODEL`.
|
|
5
|
+
|
|
3
6
|
## v2.2.19
|
|
4
7
|
- Add Claude Opus 4.8 (`claude-opus-4-8`) to the Claude model picker and make it the default Claude Code model used when no per-user model override is selected. The default can still be overridden with `CLAUDE_MODEL`, now documented in `.env.example`.
|
|
5
8
|
|
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/bot-agent.js
CHANGED
|
@@ -78,7 +78,7 @@ const CHAT_ID = CHAT_IDS[0]; // Primary owner chat for crons/notifications
|
|
|
78
78
|
const WORKSPACE = config.WORKSPACE;
|
|
79
79
|
const CLAUDE_PATH = config.CLAUDE_PATH;
|
|
80
80
|
const CURSOR_PATH = config.CURSOR_PATH || null;
|
|
81
|
-
const DEFAULT_CLAUDE_MODEL = config.CLAUDE_MODEL || process.env.CLAUDE_MODEL || "claude-
|
|
81
|
+
const DEFAULT_CLAUDE_MODEL = config.CLAUDE_MODEL || process.env.CLAUDE_MODEL || "claude-fable-5";
|
|
82
82
|
|
|
83
83
|
// Resolve Cursor Agent CLI (optional)
|
|
84
84
|
let resolvedCursorPath = CURSOR_PATH;
|
|
@@ -1661,9 +1661,9 @@ bot.onText(/\/model$/, (msg) => {
|
|
|
1661
1661
|
[{ text: "GPT-5.4", callback_data: "m:gpt-5.4-medium" }, { text: "GPT-5.4 High", callback_data: "m:gpt-5.4-high" }],
|
|
1662
1662
|
[{ text: "Auto", callback_data: "m:auto" }, { text: "Default", callback_data: "m:default" }],
|
|
1663
1663
|
] : [
|
|
1664
|
-
[{ text: "
|
|
1665
|
-
[{ text: "
|
|
1666
|
-
[{ text: "Default", callback_data: "m:default" }],
|
|
1664
|
+
[{ text: "Fable 5", callback_data: "m:claude-fable-5" }, { text: "Opus 4.8", callback_data: "m:claude-opus-4-8" }],
|
|
1665
|
+
[{ text: "Opus 4.7", callback_data: "m:claude-opus-4-7" }, { text: "Sonnet", callback_data: "m:sonnet" }],
|
|
1666
|
+
[{ text: "Haiku", callback_data: "m:haiku" }, { text: "Default", callback_data: "m:default" }],
|
|
1667
1667
|
];
|
|
1668
1668
|
const label = settings.backend === "cursor" ? "Cursor Agent" : "Claude Code";
|
|
1669
1669
|
send(`${label} model: ${settings.model || "default"}\n\nOr type /model <name> for any model.`, { keyboard: { inline_keyboard: keyboard } });
|
package/core/config.js
CHANGED
|
@@ -47,7 +47,7 @@ const WORKSPACE = config.WORKSPACE;
|
|
|
47
47
|
const CLAUDE_PATH = config.CLAUDE_PATH;
|
|
48
48
|
const CURSOR_PATH = config.CURSOR_PATH || null;
|
|
49
49
|
const CODEX_PATH = config.CODEX_PATH || null;
|
|
50
|
-
const DEFAULT_CLAUDE_MODEL = config.CLAUDE_MODEL || process.env.CLAUDE_MODEL || "claude-
|
|
50
|
+
const DEFAULT_CLAUDE_MODEL = config.CLAUDE_MODEL || process.env.CLAUDE_MODEL || "claude-fable-5";
|
|
51
51
|
const AUTO_COMPACT_TOKENS = parseInt(config.AUTO_COMPACT_TOKENS || process.env.AUTO_COMPACT_TOKENS || "380000", 10);
|
|
52
52
|
const MIN_COMPACT_INTERVAL_MS = parseInt(config.MIN_COMPACT_INTERVAL_MS || process.env.MIN_COMPACT_INTERVAL_MS || "1800000", 10);
|
|
53
53
|
const PROJECT_TRANSCRIPTS = configTruthy(config.PROJECT_TRANSCRIPTS || process.env.PROJECT_TRANSCRIPTS, true);
|
package/core/handlers.js
CHANGED
|
@@ -535,7 +535,10 @@ register({
|
|
|
535
535
|
const rows = [];
|
|
536
536
|
rows.push([{ text: "── Claude ──", callback_data: "noop" }]);
|
|
537
537
|
rows.push([
|
|
538
|
+
{ text: "Fable 5", callback_data: "mb:claude:claude-fable-5" },
|
|
538
539
|
{ text: "Opus 4.8", callback_data: "mb:claude:claude-opus-4-8" },
|
|
540
|
+
]);
|
|
541
|
+
rows.push([
|
|
539
542
|
{ text: "Opus 4.7", callback_data: "mb:claude:claude-opus-4-7" },
|
|
540
543
|
{ text: "Opus 4.6", callback_data: "mb:claude:claude-opus-4-6" },
|
|
541
544
|
]);
|
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