alvin-bot 4.4.3 → 4.4.4
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 +18 -0
- package/dist/migrate.js +25 -2
- package/dist/paths.js +11 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Alvin Bot are documented here.
|
|
4
4
|
|
|
5
|
+
## [4.4.4] — 2026-04-09
|
|
6
|
+
|
|
7
|
+
### 🔐 Security / Data Layout
|
|
8
|
+
|
|
9
|
+
**`.env` now lives only in `DATA_DIR`** — The `ENV_FILE` path constant in `src/paths.ts` has been moved from `BOT_ROOT/.env` to `DATA_DIR/.env` (e.g. `~/.alvin-bot/.env`). This fixes a latent drift bug affecting 6 code sites in `web/server.ts`, `web/setup-api.ts`, `web/doctor-api.ts`, and `services/fallback-order.ts`: before this release, the Web UI's Settings tab, the setup wizard, the doctor repair flow, and the `/fallback` sync were all writing to `BOT_ROOT/.env`, while the bot's config loader in `src/config.ts` reads from `DATA_DIR/.env` first. Changes made through any of those tools were silently written to a file the bot never reads (for globally-installed users, `BOT_ROOT` is inside `node_modules/alvin-bot/` and gets wiped on `npm update -g`).
|
|
10
|
+
|
|
11
|
+
Why this also matters for security: keeping `.env` inside the code repo is defense-in-depth weak. `.gitignore` can be edited, editors create swap files (`.env.swp`, `.env~`), `git add -f` bypasses ignores, backup tools sync whole project folders, and screensharing shows project directories. Secrets belong physically outside the repo.
|
|
12
|
+
|
|
13
|
+
**Automatic migration for legacy installs** — `src/migrate.ts` now copies a legacy `BOT_ROOT/.env` to `DATA_DIR/.env` on first run (only if the destination doesn't exist) and enforces `0600` mode regardless of the source permissions. `hasLegacyData()` now recognizes a stray `BOT_ROOT/.env` as a migration trigger. No action is required from existing users — the bot migrates itself.
|
|
14
|
+
|
|
15
|
+
### 📦 Compatibility
|
|
16
|
+
|
|
17
|
+
No breaking changes. Existing installs upgrade in place and are auto-migrated.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm update -g alvin-bot
|
|
21
|
+
```
|
|
22
|
+
|
|
5
23
|
## [4.4.3] — 2026-04-09
|
|
6
24
|
|
|
7
25
|
### 🔐 Security
|
package/dist/migrate.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import fs from "fs";
|
|
22
22
|
import { resolve } from "path";
|
|
23
|
-
import { BOT_ROOT, MEMORY_DIR, USERS_DIR, BACKUP_DIR, SOUL_FILE, TOOLS_MD, TOOLS_JSON, CRON_FILE, MCP_CONFIG, FALLBACK_FILE, CUSTOM_MODELS, WA_GROUPS, WHATSAPP_AUTH, WA_MEDIA_DIR, ACCESS_FILE, SUDO_ENC_FILE, SUDO_KEY_FILE, MEMORY_FILE, EMBEDDINGS_IDX } from "./paths.js";
|
|
23
|
+
import { BOT_ROOT, MEMORY_DIR, USERS_DIR, BACKUP_DIR, SOUL_FILE, TOOLS_MD, TOOLS_JSON, CRON_FILE, MCP_CONFIG, FALLBACK_FILE, CUSTOM_MODELS, WA_GROUPS, WHATSAPP_AUTH, WA_MEDIA_DIR, ACCESS_FILE, SUDO_ENC_FILE, SUDO_KEY_FILE, MEMORY_FILE, EMBEDDINGS_IDX, ENV_FILE } from "./paths.js";
|
|
24
24
|
/**
|
|
25
25
|
* Check if legacy data exists in the old locations.
|
|
26
26
|
*/
|
|
@@ -31,7 +31,13 @@ export function hasLegacyData() {
|
|
|
31
31
|
resolve(BOT_ROOT, "docs", "users"),
|
|
32
32
|
resolve(BOT_ROOT, "data", "access.json"),
|
|
33
33
|
resolve(BOT_ROOT, "SOUL.md"),
|
|
34
|
-
|
|
34
|
+
// A BOT_ROOT/.env without a corresponding DATA_DIR/.env is a legacy layout
|
|
35
|
+
// — the loader prefers DATA_DIR, so keeping .env in BOT_ROOT silently
|
|
36
|
+
// breaks Settings/Setup/Doctor/fallback-order sync.
|
|
37
|
+
(fs.existsSync(resolve(BOT_ROOT, ".env")) && !fs.existsSync(ENV_FILE))
|
|
38
|
+
? resolve(BOT_ROOT, ".env")
|
|
39
|
+
: "",
|
|
40
|
+
].filter(Boolean);
|
|
35
41
|
return legacyIndicators.some(p => fs.existsSync(p));
|
|
36
42
|
}
|
|
37
43
|
/**
|
|
@@ -47,6 +53,21 @@ function copyIfNew(src, dest) {
|
|
|
47
53
|
}
|
|
48
54
|
return false;
|
|
49
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Copy a file if source exists and destination doesn't, then enforce a specific file mode.
|
|
58
|
+
* Used for files containing secrets (e.g. .env) where 0600 must be guaranteed
|
|
59
|
+
* regardless of the source file's permissions or the process umask.
|
|
60
|
+
*/
|
|
61
|
+
function copyIfNewWithMode(src, dest, mode) {
|
|
62
|
+
const copied = copyIfNew(src, dest);
|
|
63
|
+
if (copied) {
|
|
64
|
+
try {
|
|
65
|
+
fs.chmodSync(dest, mode);
|
|
66
|
+
}
|
|
67
|
+
catch { /* best effort */ }
|
|
68
|
+
}
|
|
69
|
+
return copied;
|
|
70
|
+
}
|
|
50
71
|
/**
|
|
51
72
|
* Recursively copy a directory if source exists and destination doesn't have the files.
|
|
52
73
|
*/
|
|
@@ -89,6 +110,8 @@ export function migrateFromLegacy() {
|
|
|
89
110
|
skipped.push(label);
|
|
90
111
|
}
|
|
91
112
|
// ── Single files ─────────────────────────────────────────
|
|
113
|
+
// .env → .env (secrets — enforce 0600 mode regardless of source perms)
|
|
114
|
+
track(".env → .env", copyIfNewWithMode(resolve(BOT_ROOT, ".env"), ENV_FILE, 0o600));
|
|
92
115
|
// SOUL.md → soul.md
|
|
93
116
|
track("SOUL.md → soul.md", copyIfNew(resolve(BOT_ROOT, "SOUL.md"), SOUL_FILE));
|
|
94
117
|
// TOOLS.md → tools.md
|
package/dist/paths.js
CHANGED
|
@@ -23,13 +23,22 @@ export const PLUGINS_DIR = resolve(BOT_ROOT, "plugins");
|
|
|
23
23
|
export const SKILLS_DIR = resolve(BOT_ROOT, "skills");
|
|
24
24
|
/** User skills directory (custom, outside repo) */
|
|
25
25
|
export const USER_SKILLS_DIR = resolve(DATA_DIR, "skills");
|
|
26
|
-
/** .env — Environment config (stays in BOT_ROOT for dev, or DATA_DIR for packaged) */
|
|
27
|
-
export const ENV_FILE = resolve(BOT_ROOT, ".env");
|
|
28
26
|
/** Example/template files (always in repo) */
|
|
29
27
|
export const SOUL_EXAMPLE = resolve(BOT_ROOT, "SOUL.example.md");
|
|
30
28
|
export const TOOLS_EXAMPLE_MD = resolve(BOT_ROOT, "TOOLS.example.md");
|
|
31
29
|
export const TOOLS_EXAMPLE_JSON = resolve(BOT_ROOT, "docs", "tools.example.json");
|
|
32
30
|
// ── Data paths (DATA_DIR = ~/.alvin-bot) ───────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* .env — Environment config with secrets (BOT_TOKEN, API keys, etc.)
|
|
33
|
+
*
|
|
34
|
+
* Lives in DATA_DIR (outside the code repo) for three reasons:
|
|
35
|
+
* 1. Defense in depth against accidental commits — secrets never touch BOT_ROOT
|
|
36
|
+
* 2. Survives `npm update -g` (BOT_ROOT in global installs = node_modules, gets wiped)
|
|
37
|
+
* 3. Consistent with the loader priority in src/config.ts (DATA_DIR is Priority 1)
|
|
38
|
+
*
|
|
39
|
+
* Legacy installs with BOT_ROOT/.env are auto-migrated on first run (see src/migrate.ts).
|
|
40
|
+
*/
|
|
41
|
+
export const ENV_FILE = resolve(DATA_DIR, ".env");
|
|
33
42
|
/** memory/ — Daily logs and embeddings */
|
|
34
43
|
export const MEMORY_DIR = resolve(DATA_DIR, "memory");
|
|
35
44
|
/** memory/MEMORY.md — Long-term curated memory */
|