@inetafrica/open-claudia 2.2.18 → 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/.env.example CHANGED
@@ -16,6 +16,8 @@ KAZEE_DEBUG_EVENTS=
16
16
 
17
17
  WORKSPACE=/path/to/your/workspace
18
18
  CLAUDE_PATH=/path/to/claude
19
+ # Default Claude Code model when users haven't selected one with /model.
20
+ CLAUDE_MODEL=claude-opus-4-8
19
21
  CURSOR_PATH=
20
22
  CODEX_PATH=
21
23
  AUTO_COMPACT_TOKENS=280000
package/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.2.19
4
+ - 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
+
3
6
  ## v2.2.18
4
7
  - Fix the legacy Telegram send/edit path so replies default to Telegram HTML parse mode and go through the shared formatter, preventing raw `<b>`, `<a>`, and related tags from leaking in chat.
5
8
  - Preserve safe Telegram HTML tags before Markdown conversion, so underscores inside link URLs such as Higgsfield `wan2_2_video` are not misread as italics and do not break rendered links.
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,6 +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-opus-4-8";
81
82
 
82
83
  // Resolve Cursor Agent CLI (optional)
83
84
  let resolvedCursorPath = CURSOR_PATH;
@@ -1148,7 +1149,7 @@ function buildClaudeArgs(prompt, opts = {}) {
1148
1149
  "--append-system-prompt", buildSystemPrompt()];
1149
1150
  if (opts.continueSession) args.push("--continue");
1150
1151
  else if (lastSessionId && !opts.fresh) args.push("--resume", lastSessionId);
1151
- if (settings.model) args.push("--model", settings.model);
1152
+ args.push("--model", settings.model || DEFAULT_CLAUDE_MODEL);
1152
1153
  if (settings.effort) args.push("--effort", settings.effort);
1153
1154
  if (settings.budget) args.push("--max-budget-usd", String(settings.budget));
1154
1155
  if (settings.permissionMode) args.push("--permission-mode", settings.permissionMode);
@@ -1195,7 +1196,7 @@ async function runClaudeChat(prompt, cwd, replyToMsgId) {
1195
1196
  const args = ["-p", "--verbose", "--output-format", "stream-json",
1196
1197
  "--append-system-prompt", buildSystemPrompt(),
1197
1198
  "--dangerously-skip-permissions"];
1198
- if (settings.model) args.push("--model", settings.model);
1199
+ args.push("--model", settings.model || DEFAULT_CLAUDE_MODEL);
1199
1200
  args.push(contextPrompt);
1200
1201
 
1201
1202
  const proc = spawn(CLAUDE_PATH, args, {
@@ -1660,7 +1661,8 @@ bot.onText(/\/model$/, (msg) => {
1660
1661
  [{ text: "GPT-5.4", callback_data: "m:gpt-5.4-medium" }, { text: "GPT-5.4 High", callback_data: "m:gpt-5.4-high" }],
1661
1662
  [{ text: "Auto", callback_data: "m:auto" }, { text: "Default", callback_data: "m:default" }],
1662
1663
  ] : [
1663
- [{ text: "Opus", callback_data: "m:opus" }, { text: "Sonnet", callback_data: "m:sonnet" }, { text: "Haiku", callback_data: "m:haiku" }],
1664
+ [{ text: "Opus 4.8", callback_data: "m:claude-opus-4-8" }, { text: "Opus 4.7", callback_data: "m:claude-opus-4-7" }],
1665
+ [{ text: "Sonnet", callback_data: "m:sonnet" }, { text: "Haiku", callback_data: "m:haiku" }],
1664
1666
  [{ text: "Default", callback_data: "m:default" }],
1665
1667
  ];
1666
1668
  const label = settings.backend === "cursor" ? "Cursor Agent" : "Claude Code";
package/core/config.js CHANGED
@@ -47,6 +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-opus-4-8";
50
51
  const AUTO_COMPACT_TOKENS = parseInt(config.AUTO_COMPACT_TOKENS || process.env.AUTO_COMPACT_TOKENS || "380000", 10);
51
52
  const MIN_COMPACT_INTERVAL_MS = parseInt(config.MIN_COMPACT_INTERVAL_MS || process.env.MIN_COMPACT_INTERVAL_MS || "1800000", 10);
52
53
  const PROJECT_TRANSCRIPTS = configTruthy(config.PROJECT_TRANSCRIPTS || process.env.PROJECT_TRANSCRIPTS, true);
@@ -187,6 +188,7 @@ module.exports = {
187
188
  TOKEN, CHAT_IDS, CHAT_ID,
188
189
  WORKSPACE,
189
190
  CLAUDE_PATH,
191
+ DEFAULT_CLAUDE_MODEL,
190
192
  CURSOR_PATH,
191
193
  CODEX_PATH,
192
194
  resolvedCursorPath,
package/core/handlers.js CHANGED
@@ -9,7 +9,7 @@ const { execSync } = require("child_process");
9
9
  const {
10
10
  WORKSPACE, FULL_PATH, AUTO_COMPACT_TOKENS, CONFIG_DIR,
11
11
  config, saveEnvKey,
12
- CHAT_ID, resolvedCursorPath, resolvedCodexPath, SOUL_FILE,
12
+ CHAT_ID, DEFAULT_CLAUDE_MODEL, resolvedCursorPath, resolvedCodexPath, SOUL_FILE,
13
13
  } = require("./config");
14
14
  const { register } = require("./commands");
15
15
  const { send, deleteMessage } = require("./io");
@@ -535,8 +535,11 @@ register({
535
535
  const rows = [];
536
536
  rows.push([{ text: "── Claude ──", callback_data: "noop" }]);
537
537
  rows.push([
538
+ { text: "Opus 4.8", callback_data: "mb:claude:claude-opus-4-8" },
538
539
  { text: "Opus 4.7", callback_data: "mb:claude:claude-opus-4-7" },
539
540
  { text: "Opus 4.6", callback_data: "mb:claude:claude-opus-4-6" },
541
+ ]);
542
+ rows.push([
540
543
  { text: "Sonnet 4.6", callback_data: "mb:claude:claude-sonnet-4-6" },
541
544
  { text: "Haiku", callback_data: "mb:claude:claude-haiku-4-5-20251001" },
542
545
  ]);
@@ -563,7 +566,7 @@ register({
563
566
  { text: "o4-mini", callback_data: "mb:codex:o4-mini" },
564
567
  ]);
565
568
  }
566
- rows.push([{ text: "Default (current backend)", callback_data: "m:default" }]);
569
+ rows.push([{ text: `Default (Claude: ${DEFAULT_CLAUDE_MODEL})`, callback_data: "m:default" }]);
567
570
  const beLabel = settings.backend === "cursor" ? "Cursor" : settings.backend === "codex" ? "Codex" : "Claude";
568
571
  send(`Current: ${beLabel} · ${settings.model || "default"}\n\nPick a model — backend switches automatically.\nOr type /model <name>.`, { keyboard: { inline_keyboard: rows } });
569
572
  },
package/core/runner.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  const { spawn } = require("child_process");
7
7
  const {
8
- CLAUDE_PATH, resolvedCursorPath, resolvedCodexPath,
8
+ CLAUDE_PATH, DEFAULT_CLAUDE_MODEL, resolvedCursorPath, resolvedCodexPath,
9
9
  AUTO_COMPACT_TOKENS, MIN_COMPACT_INTERVAL_MS, MAX_PROCESS_TIMEOUT, COMPACT_SUMMARY_TIMEOUT, botSubprocessEnv,
10
10
  } = require("./config");
11
11
  const { currentState, saveState, recordSession, userOwnsClaudeSession } = require("./state");
@@ -124,7 +124,7 @@ function buildClaudeArgs(prompt, opts = {}) {
124
124
  state.lastSessionId = null;
125
125
  }
126
126
  }
127
- if (settings.model) args.push("--model", settings.model);
127
+ args.push("--model", settings.model || DEFAULT_CLAUDE_MODEL);
128
128
  if (settings.effort) args.push("--effort", settings.effort);
129
129
  if (settings.budget) args.push("--max-budget-usd", String(settings.budget));
130
130
  if (settings.permissionMode) args.push("--permission-mode", settings.permissionMode);
@@ -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 Playwright MCP setup
340
- try {
341
- const root = execSync("npm root -g", { encoding: "utf-8", stdio: "pipe" }).trim();
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.warnings.push("~/.claude/mcp.json not found (MCP tools unavailable)");
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
- * Ensure ~/.claude/mcp.json has the Playwright MCP server configured.
441
- * Idempotent won't duplicate if already present.
442
- * Returns { configured: bool, alreadyConfigured: bool, error?: string }
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 ensureMcpConfig() {
445
- const claudeDir = path.dirname(MCP_CONFIG_FILE);
446
-
447
- // Ensure ~/.claude directory exists
448
- if (!fs.existsSync(claudeDir)) {
449
- try {
450
- fs.mkdirSync(claudeDir, { recursive: true });
451
- } catch (e) {
452
- return { configured: false, error: `Failed to create ${claudeDir}: ${e.message}` };
453
- }
454
- }
455
-
456
- // Load existing config or start fresh
457
- let mcpConfig = { mcpServers: {} };
458
- if (fs.existsSync(MCP_CONFIG_FILE)) {
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
- // Step 1: Install Playwright MCP globally
513
- results.steps.playwright_mcp = ensurePlaywrightMcp();
514
- if (!results.steps.playwright_mcp.installed) {
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
- ensurePlaywrightMcp,
558
- ensureMcpConfig,
462
+ ensureAgentBrowser,
559
463
  REQUIRED_ENV_KEYS,
560
464
  OPTIONAL_ENV_KEYS,
561
465
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "2.2.18",
3
+ "version": "2.2.20",
4
4
  "description": "Your always-on AI coding assistant — Claude Code, Cursor Agent, and OpenAI Codex via Telegram or Kazee Chat",
5
5
  "main": "bot.js",
6
6
  "bin": {