limbo-ai 2026.4.3 → 2026.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/cli.js CHANGED
@@ -2307,7 +2307,7 @@ function selfUpdateCli() {
2307
2307
 
2308
2308
  if (isGlobal) {
2309
2309
  log(`Updating CLI: ${pkg.version} → ${latest}...`);
2310
- execSync('npm install -g limbo-ai@latest', { stdio: 'inherit', timeout: 300000 });
2310
+ execSync('npm install -g limbo-ai@latest', { stdio: 'inherit' });
2311
2311
  ok(`CLI updated to ${latest}.`);
2312
2312
  try { fs.unlinkSync(UPDATE_CHECK_FILE); } catch {}
2313
2313
  return true;
@@ -2366,6 +2366,34 @@ function cmdUpdate() {
2366
2366
 
2367
2367
  log('Pulling latest image...');
2368
2368
  run(`docker compose -f "${COMPOSE_FILE}" pull -q`);
2369
+
2370
+ // ── Version parity check ──────────────────────────────────────────────
2371
+ // The CLI and Docker image MUST be the same version. If selfUpdateCli
2372
+ // failed (network, timeout killed by an older CLI, npm error) but the
2373
+ // image was updated, the compose template from the old CLI is wrong for
2374
+ // the new image. Detect the mismatch and force a CLI update + re-exec.
2375
+ const cliVersion = require('./package.json').version;
2376
+ try {
2377
+ const imageVersion = execSync(
2378
+ `docker compose -f "${COMPOSE_FILE}" run --rm --no-deps --entrypoint node limbo -e "process.stdout.write(require('/app/package.json').version)"`,
2379
+ { encoding: 'utf8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] }
2380
+ ).trim();
2381
+ if (imageVersion && imageVersion !== cliVersion) {
2382
+ warn(`CLI (${cliVersion}) ≠ image (${imageVersion}) — updating CLI to match...`);
2383
+ try {
2384
+ execSync('npm install -g limbo-ai@latest', { stdio: 'inherit' });
2385
+ log('Re-executing with matched CLI...');
2386
+ const { execFileSync } = require('child_process');
2387
+ execFileSync(process.execPath, process.argv.slice(1), { stdio: 'inherit' });
2388
+ return;
2389
+ } catch {
2390
+ warn('Could not update CLI. Continuing with current version.');
2391
+ }
2392
+ }
2393
+ } catch {
2394
+ // Can't check image version (image not pulled yet, etc.) — continue.
2395
+ }
2396
+
2369
2397
  log('Restarting...');
2370
2398
  run(`docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans`);
2371
2399
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "limbo-ai",
3
- "version": "2026.4.3",
3
+ "version": "2026.4.4",
4
4
  "description": "Your personal AI memory agent — install and manage Limbo via npx",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -4,6 +4,12 @@
4
4
  # via gosu (same pattern as PostgreSQL, Redis, and other Docker official images).
5
5
  set -e
6
6
 
7
+ # ── Fix data-dir ownership (MUST be first) ───────────────────────────────────
8
+ # Bind-mounted dirs may be owned by the host user (uid ≠ 999). Named volumes
9
+ # may be root-owned. chown everything to limbo:limbo so the app can read/write.
10
+ # This runs before mkdir, before logging, before anything that touches /data.
11
+ chown -R limbo:limbo /data /home/limbo/.openclaw /home/limbo/.npm 2>/dev/null || true
12
+
7
13
  LOG_DIR="/data/logs"
8
14
  LOG_FILE="$LOG_DIR/startup.log"
9
15
 
@@ -17,43 +23,44 @@ log() {
17
23
  }
18
24
 
19
25
  log "INFO Limbo container starting"
20
-
21
- # ── Fix data-dir ownership ───────────────────────────────────────────────────
22
- # Bind-mounted dirs may be owned by the host user (uid ≠ 999). Named volumes
23
- # may be root-owned. chown everything to limbo:limbo so the app can read/write.
24
- chown -R limbo:limbo /data /home/limbo/.openclaw /home/limbo/.npm 2>/dev/null || true
25
26
  log "INFO Data directory ownership fixed"
26
27
 
27
- # ── Migrate legacy Docker secrets to .env ────────────────────────────────────
28
- # Pre-v2026.4.3 composes mounted tokens as Docker secrets at /run/secrets/.
29
- # The host CLI tried to migrate them but often failed because the secret files
30
- # are root-owned and the CLI runs as a non-root host user. The container CAN
31
- # read /run/secrets (root access before gosu), so we do it here — idempotent.
32
- if [ -d /run/secrets ]; then
33
- _env_file="/data/config/.env"
34
- [ -f "$_env_file" ] || touch "$_env_file"
35
- _migrated=0
36
- for _pair in \
37
- "telegram_bot_token:TELEGRAM_BOT_TOKEN" \
38
- "groq_api_key:GROQ_API_KEY" \
39
- "brave_api_key:BRAVE_API_KEY" \
40
- "gateway_token:GATEWAY_TOKEN" \
41
- "llm_api_key:LLM_API_KEY" \
42
- "google_client_id:GOOGLE_CLIENT_ID" \
43
- "google_client_secret:GOOGLE_CLIENT_SECRET"; do
44
- _file="${_pair%%:*}"
45
- _var="${_pair##*:}"
46
- _path="/run/secrets/$_file"
28
+ # ── Migrate legacy tokens to .env ────────────────────────────────────────────
29
+ # Pre-v2026.4.3 installs stored tokens in separate files:
30
+ # - /run/secrets/* (Docker secrets, old compose)
31
+ # - /home/limbo/.openclaw/secrets/* (written by setup-server inside container)
32
+ # The host CLI migration often failed because these files are root-owned.
33
+ # The container runs as root here (before gosu), so we can read everything.
34
+ _env_file="/data/config/.env"
35
+ [ -f "$_env_file" ] || touch "$_env_file"
36
+ _migrated=0
37
+ for _pair in \
38
+ "telegram_bot_token:TELEGRAM_BOT_TOKEN" \
39
+ "telegram_chat_id:TELEGRAM_CHAT_ID" \
40
+ "groq_api_key:GROQ_API_KEY" \
41
+ "brave_api_key:BRAVE_API_KEY" \
42
+ "gateway_token:GATEWAY_TOKEN" \
43
+ "llm_api_key:LLM_API_KEY" \
44
+ "google_client_id:GOOGLE_CLIENT_ID" \
45
+ "google_client_secret:GOOGLE_CLIENT_SECRET"; do
46
+ _file="${_pair%%:*}"
47
+ _var="${_pair##*:}"
48
+ # Skip if already in .env
49
+ grep -q "^${_var}=" "$_env_file" 2>/dev/null && continue
50
+ # Search all legacy paths (first non-empty wins)
51
+ for _dir in /run/secrets /home/limbo/.openclaw/secrets; do
52
+ _path="$_dir/$_file"
47
53
  if [ -f "$_path" ]; then
48
54
  _val="$(cat "$_path" 2>/dev/null | tr -d '\n')"
49
- if [ -n "$_val" ] && ! grep -q "^${_var}=" "$_env_file" 2>/dev/null; then
55
+ if [ -n "$_val" ]; then
50
56
  echo "${_var}=${_val}" >> "$_env_file"
51
57
  _migrated=$((_migrated + 1))
58
+ break
52
59
  fi
53
60
  fi
54
61
  done
55
- [ "$_migrated" -gt 0 ] && log "INFO Migrated $_migrated token(s) from /run/secrets to .env"
56
- fi
62
+ done
63
+ [ "$_migrated" -gt 0 ] && log "INFO Migrated $_migrated token(s) from legacy secret files to .env"
57
64
 
58
65
  # ── Load config from .env ────────────────────────────────────────────────────
59
66
  # All tokens live inside /data/config/.env. Source it now so the rest of the