@aliwey/bmo 2.0.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 (100) hide show
  1. package/README.md +90 -0
  2. package/bin/bmo.js +188 -0
  3. package/cli.py +1129 -0
  4. package/config/__init__.py +0 -0
  5. package/config/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/config/__pycache__/settings.cpython-313.pyc +0 -0
  7. package/config/__pycache__/system-prompt.cpython-313.pyc +0 -0
  8. package/config/settings.py +104 -0
  9. package/config/system-prompt.json +18 -0
  10. package/core/__init__.py +0 -0
  11. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  12. package/core/__pycache__/bfp_a2a_bridge.cpython-313.pyc +0 -0
  13. package/core/__pycache__/bfp_agent.cpython-313.pyc +0 -0
  14. package/core/__pycache__/bfp_agent_card.cpython-313.pyc +0 -0
  15. package/core/__pycache__/bfp_connector.cpython-313.pyc +0 -0
  16. package/core/__pycache__/bfp_discovery.cpython-313.pyc +0 -0
  17. package/core/__pycache__/bfp_identity.cpython-313.pyc +0 -0
  18. package/core/__pycache__/bfp_tasks.cpython-313.pyc +0 -0
  19. package/core/__pycache__/bfp_transport.cpython-313.pyc +0 -0
  20. package/core/__pycache__/bmo_engine.cpython-313.pyc +0 -0
  21. package/core/__pycache__/bot_client.cpython-313.pyc +0 -0
  22. package/core/__pycache__/budget_tracker.cpython-313.pyc +0 -0
  23. package/core/__pycache__/cli_renderer.cpython-313.pyc +0 -0
  24. package/core/__pycache__/goal_runner.cpython-313.pyc +0 -0
  25. package/core/__pycache__/request_worker.cpython-313.pyc +0 -0
  26. package/core/__pycache__/security.cpython-313.pyc +0 -0
  27. package/core/__pycache__/shared_state.cpython-313.pyc +0 -0
  28. package/core/__pycache__/worker_manager.cpython-313.pyc +0 -0
  29. package/core/__pycache__/worker_multiproc.cpython-313.pyc +0 -0
  30. package/core/__pycache__/worker_protocol.cpython-313.pyc +0 -0
  31. package/core/__pycache__/worker_subprocess.cpython-313.pyc +0 -0
  32. package/core/bfp_a2a_bridge.py +399 -0
  33. package/core/bfp_agent.py +98 -0
  34. package/core/bfp_agent_card.py +161 -0
  35. package/core/bfp_connector.py +177 -0
  36. package/core/bfp_discovery.py +105 -0
  37. package/core/bfp_identity.py +83 -0
  38. package/core/bfp_tasks.py +70 -0
  39. package/core/bfp_transport.py +368 -0
  40. package/core/bmo_engine.py +405 -0
  41. package/core/bot_client.py +838 -0
  42. package/core/budget_tracker.py +62 -0
  43. package/core/cli_renderer.py +177 -0
  44. package/core/goal_runner.py +129 -0
  45. package/core/request_worker.py +242 -0
  46. package/core/security.py +42 -0
  47. package/core/shared_state.py +4 -0
  48. package/core/worker_manager.py +71 -0
  49. package/core/worker_multiproc.py +155 -0
  50. package/core/worker_protocol.py +30 -0
  51. package/core/worker_subprocess.py +222 -0
  52. package/handlers/__init__.py +0 -0
  53. package/handlers/__pycache__/__init__.cpython-313.pyc +0 -0
  54. package/handlers/__pycache__/messages.cpython-313.pyc +0 -0
  55. package/handlers/messages.py +2761 -0
  56. package/main.py +125 -0
  57. package/memory.md +43 -0
  58. package/models/__init__.py +0 -0
  59. package/models/__pycache__/__init__.cpython-313.pyc +0 -0
  60. package/models/__pycache__/chat_models.cpython-313.pyc +0 -0
  61. package/models/chat_models.py +143 -0
  62. package/package.json +50 -0
  63. package/registry/worker.js +108 -0
  64. package/registry/wrangler.toml +11 -0
  65. package/requirements.txt +13 -0
  66. package/scripts/bmo_init.js +115 -0
  67. package/scripts/postinstall.js +265 -0
  68. package/scripts/relay_cmd.js +276 -0
  69. package/scripts/web_cmd.js +136 -0
  70. package/setup.py +26 -0
  71. package/storage/__init__.py +0 -0
  72. package/storage/__pycache__/__init__.cpython-313.pyc +0 -0
  73. package/storage/__pycache__/sqlite_storage.cpython-313.pyc +0 -0
  74. package/storage/__pycache__/storage.cpython-313.pyc +0 -0
  75. package/storage/sqlite_storage.py +658 -0
  76. package/storage/storage.py +265 -0
  77. package/tools/__pycache__/bfp_relay.cpython-313.pyc +0 -0
  78. package/tools/__pycache__/get_session_summaries.cpython-313.pyc +0 -0
  79. package/tools/__pycache__/mcp_bridge.cpython-313.pyc +0 -0
  80. package/tools/__pycache__/mcp_server.cpython-313.pyc +0 -0
  81. package/tools/__pycache__/run_mcp_standalone.cpython-313.pyc +0 -0
  82. package/tools/__pycache__/task_registry.cpython-313.pyc +0 -0
  83. package/tools/__pycache__/test_mcp_connection.cpython-313.pyc +0 -0
  84. package/tools/bfp_relay.py +359 -0
  85. package/tools/bot.db +0 -0
  86. package/tools/get_session_summaries.py +45 -0
  87. package/tools/mcp_bridge.py +109 -0
  88. package/tools/mcp_server.py +531 -0
  89. package/tools/register_mcp_task.py +20 -0
  90. package/tools/run_detached.bat +32 -0
  91. package/tools/run_mcp_standalone.py +16 -0
  92. package/tools/task_registry.py +184 -0
  93. package/tools/test_mcp_connection.py +80 -0
  94. package/webchat/package-lock.json +1528 -0
  95. package/webchat/package.json +12 -0
  96. package/webchat/public/app.js +1293 -0
  97. package/webchat/public/index.html +226 -0
  98. package/webchat/public/index.html.bak +416 -0
  99. package/webchat/public/styles.css +2435 -0
  100. package/webchat/server.js +645 -0
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # BMO — OpenCode AI Coding Assistant
2
+
3
+ BMO is a unified, self-contained AI coding assistant with Telegram, CLI, and Webchat interfaces. It connects to a local [OpenCode](https://github.com/opencode-ai/opencode) instance and extends it with persistent memory, tool access, custom agents, and a decentralized peer-to-peer Agent Discovery Protocol (BFP).
4
+
5
+ ---
6
+
7
+ ## 🚀 Installation & Setup
8
+
9
+ BMO is published as a global Node/npm command-line tool. The installer automatically downloads a minimal embedded Python environment, installs python dependencies, and sets up everything required.
10
+
11
+ ### 1. Install Globally via npm
12
+ ```bash
13
+ npm install -g @aliwey/bmo
14
+ ```
15
+
16
+ ### 2. Run the Configuration Wizard
17
+ Configure your Telegram Bot Father token, admin Telegram user ID, model settings, and peer registry:
18
+ ```bash
19
+ bmo init
20
+ ```
21
+ This generates your configuration file at `~/.bmo/.env`.
22
+
23
+ ---
24
+
25
+ ## ⚡ Quick Start
26
+
27
+ ### 🤖 Start the Bot & CLI
28
+ To launch the Telegram bot polling and the interactive terminal TUI:
29
+ ```bash
30
+ bmo
31
+ ```
32
+ *Note: If your local OpenCode instance is not running, BMO will automatically attempt to launch it.*
33
+
34
+ ### 🌐 Launch the Web Chat Interface
35
+ Starts the unified Node.js/Express Web Chat and opens a secure public Cloudflare Tunnel so you can chat from any web browser:
36
+ ```bash
37
+ bmo web
38
+ ```
39
+
40
+ ### 📡 Go Online (Peer Relay)
41
+ Start a BFP (BMO Friendship Protocol) peer relay node to make your agent discoverable by other BMO peers on the registry:
42
+ ```bash
43
+ bmo relay
44
+ ```
45
+ * To run privately without listing on the public registry: `bmo relay --private`
46
+ * To stop the relay background services: `bmo relay --stop`
47
+
48
+ ---
49
+
50
+ ## 🛠️ CLI Commands & Subcommands
51
+
52
+ ```bash
53
+ bmo [command] [options]
54
+ ```
55
+
56
+ | Command | Option | Description |
57
+ |---|---|---|
58
+ | `bmo` | — | Starts the Telegram bot & CLI TUI |
59
+ | `bmo init` | — | Runs the interactive setup wizard |
60
+ | `bmo web` | — | Starts Express Webchat + Cloudflare tunnel |
61
+ | `bmo relay` | `[--private] [--stop]` | Starts/stops the BFP capability relay |
62
+ | `bmo --version` | — | Prints the package version |
63
+ | `bmo --update` | — | Automatically pulls and applies the latest updates |
64
+
65
+ ---
66
+
67
+ ## ⚙️ Configuration & Storage
68
+
69
+ All user configurations, chat databases, and logs are kept isolated in the user's home directory under `~/.bmo/`:
70
+
71
+ * **`~/.bmo/.env`** — Bot tokens, model configurations, and ports.
72
+ * **`~/.bmo/data/bot.db`** — Shared SQLite database (WAL mode) containing all sessions, messages, memory, and custom agents.
73
+ * **`~/.bmo/logs/`** — Log files for the bot, webchat, and tunnel.
74
+
75
+ ---
76
+
77
+ ## 👥 Telegram interface
78
+
79
+ Use the persistent keyboard controls at the bottom of the chat, or the inline control menu (`/menu` command) to:
80
+ - **🆕 New / 💾 Save / 📂 Load** sessions
81
+ - **🤖 Choose Model** dynamically (e.g. OpenAI, Anthropic, DeepSeek, or local)
82
+ - **⚡ Operation Modes**: `plan` (outline only), `ask` (clarifying questions first), or `execute` (write code and run commands)
83
+ - **🎭 Agents & 🛠️ Skills**: Enable custom or generated personas and load specialized capabilities
84
+ - **🌐 Chat on Web**: Dynamically launch a cloudflared-tunneled web interface directly from Telegram
85
+
86
+ ---
87
+
88
+ ## 📄 License
89
+
90
+ MIT License. Developed by Aliwey.
package/bin/bmo.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BMO CLI entry point — @aliwey/bmo
4
+ * Routes subcommands and manages OpenCode lifecycle.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { spawn, execSync } = require('child_process');
10
+ const net = require('net');
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const os = require('os');
14
+
15
+ const PKG_DIR = path.join(__dirname, '..');
16
+ const BMO_HOME = process.env.BMO_HOME || path.join(os.homedir(), '.bmo');
17
+ const VERSION = require('../package.json').version;
18
+
19
+ // ── Helpers ─────────────────────────────────────────────────────────────────
20
+
21
+ function sleep(ms) {
22
+ return new Promise(resolve => setTimeout(resolve, ms));
23
+ }
24
+
25
+ async function isPortOpen(port, host = '127.0.0.1') {
26
+ return new Promise(resolve => {
27
+ const s = net.createConnection({ port, host });
28
+ s.setTimeout(500);
29
+ s.on('connect', () => { s.destroy(); resolve(true); });
30
+ s.on('timeout', () => { s.destroy(); resolve(false); });
31
+ s.on('error', () => resolve(false));
32
+ });
33
+ }
34
+
35
+ /** Resolve the embedded or system Python executable */
36
+ function getPython() {
37
+ const embeddedWin = path.join(BMO_HOME, 'python', 'python.exe');
38
+ const embeddedUnix = path.join(BMO_HOME, 'python', 'bin', 'python3');
39
+ if (fs.existsSync(embeddedWin)) return embeddedWin;
40
+ if (fs.existsSync(embeddedUnix)) return embeddedUnix;
41
+ // Fall back to system Python
42
+ for (const cmd of ['python3', 'python']) {
43
+ try { execSync(`${cmd} --version`, { stdio: 'ignore' }); return cmd; } catch {}
44
+ }
45
+ console.error('❌ Python not found. Run: bmo init');
46
+ process.exit(1);
47
+ }
48
+
49
+ /** Start OpenCode if not already running. Returns the child if we own it. */
50
+ async function ensureOpenCode() {
51
+ const port = parseInt(process.env.OPENCODE_PORT || '4800');
52
+ if (await isPortOpen(port)) {
53
+ process.stdout.write(`✓ OpenCode already running on port ${port}\n`);
54
+ return null; // don't own it
55
+ }
56
+
57
+ // Check opencode is installed
58
+ try { execSync('opencode --version', { stdio: 'ignore' }); }
59
+ catch {
60
+ console.error('❌ opencode not found. Installing...');
61
+ try { execSync('npm install -g opencode-ai', { stdio: 'inherit' }); }
62
+ catch { console.error('❌ Could not install opencode-ai. Please run: npm install -g opencode-ai'); process.exit(1); }
63
+ }
64
+
65
+ process.stdout.write(`⏳ Starting OpenCode server on port ${port}...\n`);
66
+ const child = spawn('opencode', ['serve', '--port', String(port)], {
67
+ detached: false,
68
+ stdio: 'ignore',
69
+ cwd: PKG_DIR,
70
+ });
71
+
72
+ child.on('error', err => {
73
+ console.error(`❌ Failed to start OpenCode: ${err.message}`);
74
+ process.exit(1);
75
+ });
76
+
77
+ // Wait up to 15s for port to open
78
+ for (let i = 0; i < 30; i++) {
79
+ await sleep(500);
80
+ if (await isPortOpen(port)) {
81
+ process.stdout.write(`✓ OpenCode server ready\n`);
82
+ return child;
83
+ }
84
+ }
85
+ console.error('❌ OpenCode server did not start in time.');
86
+ child.kill();
87
+ process.exit(1);
88
+ }
89
+
90
+ // ── Command routing ──────────────────────────────────────────────────────────
91
+
92
+ const [,, cmd, ...args] = process.argv;
93
+
94
+ (async () => {
95
+ // ── bmo --version ─────────────────────────────────────────────────────
96
+ if (cmd === '--version' || cmd === '-v') {
97
+ console.log(`@aliwey/bmo v${VERSION}`);
98
+ process.exit(0);
99
+ }
100
+
101
+ // ── bmo --help ───────────────────────────────────────────────────────
102
+ if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
103
+ const PKG_JSON = require('../package.json');
104
+ const BMO_HOME_DISPLAY = process.env.BMO_HOME || path.join(os.homedir(), '.bmo');
105
+ console.log(`
106
+ @aliwey/bmo v${PKG_JSON.version} — AI coding assistant with Telegram, CLI & Web sync
107
+
108
+ Usage:
109
+ bmo Start the BMO CLI (auto-starts OpenCode if needed)
110
+ bmo init Run the setup wizard (creates ~/.bmo/.env)
111
+ bmo web Start webchat + Cloudflare tunnel, print public URL
112
+ bmo relay Start BFP relay + register with discovery registry
113
+ bmo relay --stop Stop relay + deregister
114
+ bmo relay --private Start relay without registering (private mode)
115
+ bmo --version Show version
116
+ bmo --update Update to latest version
117
+ bmo --update <ver> Update to a specific version
118
+ bmo --help Show this help
119
+
120
+ Data lives in: ${BMO_HOME_DISPLAY}
121
+ `);
122
+ process.exit(0);
123
+ }
124
+
125
+ // ── bmo --update [version] ────────────────────────────────────────────────
126
+ if (cmd === '--update') {
127
+ const ver = args[0] ? `@${args[0]}` : '@latest';
128
+ console.log(`🔄 Updating to @aliwey/bmo${ver}...`);
129
+ try {
130
+ execSync(`npm install -g @aliwey/bmo${ver}`, { stdio: 'inherit' });
131
+ } catch {
132
+ console.error('❌ Update failed. Try manually: npm install -g @aliwey/bmo');
133
+ process.exit(1);
134
+ }
135
+ process.exit(0);
136
+ }
137
+
138
+ // ── bmo init ──────────────────────────────────────────────────────────────
139
+ if (cmd === 'init') {
140
+ require('../scripts/bmo_init.js');
141
+ return;
142
+ }
143
+
144
+ // ── bmo web ───────────────────────────────────────────────────────────────
145
+ if (cmd === 'web') {
146
+ require('../scripts/web_cmd.js');
147
+ return;
148
+ }
149
+
150
+ // ── bmo relay [--private|--stop] ──────────────────────────────────────────
151
+ if (cmd === 'relay') {
152
+ process.argv = [process.argv[0], process.argv[1], ...args]; // pass flags
153
+ require('../scripts/relay_cmd.js');
154
+ return;
155
+ }
156
+
157
+ // ── bmo (default: CLI) ────────────────────────────────────────────────────
158
+ const openCodeChild = await ensureOpenCode();
159
+
160
+ // Clean up owned OpenCode on exit
161
+ if (openCodeChild) {
162
+ const cleanup = () => { try { openCodeChild.kill(); } catch {} };
163
+ process.on('exit', cleanup);
164
+ process.on('SIGINT', () => { cleanup(); process.exit(0); });
165
+ process.on('SIGTERM', () => { cleanup(); process.exit(0); });
166
+ }
167
+
168
+ // Launch BMO Python CLI — exactly like running `python cli.py`
169
+ const python = getPython();
170
+ const cliScript = path.join(PKG_DIR, 'cli.py');
171
+
172
+ const bmo = spawn(python, [cliScript], {
173
+ stdio: 'inherit',
174
+ cwd: PKG_DIR,
175
+ env: {
176
+ ...process.env,
177
+ BMO_HOME,
178
+ PYTHONUNBUFFERED: '1',
179
+ },
180
+ });
181
+
182
+ bmo.on('error', err => {
183
+ console.error(`❌ Failed to start BMO: ${err.message}`);
184
+ process.exit(1);
185
+ });
186
+
187
+ bmo.on('exit', code => process.exit(code ?? 0));
188
+ })();