nothumanallowed 16.0.64 → 17.0.0

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.
Files changed (56) hide show
  1. package/README.md +98 -26
  2. package/bin/nha +3 -2
  3. package/package.json +7 -4
  4. package/src/bootstrap.mjs +30 -66
  5. package/src/cli.mjs +62 -20
  6. package/src/commands/autostart.mjs +1 -1
  7. package/src/commands/chat.mjs +1 -1
  8. package/src/commands/ops.mjs +1 -1
  9. package/src/config.mjs +37 -1
  10. package/src/constants.mjs +8 -2
  11. package/src/downloader.mjs +8 -3
  12. package/src/legion/agents/ade.mjs +201 -0
  13. package/src/legion/agents/athena.mjs +162 -0
  14. package/src/legion/agents/atlas.mjs +159 -0
  15. package/src/legion/agents/babel.mjs +167 -0
  16. package/src/legion/agents/cartographer.mjs +213 -0
  17. package/src/legion/agents/cassandra.mjs +185 -0
  18. package/src/legion/agents/conductor.mjs +183 -0
  19. package/src/legion/agents/cron.mjs +165 -0
  20. package/src/legion/agents/echo.mjs +165 -0
  21. package/src/legion/agents/edi.mjs +166 -0
  22. package/src/legion/agents/epicure.mjs +211 -0
  23. package/src/legion/agents/flux.mjs +165 -0
  24. package/src/legion/agents/forge.mjs +166 -0
  25. package/src/legion/agents/glitch.mjs +169 -0
  26. package/src/legion/agents/heimdall.mjs +171 -0
  27. package/src/legion/agents/herald.mjs +208 -0
  28. package/src/legion/agents/hermes.mjs +163 -0
  29. package/src/legion/agents/jarvis.mjs +163 -0
  30. package/src/legion/agents/link.mjs +163 -0
  31. package/src/legion/agents/logos.mjs +171 -0
  32. package/src/legion/agents/macro.mjs +158 -0
  33. package/src/legion/agents/mercury.mjs +234 -0
  34. package/src/legion/agents/murasaki.mjs +163 -0
  35. package/src/legion/agents/muse.mjs +217 -0
  36. package/src/legion/agents/navi.mjs +161 -0
  37. package/src/legion/agents/oracle.mjs +165 -0
  38. package/src/legion/agents/pipe.mjs +160 -0
  39. package/src/legion/agents/polyglot.mjs +226 -0
  40. package/src/legion/agents/prometheus.mjs +171 -0
  41. package/src/legion/agents/quill.mjs +165 -0
  42. package/src/legion/agents/saber.mjs +162 -0
  43. package/src/legion/agents/sauron.mjs +173 -0
  44. package/src/legion/agents/scheherazade.mjs +179 -0
  45. package/src/legion/agents/shell.mjs +166 -0
  46. package/src/legion/agents/shogun.mjs +163 -0
  47. package/src/legion/agents/tempest.mjs +211 -0
  48. package/src/legion/agents/veritas.mjs +166 -0
  49. package/src/legion/agents/zero.mjs +162 -0
  50. package/src/legion/legion-x.mjs +12114 -0
  51. package/src/legion-bundle.mjs +64 -0
  52. package/src/legion-config.mjs +134 -0
  53. package/src/services/llm.mjs +44 -15
  54. package/src/services/message-responder.mjs +1 -1
  55. package/src/spawn.mjs +22 -44
  56. package/src/updater.mjs +48 -63
package/README.md CHANGED
@@ -8,21 +8,27 @@
8
8
  # Install globally
9
9
  npm install -g nothumanallowed
10
10
 
11
- # Configure your LLM provider (or use Liara free — no API key needed)
11
+ # Configure your LLM provider with your own API key...
12
12
  nha config set provider anthropic
13
13
  nha config set key sk-ant-api03-YOUR_KEY
14
14
 
15
+ # ...or, for deliberations, a local model with no key at all
16
+ nha config set legion-provider ollama
17
+ nha config set ollama-model qwen2.5:7b
18
+
15
19
  # Ask a single agent directly (no server, instant response)
16
20
  nha ask saber "Audit this Express app for OWASP Top 10"
17
21
  nha ask oracle "Analyze this dataset" --file data.csv
18
22
 
19
- # Run multi-agent deliberation
23
+ # Run multi-agent deliberation, entirely on your machine
20
24
  nha run "Design a Kubernetes deployment for a 10K RPS API"
21
25
 
22
26
  # Open the web UI with Studio, Chat, Email, Calendar, Drive, Tasks and more
23
27
  nha ui
24
28
  ```
25
29
 
30
+ **Do I need a local model?** No. You need one LLM, and you choose which: an API key of a cloud provider, or a model running on your machine. A local model is the option that needs no key and sends nothing out. The hosted free tier (Liara) that earlier versions used by default is currently offline: set a provider before the first prompt.
31
+
26
32
  ## Studio — Visual Agentic Workflows
27
33
 
28
34
  Studio is a visual workflow builder inside the `nha ui` web interface. Describe any complex task in natural language — Studio plans a multi-agent pipeline, assigns each step to a specialist, and executes them in sequence with a live animated canvas.
@@ -34,7 +40,7 @@ EmailAgent → WebSearchAgent → WriterAgent
34
40
  (reads) (searches) (synthesizes)
35
41
  ```
36
42
 
37
- - **No configuration** — works with any LLM provider including Liara (free, no API key)
43
+ - **No extra configuration** — works with the LLM provider you already set
38
44
  - **Live canvas** — see each agent activate, stream output, and hand off to the next
39
45
  - **HTML dashboard** — canvas generates a downloadable visual report (HTML + PDF)
40
46
  - **Parliament mode** — enable for 2+ specialist agents to cross-read and deliberate: R1 (independent), R2 (agents read each other), R3 (HERALD mediation), convergence score
@@ -69,7 +75,7 @@ Open nha ui → click WebCraft in the sidebar
69
75
 
70
76
  ### WebCraft Agent
71
77
 
72
- An AI assistant permanently available in the chat panel. Powered by Liara (Qwen3 32B, free) or your own API key.
78
+ An AI assistant permanently available in the chat panel, powered by the LLM provider you configured.
73
79
 
74
80
  **What it can do:**
75
81
  - Edit files surgically (old → new string replace) or rewrite them completely
@@ -96,7 +102,7 @@ Add more skill files (unlimited) for specific integrations (Stripe, email templa
96
102
  | **Search** 🔍 | Grep across all project files — click a result to jump to that file |
97
103
  | **Snapshot** 💾 | Save a full point-in-time backup of all files. Restore any snapshot with one click |
98
104
  | **Plan mode** | Type `/plan your request` — agent proposes a plan first, you approve before any file is touched |
99
- | **Auto-fix** | Sandbox errors (MODULE_NOT_FOUND etc.) trigger automatic Liara fix attempts (3 free, unlimited with own key) |
105
+ | **Auto-fix** | Sandbox errors (MODULE_NOT_FOUND etc.) trigger automatic fix attempts with your provider |
100
106
 
101
107
  ### Example session
102
108
 
@@ -151,9 +157,10 @@ OpenClaw reads your email with 1 generic agent. NHA sends it through 5 specialis
151
157
 
152
158
  ### Privacy
153
159
 
154
- **Zero data touches NHA servers.** The only network calls are:
160
+ **Your emails, calendar and tasks never touch NHA servers.** The network calls are:
155
161
  - Google APIs (your OAuth token, direct from your machine)
156
162
  - Your LLM provider (your API key, direct from your machine)
163
+ - One usage ping per command to nothumanallowed.com: platform name and CLI version, nothing else (see Privacy & Ownership)
157
164
 
158
165
  All data stored locally in `~/.nha/ops/`. Tokens encrypted with AES-256-GCM. You own everything. Inspect it, delete it, export it anytime.
159
166
 
@@ -247,6 +254,62 @@ When you don't specify `--agents`, NHA automatically:
247
254
 
248
255
  This is real deliberation, not prompt chaining. Agents read and respond to each other.
249
256
 
257
+ ### It runs on your machine
258
+
259
+ Since v17 the whole deliberation is local. Legion X and the 38 agents ship inside this package; routing (PROMETHEUS), the adversarial tribunal (CASSANDRA), the convergence measurement and the final audit (ATHENA) all run with **your** models. No NHA server takes part, and nothing is downloaded to deliberate.
260
+
261
+ **Choose the models.** Any mix of:
262
+
263
+ | Kind | Providers | Needs |
264
+ |---|---|---|
265
+ | Cloud | `anthropic`, `openai`, `gemini`, `deepseek`, `grok`, `mistral`, `cohere` | that provider's API key |
266
+ | Local | `ollama` (one or several models), `local-openai` (LM Studio, llama.cpp, vLLM, any OpenAI-compatible endpoint) | no key |
267
+
268
+ ```bash
269
+ # One cloud provider
270
+ nha config set provider anthropic
271
+ nha config set key sk-ant-api03-YOUR_KEY
272
+
273
+ # More than one: agents are spread across every provider that has a key
274
+ nha config set openai-key sk-YOUR_OPENAI_KEY
275
+ nha config set gemini-key YOUR_GEMINI_KEY
276
+
277
+ # A local model, no key
278
+ nha config set legion-provider ollama
279
+ nha config set ollama-model qwen2.5:7b
280
+
281
+ # Several local models: agents are spread across them
282
+ nha config set ollama-models llama3.1,qwen2.5:7b,mistral
283
+
284
+ # An OpenAI-compatible local server
285
+ nha config set legion-provider local-openai
286
+ nha config set local-openai-url http://localhost:1234/v1/chat/completions
287
+ nha config set local-openai-model my-model
288
+ ```
289
+
290
+ Cloud and local can work together in the same deliberation. To keep a deliberation on the machine even though a cloud key is configured for chat:
291
+
292
+ ```bash
293
+ nha config set local-only true
294
+ ```
295
+
296
+ **Other settings**
297
+
298
+ | Key | What it does |
299
+ |---|---|
300
+ | `orchestrator-provider` | Which provider runs routing, tribunal and audit (default: the first available) |
301
+ | `economy` / `nha run "..." --economy` | Shorter cross-reading, synthesis from the final positions: about half the tokens |
302
+ | `cross-reading-chars` | Characters of each proposal the other agents read (0 = no cap) |
303
+ | `ollama-embed-model` | A local embedding model for a semantic convergence measurement (default: word overlap) |
304
+ | `fact-check` | Turn the claim review off (`false`) |
305
+ | `rounds`, `convergence`, `tribunal` | Deliberation rounds, convergence threshold, tribunal on/off |
306
+
307
+ **What to expect.** A deliberation is many model calls: one per agent per round, plus routing, tribunal, synthesis and audit. A typical one with six agents and three rounds used about 770,000 tokens on a cloud model, about 350,000 with `--economy`, and took about 14 minutes on a 7B model running on a laptop. Small local models complete the deliberation but follow the structured steps less reliably.
308
+
309
+ **The fact-check is a model reviewing claims.** Nothing is verified against external sources, and the output says so. Earlier versions queried a knowledge base on the NHA server; that no longer exists.
310
+
311
+ **Everything is saved.** Each deliberation leaves a transcript in `~/.legion/sessions/` (Markdown and JSON), the session state in `~/.legion/geth-sessions/`, and what the agents learned in `~/.legion/local-store.json`. `nha geth:sessions` lists them.
312
+
250
313
  ## Extensions
251
314
 
252
315
  15 downloadable agent modules for specific workflows:
@@ -270,10 +333,13 @@ nha ask forge "prompt" # DevOps & infrastructure
270
333
  nha ask saber "review this" --file app.js # Attach a file
271
334
  nha ask saber "prompt" --provider openai # Override provider
272
335
 
273
- # Multi-agent collaboration (server-routed deliberation)
336
+ # Multi-agent deliberation (runs on your machine, with your models)
274
337
  nha run "prompt" # Auto-route to best agents
275
338
  nha run "prompt" --agents saber,zero # Specific agents
339
+ nha run "prompt" --economy # About half the tokens
276
340
  nha run --file prompt.txt # From file
341
+ nha geth:sessions # Past deliberations
342
+ nha geth:providers # Providers a deliberation can use
277
343
 
278
344
  # Explore agents
279
345
  nha agents # List all 38 agents
@@ -293,44 +359,50 @@ nha pif feed # Activity feed
293
359
  nha config # Show settings
294
360
  nha config set provider anthropic
295
361
  nha config set key YOUR_KEY
296
- nha update # Update agents & core
362
+ nha config set legion-provider ollama # Deliberate with a local model
363
+ nha update # Update the package (Legion X and agents come with it)
297
364
  nha doctor # Health check
298
365
  nha mcp # Start MCP server (Claude Code, Cursor)
299
366
  ```
300
367
 
301
368
  ## Supported Providers
302
369
 
303
- Anthropic, OpenAI, Google Gemini, DeepSeek, xAI Grok, Mistral, Cohere.
370
+ **Cloud:** Anthropic, OpenAI, Google Gemini, DeepSeek, xAI Grok, Mistral, Cohere.
371
+
372
+ **Local, no key:** Ollama (one or several models) and any OpenAI-compatible endpoint. Local models are available to deliberations (`nha run`); chat and the web UI use the cloud provider set with `nha config set provider`.
304
373
 
305
- Use up to 7 simultaneously — each agent can run on a different LLM for genuine multi-model reasoning.
374
+ Use several at once — each agent can run on a different model, cloud or local, for genuine multi-model reasoning.
306
375
 
307
376
  ## Privacy & Ownership
308
377
 
309
- - **Your API key never leaves your machine** — zero-knowledge architecture
310
- - **Zero dependencies** — no supply chain risk
311
- - **Zero telemetry** — no tracking, no phone-home
312
- - **Agents are local files** — inspect, modify, fork them
313
- - **Works offline** after first install (only LLM calls need network)
378
+ - **Your API keys go to your own LLM provider and nowhere else.** They are stored in `~/.nha/config.json`, readable by your user only.
379
+ - **Deliberations run on your machine.** With local models and `local-only`, nothing leaves it.
380
+ - **One usage ping per command.** The CLI sends the platform name (`cli`) and its version to nothumanallowed.com. The server sees your IP address, as for any web request. No prompt, file, key or answer is sent.
381
+ - **Agents are local files** in `~/.nha/agents/` — inspect, modify, fork them. A package upgrade replaces them with the new version.
382
+ - **Works offline** with a local model: Legion X and the agents are in the package.
314
383
 
315
384
  ## How It Works
316
385
 
317
386
  ```
318
- Your Machine NHA Server (optional)
319
- ┌─────────────────────┐ ┌──────────────────────┐
320
- │ 38 agents run HERE │ routing │ Task decomposition │
321
- │ with YOUR API key │ ◄──────────► │ Knowledge grounding │
322
- │ │ │ (2.6M verified facts) │
323
- │ Key NEVER sent │ │ Convergence scoring │
324
- └─────────────────────┘ └──────────────────────┘
387
+ Your Machine
388
+ ┌──────────────────────────────────────────────┐
389
+ │ nha run "prompt" │
390
+ │ │
391
+ │ PROMETHEUS routes ─► 38 agents deliberate │ ┌──────────────────────┐
392
+ │ CASSANDRA challenges (round 1, 2, 3) │ ───► │ YOUR LLM provider │
393
+ │ convergence measured ─► synthesis │ │ cloud API with your │
394
+ │ ATHENA audits │ │ key, or a local model│
395
+ │ │ └──────────────────────┘
396
+ │ transcript saved in ~/.legion/sessions/ │
397
+ └──────────────────────────────────────────────┘
325
398
  ```
326
399
 
327
400
  ## Links
328
401
 
329
402
  - [Website](https://nothumanallowed.com)
330
- - [Agent Directory](https://nothumanallowed.com/gethcity) — Browse all agents
331
- - [Documentation](https://nothumanallowed.com/docs/cli)
332
- - [Parliament Theater](https://nothumanallowed.com/parliament) — Watch real agent deliberations
333
- - [Epistemic Datasets](https://nothumanallowed.com/datasets) — Download reasoning traces
403
+ - [Source code and issues](https://github.com/adoslabsproject-gif/nothumanallowed)
404
+
405
+ This README is the documentation. `nha help` lists every command; `nha agents` lists the 38 agents.
334
406
 
335
407
  ## License
336
408
 
package/bin/nha CHANGED
@@ -2,8 +2,9 @@
2
2
  /**
3
3
  * nha — NotHumanAllowed CLI
4
4
  *
5
- * Thin launcher for Legion (multi-agent deliberation) and PIF (agent social client).
6
- * Zero dependencies. Downloads core files on first run to ~/.nha/.
5
+ * Launcher for Legion X (multi-agent deliberation, shipped inside this package
6
+ * and run on this machine) and the rest of the toolkit. The first run copies
7
+ * the 38 agents to ~/.nha/agents; nothing is downloaded to deliberate.
7
8
  *
8
9
  * Usage:
9
10
  * npx nha run "Compare React vs Vue for enterprise"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "16.0.64",
4
- "description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
3
+ "version": "17.0.0",
4
+ "description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, multi-agent deliberation that runs entirely on your machine (Legion X), visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Bring your own LLM: an API key or a local model (Ollama, any OpenAI-compatible endpoint). Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "nha": "bin/nha",
@@ -32,6 +32,8 @@
32
32
  "openai",
33
33
  "gemini",
34
34
  "deepseek",
35
+ "ollama",
36
+ "local-llm",
35
37
  "telegram-bot",
36
38
  "discord-bot",
37
39
  "voice-assistant",
@@ -55,10 +57,11 @@
55
57
  "homepage": "https://nothumanallowed.com",
56
58
  "scripts": {
57
59
  "check-bundle": "node check-bundle.mjs",
60
+ "check-legion-source": "node check-legion-source.mjs",
58
61
  "build:ui": "cd ../nha-ui && pnpm build",
59
62
  "prebuild": "cd ../nha-ui && pnpm build",
60
- "test": "node test/sandbox.test.mjs",
61
- "prepublishOnly": "node check-bundle.mjs && node test/sandbox.test.mjs"
63
+ "test": "node test/sandbox.test.mjs && node --test test/legion-bundle.test.mjs test/legion-run.test.mjs test/hosted-offline.test.mjs",
64
+ "prepublishOnly": "node check-bundle.mjs && node check-legion-source.mjs && npm test"
62
65
  },
63
66
  "dependencies": {
64
67
  "acorn": "^8.16.0",
package/src/bootstrap.mjs CHANGED
@@ -1,16 +1,17 @@
1
- /** First-run provisioner — creates ~/.nha/ and downloads core files + agents */
1
+ /** First-run provisioner — creates ~/.nha/ and installs the agents shipped with the package */
2
2
 
3
3
  import fs from 'fs';
4
4
  import {
5
5
  NHA_DIR, CORE_DIR, AGENTS_DIR, EXTENSIONS_DIR, SESSIONS_DIR, MEMORY_DIR,
6
- BASE_URL, LEGION_FILE, PIF_FILE, VERSIONS_FILE, AGENTS,
6
+ BASE_URL, LEGION_FILE, PIF_FILE, VERSIONS_FILE,
7
7
  } from './constants.mjs';
8
- import { download, downloadBatch } from './downloader.mjs';
8
+ import { download } from './downloader.mjs';
9
9
  import { loadConfig, saveConfig } from './config.mjs';
10
- import { banner, info, ok, fail, warn, progress } from './ui.mjs';
10
+ import { bundledLegionVersion, syncBundledAgents } from './legion-bundle.mjs';
11
+ import { banner, info, ok, fail, warn } from './ui.mjs';
11
12
 
12
13
  /**
13
- * Check if bootstrap is needed (core files missing).
14
+ * Check if bootstrap is needed (agents not installed yet).
14
15
  */
15
16
  export function needsBootstrap() {
16
17
  if (!fs.existsSync(LEGION_FILE) || !fs.existsSync(AGENTS_DIR)) return true;
@@ -23,7 +24,10 @@ export function needsBootstrap() {
23
24
  }
24
25
 
25
26
  /**
26
- * Run full bootstrap: create dirs, download everything.
27
+ * Run full bootstrap: create dirs, install the bundled agents.
28
+ * Legion X and the agents come from the package itself, so this works offline.
29
+ * Only PIF (the client of the NHA platform) is still fetched, and its absence
30
+ * stops nothing else.
27
31
  */
28
32
  export async function bootstrap() {
29
33
  banner();
@@ -35,87 +39,47 @@ export async function bootstrap() {
35
39
  }
36
40
  ok('Created ~/.nha/ directory structure');
37
41
 
38
- // ── Download version manifest ────────────────────────────────────────────
39
- info('Fetching version manifest...');
40
- const versionsOk = await download(`${BASE_URL}/versions.json`, VERSIONS_FILE);
41
- if (!versionsOk) {
42
- fail('Could not reach nothumanallowed.com — check your connection.');
43
- process.exit(1);
44
- }
45
-
46
- let versions;
47
- try {
48
- versions = JSON.parse(fs.readFileSync(VERSIONS_FILE, 'utf-8'));
49
- } catch {
50
- fail('Corrupt versions.json');
51
- process.exit(1);
52
- }
53
-
54
- const legionVersion = versions['legion-x']?.latest ?? 'unknown';
55
- const pifVersion = versions['pif']?.latest ?? 'unknown';
56
-
57
- // ── Download core files ──────────────────────────────────────────────────
58
- info(`Downloading Legion X v${legionVersion}...`);
59
- const legionOk = await download(`${BASE_URL}/legion-x.mjs`, LEGION_FILE, { timeout: 60_000 });
60
- if (!legionOk) {
61
- fail('Failed to download Legion X');
42
+ // ── Legion X and the agents, from the package ────────────────────────────
43
+ if (!fs.existsSync(LEGION_FILE)) {
44
+ fail('Legion X is missing from this installation. Reinstall: npm install -g nothumanallowed');
62
45
  process.exit(1);
63
46
  }
64
- ok(`Legion X v${legionVersion} installed`);
47
+ ok(`Legion X v${bundledLegionVersion()} ready`);
65
48
 
66
- info(`Downloading PIF v${pifVersion}...`);
67
- const pifOk = await download(`${BASE_URL}/pif.mjs`, PIF_FILE, { timeout: 60_000 });
68
- if (!pifOk) {
69
- fail('Failed to download PIF');
49
+ const agents = syncBundledAgents();
50
+ if (agents.missing.length > 0) {
51
+ fail(`${agents.missing.length} agents are missing from this installation (${agents.missing.join(', ')}). Reinstall: npm install -g nothumanallowed`);
70
52
  process.exit(1);
71
53
  }
72
- ok(`PIF v${pifVersion} installed`);
73
-
74
- // ── Download all 38 agents ───────────────────────────────────────────────
75
- info(`Downloading ${AGENTS.length} agents...`);
76
- const agentTasks = AGENTS.map(name => ({
77
- url: `${BASE_URL}/agents/${name}.mjs`,
78
- dest: `${AGENTS_DIR}/${name}.mjs`,
79
- }));
80
-
81
- const result = await downloadBatch(agentTasks, 8, (done, total) => {
82
- progress(done, total, `agents`);
83
- });
54
+ ok(`${agents.copied} agents installed in ~/.nha/agents`);
84
55
 
85
- if (result.failed > 0) {
86
- warn(`${result.failed} agents failed to download (run 'nha update' to retry)`);
56
+ // ── PIF, the client of the NHA platform (optional) ───────────────────────
57
+ const versionsOk = await download(`${BASE_URL}/versions.json`, VERSIONS_FILE);
58
+ const pifOk = versionsOk && await download(`${BASE_URL}/pif.mjs`, PIF_FILE, { timeout: 60_000 });
59
+ if (pifOk) {
60
+ ok('PIF installed');
87
61
  } else {
88
- ok(`${result.ok} agents installed`);
62
+ warn('PIF could not be downloaded: the "nha pif" and "nha mcp" commands stay unavailable. Everything else works.');
89
63
  }
90
64
 
91
65
  // ── Initialize config (migrates legacy if present) ───────────────────────
92
66
  const config = loadConfig();
93
67
  saveConfig(config);
94
68
 
95
- // ── Symlink sessions for backward compat ─────────────────────────────────
96
- const legacySessionsDir = `${process.env.HOME}/.legion/sessions`;
97
- if (fs.existsSync(legacySessionsDir) && !fs.existsSync(SESSIONS_DIR + '/.migrated')) {
98
- try {
99
- const files = fs.readdirSync(legacySessionsDir);
100
- if (files.length > 0) {
101
- info(`Found ${files.length} legacy session files — linked`);
102
- }
103
- } catch { /* ignore */ }
104
- }
105
-
106
69
  // ── Done ─────────────────────────────────────────────────────────────────
107
70
  console.log('');
108
71
  ok('NHA is ready!\n');
109
72
 
110
73
  if (!config.llm.apiKey) {
111
- console.log(` ${'\x1b[1;33m'}Next step:${'\x1b[0m'} Choose your LLM provider:\n`);
112
- console.log(` ${'\x1b[1;32m'}Option 1 — NHA Free (no API key needed):${'\x1b[0m'}`);
113
- console.log(` nha config set provider nha`);
114
- console.log(` ${'\x1b[2m'}Powered by Liara (Qwen3 32B). Free, slower (5-15s). No key required.${'\x1b[0m'}\n`);
115
- console.log(` ${'\x1b[1;36m'}Option 2 — Your own API key (faster, more capable):${'\x1b[0m'}`);
74
+ console.log(` ${'\x1b[1;33m'}Next step:${'\x1b[0m'} choose where your models run:\n`);
75
+ console.log(` ${'\x1b[1;36m'}Option 1 — Your own API key:${'\x1b[0m'}`);
116
76
  console.log(` nha config set provider anthropic`);
117
77
  console.log(` nha config set key sk-ant-api03-YOUR_KEY_HERE`);
118
78
  console.log(` ${'\x1b[2m'}Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere${'\x1b[0m'}\n`);
79
+ console.log(` ${'\x1b[1;32m'}Option 2 — A local model, no key (deliberations with "nha run"):${'\x1b[0m'}`);
80
+ console.log(` nha config set legion-provider ollama`);
81
+ console.log(` nha config set ollama-model qwen2.5:7b`);
82
+ console.log(` ${'\x1b[2m'}Any model served by Ollama, or any OpenAI-compatible endpoint (local-openai-url).${'\x1b[0m'}\n`);
119
83
  }
120
84
 
121
85
  return true;
package/src/cli.mjs CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
- import { VERSION, NHA_DIR, AGENTS_DIR, EXTENSIONS_DIR, AGENTS, EXTENSIONS, BASE_URL } from './constants.mjs';
5
+ import { VERSION, NHA_DIR, AGENTS_DIR, EXTENSIONS_DIR, AGENTS, EXTENSIONS, BASE_URL, LEGION_FILE } from './constants.mjs';
6
6
  import { needsBootstrap, bootstrap } from './bootstrap.mjs';
7
7
  import { spawnCore } from './spawn.mjs';
8
8
  import { loadConfig, setConfigValue } from './config.mjs';
9
+ import { buildLegionConfig, legionReadiness } from './legion-config.mjs';
10
+ import { bundledLegionVersion, syncBundledAgents } from './legion-bundle.mjs';
9
11
  import { checkForUpdates, runUpdate, checkNpmVersion } from './updater.mjs';
10
12
  import { download } from './downloader.mjs';
11
13
  import { cmdAsk } from './commands/ask.mjs';
@@ -31,6 +33,9 @@ export async function main(argv) {
31
33
  if (needsBootstrap() && cmd !== 'help' && cmd !== 'version' && cmd !== '--help' && cmd !== '-h') {
32
34
  await bootstrap();
33
35
  if (cmd === 'setup') return; // setup was the goal
36
+ } else if (cmd !== 'help' && cmd !== 'version' && cmd !== '--help' && cmd !== '-h') {
37
+ // After a package upgrade the agents of the new version replace the old ones.
38
+ try { syncBundledAgents(); } catch (err) { warn(`Could not refresh the agents: ${err.message}`); }
34
39
  }
35
40
 
36
41
  // ── Telemetry ping (anonymous, fire-and-forget, non-blocking) ────────────
@@ -221,6 +226,14 @@ export async function main(argv) {
221
226
  const { cmdPlugin: runPlugin } = await import('./commands/plugin.mjs');
222
227
  return runPlugin(['run', cmd, ...args]);
223
228
  }
229
+ // Legion's own config:set writes a file that is rebuilt from the nha
230
+ // config at every run: a value set there would vanish without a word.
231
+ if (cmd === 'config:set') {
232
+ fail('Settings are kept in the nha config. Use: nha config set <key> <value>');
233
+ info('Deliberation keys: legion-provider, ollama-url, ollama-model, ollama-models, ollama-embed-model,');
234
+ info('local-openai-url, local-openai-model, local-openai-key, orchestrator-provider, economy, fact-check, cross-reading-chars');
235
+ process.exit(1);
236
+ }
224
237
  // Try as Legion command passthrough (only if legion is installed)
225
238
  try {
226
239
  const { LEGION_FILE } = await import('./constants.mjs');
@@ -422,12 +435,19 @@ async function cmdRun(args) {
422
435
  process.exit(1);
423
436
  }
424
437
 
425
- const config = loadConfig();
426
- if (!config.llm.apiKey) {
427
- fail('No API key configured. Run:');
438
+ // A deliberation runs on this machine with the user's own models: a cloud
439
+ // provider with its key, or a local model with no key at all.
440
+ const readiness = legionReadiness(buildLegionConfig(loadConfig()));
441
+ if (!readiness.ready) {
442
+ fail(readiness.reason);
443
+ console.log('');
444
+ console.log(' With an API key (anthropic, openai, gemini, deepseek, grok, mistral, cohere):');
445
+ console.log(' nha config set provider anthropic');
446
+ console.log(' nha config set key sk-ant-api03-YOUR_KEY');
428
447
  console.log('');
429
- console.log(' nha config set provider anthropic');
430
- console.log(' nha config set key sk-ant-api03-YOUR_KEY');
448
+ console.log(' With a local model, no key (Ollama or any OpenAI-compatible endpoint):');
449
+ console.log(' nha config set legion-provider ollama');
450
+ console.log(' nha config set ollama-model qwen2.5:7b');
431
451
  console.log('');
432
452
  process.exit(1);
433
453
  }
@@ -533,6 +553,8 @@ function cmdConfig(args) {
533
553
  console.log('');
534
554
  info('Keys: provider, key, openai-key, gemini-key, deepseek-key, grok-key (X.AI), groq-key (voice/Whisper), mistral-key, cohere-key, model, timeout');
535
555
  info(' verbose, immersive, deliberation, rounds, convergence, tribunal, knowledge');
556
+ info(' legion-provider, ollama-url, ollama-model, ollama-models, ollama-embed-model (local models for "nha run")');
557
+ info(' local-openai-url, local-openai-model, local-openai-key, orchestrator-provider, economy, fact-check, cross-reading-chars');
536
558
  info(' google-client-id, google-client-secret');
537
559
  info(' microsoft-client-id, microsoft-client-secret, microsoft-tenant');
538
560
  info(' telegram-bot-token, discord-bot-token, responder-auto-route');
@@ -541,7 +563,9 @@ function cmdConfig(args) {
541
563
  }
542
564
  const success = setConfigValue(key, value);
543
565
  if (success) {
544
- ok(`${key} = ${value.startsWith('sk-') ? value.slice(0, 12) + '...' : value}`);
566
+ // A secret is never echoed back, whatever it starts with.
567
+ const secret = /(key|token|secret)$/i.test(key) || value.startsWith('sk-');
568
+ ok(`${key} = ${secret ? '***' : value}`);
545
569
  } else {
546
570
  fail(`Unknown config key: ${key}`);
547
571
  }
@@ -577,6 +601,14 @@ function cmdConfig(args) {
577
601
  console.log(` Rounds: ${W}${config.deliberation.rounds}${NC}`);
578
602
  console.log(` Convergence: ${W}${config.deliberation.convergence}${NC}`);
579
603
  console.log(` Tribunal: ${config.deliberation.tribunalEnabled ? G + 'yes' : D + 'no'}${NC}`);
604
+ const legionConfig = buildLegionConfig(config);
605
+ const readiness = legionReadiness(legionConfig);
606
+ console.log(` Provider: ${W}${legionConfig.provider || '(any configured)'}${NC}`);
607
+ console.log(` Can run with: ${readiness.ready ? G + readiness.providers.join(', ') : R + readiness.reason}${NC}`);
608
+ if (legionConfig.ollamaModels || legionConfig.ollamaModel) {
609
+ console.log(` Local models: ${W}${legionConfig.ollamaModels || legionConfig.ollamaModel}${NC} ${D}(${legionConfig.ollamaUrl})${NC}`);
610
+ }
611
+ if (legionConfig.economy) console.log(` Economy: ${G}yes${NC}`);
580
612
 
581
613
  console.log(`\n ${C}Agent Identity${NC}`);
582
614
  if (config.agent.name) {
@@ -745,10 +777,10 @@ async function cmdDoctor() {
745
777
  console.log(` Node.js: ${nodeV >= 22 ? G + process.version : Y + process.version + ' (need 22+)'}${NC}`);
746
778
 
747
779
  // Check core files
748
- const legionOk = fs.existsSync(path.join(NHA_DIR, 'core', 'legion-x.mjs'));
780
+ const legionOk = fs.existsSync(LEGION_FILE);
749
781
  const pifOk = fs.existsSync(path.join(NHA_DIR, 'core', 'pif.mjs'));
750
- console.log(` Legion X: ${legionOk ? G + 'installed' : R + 'missing'}${NC}`);
751
- console.log(` PIF: ${pifOk ? G + 'installed' : R + 'missing'}${NC}`);
782
+ console.log(` Legion X: ${legionOk ? G + 'v' + bundledLegionVersion() + ' (bundled)' : R + 'missing'}${NC}`);
783
+ console.log(` PIF: ${pifOk ? G + 'installed' : D + 'not installed (optional)'}${NC}`);
752
784
 
753
785
  // Check agents
754
786
  const agentCount = AGENTS.filter(a => fs.existsSync(path.join(AGENTS_DIR, `${a}.mjs`))).length;
@@ -769,6 +801,10 @@ async function cmdDoctor() {
769
801
  // Check API key
770
802
  console.log(` API Key: ${config.llm.apiKey ? G + 'configured (' + config.llm.provider + ')' : R + 'NOT SET'}${NC}`);
771
803
 
804
+ // Deliberations run locally: which of the user's providers they can use
805
+ const readiness = legionReadiness(buildLegionConfig(config));
806
+ console.log(` Deliberations: ${readiness.ready ? G + 'ready (' + readiness.providers.join(', ') + ')' : R + readiness.reason}${NC}`);
807
+
772
808
  // Check connectivity
773
809
  try {
774
810
  const res = await fetch('https://nothumanallowed.com/api/v1/health', {
@@ -787,24 +823,27 @@ async function cmdDoctor() {
787
823
  if (!config.llm.apiKey) {
788
824
  warn('Configure an API key: nha config set provider anthropic && nha config set key YOUR_KEY');
789
825
  }
790
- if (!legionOk || !pifOk) {
791
- warn('Missing core files. Run "nha update" to re-download.');
826
+ if (!readiness.ready) {
827
+ warn('For "nha run" with a local model and no key: nha config set legion-provider ollama && nha config set ollama-model <model>');
828
+ }
829
+ if (!legionOk) {
830
+ warn('Legion X is missing from this installation. Reinstall: npm install -g nothumanallowed');
792
831
  }
793
832
  if (agentCount < AGENTS.length) {
794
- warn(`${AGENTS.length - agentCount} agents missing. Run "nha update" to re-download.`);
833
+ warn(`${AGENTS.length - agentCount} agents missing. Run "nha update" to restore them from the package.`);
795
834
  }
796
835
  }
797
836
 
798
837
  // ── nha version ────────────────────────────────────────────────────────────
799
838
  function cmdVersion() {
800
839
  console.log(`nha v${VERSION}`);
840
+ console.log(`Legion X v${bundledLegionVersion()}`);
801
841
 
802
- // Show core versions if available
842
+ // PIF is downloaded separately: show its version if it is there
803
843
  const versionsFile = path.join(NHA_DIR, 'core', 'versions.json');
804
844
  if (fs.existsSync(versionsFile)) {
805
845
  try {
806
846
  const v = JSON.parse(fs.readFileSync(versionsFile, 'utf-8'));
807
- if (v['legion-x']?.latest) console.log(`Legion X v${v['legion-x'].latest}`);
808
847
  if (v['pif']?.latest) console.log(`PIF v${v['pif'].latest}`);
809
848
  } catch {}
810
849
  }
@@ -823,8 +862,10 @@ function cmdHelp() {
823
862
  console.log(` agents info <name> Show agent capabilities & domain`);
824
863
  console.log(` scan <path> Security scan a project with SABER + ZERO`);
825
864
  console.log(` scan . ${D}--output report.md${NC} Save report to file`);
826
- console.log(` run "prompt" Multi-agent collaboration (server-routed)`);
827
- console.log(` run "prompt" ${D}--agents saber,zero${NC} Collaborate with specific agents\n`);
865
+ console.log(` run "prompt" Multi-agent deliberation, on this machine with your own models`);
866
+ console.log(` run "prompt" ${D}--agents saber,zero${NC} Deliberate with specific agents`);
867
+ console.log(` run "prompt" ${D}--economy${NC} About half the tokens (shorter cross-reading)`);
868
+ console.log(` ${D}Local model, no key: config set legion-provider ollama + config set ollama-model <model>${NC}\n`);
828
869
 
829
870
  console.log(` ${C}Daily Operations${NC} ${D}(Gmail + Calendar + Tasks)${NC}`);
830
871
  console.log(` ui Open local web dashboard (http://127.0.0.1:3847)`);
@@ -902,7 +943,7 @@ function cmdHelp() {
902
943
  console.log(` ${C}Configuration${NC}`);
903
944
  console.log(` config Show current config`);
904
945
  console.log(` config set <k> <v> Set a config value`);
905
- console.log(` update Update agents, core files & npm package`);
946
+ console.log(` update Update the npm package (Legion X and agents come with it)`);
906
947
  console.log(` upgrade Update npm package only (alias: self-update)`);
907
948
  console.log(` doctor Health check`);
908
949
  console.log(` mcp Start MCP server (Claude Code, Cursor)\n`);
@@ -914,6 +955,7 @@ function cmdHelp() {
914
955
 
915
956
  console.log(` ${D}38 agents: security, code, data, devops, creative, integration, and more.${NC}`);
916
957
  console.log(` ${D}Use them solo or let them collaborate via multi-round deliberation.${NC}`);
917
- console.log(` ${D}Your API key never leaves your machine. Zero dependencies. Zero telemetry.${NC}`);
918
- console.log(` ${D}Docs: https://nothumanallowed.com/docs/cli — v${VERSION}${NC}\n`);
958
+ console.log(` ${D}Your API keys go to your own LLM provider and nowhere else.${NC}`);
959
+ console.log(` ${D}Each command sends one usage ping (platform and version) to nothumanallowed.com.${NC}`);
960
+ console.log(` ${D}Docs: https://github.com/adoslabsproject-gif/nothumanallowed#readme — v${VERSION}${NC}\n`);
919
961
  }
@@ -146,7 +146,7 @@ function launchdStatus() {
146
146
  function generateSystemdUnit() {
147
147
  return `[Unit]
148
148
  Description=NHA PAO Background Daemon
149
- Documentation=https://nothumanallowed.com/docs/cli
149
+ Documentation=https://github.com/adoslabsproject-gif/nothumanallowed#readme
150
150
  After=network-online.target
151
151
  Wants=network-online.target
152
152
 
@@ -572,7 +572,7 @@ export async function cmdChat(args) {
572
572
 
573
573
  const activeProvider = config.llm.provider || 'anthropic';
574
574
  if (activeProvider !== 'nha' && !config.llm.apiKey) {
575
- fail('No API key configured. Run: nha config set key YOUR_KEY\n Or use the free tier: nha config set provider nha');
575
+ fail('No API key configured. Run: nha config set key YOUR_KEY');
576
576
  process.exit(1);
577
577
  }
578
578
 
@@ -105,7 +105,7 @@ export async function cmdOps(args) {
105
105
  let inactiveHintTel = 'configured but inactive (try: nha ops stop && nha ops start)';
106
106
  if (reason.startsWith('missing_key:')) {
107
107
  const p = reason.slice('missing_key:'.length);
108
- inactiveHintTel = `configured but LLM key missing for provider "${p}" — fix with: nha config set provider nha (free Liara) OR nha config set ${p}-key YOUR_KEY`;
108
+ inactiveHintTel = `configured but LLM key missing for provider "${p}" — fix with: nha config set key YOUR_KEY`;
109
109
  }
110
110
  console.log(` Telegram: ${responder.telegram ? G + 'active' + NC : telegramConfigured ? Y + inactiveHintTel + NC : D + 'not configured' + NC}`);
111
111
  console.log(` Discord: ${responder.discord ? G + 'active' + NC : discordConfigured ? Y + inactiveHintTel + NC : D + 'not configured' + NC}`);