@nexus-cortex/cli 4.36.2 → 4.37.1
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/.env.example +358 -0
- package/bin/cortex.js +97 -2
- package/package.json +3 -2
package/.env.example
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Nexus Cortex — Complete Environment Configuration Reference
|
|
2
|
+
# Copy to .env and fill in the API keys for the providers you use. Every other value
|
|
3
|
+
# has a proven-optimal default.
|
|
4
|
+
#
|
|
5
|
+
# WARNING: The Settings Configurator (/config) regenerates .env from SettingsSchema.
|
|
6
|
+
# Runtime-only vars (YOLO, SERVER_IDLE_TIMEOUT, WEB_TOOLS_MODEL, etc.) are NOT in the
|
|
7
|
+
# schema — they will be silently dropped unless the SettingsWriter preserves them.
|
|
8
|
+
#
|
|
9
|
+
# Keep this file and .env structurally in sync: same comments + ordering, only the
|
|
10
|
+
# values differ (.env = your real config, this file = template/defaults).
|
|
11
|
+
|
|
12
|
+
# ============================================
|
|
13
|
+
# API KEYS
|
|
14
|
+
# ============================================
|
|
15
|
+
# A model is only available if its provider's key is set. Leave the rest blank.
|
|
16
|
+
|
|
17
|
+
# Claude (Anthropic) — Fable / Opus / Sonnet / Haiku
|
|
18
|
+
ANTHROPIC_API_KEY=
|
|
19
|
+
# OpenAI — GPT / o-series
|
|
20
|
+
OPENAI_API_KEY=
|
|
21
|
+
# Gemini (legacy key; GEMINI_API_KEY takes priority when both are set)
|
|
22
|
+
GOOGLE_API_KEY=
|
|
23
|
+
# Gemini (preferred key; falls back to GOOGLE_API_KEY)
|
|
24
|
+
GEMINI_API_KEY=
|
|
25
|
+
# xAI — Grok
|
|
26
|
+
XAI_API_KEY=
|
|
27
|
+
# DeepSeek (the default model's provider)
|
|
28
|
+
DEEPSEEK_API_KEY=
|
|
29
|
+
# Nvidia (no standalone NVIDIA cards registered yet — currently reached via Cloudflare)
|
|
30
|
+
NVIDIA_API_KEY=
|
|
31
|
+
# Inception Labs (Mercury diffusion models — mercury-2)
|
|
32
|
+
INCEPTION_API_KEY=
|
|
33
|
+
# Cloudflare Workers AI (@cf/* models). Requires CLOUDFLARE_ACCOUNT_ID.
|
|
34
|
+
CLOUDFLARE_API_TOKEN=
|
|
35
|
+
# Cloudflare account id (paired with the token above)
|
|
36
|
+
CLOUDFLARE_ACCOUNT_ID=
|
|
37
|
+
# Alibaba Qwen (DashScope) — qwen-* models
|
|
38
|
+
DASHSCOPE_API_KEY=
|
|
39
|
+
# MiniMax — minimax-* models
|
|
40
|
+
MINIMAX_API_KEY=
|
|
41
|
+
# Moonshot AI (Kimi) — moonshot-* / kimi-* models
|
|
42
|
+
MOONSHOT_API_KEY=
|
|
43
|
+
# Zhipu AI (GLM) — glm-* models
|
|
44
|
+
ZHIPU_API_KEY=
|
|
45
|
+
# Hugging Face Inference (no HF models registered yet; HUGGINGFACE_TOKEN is also accepted)
|
|
46
|
+
HUGGINGFACE_API_KEY=
|
|
47
|
+
HUGGINGFACE_TOKEN=
|
|
48
|
+
|
|
49
|
+
# Authentication method for Anthropic: auto (try OAuth first, fall back to API key),
|
|
50
|
+
# oauth (OAuth only), api-key (API key only). Options: auto, oauth, api-key
|
|
51
|
+
ANTHROPIC_AUTH_METHOD=api-key
|
|
52
|
+
# OAuth token override (alternative to ~/.claude/.credentials.json)
|
|
53
|
+
CLAUDE_CODE_OAUTH_TOKEN=
|
|
54
|
+
|
|
55
|
+
# ============================================
|
|
56
|
+
# MODEL CONFIGURATION
|
|
57
|
+
# ============================================
|
|
58
|
+
|
|
59
|
+
# Default model for new sessions (any registry ID or alias — `cortex models list`)
|
|
60
|
+
DEFAULT_MODEL_ID=deepseek-v4-pro
|
|
61
|
+
# Cheaper model for context compaction & mentorship
|
|
62
|
+
HELPER_MODEL_ID=deepseek-v4-flash
|
|
63
|
+
|
|
64
|
+
# Model that backs WebSearch / WebFetch tools. Provider auto-detected from ID prefix.
|
|
65
|
+
# When unset, auto-picks based on available API keys (Gemini > Anthropic > XAI),
|
|
66
|
+
# falling back to DuckDuckGo HTML scrape if no provider key configured.
|
|
67
|
+
#
|
|
68
|
+
# Gemini models (uses @google/genai SDK — googleSearch grounding + urlContext):
|
|
69
|
+
# gemini-2.5-flash — fast, free-tier friendly, good grounding with citations
|
|
70
|
+
# gemini-2.5-flash-lite — cheapest, fewer grounding chunks but still functional
|
|
71
|
+
# gemini-2.5-pro — best grounding quality (most chunks/supports), slower
|
|
72
|
+
# gemini-3.5-flash — latest GA flash, strong agentic grounding, search + url context
|
|
73
|
+
# gemini-3.1-pro-preview — latest pro preview, rich grounding, slowest
|
|
74
|
+
# gemini-3-flash-preview — preview alias for gemini-3.5-flash
|
|
75
|
+
#
|
|
76
|
+
# Other providers (provider-native search APIs with citations):
|
|
77
|
+
# claude-haiku-4-5 — Anthropic web_search server tool (inline source citations)
|
|
78
|
+
# grok-4-fast-non-reasoning — XAI Responses API web_search (URL citations)
|
|
79
|
+
# grok-4.3 — XAI Responses API web_search (reasoning model, URL citations)
|
|
80
|
+
WEB_TOOLS_MODEL=gemini-2.5-flash
|
|
81
|
+
|
|
82
|
+
# ============================================
|
|
83
|
+
# SYSTEM SETTINGS
|
|
84
|
+
# ============================================
|
|
85
|
+
|
|
86
|
+
# Verbose debug logging (system-message assembly, routes). Toggle at runtime via /debug.
|
|
87
|
+
DEBUG=false
|
|
88
|
+
# Allow emoji in CLI output (false = plain text)
|
|
89
|
+
USE_EMOJI=false
|
|
90
|
+
# Project root the tools operate on. Leave EMPTY to use the launch cwd (the cwd
|
|
91
|
+
# model, like Claude Code): cd into your project and run cortex. Only set this for
|
|
92
|
+
# headless setups where the server's cwd is not the project. When set, it becomes
|
|
93
|
+
# canonical and PROJECT_ROOT is derived from it (they can never diverge).
|
|
94
|
+
PROJECT_PATH=
|
|
95
|
+
|
|
96
|
+
# ============================================
|
|
97
|
+
# REACTIVE MENTORSHIP (AI-to-AI self-improvement)
|
|
98
|
+
# ============================================
|
|
99
|
+
|
|
100
|
+
# Master switch for the mentorship system
|
|
101
|
+
MENTORSHIP_ENABLED=false
|
|
102
|
+
# Trigger helper-model review on tool errors (only applies when mentorship is enabled)
|
|
103
|
+
MENTORSHIP_TRIGGER_ON_ERROR=false
|
|
104
|
+
# Minimum error severity to trigger. Options: low, medium, high
|
|
105
|
+
MENTORSHIP_ERROR_THRESHOLD=medium
|
|
106
|
+
# React to @ultrathink / @analyze / @rethink keywords
|
|
107
|
+
MENTORSHIP_KEYWORDS_ENABLED=false
|
|
108
|
+
# Additional trigger keywords (comma-separated, e.g. @help,@stuck)
|
|
109
|
+
MENTORSHIP_CUSTOM_KEYWORDS=
|
|
110
|
+
# Model used for mentorship guidance (overrides HELPER_MODEL_ID)
|
|
111
|
+
MENTORSHIP_HELPER_MODEL=@cf/google/gemma-4-26b-a4b-it
|
|
112
|
+
# Periodic mentorship review every N turns
|
|
113
|
+
MENTORSHIP_TURN_BASED_ENABLED=false
|
|
114
|
+
# Turns between periodic reviews (1-50)
|
|
115
|
+
MENTORSHIP_TURN_INTERVAL=10
|
|
116
|
+
# Inject thinking assistance for non-reasoning models
|
|
117
|
+
MENTORSHIP_INTERLEAVED_THINKING=false
|
|
118
|
+
# Detect and alert on repeated failure patterns
|
|
119
|
+
MENTORSHIP_PATTERN_DETECTION=false
|
|
120
|
+
# Number of similar errors to trigger pattern detection (2-10)
|
|
121
|
+
MENTORSHIP_PATTERN_THRESHOLD=3
|
|
122
|
+
# Post-turn summary + next-action prediction via the helper model
|
|
123
|
+
TURN_SUMMARY_PREDICTION=false
|
|
124
|
+
|
|
125
|
+
# ============================================
|
|
126
|
+
# CONTEXT MANAGEMENT
|
|
127
|
+
# ============================================
|
|
128
|
+
|
|
129
|
+
# Anthropic prompt caching (up to ~90% input-token savings)
|
|
130
|
+
ANTHROPIC_PROMPT_CACHING=true
|
|
131
|
+
# Strategy when context overflows: priority-based preserves critical context + tool pairs;
|
|
132
|
+
# sliding-window is dumb recency and can orphan tool_use -> Anthropic 400.
|
|
133
|
+
# Options: sliding-window, priority-based
|
|
134
|
+
CONTEXT_BUDGET_STRATEGY=priority-based
|
|
135
|
+
|
|
136
|
+
# ============================================
|
|
137
|
+
# SESSION CONFIGURATION
|
|
138
|
+
# ============================================
|
|
139
|
+
|
|
140
|
+
# Where JSONL session files are written
|
|
141
|
+
SESSION_STORAGE_DIR=.cortex/sessions
|
|
142
|
+
# Auto-inject connected MCP servers' tools into every turn
|
|
143
|
+
MCP_AUTO_INJECT=false
|
|
144
|
+
# Auto-research subagents. off = disabled (PM never told; main context stays clean).
|
|
145
|
+
# native = PM delegates to autoresearch-agent subagents that run experiments with the
|
|
146
|
+
# internal tools. mcp = the subagents route experiment-running to the external
|
|
147
|
+
# configured auto-research MCP server instead (MCP integration ships separately). Options: off, native, mcp
|
|
148
|
+
AUTORESEARCH_AGENTS=off
|
|
149
|
+
# Cap on bytes per injected project doc (CORTEX.md, MEMORY.md, etc.). 0 = unlimited.
|
|
150
|
+
SYSTEM_MESSAGE_DOC_MAX_BYTES=0
|
|
151
|
+
|
|
152
|
+
# ============================================
|
|
153
|
+
# LOOP CONTROL
|
|
154
|
+
# ============================================
|
|
155
|
+
|
|
156
|
+
# Maximum tool executions per turn
|
|
157
|
+
MAX_TOOL_ITERATIONS=50
|
|
158
|
+
# Stop the turn after this many consecutive all-error iterations
|
|
159
|
+
MAX_CONSECUTIVE_ERRORS=3
|
|
160
|
+
# Soft per-turn tool-call budget (R29b brake: escalating reminders, hard cap at 2x)
|
|
161
|
+
TOOL_BUDGET_SOFT=15
|
|
162
|
+
# Per-tool execution timeout in milliseconds (2 minutes)
|
|
163
|
+
TOOL_TIMEOUT_MS=120000
|
|
164
|
+
# Identical tool calls before loop detection breaks the turn
|
|
165
|
+
MAX_LOOP_REPETITIONS=5
|
|
166
|
+
|
|
167
|
+
# ============================================
|
|
168
|
+
# PROVIDER TOOLING (server-side tools / API surface)
|
|
169
|
+
# ============================================
|
|
170
|
+
|
|
171
|
+
# Inject server-side tools (web_search, x_search, code_execution) into XAI/OpenAI requests.
|
|
172
|
+
# When true, overrides to the Responses API at request time regardless of XAI_API_MODE.
|
|
173
|
+
# R37/R38 benchmarks: hybrid (true) is 20-26% faster than client-only with equal accuracy.
|
|
174
|
+
ENABLE_SERVER_SIDE_TOOLS=true
|
|
175
|
+
# Default API pattern for XAI models at registration time (ENABLE_SERVER_SIDE_TOOLS=true
|
|
176
|
+
# overrides at request time). Options: messages | responses
|
|
177
|
+
XAI_API_MODE=messages
|
|
178
|
+
# OpenAI request surface. Options: chat/completions | responses (opt into hosted tools)
|
|
179
|
+
OPENAI_API_MODE=
|
|
180
|
+
# Only load essential tools initially; the model discovers the rest via SearchTools.
|
|
181
|
+
# R43 benchmark: 77% input-token reduction on the first turn.
|
|
182
|
+
ENABLE_DEFERRED_TOOL_LOADING=true
|
|
183
|
+
# Programmatic Tool Calling (compose tool calls in a script) — experimental
|
|
184
|
+
ENABLE_PTC=false
|
|
185
|
+
# Allow local code-execution tooling
|
|
186
|
+
ENABLE_LOCAL_CODE_EXECUTION=false
|
|
187
|
+
|
|
188
|
+
# ============================================
|
|
189
|
+
# MODEL ROUTER (auto model selection)
|
|
190
|
+
# ============================================
|
|
191
|
+
|
|
192
|
+
# Auto-select a model based on task type + benchmark history (routes model="auto")
|
|
193
|
+
MODEL_ROUTER_ENABLED=false
|
|
194
|
+
# Routing strategy: auto (classify the prompt) | matrix-only (require explicit task type)
|
|
195
|
+
MODEL_ROUTER_STRATEGY=auto
|
|
196
|
+
# Record turn metrics to .cortex/router-matrix.jsonl (independent of routing being enabled)
|
|
197
|
+
MODEL_ROUTER_RECORD=true
|
|
198
|
+
# Models the router must NEVER auto-select (cost/policy bans), comma-separated.
|
|
199
|
+
# A trailing '*' is a prefix wildcard: 'grok*' excludes EVERY xAI model (present and
|
|
200
|
+
# future) so exploration can never route a sub-agent to a grok model — honors the
|
|
201
|
+
# standing cost constraint.
|
|
202
|
+
MODEL_ROUTER_EXCLUDE=grok*
|
|
203
|
+
# Opt-in explore/exploit (Thompson sampling) for model='auto'. OFF = conservative greedy
|
|
204
|
+
# trust-gated routing. ON = posterior-sample each model and route to the sampled-argmax,
|
|
205
|
+
# so thinly-sampled models get a chance and the matrix stops being self-confirming.
|
|
206
|
+
MODEL_ROUTER_EXPLORATION=false
|
|
207
|
+
# Trust gate for sub-agent model='auto' dispatch (works even when ENABLED=false — passing
|
|
208
|
+
# 'auto' is the orchestrator's explicit per-dispatch opt-in):
|
|
209
|
+
# Min task-classification confidence (0-1) before 'auto' routes; else inherits parent model
|
|
210
|
+
ROUTER_MIN_CONFIDENCE=0.3
|
|
211
|
+
# Min real benchmark samples a task type needs before 'auto' trusts the matrix
|
|
212
|
+
# recommendation; else inherits parent. Met as MODEL_ROUTER_RECORD accumulates data.
|
|
213
|
+
ROUTER_MIN_SAMPLES=3
|
|
214
|
+
|
|
215
|
+
# ============================================
|
|
216
|
+
# END-OF-TURN AUDIT / TRAINING SUBSTRATE (opt-in)
|
|
217
|
+
# ============================================
|
|
218
|
+
|
|
219
|
+
# Mandatory EndTurn pre-delivery self-audit + Stage 2/3 verifiers. Default OFF; ON =
|
|
220
|
+
# graded cortex-channel training records (the tool is hidden when off).
|
|
221
|
+
CORTEX_ENDTURN_GATE=false
|
|
222
|
+
|
|
223
|
+
# ============================================
|
|
224
|
+
# DECISION STORE (prior-recall + recording)
|
|
225
|
+
# ============================================
|
|
226
|
+
|
|
227
|
+
# Append each tool decision to <PROJECT_ROOT>/.cortex/decisions.jsonl (default ON)
|
|
228
|
+
CORTEX_RECORD_DECISIONS=true
|
|
229
|
+
# Inject prior decisions as a <system-reminder> before tool use (default ON)
|
|
230
|
+
CORTEX_LOOKUP_PRIOR_DECISIONS=true
|
|
231
|
+
# decisions.jsonl self-rotates at this byte cap (default 2 MB)
|
|
232
|
+
CORTEX_DECISIONS_MAX_BYTES=2097152
|
|
233
|
+
# Per-turn "Repository State" harness-note: git branch, uncommitted changes in the project
|
|
234
|
+
# subtree, recent commits, and a cross-agent staleness warning (files you read that another
|
|
235
|
+
# agent/the user has since changed on disk). Lets two agents share one working tree safely.
|
|
236
|
+
# Default ON; set "false" to disable.
|
|
237
|
+
CORTEX_GIT_CONTEXT=true
|
|
238
|
+
|
|
239
|
+
# ============================================
|
|
240
|
+
# ORCHESTRATOR MODE
|
|
241
|
+
# ============================================
|
|
242
|
+
|
|
243
|
+
# Orchestrator lifecycle: persistent (default) | stateless (clean per request) | server
|
|
244
|
+
CORTEX_MODE=persistent
|
|
245
|
+
# Server URL when CORTEX_MODE=server (HTTP client mode)
|
|
246
|
+
CORTEX_SERVER_URL=http://localhost:4000
|
|
247
|
+
|
|
248
|
+
# ============================================
|
|
249
|
+
# AGENT WORKSPACE
|
|
250
|
+
# ============================================
|
|
251
|
+
|
|
252
|
+
# tmux visual monitoring for parallel agent teams (one live pane per agent)
|
|
253
|
+
AGENT_TMUX_MONITOR=false
|
|
254
|
+
|
|
255
|
+
# ============================================
|
|
256
|
+
# TESTING
|
|
257
|
+
# ============================================
|
|
258
|
+
|
|
259
|
+
# Run real-API smoke tests instead of mocked ones
|
|
260
|
+
ENABLE_SMOKE_TESTS=false
|
|
261
|
+
|
|
262
|
+
# Run the Chromium browser-integration test suite (drives a real browser,
|
|
263
|
+
# downloads http-server via npx — opt-in; default npm test skips it)
|
|
264
|
+
ENABLE_BROWSER_TESTS=false
|
|
265
|
+
|
|
266
|
+
# ============================================
|
|
267
|
+
# DEBUG FLAGS
|
|
268
|
+
# ============================================
|
|
269
|
+
|
|
270
|
+
# Log raw API request/response payloads
|
|
271
|
+
DEBUG_PAYLOAD=false
|
|
272
|
+
# Verbose system-message assembly logging (also enabled by DEBUG=true)
|
|
273
|
+
DEBUG_SYSTEM_MESSAGES=false
|
|
274
|
+
# Show thinking/reasoning content in the CLI. For Anthropic Opus 4.7/4.8 (adaptive
|
|
275
|
+
# thinking) 'true' requests display:'summarized', which BILLS extra output tokens
|
|
276
|
+
# ($25/1M on Opus 4.8); 'false' keeps reasoning omitted (empty thinking blocks, $0).
|
|
277
|
+
# Other providers (XAI/DeepSeek/GPT) stream reasoning for free regardless.
|
|
278
|
+
DEBUG_THINKING=false
|
|
279
|
+
|
|
280
|
+
# ============================================
|
|
281
|
+
# TOOL & PATH OVERRIDES
|
|
282
|
+
# ============================================
|
|
283
|
+
|
|
284
|
+
# Override the Chromium binary used by web/browse tools (auto-detected if unset)
|
|
285
|
+
CHROMIUM_BIN=
|
|
286
|
+
# Alternate Chromium path honored by Playwright
|
|
287
|
+
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=
|
|
288
|
+
# Override the tmux binary used by visual agent monitoring (auto-detected if unset)
|
|
289
|
+
TMUX_BIN=
|
|
290
|
+
# Google Vertex AI project (only when using Vertex instead of the Gemini API)
|
|
291
|
+
GOOGLE_CLOUD_PROJECT=
|
|
292
|
+
# Extra directories to grant tool access to, OUTSIDE the project root (the --add-dir
|
|
293
|
+
# mechanism — your explicit permission). Path-delimiter separated (':' on Linux/macOS).
|
|
294
|
+
# Example: /data/shared:/opt/reference. Prefer the --add-dir flag for one-offs.
|
|
295
|
+
CORTEX_ADD_DIRS=
|
|
296
|
+
|
|
297
|
+
# ============================================
|
|
298
|
+
# GIT / PR ACCESS CONTROL
|
|
299
|
+
# ============================================
|
|
300
|
+
# Controls the git/PR tools (PRAgent, WorkspaceManager) and the /v1/pr/* server routes.
|
|
301
|
+
# Input-format validation (which blocks shell/argument injection) is ALWAYS on; the
|
|
302
|
+
# allow-lists below are opt-in defense-in-depth.
|
|
303
|
+
#
|
|
304
|
+
# Comma list of repos the tools may touch: exact "owner/repo", "owner/*", or "*".
|
|
305
|
+
# Unset = ALL repos are permitted — the convenient default for single-user/local use.
|
|
306
|
+
# For shared or multi-tenant deployments, RESTRICT this to the repos you trust the tools
|
|
307
|
+
# with, e.g. GIT_ALLOWED_REPOS=me/app,me/*
|
|
308
|
+
GIT_ALLOWED_REPOS=
|
|
309
|
+
# Comma list of allowed actions: review,list,create,post-review,clone,worktree,diff,cleanup,status
|
|
310
|
+
# Unset or * = all actions allowed.
|
|
311
|
+
GIT_ALLOWED_ACTIONS=
|
|
312
|
+
# Token for gh/git. Injected into the subprocess env as GH_TOKEN/GITHUB_TOKEN only —
|
|
313
|
+
# never placed on argv or in a clone URL. Leave unset to use gh's own auth.
|
|
314
|
+
GIT_AUTH_TOKEN=
|
|
315
|
+
# GitHub (Enterprise) host for git/PR tools. Default: github.com
|
|
316
|
+
GIT_HOST=github.com
|
|
317
|
+
# HMAC secret for the /v1/pr/webhook endpoint (GitHub X-Hub-Signature-256).
|
|
318
|
+
# Unset = the webhook is DISABLED (returns 401) rather than accepting unsigned calls.
|
|
319
|
+
GITHUB_WEBHOOK_SECRET=
|
|
320
|
+
|
|
321
|
+
# ============================================
|
|
322
|
+
# SERVER LIFECYCLE (startup / shutdown / resume)
|
|
323
|
+
# ============================================
|
|
324
|
+
|
|
325
|
+
# HTTP server port (falls back to the next free port if taken)
|
|
326
|
+
PORT=4000
|
|
327
|
+
# Startup resume: load the most recent session on boot — e.g. after an idle-shutdown ->
|
|
328
|
+
# wake cycle. For a specific session set RESUME_SESSION_ID instead. Default: fresh session.
|
|
329
|
+
AUTO_RESUME=false
|
|
330
|
+
# Resume a specific session UUID on boot (overrides AUTO_RESUME)
|
|
331
|
+
RESUME_SESSION_ID=
|
|
332
|
+
# Inactivity shutdown: auto-stop after N seconds with no requests.
|
|
333
|
+
# 0 = never (always-on daemon)
|
|
334
|
+
# N>0 = stop after N seconds idle. Pair with a process manager (systemd, pm2) +
|
|
335
|
+
# AUTO_RESUME=true for "sleep when idle, resume on wake" — cost-efficient
|
|
336
|
+
# on-demand use without losing conversation state.
|
|
337
|
+
SERVER_IDLE_TIMEOUT=0
|
|
338
|
+
# Graceful shutdown: max ms to let in-flight connections drain before forcing exit
|
|
339
|
+
# (0 = wait indefinitely). On shutdown the dashboard view server, idle timer, and
|
|
340
|
+
# cleanup intervals are all closed cleanly first.
|
|
341
|
+
SHUTDOWN_GRACE_MS=10000
|
|
342
|
+
# Opt-in: start the sandbox+tmux dashboard (default off; binds an extra port).
|
|
343
|
+
# MASTER SWITCH for the tmux/sandbox web dashboard (the 4001 view server).
|
|
344
|
+
# false = the dashboard NEVER starts (not at boot, not on TmuxSession/CreateArtifact
|
|
345
|
+
# use) and tools return guidance instead of view URLs. Set true to enable; if it
|
|
346
|
+
# fails to start, check for a port conflict on DASHBOARD_PORT (retries 10 ports).
|
|
347
|
+
ENABLE_DASHBOARD=false
|
|
348
|
+
# Dashboard port — honored whether started eagerly (above) or lazily by a tool
|
|
349
|
+
DASHBOARD_PORT=4001
|
|
350
|
+
|
|
351
|
+
# ============================================
|
|
352
|
+
# RUNTIME FLAGS
|
|
353
|
+
# ============================================
|
|
354
|
+
|
|
355
|
+
# Auto-approve ALL tool executions (bypasses the permission system). Use with caution.
|
|
356
|
+
YOLO=false
|
|
357
|
+
# Proactive mentorship discovery (runtime flag)
|
|
358
|
+
MENTORSHIP_ACTIVE_DISCOVERY=false
|
package/bin/cortex.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { spawn, spawnSync } from 'child_process';
|
|
26
26
|
import { fileURLToPath } from 'url';
|
|
27
27
|
import { dirname, join, resolve } from 'path';
|
|
28
|
-
import { existsSync, readFileSync, realpathSync, mkdirSync, openSync } from 'fs';
|
|
28
|
+
import { existsSync, readFileSync, realpathSync, mkdirSync, openSync, writeFileSync, copyFileSync } from 'fs';
|
|
29
29
|
import { homedir } from 'os';
|
|
30
30
|
import { createRequire } from 'module';
|
|
31
31
|
|
|
@@ -354,8 +354,95 @@ async function startServer() {
|
|
|
354
354
|
// Where the auto-started background server logs to. Honors CORTEX_HOME/HOME so
|
|
355
355
|
// it lands beside the rest of the global config (~/.cortex/server.log).
|
|
356
356
|
function getServerLogPath() {
|
|
357
|
+
return join(getGlobalCortexDir(), 'server.log');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function getGlobalCortexDir() {
|
|
357
361
|
const base = process.env.CORTEX_HOME || homedir() || process.cwd();
|
|
358
|
-
return join(base, '.cortex'
|
|
362
|
+
return join(base, '.cortex');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Is at least one provider API key resolvable — from the environment/secrets store
|
|
366
|
+
// OR from a .env file (cwd or global)? A BLANK value in a .env (e.g. `DEEPSEEK_API_KEY=`)
|
|
367
|
+
// does NOT count: the loader treats it as falsy and falls through to process.env, which is
|
|
368
|
+
// exactly how the secrets-store model works (blank .env + key injected via env). So we only
|
|
369
|
+
// count a non-empty, non-commented assignment, or a non-empty process.env var.
|
|
370
|
+
function hasAnyApiKey() {
|
|
371
|
+
const isKeyName = (k) => /(_API_KEY|_API_TOKEN|_OAUTH_TOKEN)$/.test(k);
|
|
372
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
373
|
+
if (isKeyName(k) && String(v || '').trim()) return true;
|
|
374
|
+
}
|
|
375
|
+
for (const p of [join(process.cwd(), '.env'), join(getGlobalCortexDir(), '.env')]) {
|
|
376
|
+
try {
|
|
377
|
+
if (!existsSync(p)) continue;
|
|
378
|
+
for (const line of readFileSync(p, 'utf8').split('\n')) {
|
|
379
|
+
if (/^\s*#/.test(line)) continue;
|
|
380
|
+
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(\S.*?)\s*$/);
|
|
381
|
+
if (m && isKeyName(m[1]) && m[2].trim()) return true;
|
|
382
|
+
}
|
|
383
|
+
} catch { /* unreadable .env — ignore */ }
|
|
384
|
+
}
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// A blank-value config template — mirrors the project's own .env: every key declared but
|
|
389
|
+
// EMPTY, so the loader falls through to the environment/secrets store. NEVER put a non-empty
|
|
390
|
+
// placeholder here — it would override a real secret in process.env.
|
|
391
|
+
const BLANK_ENV_TEMPLATE = `# Nexus Cortex configuration.
|
|
392
|
+
# Blank values are read from your environment / secrets store (a value set in the
|
|
393
|
+
# environment wins when the line below is left blank). Fill a key here for local use,
|
|
394
|
+
# or leave blank and inject it via your secrets store. Run "cortex config init --force"
|
|
395
|
+
# for the full, commented template.
|
|
396
|
+
|
|
397
|
+
DEFAULT_MODEL_ID=deepseek-v4-pro
|
|
398
|
+
|
|
399
|
+
ANTHROPIC_API_KEY=
|
|
400
|
+
OPENAI_API_KEY=
|
|
401
|
+
GEMINI_API_KEY=
|
|
402
|
+
DEEPSEEK_API_KEY=
|
|
403
|
+
XAI_API_KEY=
|
|
404
|
+
`;
|
|
405
|
+
|
|
406
|
+
// Preflight for the prompt path: a model run needs a key. If none is resolvable, seed a
|
|
407
|
+
// blank ~/.cortex/.env so there's a findable file to edit (it didn't exist before the first
|
|
408
|
+
// run), print clear guidance, and EXIT WITHOUT starting a server — so we never leave a
|
|
409
|
+
// keyless server running that would force a --shutdown + re-invoke. When a key IS present
|
|
410
|
+
// (a filled .env, or secrets in the environment), this is a no-op and the run proceeds —
|
|
411
|
+
// one-shot. The container case (secrets in env) hits the no-op path and writes nothing.
|
|
412
|
+
async function ensureKeysOrExit() {
|
|
413
|
+
if (hasAnyApiKey()) return;
|
|
414
|
+
// No key resolvable. If a server is already running, it was started with no key and has
|
|
415
|
+
// cached that empty config — so even after you add a key it would keep failing until a
|
|
416
|
+
// restart. Drain it now (graceful) so the NEXT run (with your key) starts a fresh server
|
|
417
|
+
// that picks the key up — no manual --shutdown dance.
|
|
418
|
+
try {
|
|
419
|
+
if (await isServerUp()) {
|
|
420
|
+
try { await fetch(`${BASE_URL}/shutdown`, { method: 'POST', signal: AbortSignal.timeout(5000) }); } catch { /* best-effort */ }
|
|
421
|
+
process.stderr.write('[cortex] Stopped the running server so it will pick up your key on the next run.\n');
|
|
422
|
+
}
|
|
423
|
+
} catch { /* never block on the drain */ }
|
|
424
|
+
const dir = getGlobalCortexDir();
|
|
425
|
+
const envPath = join(dir, '.env');
|
|
426
|
+
let seeded = false;
|
|
427
|
+
if (!existsSync(envPath)) {
|
|
428
|
+
try {
|
|
429
|
+
mkdirSync(dir, { recursive: true });
|
|
430
|
+
// Prefer the shipped .env.example (the full, canonical blank-value template) and
|
|
431
|
+
// copy it to ~/.cortex/.env — i.e. .env.example becomes .env, no codegen. Fall back
|
|
432
|
+
// to the minimal inline template only if the example isn't found in the install.
|
|
433
|
+
const example = [join(CLI_PKG_ROOT, '.env.example'), join(MONOREPO_ROOT, '.env.example')]
|
|
434
|
+
.find((p) => existsSync(p));
|
|
435
|
+
if (example) copyFileSync(example, envPath);
|
|
436
|
+
else writeFileSync(envPath, BLANK_ENV_TEMPLATE);
|
|
437
|
+
seeded = true;
|
|
438
|
+
} catch { /* read-only home — still print guidance below */ }
|
|
439
|
+
}
|
|
440
|
+
process.stderr.write('\n[cortex] No API key found — a model run needs one.\n');
|
|
441
|
+
if (seeded) process.stderr.write(`[cortex] Created ${envPath}\n`);
|
|
442
|
+
process.stderr.write('[cortex] Add a provider key, then run again:\n');
|
|
443
|
+
process.stderr.write(`[cortex] - edit ${envPath} (e.g. DEEPSEEK_API_KEY=sk-...)\n`);
|
|
444
|
+
process.stderr.write('[cortex] - or inject it via your environment / secrets store (read from there too)\n');
|
|
445
|
+
process.exit(1);
|
|
359
446
|
}
|
|
360
447
|
|
|
361
448
|
async function ensureServer() {
|
|
@@ -406,6 +493,14 @@ async function run() {
|
|
|
406
493
|
await checkForUpdate();
|
|
407
494
|
} catch { /* dist absent in a source checkout, or check failed — never block */ }
|
|
408
495
|
|
|
496
|
+
// A prompt/agent run needs a model key. Check BEFORE starting the server so we never
|
|
497
|
+
// leave a keyless server running (which forced the old --shutdown + re-invoke dance).
|
|
498
|
+
// Read-only ops (--stats/--sessions/--tmux/--pr, no prompt) don't need a key and are
|
|
499
|
+
// fine against a keyless server, so only gate the actual prompt path.
|
|
500
|
+
if (prompt) {
|
|
501
|
+
await ensureKeysOrExit();
|
|
502
|
+
}
|
|
503
|
+
|
|
409
504
|
await ensureServer();
|
|
410
505
|
|
|
411
506
|
// --tmux: list active tmux sessions (served by SandboxViewServer on port+1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexus-cortex/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.37.1",
|
|
4
4
|
"description": "Nexus Cortex CLI - Terminal interface for multi-provider LLM orchestration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"bin",
|
|
78
78
|
"README.md",
|
|
79
79
|
"LICENSE",
|
|
80
|
-
"NOTICE"
|
|
80
|
+
"NOTICE",
|
|
81
|
+
".env.example"
|
|
81
82
|
]
|
|
82
83
|
}
|