@inetafrica/open-claudia 1.12.1 → 1.13.0

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.
Files changed (3) hide show
  1. package/bot.js +16 -0
  2. package/health.js +170 -0
  3. package/package.json +1 -1
package/bot.js CHANGED
@@ -1147,6 +1147,11 @@ bot.onText(/\/upgrade$/, async (msg) => {
1147
1147
  cwd: process.env.HOME || require("os").homedir(),
1148
1148
  env: { ...process.env, PATH: FULL_PATH, HOME: process.env.HOME || require("os").homedir() },
1149
1149
  });
1150
+ // Run idempotent setup (install/configure tools like Playwright MCP)
1151
+ try {
1152
+ const { ensureSetup: postUpgradeSetup } = require("./health");
1153
+ postUpgradeSetup();
1154
+ } catch (e) { /* setup will run on next boot anyway */ }
1150
1155
  // Read version and changelog from newly installed package
1151
1156
  const root = execSync("npm root -g", { encoding: "utf-8", cwd: process.env.HOME || require("os").homedir(), env: { ...process.env, PATH: FULL_PATH } }).trim();
1152
1157
  const newPkg = JSON.parse(fs.readFileSync(path.join(root, "@inetafrica", "open-claudia", "package.json"), "utf-8"));
@@ -1689,6 +1694,17 @@ bot.on("message", async (msg) => {
1689
1694
  });
1690
1695
 
1691
1696
  // ── Startup ─────────────────────────────────────────────────────────
1697
+ // Idempotent setup: ensure tools & config are in place (safe on every boot)
1698
+ try {
1699
+ const { ensureSetup, formatSetupResults } = require("./health");
1700
+ const setupResult = ensureSetup();
1701
+ console.log("Setup check:");
1702
+ console.log(formatSetupResults(setupResult));
1703
+ if (!setupResult.ok) console.warn("Some setup steps failed — browser tools may be unavailable.");
1704
+ } catch (e) {
1705
+ console.warn("Setup check skipped:", e.message);
1706
+ }
1707
+
1692
1708
  initCrons();
1693
1709
  console.log("Claude Code Telegram bot running");
1694
1710
  console.log(`Workspace: ${WORKSPACE}`);
package/health.js CHANGED
@@ -336,6 +336,34 @@ 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
+ }
363
+ } else {
364
+ results.warnings.push("~/.claude/mcp.json not found (MCP tools unavailable)");
365
+ }
366
+
339
367
  return results;
340
368
  }
341
369
 
@@ -376,6 +404,144 @@ function formatResults(results, verbose = false) {
376
404
  return lines.join("\n");
377
405
  }
378
406
 
407
+ // ── Idempotent setup: ensures required tools and config are in place ──
408
+
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
+ /**
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 }
443
+ */
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
+ }
502
+ }
503
+
504
+ /**
505
+ * Run all idempotent setup steps.
506
+ * Safe to call on every startup and after every upgrade.
507
+ * Returns { ok: bool, steps: { name: result } }
508
+ */
509
+ function ensureSetup() {
510
+ const results = { ok: true, steps: {} };
511
+
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) {
521
+ results.ok = false;
522
+ }
523
+
524
+ return results;
525
+ }
526
+
527
+ /**
528
+ * Format ensureSetup results for logging/display
529
+ */
530
+ function formatSetupResults(results) {
531
+ const lines = [];
532
+ for (const [name, result] of Object.entries(results.steps)) {
533
+ const label = name.replace(/_/g, " ");
534
+ if (result.alreadyInstalled || result.alreadyConfigured) {
535
+ lines.push(` ${label}: already set up`);
536
+ } else if (result.installed || result.configured) {
537
+ lines.push(` ${label}: configured`);
538
+ } else {
539
+ lines.push(` ${label}: FAILED — ${result.error || "unknown error"}`);
540
+ }
541
+ }
542
+ return lines.join("\n");
543
+ }
544
+
379
545
  module.exports = {
380
546
  runHealthChecks,
381
547
  formatResults,
@@ -386,6 +552,10 @@ module.exports = {
386
552
  checkNodeVersion,
387
553
  checkForConflictingProcesses,
388
554
  binaryExists,
555
+ ensureSetup,
556
+ formatSetupResults,
557
+ ensurePlaywrightMcp,
558
+ ensureMcpConfig,
389
559
  REQUIRED_ENV_KEYS,
390
560
  OPTIONAL_ENV_KEYS,
391
561
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {