alvin-bot 4.22.3 → 4.23.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to Alvin Bot are documented here.
4
4
 
5
+ ## [4.23.0] — 2026-05-12
6
+
7
+ ### Installer: bootstrap Node automatically + optional capability tools
8
+
9
+ The first-time install path no longer assumes Node (or Homebrew, or anything beyond a normal Mac/Linux shell) is already in place, and the setup wizard now offers to install a curated set of universally useful CLIs in one step.
10
+
11
+ - **`install.sh` — auto-bootstraps Node.** New `ensure_brew()` + `ensure_node()` helpers replace the old "fail with manual install instructions" behaviour. On macOS, if Homebrew is missing, the installer offers to install it via the official `curl|bash` (with `NONINTERACTIVE=1` so it doesn't pause for ENTER). On Debian/Ubuntu, offers Node 22 via the NodeSource repo (with sudo confirmation). Non-interactive shells fall back to clear manual-install messages. Skipping the prompts is always an option. `eval "$(brew shellenv)"` is invoked after a fresh brew install so the rest of the script sees brew on PATH.
12
+
13
+ - **`alvin-bot setup` — optional tools step.** After Telegram + AI provider config, the wizard now lists eight commonly useful tools and lets the user install just the ones they want with a comma-separated picklist (or `a` for all missing, `n` to skip). Already-installed tools are marked `✓` and skipped automatically. Install failures don't block bot setup — the user gets a working bot regardless.
14
+
15
+ - **New `alvin-bot tools` command.** Re-runnable later: `alvin-bot tools list` shows what's installed, `alvin-bot tools install` opens the same interactive menu the setup wizard uses.
16
+
17
+ Tools offered (curated, all generic — none require maintainer-specific creds or workflows):
18
+
19
+ | Tool | What it unlocks |
20
+ |---|---|
21
+ | Playwright + Chromium | Tier 1 stealth browser automation (web-research, social-fetch, browser-manager) |
22
+ | agent-browser CLI | Tier 1.5 token-efficient web automation (Vercel Labs) |
23
+ | ffmpeg | Audio/video processing for media-transcribe, video-generate, voice features |
24
+ | ImageMagick | Image conversion & manipulation for image-generate and visual skills |
25
+ | Pandoc | Markdown ↔ PDF/DOCX/HTML conversion for document-creation skill |
26
+ | ripgrep | Fast file/code search (`alvin-bot search`, code-aware skills) |
27
+ | jq | JSON parsing for helper scripts |
28
+ | himalaya | Multi-account IMAP/SMTP email CLI (configure after install) |
29
+
5
30
  ## [4.22.3] — 2026-05-12
6
31
 
7
32
  ### Fixed
package/bin/cli.js CHANGED
@@ -128,6 +128,237 @@ const PROVIDERS = [
128
128
  },
129
129
  ];
130
130
 
131
+ // ── Optional capability tools ───────────────────────────────────────────────
132
+ //
133
+ // Curated, universally useful CLIs that significantly expand what skills can
134
+ // do. All optional, all opt-in. The list is intentionally short and generic —
135
+ // no maintainer-specific credentials or workflows are required to make any of
136
+ // these useful to a public user; auth/config is per-user and out of scope here.
137
+ //
138
+ // Each entry has:
139
+ // id machine-readable key
140
+ // name shown to the user
141
+ // desc one-liner explaining what skills/features it unlocks
142
+ // check() returns boolean — is it already installed?
143
+ // install either a shell-command string (single platform) or an object
144
+ // { macos, linux } with platform-specific shell commands, or a
145
+ // function that does whatever it takes
146
+ // skipOnLinuxIf optional: skip the Linux install if this command is missing
147
+ // (e.g. himalaya on Linux needs cargo)
148
+ const OPTIONAL_TOOLS = [
149
+ {
150
+ id: "playwright",
151
+ name: "Playwright + Chromium",
152
+ desc: "Browser automation (Tier 1 stealth) — used by web-research, social-fetch, browser-manager",
153
+ check: () => hasCommand("playwright") || hasCommand("npx") && tryNpx("playwright --version"),
154
+ install: () => {
155
+ execSync("npm install -g playwright", { stdio: "inherit" });
156
+ execSync("npx playwright install chromium", { stdio: "inherit" });
157
+ },
158
+ },
159
+ {
160
+ id: "agent-browser",
161
+ name: "agent-browser CLI (Vercel Labs)",
162
+ desc: "Tier 1.5 token-efficient web automation — ~90% cheaper than raw Playwright for AI-driven tasks",
163
+ check: () => hasCommand("agent-browser"),
164
+ install: () => {
165
+ execSync("npm install -g agent-browser", { stdio: "inherit" });
166
+ // Best-effort: ignore failure if the install subcommand isn't there
167
+ try { execSync("agent-browser install", { stdio: "inherit" }); } catch {}
168
+ },
169
+ },
170
+ {
171
+ id: "ffmpeg",
172
+ name: "ffmpeg",
173
+ desc: "Audio/video processing — required by media-transcribe, video-generate, voice features",
174
+ check: () => hasCommand("ffmpeg"),
175
+ install: { macos: "brew install ffmpeg", linux: "sudo apt-get install -y ffmpeg" },
176
+ },
177
+ {
178
+ id: "imagemagick",
179
+ name: "ImageMagick",
180
+ desc: "Image conversion & manipulation — used by image-generate and visual skills",
181
+ check: () => hasCommand("magick") || hasCommand("convert"),
182
+ install: { macos: "brew install imagemagick", linux: "sudo apt-get install -y imagemagick" },
183
+ },
184
+ {
185
+ id: "pandoc",
186
+ name: "Pandoc",
187
+ desc: "Document conversion (Markdown ↔ PDF/DOCX/HTML) — used by document-creation skill",
188
+ check: () => hasCommand("pandoc"),
189
+ install: { macos: "brew install pandoc", linux: "sudo apt-get install -y pandoc" },
190
+ },
191
+ {
192
+ id: "ripgrep",
193
+ name: "ripgrep (rg)",
194
+ desc: "Fast file & code search — used by `alvin-bot search` and code-aware skills",
195
+ check: () => hasCommand("rg"),
196
+ install: { macos: "brew install ripgrep", linux: "sudo apt-get install -y ripgrep" },
197
+ },
198
+ {
199
+ id: "jq",
200
+ name: "jq",
201
+ desc: "JSON parsing for shell scripts — used by many helper scripts and integrations",
202
+ check: () => hasCommand("jq"),
203
+ install: { macos: "brew install jq", linux: "sudo apt-get install -y jq" },
204
+ },
205
+ {
206
+ id: "himalaya",
207
+ name: "himalaya (email CLI)",
208
+ desc: "Multi-account IMAP/SMTP email CLI — bring your own email accounts, configure after install",
209
+ check: () => hasCommand("himalaya"),
210
+ install: { macos: "brew install himalaya", linux: "cargo install himalaya" },
211
+ skipOnLinuxIf: "cargo",
212
+ },
213
+ ];
214
+
215
+ function hasCommand(name) {
216
+ try {
217
+ execSync(`command -v ${name}`, { stdio: "pipe" });
218
+ return true;
219
+ } catch {
220
+ return false;
221
+ }
222
+ }
223
+
224
+ function tryNpx(args) {
225
+ try {
226
+ execSync(`npx --no-install ${args}`, { stdio: "pipe", timeout: 5000 });
227
+ return true;
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Make sure `brew` is reachable in the current process. The setup wizard
235
+ * may be running in a fresh shell right after `install.sh` installed brew
236
+ * for the first time — in that case `/opt/homebrew/bin` may not be in
237
+ * PATH yet. Side-loads it if we find the binary.
238
+ */
239
+ function ensureBrewOnPath() {
240
+ if (hasCommand("brew")) return true;
241
+ for (const candidate of ["/opt/homebrew/bin", "/usr/local/bin"]) {
242
+ if (existsSync(join(candidate, "brew"))) {
243
+ process.env.PATH = `${candidate}:${process.env.PATH || ""}`;
244
+ return true;
245
+ }
246
+ }
247
+ return false;
248
+ }
249
+
250
+ /**
251
+ * Print the status of optional tools without prompting. Used by
252
+ * `alvin-bot tools list` for inspection and scripting.
253
+ */
254
+ function listOptionalTools() {
255
+ const platform = process.platform === "darwin" ? "macos"
256
+ : process.platform === "linux" ? "linux"
257
+ : null;
258
+ console.log(`\n📦 Optional capability tools — ${platform || process.platform}\n`);
259
+ const states = OPTIONAL_TOOLS.map(t => ({ ...t, installed: t.check() }));
260
+ states.forEach((t, i) => {
261
+ const mark = t.installed ? "✓" : "·";
262
+ console.log(` [${String(i + 1).padStart(2)}] ${mark} ${t.name}`);
263
+ console.log(` ${t.desc}`);
264
+ });
265
+ const missing = states.filter(t => !t.installed).length;
266
+ console.log(`\n ${states.length - missing} installed, ${missing} missing.`);
267
+ if (missing > 0) {
268
+ console.log(` Install missing with: alvin-bot tools install\n`);
269
+ } else {
270
+ console.log("");
271
+ }
272
+ }
273
+
274
+ async function runOptionalToolsStep() {
275
+ // Skip on platforms we don't have install commands for.
276
+ const platform = process.platform === "darwin" ? "macos"
277
+ : process.platform === "linux" ? "linux"
278
+ : null;
279
+ if (!platform) {
280
+ return;
281
+ }
282
+
283
+ // On macOS, anything that uses brew needs brew on PATH.
284
+ if (platform === "macos") {
285
+ if (!ensureBrewOnPath()) {
286
+ console.log("\n ℹ️ Skipping optional tools step — Homebrew not found.");
287
+ console.log(" Install brew from https://brew.sh and re-run 'alvin-bot setup' to revisit.");
288
+ return;
289
+ }
290
+ }
291
+
292
+ console.log(`\n━━━ Optional capability tools ━━━\n`);
293
+ console.log("These CLIs significantly expand what skills can do. All optional — skip");
294
+ console.log("now and run 'alvin-bot setup' later to revisit. Already-installed tools");
295
+ console.log("are skipped automatically.\n");
296
+
297
+ const states = OPTIONAL_TOOLS.map(t => ({ ...t, installed: t.check() }));
298
+ states.forEach((t, i) => {
299
+ const mark = t.installed ? "✓" : " ";
300
+ console.log(` [${String(i + 1).padStart(2)}] ${mark} ${t.name}`);
301
+ console.log(` ${t.desc}`);
302
+ });
303
+ console.log(`\n ✓ = already installed (will be skipped)\n`);
304
+
305
+ const ans = (await ask(" Install which? Comma-separated numbers, [a]ll missing, [n]one [n]: ")).trim().toLowerCase();
306
+
307
+ let selected = [];
308
+ if (ans === "" || ans === "n" || ans === "none") {
309
+ console.log(" Skipped — no tools installed this round.\n");
310
+ return;
311
+ } else if (ans === "a" || ans === "all") {
312
+ selected = states.filter(t => !t.installed);
313
+ } else {
314
+ const indices = ans.split(",").map(s => parseInt(s.trim(), 10)).filter(n => Number.isFinite(n));
315
+ selected = indices.map(n => states[n - 1]).filter(t => t && !t.installed);
316
+ }
317
+
318
+ if (selected.length === 0) {
319
+ console.log(" Nothing to install — every pick was either invalid or already present.\n");
320
+ return;
321
+ }
322
+
323
+ const installed = [];
324
+ const failed = [];
325
+ for (const tool of selected) {
326
+ // Linux-only fallbacks: skip if a hard prereq is missing.
327
+ if (platform === "linux" && tool.skipOnLinuxIf && !hasCommand(tool.skipOnLinuxIf)) {
328
+ console.log(`\n ⚠️ Skipping ${tool.name} — needs '${tool.skipOnLinuxIf}' which isn't installed.`);
329
+ failed.push({ tool, reason: `missing prerequisite: ${tool.skipOnLinuxIf}` });
330
+ continue;
331
+ }
332
+ console.log(`\n📦 Installing ${tool.name}...`);
333
+ try {
334
+ if (typeof tool.install === "function") {
335
+ tool.install();
336
+ } else if (typeof tool.install === "object" && tool.install[platform]) {
337
+ execSync(tool.install[platform], { stdio: "inherit" });
338
+ } else {
339
+ console.log(` ⚠️ No install command for ${platform}. Skipping.`);
340
+ failed.push({ tool, reason: `no install method for ${platform}` });
341
+ continue;
342
+ }
343
+ installed.push(tool);
344
+ console.log(` ✅ ${tool.name} installed.`);
345
+ } catch (err) {
346
+ const msg = err && err.message ? err.message : String(err);
347
+ console.log(` ❌ ${tool.name} install failed: ${msg.split("\n")[0]}`);
348
+ console.log(` Bot setup will continue — retry this tool later with its native install command.`);
349
+ failed.push({ tool, reason: "install command failed" });
350
+ }
351
+ }
352
+
353
+ console.log("");
354
+ if (installed.length) {
355
+ console.log(` ✅ Installed: ${installed.map(t => t.name).join(", ")}`);
356
+ }
357
+ if (failed.length) {
358
+ console.log(` ⚠️ Skipped/failed: ${failed.map(f => f.tool.name).join(", ")}`);
359
+ }
360
+ }
361
+
131
362
  // ── Offline mode: Ollama + Gemma 4 E4B ─────────────────────────────────────
132
363
 
133
364
  /**
@@ -1197,6 +1428,11 @@ async function setup() {
1197
1428
  console.log(" ✅ CLAUDE.md initialized from example");
1198
1429
  }
1199
1430
 
1431
+ // ── Optional capability tools (curated list of useful CLIs) ──────────────
1432
+ // Offered after the core setup (Telegram + AI provider) is complete so the
1433
+ // user always gets a working bot first — tool installs are pure upside.
1434
+ await runOptionalToolsStep();
1435
+
1200
1436
  // ── Build (only for local/dev installs — global npm installs already have dist/)
1201
1437
  const isGlobalInstall = !existsSync(resolve(process.cwd(), "tsconfig.json"));
1202
1438
  if (!isGlobalInstall) {
@@ -1926,6 +2162,23 @@ switch (cmd) {
1926
2162
  case "doctor":
1927
2163
  doctor().catch(console.error);
1928
2164
  break;
2165
+ case "tools": {
2166
+ const sub = process.argv[3] || "list";
2167
+ if (sub === "list" || sub === "ls" || sub === "status") {
2168
+ listOptionalTools();
2169
+ } else if (sub === "install") {
2170
+ runOptionalToolsStep().then(() => closeRL()).catch(err => {
2171
+ console.error(err);
2172
+ closeRL();
2173
+ });
2174
+ } else {
2175
+ console.log("Usage: alvin-bot tools <list|install>");
2176
+ console.log("");
2177
+ console.log(" list — Show installed / missing optional tools.");
2178
+ console.log(" install — Interactive menu to install missing tools.");
2179
+ }
2180
+ break;
2181
+ }
1929
2182
  case "update":
1930
2183
  update().catch(console.error);
1931
2184
  break;
@@ -2288,6 +2541,7 @@ ${t("cli.commands")}
2288
2541
  doctor ${t("cli.doctorDesc")}
2289
2542
  audit Security health check (permissions, secrets, config)
2290
2543
  search Search your assets, memories, and skills
2544
+ tools List / install optional capability tools (ffmpeg, pandoc, …)
2291
2545
  browser Manage bot-owned Chromium (start/stop/goto/shot/doctor)
2292
2546
  update ${t("cli.updateDesc")}
2293
2547
  start ${t("cli.startDesc")} (background via PM2)
package/install.sh CHANGED
@@ -54,22 +54,111 @@ check_git() {
54
54
  ok "Git found: $(git --version)"
55
55
  }
56
56
 
57
- check_node() {
58
- if ! command -v node &>/dev/null; then
59
- fail "Node.js is not installed (>= $MIN_NODE_VERSION required).
60
- Install via: https://nodejs.org or
61
- macOS: brew install node@22
62
- Linux: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt install -y nodejs"
57
+ # Is this shell interactive enough to prompt the user?
58
+ is_interactive() {
59
+ [ -t 0 ] && [ -t 1 ]
60
+ }
61
+
62
+ # Ensure Homebrew is available on macOS. Offers to auto-install via the
63
+ # official curl|bash sequence. A no-op on non-macOS systems.
64
+ ensure_brew() {
65
+ [ "$OS" = "macOS" ] || return 0
66
+ if command -v brew &>/dev/null; then
67
+ ok "Homebrew found: $(brew --version | head -1)"
68
+ return 0
69
+ fi
70
+ warn "Homebrew is not installed."
71
+ if ! is_interactive; then
72
+ fail "Non-interactive shell — cannot prompt. Install Homebrew manually: https://brew.sh"
63
73
  fi
74
+ echo ""
75
+ read -r -p " Install Homebrew automatically? [Y/n] " ans
76
+ case "${ans:-Y}" in
77
+ [Yy]*)
78
+ info "Installing Homebrew (the official installer may prompt for sudo)..."
79
+ # NONINTERACTIVE=1 tells brew's installer to skip the ENTER-to-confirm
80
+ # pause — the user already opted in here.
81
+ NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \
82
+ || fail "Homebrew install failed. See https://brew.sh for manual instructions."
83
+ # Add brew to PATH for the remainder of this script.
84
+ if [ -x /opt/homebrew/bin/brew ]; then
85
+ eval "$(/opt/homebrew/bin/brew shellenv)"
86
+ elif [ -x /usr/local/bin/brew ]; then
87
+ eval "$(/usr/local/bin/brew shellenv)"
88
+ fi
89
+ ok "Homebrew installed."
90
+ ;;
91
+ *)
92
+ fail "Cannot continue without Homebrew. Install manually: https://brew.sh"
93
+ ;;
94
+ esac
95
+ }
64
96
 
65
- local node_ver
66
- node_ver=$(node -v | sed 's/^v//' | cut -d. -f1)
67
- if [ "$node_ver" -lt "$MIN_NODE_VERSION" ]; then
68
- fail "Node.js >= $MIN_NODE_VERSION required, but found v$(node -v). Please upgrade."
97
+ # Ensure Node.js >= MIN_NODE_VERSION. Offers to install via brew (macOS) or
98
+ # NodeSource (Debian/Ubuntu) if missing or too old. Falls back to a clear
99
+ # manual-install message on unsupported platforms.
100
+ ensure_node() {
101
+ if command -v node &>/dev/null; then
102
+ local node_ver
103
+ node_ver=$(node -v | sed 's/^v//' | cut -d. -f1)
104
+ if [ "$node_ver" -ge "$MIN_NODE_VERSION" ]; then
105
+ ok "Node.js found: $(node -v)"
106
+ return 0
107
+ fi
108
+ warn "Node.js v$node_ver is too old (need >= $MIN_NODE_VERSION)."
109
+ else
110
+ warn "Node.js is not installed."
69
111
  fi
70
- ok "Node.js found: $(node -v)"
112
+
113
+ if ! is_interactive; then
114
+ fail "Non-interactive shell — install Node >= $MIN_NODE_VERSION manually: https://nodejs.org"
115
+ fi
116
+
117
+ case "$OS" in
118
+ macOS)
119
+ ensure_brew
120
+ echo ""
121
+ read -r -p " Install Node via Homebrew? [Y/n] " ans
122
+ case "${ans:-Y}" in
123
+ [Yy]*)
124
+ info "Installing Node (latest LTS via brew)..."
125
+ brew install node || fail "brew install node failed."
126
+ ok "Node installed: $(node -v)"
127
+ ;;
128
+ *)
129
+ fail "Cannot continue without Node. Install manually: https://nodejs.org"
130
+ ;;
131
+ esac
132
+ ;;
133
+ Linux|WSL)
134
+ if command -v apt-get &>/dev/null; then
135
+ echo ""
136
+ warn "About to install Node 22 from the official NodeSource repo. This needs sudo."
137
+ read -r -p " Continue? [Y/n] " ans
138
+ case "${ans:-Y}" in
139
+ [Yy]*)
140
+ info "Adding NodeSource repo..."
141
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - \
142
+ || fail "NodeSource setup failed."
143
+ info "Installing nodejs..."
144
+ sudo apt-get install -y nodejs || fail "apt-get install nodejs failed."
145
+ ok "Node installed: $(node -v)"
146
+ ;;
147
+ *)
148
+ fail "Cannot continue without Node. Install manually: https://nodejs.org"
149
+ ;;
150
+ esac
151
+ else
152
+ fail "Auto-install on Linux only supports apt-based distros. Install Node manually: https://nodejs.org"
153
+ fi
154
+ ;;
155
+ *)
156
+ fail "Cannot bootstrap Node on $OS — install manually."
157
+ ;;
158
+ esac
71
159
  }
72
160
 
161
+ # npm comes bundled with node, but verify just in case.
73
162
  check_npm() {
74
163
  if ! command -v npm &>/dev/null; then
75
164
  fail "npm is not installed. It should come with Node.js — please reinstall Node."
@@ -136,7 +225,7 @@ main() {
136
225
 
137
226
  detect_os
138
227
  check_git
139
- check_node
228
+ ensure_node
140
229
  check_npm
141
230
 
142
231
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alvin-bot",
3
- "version": "4.22.3",
3
+ "version": "4.23.0",
4
4
  "description": "Alvin Bot — Your personal AI agent on Telegram, WhatsApp, Discord, Signal, and Web.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
Binary file