anurag008w commited on
Commit
6bce97b
Β·
unverified Β·
2 Parent(s): 211a067b3c3254

Merge pull request #25 from anurag162008/codex/find-automatic-package-installation-method-d90k9g

Browse files
.env.example CHANGED
@@ -132,7 +132,10 @@ LLM_MODEL=anthropic/claude-sonnet-4-5
132
  # have multiple accounts or want to spread rate-limit quota.
133
  #
134
  # Pattern: <PROVIDER>_API_KEYS=key1,key2,key3
135
- # Fallback order: plural pool β†’ singular key β†’ LLM_API_KEY
 
 
 
136
  #
137
  # Uncomment and fill in only the providers you use:
138
  #
 
132
  # have multiple accounts or want to spread rate-limit quota.
133
  #
134
  # Pattern: <PROVIDER>_API_KEYS=key1,key2,key3
135
+ # Fallback order: plural pool β†’ singular key β†’ LLM_API_KEY (optional)
136
+ # Set false only if you want to disable global LLM_API_KEY fallback
137
+ # across providers.
138
+ LLM_API_KEY_FALLBACK_ENABLED=true
139
  #
140
  # Uncomment and fill in only the providers you use:
141
  #
ENV_VARIABLES_FULL_LIST.md ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ENV Variables Full List (Repo Scope)
2
+
3
+ > Internal reference file. Not linked from README/docs navigation.
4
+
5
+ ## 1) User-settable (Space Secrets / `.env`)
6
+
7
+ These are the main variables you can set directly.
8
+
9
+ ### Core startup/auth
10
+ - `LLM_API_KEY` β€” string API key. Example: `LLM_API_KEY=sk-ant-xxxx`
11
+ - `LLM_MODEL` β€” `provider/model-id` string. Examples:
12
+ - `LLM_MODEL=anthropic/claude-sonnet-4-5`
13
+ - `LLM_MODEL=nvidia/meta/llama-3.1-70b-instruct`
14
+ - `GATEWAY_TOKEN` β€” random secret string. Example: `GATEWAY_TOKEN=9d2f...`
15
+ - `OPENCLAW_PASSWORD` β€” plaintext password string. Example: `OPENCLAW_PASSWORD=MyStrongPass123`
16
+
17
+ ### Provider fallback/selection
18
+ - `LLM_API_KEY_FALLBACK_ENABLED` β€” boolean-like:
19
+ - enabled: `true`, `1`, `yes`, `on`
20
+ - disabled: `false`, `0`, `no`, `off`
21
+ - default/example: `LLM_API_KEY_FALLBACK_ENABLED=true`
22
+
23
+ ### Provider-specific keys (single-key form)
24
+ Use plain provider key strings. Examples:
25
+ - `NVIDIA_API_KEY=nvapi-xxxx`
26
+ - `OPENAI_API_KEY=sk-xxxx`
27
+ - `GEMINI_API_KEY=AIzaSyxxxx`
28
+
29
+ Full list:
30
+ - `ANTHROPIC_API_KEY`
31
+ - `OPENAI_API_KEY`
32
+ - `GEMINI_API_KEY`
33
+ - `DEEPSEEK_API_KEY`
34
+ - `OPENROUTER_API_KEY`
35
+ - `KILOCODE_API_KEY`
36
+ - `OPENCODE_API_KEY`
37
+ - `ZAI_API_KEY`
38
+ - `MOONSHOT_API_KEY`
39
+ - `KIMI_API_KEY`
40
+ - `MINIMAX_API_KEY`
41
+ - `MODELSTUDIO_API_KEY`
42
+ - `XIAOMI_API_KEY`
43
+ - `VOLCANO_ENGINE_API_KEY`
44
+ - `BYTEPLUS_API_KEY`
45
+ - `QIANFAN_API_KEY`
46
+ - `MISTRAL_API_KEY`
47
+ - `XAI_API_KEY`
48
+ - `NVIDIA_API_KEY`
49
+ - `COHERE_API_KEY`
50
+ - `GROQ_API_KEY`
51
+ - `TOGETHER_API_KEY`
52
+ - `CEREBRAS_API_KEY`
53
+ - `VENICE_API_KEY`
54
+ - `SYNTHETIC_API_KEY`
55
+ - `COPILOT_GITHUB_TOKEN`
56
+ - `HUGGINGFACE_HUB_TOKEN`
57
+ - `AI_GATEWAY_API_KEY`
58
+
59
+ ### Provider key pools (comma-separated rotation)
60
+ Format: comma-separated keys, no spaces required. Examples:
61
+ - `NVIDIA_API_KEYS=nvapi-key1,nvapi-key2`
62
+ - `OPENAI_API_KEYS=sk-a,sk-b,sk-c`
63
+
64
+ Full list:
65
+ - `ANTHROPIC_API_KEYS`
66
+ - `OPENAI_API_KEYS`
67
+ - `GEMINI_API_KEYS`
68
+ - `DEEPSEEK_API_KEYS`
69
+ - `OPENROUTER_API_KEYS`
70
+ - `KILOCODE_API_KEYS`
71
+ - `OPENCODE_API_KEYS`
72
+ - `ZAI_API_KEYS`
73
+ - `MOONSHOT_API_KEYS`
74
+ - `MINIMAX_API_KEYS`
75
+ - `XIAOMI_API_KEYS`
76
+ - `VOLCANO_ENGINE_API_KEYS`
77
+ - `BYTEPLUS_API_KEYS`
78
+ - `MISTRAL_API_KEYS`
79
+ - `XAI_API_KEYS`
80
+ - `NVIDIA_API_KEYS`
81
+ - `GROQ_API_KEYS`
82
+ - `COHERE_API_KEYS`
83
+ - `TOGETHER_API_KEYS`
84
+ - `CEREBRAS_API_KEYS`
85
+ - `HUGGINGFACE_HUB_TOKENS`
86
+
87
+ ### Telegram / WhatsApp
88
+ - `TELEGRAM_BOT_TOKEN` β€” bot token string. Example: `TELEGRAM_BOT_TOKEN=123456:ABC...`
89
+ - `TELEGRAM_ALLOWED_USERS` β€” comma-separated numeric IDs. Example: `TELEGRAM_ALLOWED_USERS=12345,67890`
90
+ - `TELEGRAM_USER_ID` β€” legacy single numeric ID. Example: `TELEGRAM_USER_ID=12345`
91
+ - `TELEGRAM_USER_IDS` β€” legacy comma-separated IDs. Example: `TELEGRAM_USER_IDS=12345,67890`
92
+ - `WHATSAPP_ENABLED` β€” boolean-like (`true/false`, `1/0`, `yes/no`, `on/off`). Example: `WHATSAPP_ENABLED=true`
93
+
94
+ ### Backup/sync
95
+ - `HF_TOKEN` β€” HF token string. Example: `HF_TOKEN=hf_xxxxx`
96
+ - `HF_USERNAME` β€” HF username string. Example: `HF_USERNAME=myhandle`
97
+ - `BACKUP_DATASET_NAME` β€” dataset slug string. Example: `BACKUP_DATASET_NAME=huggingclaw-backup`
98
+ - `SYNC_INTERVAL` β€” integer seconds. Example: `SYNC_INTERVAL=180`
99
+ - `SYNC_START_DELAY` β€” integer seconds. Example: `SYNC_START_DELAY=10`
100
+ - `SYNC_MAX_FILE_BYTES` β€” integer bytes. Example: `SYNC_MAX_FILE_BYTES=52428800`
101
+ - `OPENCLAW_CONFIG_WATCH_INTERVAL` β€” number (supports decimal seconds). Example: `OPENCLAW_CONFIG_WATCH_INTERVAL=1`
102
+ - `OPENCLAW_CONFIG_SETTLE_SECONDS` β€” number (supports decimal seconds). Example: `OPENCLAW_CONFIG_SETTLE_SECONDS=3`
103
+
104
+ ### Gateway/runtime tuning
105
+ - `GATEWAY_HOST` β€” hostname/IP string. Example: `GATEWAY_HOST=127.0.0.1`
106
+ - `GATEWAY_PORT` β€” integer port. Example: `GATEWAY_PORT=3456`
107
+ - `GATEWAY_VERBOSE` β€” boolean-like. Example: `GATEWAY_VERBOSE=true`
108
+ - `GATEWAY_READY_TIMEOUT` β€” integer seconds. Example: `GATEWAY_READY_TIMEOUT=60`
109
+ - `GATEWAY_MAX_RESTARTS` β€” integer count. Example: `GATEWAY_MAX_RESTARTS=10`
110
+ - `GATEWAY_RESTART_DELAY` β€” integer seconds. Example: `GATEWAY_RESTART_DELAY=2`
111
+ - `TRUSTED_PROXIES` β€” comma-separated CIDR/IP list. Example: `TRUSTED_PROXIES=127.0.0.1,10.0.0.0/8`
112
+ - `ALLOWED_ORIGINS` β€” comma-separated origins. Example: `ALLOWED_ORIGINS=https://a.com,https://b.com`
113
+ - `PORT` β€” integer external port. Example: `PORT=7860`
114
+
115
+ ### Browser/plugin controls
116
+ - `BROWSER_PLUGIN_MODE` β€” mode string (`auto`, `builtin`, `external`, etc. depending on runtime). Example: `BROWSER_PLUGIN_MODE=auto`
117
+ - `BROWSER_EXECUTABLE_PATH` β€” absolute path. Example: `BROWSER_EXECUTABLE_PATH=/usr/bin/chromium`
118
+ - `BROWSER_DISABLED` β€” boolean-like. Example: `BROWSER_DISABLED=false`
119
+ - `ACP_PLUGIN_MODE` β€” mode string. Example: `ACP_PLUGIN_MODE=auto`
120
+ - `ACPX_DISABLED` β€” boolean-like. Example: `ACPX_DISABLED=true`
121
+
122
+ ### OpenClaw logging/behavior
123
+ - `OPENCLAW_VERSION` β€” image/app version string. Example: `OPENCLAW_VERSION=latest`
124
+ - `OPENCLAW_RUNTIME_VERSION` β€” runtime tag string. Example: `OPENCLAW_RUNTIME_VERSION=v1.2.3`
125
+ - `OPENCLAW_DISPLAY_VERSION` β€” display label string. Example: `OPENCLAW_DISPLAY_VERSION=Custom`
126
+ - `OPENCLAW_DISABLE_BONJOUR` β€” boolean-like. Example: `OPENCLAW_DISABLE_BONJOUR=true`
127
+ - `OPENCLAW_CONSOLE_LOG_LEVEL` β€” log level (`trace|debug|info|warn|error`). Example: `OPENCLAW_CONSOLE_LOG_LEVEL=warn`
128
+ - `OPENCLAW_CONSOLE_LOG_STYLE` β€” style string. Example: `OPENCLAW_CONSOLE_LOG_STYLE=pretty`
129
+ - `OPENCLAW_FILE_LOG_LEVEL` β€” log level (`trace|debug|info|warn|error`). Example: `OPENCLAW_FILE_LOG_LEVEL=info`
130
+
131
+ ### Custom OpenAI-compatible provider
132
+ - `CUSTOM_PROVIDER_NAME` β€” unique provider prefix. Example: `CUSTOM_PROVIDER_NAME=modal`
133
+ - `CUSTOM_BASE_URL` β€” base URL only (not `/chat/completions`). Example: `CUSTOM_BASE_URL=https://api.modal.com/v1`
134
+ - `CUSTOM_MODEL_ID` β€” model id string. Example: `CUSTOM_MODEL_ID=zai-org/GLM-5.1-FP8`
135
+ - `CUSTOM_MODEL_NAME` β€” display name. Example: `CUSTOM_MODEL_NAME=GLM 5.1 FP8`
136
+ - `CUSTOM_API_KEY` β€” provider key string. Example: `CUSTOM_API_KEY=sk-xxxx`
137
+ - `CUSTOM_API_TYPE` β€” API mode string. Example: `CUSTOM_API_TYPE=openai-completions`
138
+ - `CUSTOM_CONTEXT_WINDOW` β€” integer token window. Example: `CUSTOM_CONTEXT_WINDOW=128000`
139
+ - `CUSTOM_MAX_TOKENS` β€” integer max tokens. Example: `CUSTOM_MAX_TOKENS=500`
140
+
141
+ ### Cloudflare proxy/keepalive
142
+ - `CLOUDFLARE_WORKERS_TOKEN` β€” API token string. Example: `CLOUDFLARE_WORKERS_TOKEN=cf_xxx`
143
+ - `CLOUDFLARE_WORKER_NAME` β€” worker name string. Example: `CLOUDFLARE_WORKER_NAME=huggingclaw-proxy`
144
+ - `CLOUDFLARE_ACCOUNT_ID` β€” account id string. Example: `CLOUDFLARE_ACCOUNT_ID=abc123...`
145
+ - `CLOUDFLARE_PROXY_URL` β€” full proxy URL. Example: `CLOUDFLARE_PROXY_URL=https://my-worker.workers.dev`
146
+ - `CLOUDFLARE_PROXY_SECRET` β€” shared secret string. Example: `CLOUDFLARE_PROXY_SECRET=long-random-secret`
147
+ - `CLOUDFLARE_PROXY_DOMAINS` β€” comma-separated domains. Example: `CLOUDFLARE_PROXY_DOMAINS=api.openai.com,api.anthropic.com`
148
+ - `CLOUDFLARE_PROXY_DEBUG` β€” boolean-like. Example: `CLOUDFLARE_PROXY_DEBUG=false`
149
+ - `CLOUDFLARE_KEEPALIVE_ENABLED` β€” boolean-like. Example: `CLOUDFLARE_KEEPALIVE_ENABLED=true`
150
+ - `CLOUDFLARE_KEEPALIVE_URL` β€” URL string. Example: `CLOUDFLARE_KEEPALIVE_URL=https://space-url.hf.space`
151
+ - `CLOUDFLARE_KEEPALIVE_CRON` β€” cron string. Example: `CLOUDFLARE_KEEPALIVE_CRON=*/5 * * * *`
152
+ - `CLOUDFLARE_KEEPALIVE_WORKER_NAME` β€” worker name string. Example: `CLOUDFLARE_KEEPALIVE_WORKER_NAME=huggingclaw-keepalive`
153
+
154
+ ### Startup command replay/install helpers
155
+ - `HUGGINGCLAW_RUN` β€” shell commands string. Example: `HUGGINGCLAW_RUN=apt-get update && apt-get install -y ffmpeg`
156
+ - `HUGGINGCLAW_APT_PACKAGES` β€” comma/space separated package names. Example: `HUGGINGCLAW_APT_PACKAGES=ffmpeg,git`
157
+ - `HUGGINGCLAW_PIP_PACKAGES` β€” pip package list string. Example: `HUGGINGCLAW_PIP_PACKAGES=yt-dlp,requests`
158
+ - `HUGGINGCLAW_NPM_PACKAGES` β€” npm package list string. Example: `HUGGINGCLAW_NPM_PACKAGES=sharp,axios`
159
+ - `HUGGINGCLAW_OPENCLAW_PLUGINS` β€” plugin ids list string. Example: `HUGGINGCLAW_OPENCLAW_PLUGINS=@openclaw/telegram,@openclaw/whatsapp`
160
+ - `HUGGINGCLAW_STARTUP_SCRIPT` β€” shell script string. Example: `HUGGINGCLAW_STARTUP_SCRIPT=echo hello`
161
+ - `HUGGINGCLAW_STARTUP_SCRIPT_B64` β€” base64 script content. Example: `HUGGINGCLAW_STARTUP_SCRIPT_B64=IyEvYmluL2Jhc2gKZWNobyBoaQ==`
162
+ - `HUGGINGCLAW_STARTUP_COMMANDS` β€” newline/semicolon commands string.
163
+ - `HUGGINGCLAW_STARTUP_STRICT` β€” boolean-like. Example: `HUGGINGCLAW_STARTUP_STRICT=true`
164
+ - `WEBHOOK_URL` β€” webhook endpoint URL. Example: `WEBHOOK_URL=https://hooks.example.com/hc`
165
+
166
+ ---
167
+
168
+ ## 2) Runtime/Internal envs (normally do **not** set manually)
169
+
170
+ These are mostly derived/temporary/process variables used by scripts:
171
+
172
+ - `APP_BASE`
173
+ - `BACKUP_DATASET`
174
+ - `BROWSER_SHOULD_ENABLE`
175
+ - `CF_PROXY_ENV_FILE`
176
+ - `CLEAN_TG_TOKEN`
177
+ - `CONFIG_JSON`
178
+ - `CUSTOM_BASE_URL_NORMALIZED`
179
+ - `CUSTOM_PROVIDER_NORMALIZED`
180
+ - `CUSTOM_PROVIDER_OK`
181
+ - `DEBIAN_FRONTEND`
182
+ - `ERRORS`
183
+ - `EXISTING_CONFIG`
184
+ - `GATEWAY_EXIT_CODE`
185
+ - `GATEWAY_PID`
186
+ - `GATEWAY_RESTART_COUNT`
187
+ - `GUARDIAN_PID`
188
+ - `HC_STARTUP_FAILURES`
189
+ - `HC_STARTUP_INDEX`
190
+ - `HC_STARTUP_STRICT_NORMALIZED`
191
+ - `HC_STARTUP_VAR`
192
+ - `HOME`
193
+ - `IDS_JSON`
194
+ - `INSTALLS`
195
+ - `LLM_PROVIDER`
196
+ - `NODE_OPTIONS`
197
+ - `NPM_CONFIG_PREFIX`
198
+ - `OPENCLAW_APP_DIR`
199
+ - `OPENCLAW_CONSOLE_LOG_LEVEL_CONFIGURED`
200
+ - `OPENCLAW_CONSOLE_LOG_STYLE_CONFIGURED`
201
+ - `OPENCLAW_FILE_LOG_LEVEL_CONFIGURED`
202
+ - `ORIGINS_JSON`
203
+ - `PATCHED`
204
+ - `PATH`
205
+ - `PLUGIN_ALLOW_JSON`
206
+ - `PROXIES_JSON`
207
+ - `PROXY_URL`
208
+ - `PYTHONUSERBASE`
209
+ - `RESET_MARKER_PATH`
210
+ - `SPACE_AUTHOR_NAME`
211
+ - `SPACE_HOST`
212
+ - `SPACE_REPO_NAME`
213
+ - `STARTUP_FILE`
214
+ - `SYNC_LOOP_PID`
215
+ - `VIRTUAL_ENV`
216
+ - `WEBHOOK_BODY`
217
+ - `WHATSAPP_CONFIG_ENABLED`
218
+ - `WHATSAPP_ENABLED_CONFIGURED`
219
+ - `WHATSAPP_ENABLED_NORMALIZED`
README.md CHANGED
@@ -250,10 +250,10 @@ GEMINI_API_KEYS=AIza-key1,AIza-key2
250
  **Fallback chain** (per provider):
251
  1. `{PROVIDER}_API_KEYS` β€” comma-separated pool *(preferred)*
252
  2. `{PROVIDER}_API_KEY` β€” single dedicated key
253
- 3. `LLM_API_KEY` β€” universal fallback *(default for all providers)*
254
 
255
  > [!TIP]
256
- > If you only set `LLM_API_KEY`, all providers use it as a fallback automatically β€” no extra config needed. Add per-provider pools only when you need multi-key rotation.
257
 
258
  Supported per-provider variables: `ANTHROPIC_API_KEYS`, `OPENAI_API_KEYS`, `GEMINI_API_KEYS`, `DEEPSEEK_API_KEYS`, `GROQ_API_KEYS`, `MISTRAL_API_KEYS`, `OPENROUTER_API_KEYS`, `XAI_API_KEYS`, `NVIDIA_API_KEYS`, `COHERE_API_KEYS`, `TOGETHER_API_KEYS`, `CEREBRAS_API_KEYS`, and more β€” see `.env.example` for the full list.
259
 
 
250
  **Fallback chain** (per provider):
251
  1. `{PROVIDER}_API_KEYS` β€” comma-separated pool *(preferred)*
252
  2. `{PROVIDER}_API_KEY` β€” single dedicated key
253
+ 3. `LLM_API_KEY` β€” universal fallback *(enabled by default; disable with `LLM_API_KEY_FALLBACK_ENABLED=false`)*
254
 
255
  > [!TIP]
256
+ > By default, `LLM_API_KEY` fallback is enabled for compatibility. Set `LLM_API_KEY_FALLBACK_ENABLED=false` if you want strict provider-only activation.
257
 
258
  Supported per-provider variables: `ANTHROPIC_API_KEYS`, `OPENAI_API_KEYS`, `GEMINI_API_KEYS`, `DEEPSEEK_API_KEYS`, `GROQ_API_KEYS`, `MISTRAL_API_KEYS`, `OPENROUTER_API_KEYS`, `XAI_API_KEYS`, `NVIDIA_API_KEYS`, `COHERE_API_KEYS`, `TOGETHER_API_KEYS`, `CEREBRAS_API_KEYS`, and more β€” see `.env.example` for the full list.
259
 
multi-provider-key-rotator.cjs CHANGED
@@ -8,7 +8,7 @@
8
  *
9
  * For each provider you can supply a comma-separated pool:
10
  * ANTHROPIC_API_KEYS=key1,key2,key3
11
- * Falls back to the singular env var, then to LLM_API_KEY.
12
  *
13
  * Keys are rotated round-robin per provider independently.
14
  *
@@ -30,7 +30,9 @@ const log = (...args) => console.error(...args);
30
  // envPlural – env var that holds a comma-separated key pool (preferred)
31
  // envSingular – env var that holds a single key (fallback)
32
  //
33
- // LLM_API_KEY is the final fallback for every provider.
 
 
34
  //
35
  const PROVIDERS = [
36
  {
@@ -180,6 +182,8 @@ function normalizeKeys(...inputs) {
180
 
181
  // Build per-provider key pools + rotation indices
182
  const providerState = PROVIDERS.map(p => {
 
 
183
  const dedicatedKeys = normalizeKeys(
184
  process.env[p.envPlural] || '',
185
  process.env[p.envSingular] || '',
@@ -187,7 +191,11 @@ const providerState = PROVIDERS.map(p => {
187
  const hasDedicated = dedicatedKeys.length > 0;
188
  const keys = hasDedicated
189
  ? dedicatedKeys
190
- : normalizeKeys(process.env.LLM_API_KEY || '');
 
 
 
 
191
 
192
  if (hasDedicated) {
193
  log(`[key-rotator] ${p.name}: ${keys.length} key${keys.length === 1 ? '' : 's'}`);
@@ -208,6 +216,8 @@ const fallbackCount = providerState.filter(p => {
208
  }).length;
209
  if (fallbackCount > 0) {
210
  log(`[key-rotator] ${fallbackCount} provider(s) using LLM_API_KEY fallback`);
 
 
211
  }
212
 
213
  // ─── Runtime helpers ─────────────────────────────────────────────────────────
 
8
  *
9
  * For each provider you can supply a comma-separated pool:
10
  * ANTHROPIC_API_KEYS=key1,key2,key3
11
+ * Falls back to the singular env var, and optionally to LLM_API_KEY.
12
  *
13
  * Keys are rotated round-robin per provider independently.
14
  *
 
30
  // envPlural – env var that holds a comma-separated key pool (preferred)
31
  // envSingular – env var that holds a single key (fallback)
32
  //
33
+ // LLM_API_KEY fallback can be controlled via:
34
+ // LLM_API_KEY_FALLBACK_ENABLED=true|false
35
+ // Default is enabled for backwards compatibility.
36
  //
37
  const PROVIDERS = [
38
  {
 
182
 
183
  // Build per-provider key pools + rotation indices
184
  const providerState = PROVIDERS.map(p => {
185
+ const llmFallbackRaw = String(process.env.LLM_API_KEY_FALLBACK_ENABLED || '').trim().toLowerCase();
186
+ const llmFallbackEnabled = !/^(0|false|no|off)$/.test(llmFallbackRaw);
187
  const dedicatedKeys = normalizeKeys(
188
  process.env[p.envPlural] || '',
189
  process.env[p.envSingular] || '',
 
191
  const hasDedicated = dedicatedKeys.length > 0;
192
  const keys = hasDedicated
193
  ? dedicatedKeys
194
+ : (
195
+ llmFallbackEnabled
196
+ ? normalizeKeys(process.env.LLM_API_KEY || '')
197
+ : []
198
+ );
199
 
200
  if (hasDedicated) {
201
  log(`[key-rotator] ${p.name}: ${keys.length} key${keys.length === 1 ? '' : 's'}`);
 
216
  }).length;
217
  if (fallbackCount > 0) {
218
  log(`[key-rotator] ${fallbackCount} provider(s) using LLM_API_KEY fallback`);
219
+ } else if (process.env.LLM_API_KEY && /^(0|false|no|off)$/i.test(String(process.env.LLM_API_KEY_FALLBACK_ENABLED || ''))) {
220
+ log('[key-rotator] LLM_API_KEY fallback disabled (set LLM_API_KEY_FALLBACK_ENABLED=true to re-enable)');
221
  }
222
 
223
  // ─── Runtime helpers ─────────────────────────────────────────────────────────
openclaw-sync.py CHANGED
@@ -70,6 +70,7 @@ EXCLUDED_STATE_NAMES = {
70
  "openclaw-app",
71
  "gateway.log",
72
  "browser",
 
73
  }
74
  WHATSAPP_CREDS_DIR = OPENCLAW_HOME / "credentials" / "whatsapp" / "default"
75
  WHATSAPP_BACKUP_DIR = STATE_DIR / "credentials" / "whatsapp" / "default"
 
70
  "openclaw-app",
71
  "gateway.log",
72
  "browser",
73
+ "npm",
74
  }
75
  WHATSAPP_CREDS_DIR = OPENCLAW_HOME / "credentials" / "whatsapp" / "default"
76
  WHATSAPP_BACKUP_DIR = STATE_DIR / "credentials" / "whatsapp" / "default"