alvin-bot 4.22.2 → 4.22.3
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 +14 -0
- package/README.md +2 -0
- package/alvin-bot-4.22.3.tgz +0 -0
- package/bin/cli.js +101 -1
- package/dist/providers/codex-cli-provider.js +6 -0
- package/package.json +1 -1
- package/02_yandex.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Alvin Bot are documented here.
|
|
4
4
|
|
|
5
|
+
## [4.22.3] — 2026-05-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Codex CLI provider:** close stdin after spawn so `codex exec` returns immediately. The provider opened stdin as a pipe but never sent EOF — `codex exec` then printed `Reading additional input from stdin...` and blocked until the 120 s spawn timeout, which surfaced on the chat side as "no reply" / empty Telegram messages whenever a user picked Codex CLI as their AI provider. Single-line fix (`proc.stdin.end()`) plus an explanatory comment. ([#1](https://github.com/alvbln/Alvin-Bot/pull/1))
|
|
10
|
+
|
|
11
|
+
### macOS UX: surface Full Disk Access gaps under launchd ([#2](https://github.com/alvbln/Alvin-Bot/pull/2))
|
|
12
|
+
|
|
13
|
+
When the bot runs as a LaunchAgent, macOS TCC binds permissions to the `node` binary's real Cellar path. Anything the bot spawns (Codex CLI, file-reading skills, plugins touching `~/Documents`/`~/Desktop`) inherits that identity, and without Full Disk Access on `node` those reads silently fail — no dialog appears under launchd. Fresh public users were hitting this without knowing what was going on; the failure mode (spawned tools producing empty output) looked like a bot bug.
|
|
14
|
+
|
|
15
|
+
- **`alvin-bot launchd install`** now detects FDA after a successful install and either confirms it's granted or prints a prominent warning with: the exact Cellar path to add (resolved via `realpathSync`), the `open "x-apple.systempreferences:..."` command for the right pane, the `launchctl kickstart` command to apply the grant, and a heads-up that `brew upgrade node` invalidates the grant (TCC binds to the versioned Cellar path).
|
|
16
|
+
- **`alvin-bot doctor`** gains a "macOS permissions" section that shows FDA status and how to fix — useful after `brew upgrade node` rolls forward to a new Cellar path and the old grant becomes stale.
|
|
17
|
+
- **README** "A note on permission prompts" is extended with the launchd/FDA caveat, linking to the macOS Setup Guide PDF.
|
|
18
|
+
|
|
5
19
|
## [4.22.2] — 2026-05-11
|
|
6
20
|
|
|
7
21
|
### Docs
|
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ Free AI providers available — no credit card needed. **Privacy-first?** Pick t
|
|
|
68
68
|
|
|
69
69
|
The first time Alvin reaches for a new tool — a shell command, a file read, a web fetch — you may see a permission prompt from the underlying agent runtime asking whether to allow it. Those prompts come from Alvin himself, not from a third party. Approving one expands what he can do for you autonomously; denying keeps the scope narrow. The more you allow, the more capable and hands-off he becomes — you stay in control either way, and you can always revoke a permission later.
|
|
70
70
|
|
|
71
|
+
**macOS only — one extra step under launchd.** If you install Alvin as a background service (`alvin-bot launchd install`), macOS won't be able to show you those permission dialogs interactively anymore. To let the bot and anything it spawns (Codex CLI, file-reading skills) actually read your files, grant **Full Disk Access** to `node` once: System Settings → Privacy & Security → Full Disk Access → **+** → add `/opt/homebrew/Cellar/node/<version>/bin/node` (find the exact path with `readlink -f "$(which node)"`). `alvin-bot launchd install` and `alvin-bot doctor` will both detect and remind you with the exact path. After `brew upgrade node` you'll need to re-grant, because TCC binds to the versioned Cellar path. The printable [macOS Setup Guide PDF](https://github.com/alvbln/Alvin-Bot/releases/latest/download/Alvin-Bot-macOS-Setup-Guide.pdf) covers this end-to-end.
|
|
72
|
+
|
|
71
73
|
### 📘 First-time setup walkthroughs
|
|
72
74
|
|
|
73
75
|
Step-by-step printable PDF guides:
|
|
Binary file
|
package/bin/cli.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { createInterface } from "readline";
|
|
20
|
-
import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
20
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync, readdirSync, statSync, accessSync, realpathSync, constants as fsConstants } from "fs";
|
|
21
21
|
import { resolve, join } from "path";
|
|
22
22
|
import { homedir } from "os";
|
|
23
23
|
import { execSync } from "child_process";
|
|
@@ -1515,6 +1515,21 @@ async function doctor() {
|
|
|
1515
1515
|
console.log(` ${hasChrome ? "✅" : "⚠️ "} WhatsApp (Chrome: ${hasChrome ? "found" : "not found"})`);
|
|
1516
1516
|
}
|
|
1517
1517
|
|
|
1518
|
+
// ── macOS permissions (TCC) ────────────────────────────────────────────
|
|
1519
|
+
if (process.platform === "darwin") {
|
|
1520
|
+
console.log("\n macOS permissions:");
|
|
1521
|
+
const fda = checkMacosFullDiskAccess(process.execPath);
|
|
1522
|
+
if (fda.hasFDA) {
|
|
1523
|
+
console.log(` ✅ Full Disk Access — granted to ${fda.realNodePath}`);
|
|
1524
|
+
} else {
|
|
1525
|
+
console.log(` ⚠️ Full Disk Access NOT granted to node (${fda.realNodePath})`);
|
|
1526
|
+
console.log(` Spawned tools (codex CLI, file-reading skills) may silently fail`);
|
|
1527
|
+
console.log(` to read protected directories under launchd. To fix:`);
|
|
1528
|
+
console.log(` open "${fda.paneUrl}"`);
|
|
1529
|
+
console.log(` and add the path above with the "+" button.`);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1518
1533
|
console.log("");
|
|
1519
1534
|
}
|
|
1520
1535
|
|
|
@@ -1557,6 +1572,57 @@ async function version() {
|
|
|
1557
1572
|
|
|
1558
1573
|
// ── LaunchAgent helpers (macOS only) ────────────────────────────────────────
|
|
1559
1574
|
|
|
1575
|
+
/**
|
|
1576
|
+
* Inspect the macOS Full Disk Access (TCC) state for the node binary that
|
|
1577
|
+
* the bot will run under.
|
|
1578
|
+
*
|
|
1579
|
+
* Background: macOS TCC grants permissions to specific binary paths, not to
|
|
1580
|
+
* user accounts. When `alvin-bot` runs under launchd, the bot process is
|
|
1581
|
+
* `node` from Homebrew (e.g. /opt/homebrew/Cellar/node/<version>/bin/node).
|
|
1582
|
+
* Any child process the bot spawns — `codex`, `ripgrep`, plugins reading
|
|
1583
|
+
* ~/Documents, the file-manager skill — inherits the parent's TCC identity.
|
|
1584
|
+
* If `node` doesn't have Full Disk Access, those children silently fail to
|
|
1585
|
+
* read protected directories (no dialog appears under launchd).
|
|
1586
|
+
*
|
|
1587
|
+
* Heuristic for "FDA granted": try to read `~/Library/Mail`, which lives in
|
|
1588
|
+
* the TCC-protected user-data quarantine. Successful read ⇒ FDA is on.
|
|
1589
|
+
* This matches `src/services/sudo.ts:getSudoStatus()`.
|
|
1590
|
+
*
|
|
1591
|
+
* Returns { realNodePath, hasFDA, panEUrl, settingsCommand } so callers can
|
|
1592
|
+
* print actionable guidance.
|
|
1593
|
+
*/
|
|
1594
|
+
function checkMacosFullDiskAccess(nodePath) {
|
|
1595
|
+
if (process.platform !== "darwin") {
|
|
1596
|
+
return { skipped: true };
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// Resolve symlinks — TCC tracks the real binary path, not the symlink.
|
|
1600
|
+
// /opt/homebrew/bin/node is typically a symlink into the Cellar.
|
|
1601
|
+
let realNodePath = nodePath;
|
|
1602
|
+
try {
|
|
1603
|
+
realNodePath = realpathSync(nodePath);
|
|
1604
|
+
} catch {
|
|
1605
|
+
// Fall back to the symlink path if resolution fails.
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
let hasFDA = false;
|
|
1609
|
+
try {
|
|
1610
|
+
accessSync(resolve(homedir(), "Library", "Mail"), fsConstants.R_OK);
|
|
1611
|
+
hasFDA = true;
|
|
1612
|
+
} catch {
|
|
1613
|
+
// Either Mail isn't there (very rare) or FDA is missing. Default to
|
|
1614
|
+
// "missing" — false negatives just trigger a warning the user can ignore.
|
|
1615
|
+
hasFDA = false;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
return {
|
|
1619
|
+
skipped: false,
|
|
1620
|
+
realNodePath,
|
|
1621
|
+
hasFDA,
|
|
1622
|
+
paneUrl: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles",
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1560
1626
|
/**
|
|
1561
1627
|
* Render the launchd plist that runs `node dist/index.js` as a per-user
|
|
1562
1628
|
* agent. Inherits the GUI login session so the macOS Keychain is
|
|
@@ -1726,6 +1792,40 @@ async function launchdInstall() {
|
|
|
1726
1792
|
console.log("");
|
|
1727
1793
|
console.log("💡 pm2 still has other projects running — leaving it installed.");
|
|
1728
1794
|
}
|
|
1795
|
+
|
|
1796
|
+
// ── Full Disk Access reminder ────────────────────────────────────────────
|
|
1797
|
+
//
|
|
1798
|
+
// Under launchd, the bot has no foreground Terminal to inherit TCC from.
|
|
1799
|
+
// Anything it spawns (codex CLI, file-reading skills, etc.) needs `node`
|
|
1800
|
+
// itself to have Full Disk Access — otherwise reads silently fail.
|
|
1801
|
+
const fda = checkMacosFullDiskAccess(nodePath);
|
|
1802
|
+
if (!fda.skipped && !fda.hasFDA) {
|
|
1803
|
+
console.log("");
|
|
1804
|
+
console.log("⚠️ Full Disk Access not granted to node.");
|
|
1805
|
+
console.log("");
|
|
1806
|
+
console.log(" Under launchd, the bot can't ask for permission dialogs interactively.");
|
|
1807
|
+
console.log(" Without FDA, anything the bot spawns (codex CLI, file-reading skills,");
|
|
1808
|
+
console.log(" ~/Documents access, …) will silently fail to read protected files.");
|
|
1809
|
+
console.log("");
|
|
1810
|
+
console.log(" Grant once: System Settings → Privacy & Security → Full Disk Access → +");
|
|
1811
|
+
console.log(" Then add this exact path (⌘⇧G to paste it):");
|
|
1812
|
+
console.log("");
|
|
1813
|
+
console.log(` ${fda.realNodePath}`);
|
|
1814
|
+
console.log("");
|
|
1815
|
+
console.log(" Open the right pane now:");
|
|
1816
|
+
console.log(` open "${fda.paneUrl}"`);
|
|
1817
|
+
console.log("");
|
|
1818
|
+
console.log(" After granting, restart the bot to pick up the new permission:");
|
|
1819
|
+
console.log(` launchctl kickstart -k gui/$UID/${label}`);
|
|
1820
|
+
console.log("");
|
|
1821
|
+
console.log(" Note: re-grant is required after `brew upgrade node` — TCC binds to");
|
|
1822
|
+
console.log(" the Cellar version path, which changes when node is upgraded.");
|
|
1823
|
+
} else if (!fda.skipped && fda.hasFDA) {
|
|
1824
|
+
console.log("");
|
|
1825
|
+
console.log("✅ Full Disk Access already granted to node — spawned tools can read");
|
|
1826
|
+
console.log(` protected files. (Granted path: ${fda.realNodePath})`);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1729
1829
|
process.exit(0);
|
|
1730
1830
|
}
|
|
1731
1831
|
|
|
@@ -91,6 +91,12 @@ export class CodexCLIProvider {
|
|
|
91
91
|
timeout: 120_000,
|
|
92
92
|
env: { ...process.env, NO_COLOR: "1" },
|
|
93
93
|
});
|
|
94
|
+
// Close stdin so `codex exec` doesn't wait for additional input — the
|
|
95
|
+
// prompt is passed as a positional arg, no stdin payload follows.
|
|
96
|
+
// Without this, codex prints "Reading additional input from stdin..."
|
|
97
|
+
// and blocks until the 120s timeout, causing the bot to return
|
|
98
|
+
// "keine Antwort" / empty replies on the chat side.
|
|
99
|
+
proc.stdin.end();
|
|
94
100
|
let stdout = "";
|
|
95
101
|
let stderr = "";
|
|
96
102
|
proc.stdout.on("data", (data) => {
|
package/package.json
CHANGED
package/02_yandex.png
DELETED
|
Binary file
|