agentus 0.0.0-stage → 0.2.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 (59) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +11 -0
  3. package/README.md +476 -2
  4. package/README.zh-CN.md +396 -0
  5. package/bin/agentus.mjs +147 -0
  6. package/docs/android-notify-contract.md +658 -0
  7. package/docs/dev/01-dev-loop.md +186 -0
  8. package/docs/dev/02-verify.md +237 -0
  9. package/docs/dev/03-release.md +218 -0
  10. package/docs/dev/04-recover.md +199 -0
  11. package/docs/dev/05-agent-driven.md +114 -0
  12. package/docs/dev/README.md +65 -0
  13. package/docs/installation.md +366 -0
  14. package/docs/installation.zh-CN.md +350 -0
  15. package/docs/refs/aionui-acp-rules.md +46 -0
  16. package/docs/refs/hermes-studio-ui-ideas.md +191 -0
  17. package/docs/transcript-folding.md +139 -0
  18. package/package.json +76 -5
  19. package/packages/server/mcp/plan-server.mjs +217 -0
  20. package/packages/server/mock/agent.mjs +693 -0
  21. package/packages/server/package.json +22 -0
  22. package/packages/server/src/acp/backends.ts +196 -0
  23. package/packages/server/src/acp/errors.ts +46 -0
  24. package/packages/server/src/acp/registry.ts +579 -0
  25. package/packages/server/src/acp/session-manager.ts +1868 -0
  26. package/packages/server/src/auth.ts +688 -0
  27. package/packages/server/src/dashscope.ts +235 -0
  28. package/packages/server/src/fs.ts +379 -0
  29. package/packages/server/src/hotwords.ts +132 -0
  30. package/packages/server/src/index.ts +1486 -0
  31. package/packages/server/src/notify/center.ts +994 -0
  32. package/packages/server/src/notify/types.ts +222 -0
  33. package/packages/server/src/plan/plan.ts +126 -0
  34. package/packages/server/src/pty-helper.py +135 -0
  35. package/packages/server/src/settings.ts +469 -0
  36. package/packages/server/src/store/export.ts +209 -0
  37. package/packages/server/src/store/store.ts +1238 -0
  38. package/packages/server/src/term.ts +201 -0
  39. package/packages/server/src/title.ts +109 -0
  40. package/packages/server/src/voice.ts +251 -0
  41. package/packages/shared/package.json +14 -0
  42. package/packages/shared/src/index.ts +271 -0
  43. package/packages/web/dist/assets/index-D667PX2c.css +32 -0
  44. package/packages/web/dist/assets/index-d9NzwPFu.js +283 -0
  45. package/packages/web/dist/coding-agents/hermes.png +0 -0
  46. package/packages/web/dist/coding-agents/qoder.svg +5 -0
  47. package/packages/web/dist/icons/agentus-icon-inverse.svg +301 -0
  48. package/packages/web/dist/icons/agentus-icon.svg +301 -0
  49. package/packages/web/dist/icons/agentus-mark-inverse.svg +301 -0
  50. package/packages/web/dist/icons/agentus-mark.svg +301 -0
  51. package/packages/web/dist/icons/apple-touch-icon-180.png +0 -0
  52. package/packages/web/dist/icons/favicon.ico +0 -0
  53. package/packages/web/dist/icons/icon-192.png +0 -0
  54. package/packages/web/dist/icons/icon-512.png +0 -0
  55. package/packages/web/dist/icons/icon-maskable-512.png +0 -0
  56. package/packages/web/dist/index.html +30 -0
  57. package/packages/web/dist/manifest.webmanifest +17 -0
  58. package/packages/web/dist/sw.js +61 -0
  59. package/scripts/make-cert.sh +101 -0
@@ -0,0 +1,366 @@
1
+ # Installing Agentus
2
+
3
+ [中文](./installation.zh-CN.md) · [README](../README.md)
4
+
5
+ Agentus is a multi-session web cockpit for ACP-speaking coding agents. One npm package
6
+ (`agentus`) runs a Node server that serves a built React SPA and spawns agent CLIs
7
+ (`hermes acp` / `qodercli --acp`) as child processes. The browser drives the sessions; the
8
+ reasoning stays inside the CLI subprocesses.
9
+
10
+ ## Prerequisites
11
+
12
+ Two things, both required:
13
+
14
+ - **Node >= 22.5.** The store is `node:sqlite`, which landed in 22.5. Agentus refuses to start
15
+ on anything older, with a clear message.
16
+ - **An ACP agent CLI on your PATH** — `hermes acp` or `qodercli --acp`. This is the one
17
+ prerequisite npm cannot install for you. Without one the cockpit still runs and you can still
18
+ log in; you just have no backend to open a session on.
19
+
20
+ ```bash
21
+ # check the agent side (Hermes self-checks; qoder needs a login first)
22
+ hermes acp --check
23
+ qodercli login # else newSession fails with -32000
24
+ ```
25
+
26
+ Node itself is easy to get:
27
+
28
+ ```bash
29
+ nvm install 22 && nvm use 22 # or
30
+ brew install node # or https://nodejs.org/en/download
31
+ ```
32
+
33
+ ## Install
34
+
35
+ ### One command — the published package (public)
36
+
37
+ ```bash
38
+ npx agentus # run once, nothing installed globally
39
+ npm i -g agentus # …or install it, then run `agentus`
40
+ ```
41
+
42
+ `npx agentus` runs the cockpit in the foreground — Ctrl-C stops it, and it takes its agent
43
+ children with it.
44
+
45
+ ### One command — the installer script
46
+
47
+ The same install from the repository's own script, with the checks and the fallbacks spelled out
48
+ (no `sudo`, ever):
49
+
50
+ ```bash
51
+ curl -fsSL https://raw.githubusercontent.com/agentus-org/agentus/main/scripts/install.sh | bash
52
+ ```
53
+
54
+ The installer (`scripts/install.sh`) checks Node, installs the published `agentus` package with
55
+ npm (globally), and falls back to a per-user prefix (`~/.local`) if the global tree is not
56
+ writable — it never reaches for `sudo`. It prints the start command and the URL when it is done.
57
+ It refuses to run as root unless you set `AGENTUS_ALLOW_ROOT=1`.
58
+
59
+ Overrides, passed as environment variables:
60
+
61
+ | var | default | meaning |
62
+ |---|---|---|
63
+ | `AGENTUS_VERSION` | `latest` | pin a package version, e.g. `0.1.0` |
64
+ | `AGENTUS_INSTALL_DIR` | unset | install into this `npm --prefix` instead of the global one |
65
+ | `AGENTUS_REGISTRY` | npm's own | registry to fetch from |
66
+ | `AGENTUS_ALLOW_ROOT` | `0` | `1` permits running as root |
67
+ | `AGENTUS_NODE_MIN` | `22.5` | override the Node floor check |
68
+
69
+ ```bash
70
+ # pin a version and a prefix
71
+ AGENTUS_VERSION=0.1.0 AGENTUS_INSTALL_DIR="$HOME/.local" \
72
+ bash scripts/install.sh
73
+ ```
74
+
75
+ ### From source (a checkout)
76
+
77
+ ```bash
78
+ git clone https://github.com/agentus-org/agentus.git
79
+ cd agentus
80
+ npm start # installs if needed, builds, serves (scripts/start.sh)
81
+ npm run dev -w @agentus/web # front-end hot reload (Vite :5173, reachable over LAN)
82
+ npm run agentus # the same CLI as `npx agentus`, straight from the checkout
83
+ ```
84
+
85
+ `npm start` (→ `scripts/start.sh`) checks the Node version, installs dependencies with
86
+ `NODE_ENV=development` when `node_modules` is missing, builds when the web bundle is stale, then
87
+ serves once (no watcher). `AGENTUS_PORT=9000 npm start` moves the port.
88
+
89
+ ## CLI
90
+
91
+ ```
92
+ agentus [options]
93
+
94
+ -p, --port <n> port to listen on (AGENTUS_PORT, default 8787)
95
+ -d, --data <dir> where state lives (sqlite, login, TLS material, settings)
96
+ (AGENTUS_DATA, default ~/.agentus)
97
+ -o, --open open the cockpit in your browser once it is up
98
+ --where print the resolved paths and exit (starts nothing)
99
+ -h, --help this text
100
+ -v, --version print the version
101
+ ```
102
+
103
+ `--where` is worth knowing: it prints what the server actually resolved — data dir, web bundle,
104
+ companion APK, TLS listener, Node version — and exits before anything starts. It is the one
105
+ answer to "where did my data go" (see below).
106
+
107
+ ## First run
108
+
109
+ 1. Start it: `agentus` (or `npx agentus`, or `npm start` from a checkout).
110
+ 2. Open **http://localhost:8787** and log in. Default credentials:
111
+
112
+ ```
113
+ username: admin
114
+ password: 123456 ← change it
115
+ ```
116
+
117
+ Change it in the app: **Settings → 账号** (the first card). It applies immediately and writes
118
+ `<DATA_DIR>/credentials.json` (0600, scrypt hash).
119
+ 3. Add a backend. The registry is seeded on first boot from the builtin rows (see
120
+ [Backends](#backends)); check that `hermes` or `qoder` resolves, or point a row at your own
121
+ command.
122
+ 4. **+ new session** → pick a backend and a working directory → start it.
123
+
124
+ The first session you start will use the Hermes home your own agent already uses (`~/.hermes`)
125
+ unless the backend row names another one — see [HERMES_HOME](#hermes_home).
126
+
127
+ ## Configuration reference
128
+
129
+ ### Environment variables
130
+
131
+ | var | default | meaning |
132
+ |---|---|---|
133
+ | `AGENTUS_PORT` | `8787` | server port (never reads bare `PORT`) |
134
+ | `AGENTUS_DATA` | `~/.agentus` | where state lives; see [Data directory](#data-directory) |
135
+ | `AGENTUS_HERMES_CMD` | `hermes` | binary to spawn for the hermes backend |
136
+ | `AGENTUS_QODER_CMD` | `~/.local/bin/qodercli` | ditto for qoder |
137
+ | `AGENTUS_HERMES_HOME` | `~/.hermes` | default `HERMES_HOME` for a row that does not name one |
138
+ | `AGENTUS_PERM_TIMEOUT_MS` | `300000` (5 min) | how long a permission prompt waits before auto-cancelling |
139
+ | `AGENTUS_HISTORY_PAGE` | `500` | transcript page size |
140
+ | `AGENTUS_TLS_PORT` | `8443` when a cert exists, else off | the TLS listener (`0` disables it) |
141
+ | `AGENTUS_TLS_CERT` / `AGENTUS_TLS_KEY` | `<DATA_DIR>/tls/{cert,key}.pem` | cert material for that listener |
142
+ | `AGENTUS_TLS_CA` | `<DATA_DIR>/tls/ca.pem` | the root a device installs once |
143
+ | `AGENTUS_TTS_BASE_URL` | unset | OpenAI-compatible base for server speech synthesis (`…/v1`); unset = browser voices only |
144
+ | `AGENTUS_TTS_API_KEY` / `AGENTUS_TTS_MODEL` / `AGENTUS_TTS_VOICE` | – / `tts-1` / `alloy` | ditto |
145
+ | `AGENTUS_STT_BASE_URL` | unset | OpenAI-compatible base for server transcription; unset = browser recognition only |
146
+ | `AGENTUS_STT_API_KEY` / `AGENTUS_STT_MODEL` / `AGENTUS_STT_LANGUAGE` | – / `whisper-1` / – | ditto |
147
+ | `DASHSCOPE_API_KEY` / `DASHSCOPE_BASE_URL` | – | 百炼 (DashScope) credentials; read from the process env **and** `~/.hermes/.env` |
148
+
149
+ Auth variables:
150
+
151
+ | var | default | meaning |
152
+ |---|---|---|
153
+ | `AGENTUS_AUTH` | `on` | `off` = no login at all (local hacking only) |
154
+ | `AGENTUS_USERNAME` | `admin` | operator name |
155
+ | `AGENTUS_PASSWORD` | `123456` | operator password (plaintext in env) |
156
+ | `AGENTUS_PASSWORD_HASH` | — | `scrypt:<salt>:<hex>`; wins over `AGENTUS_PASSWORD` |
157
+ | `AGENTUS_SESSION_TTL_MS` | 604800000 (7d) | session lifetime |
158
+ | `AGENTUS_AUTH_SECRET` | `<DATA_DIR>/auth.secret` | cookie signing key (0600, auto-minted) |
159
+ | `AGENTUS_AUTH_TOKEN` | `<DATA_DIR>/auth.token` | machine token for scripts (0600, auto-minted) |
160
+ | `AGENTUS_LOGIN_MAX_FAILS` / `AGENTUS_LOGIN_LOCK_MS` | `5` / `30000` | per-IP brute-force lockout |
161
+ | `AGENTUS_BASIC_AUTH` | — | HTTP Basic in front of **everything** (`:pass` checks only the password) |
162
+
163
+ The voice/theme environment variables are a **bootstrap only**: the settings page (⚙ in the rail)
164
+ owns them afterwards and stores them under `<DATA_DIR>/settings.json` (0600). What the page says
165
+ wins; the env is what makes a fresh install work before anyone opens it.
166
+
167
+ ### Data directory
168
+
169
+ `~/.agentus` by default. It holds `agentus.sqlite` (sessions + transcript), `credentials.json`,
170
+ `auth.token`, `auth.secret`, `tls/`, `settings.json`. It is created `0700` on first boot, and the
171
+ boot log prints it **and why that directory won**:
172
+
173
+ | order | rule |
174
+ |---|---|
175
+ | 1 | `AGENTUS_DATA` when it is set — every launcher and dev/QA instance pins this |
176
+ | 2 | `~/.agentus` once it holds something, or when there is no repo-side data yet |
177
+ | 3 | `<repo>/packages/server/.data` — an existing checkout that predates this layout |
178
+
179
+ Rule 3 exists so an install that quietly switched directories cannot come up with an empty
180
+ session list (which reads exactly like "my sessions are gone"). To move such a checkout over,
181
+ boot it once with `AGENTUS_DATA=~/.agentus`. `agentus --where` prints what the server resolved
182
+ instead of making you guess.
183
+
184
+ Nothing ever writes inside `node_modules`: for an installed package, `<pkg>/packages/server/.data`
185
+ would be deleted by the next `npm i -g agentus`, taking the transcript, the login and the TLS key
186
+ with it.
187
+
188
+ ### Ports
189
+
190
+ | port | speaks | for |
191
+ |---|---|---|
192
+ | `AGENTUS_PORT` (8787) | plain HTTP | LAN, loopback, `curl`, scripts — no cert warning, CI unchanged |
193
+ | `AGENTUS_TLS_PORT` (8443) | HTTPS (self-signed) | the tunnel — encrypts the public hop |
194
+ | 5173 | Vite dev server | front-end hot reload, only during `npm run dev` |
195
+
196
+ Same handler, same routes, same auth on both listeners; only the socket differs.
197
+
198
+ ### Login
199
+
200
+ The cockpit is behind a login by default. Two credentials exist:
201
+
202
+ | credential | how it travels | who uses it |
203
+ |---|---|---|
204
+ | session cookie | `HttpOnly` + `SameSite=Lax`, HMAC-SHA256 signed, 7d | the browser (`POST /api/auth/login`) |
205
+ | machine token | `Authorization: Bearer <t>` or `?token=<t>` | scripts, CI, launchers — read it from `<DATA_DIR>/auth.token` (0600) |
206
+
207
+ Sessions are stateless cookies with a server-side index at `<DATA_DIR>/sessions.json` (0600), so
208
+ revocation and "log out everywhere" are real and survive a restart. `AGENTUS_USERNAME` /
209
+ `AGENTUS_PASSWORD` / `AGENTUS_PASSWORD_HASH` are the bootstrap; once you save in the 账号 card the
210
+ file wins. Delete `credentials.json` to fall back to the environment.
211
+
212
+ ### TLS
213
+
214
+ A plain TCP tunnel (SakuraFrp, `ssh -L`, most frp setups) forwards bytes — it does not terminate
215
+ TLS for you. So the server can speak TLS itself, on a **second** listener:
216
+
217
+ ```bash
218
+ scripts/make-cert.sh # self-signed root + short-lived leaf, SAN = the name you type
219
+ # -> $AGENTUS_DATA/tls/{ca.pem,cert.pem,key.pem} (key 0600, git-ignored)
220
+ # boot log then says: https://0.0.0.0:8443 (self-signed …) ; point a tunnel at THIS port
221
+ ```
222
+
223
+ The listener also serves its own issuer at `/cert.crt`, so a device can install the root once:
224
+ `https://<your-name>:<tls-port>/cert.crt`. iOS opens the profile installer directly; Android
225
+ downloads it (Settings → Security → CA certificate); iOS also needs *About → Certificate Trust
226
+ Settings → enable*. It is public material — the private key never leaves `<DATA_DIR>/tls/`.
227
+
228
+ `make-cert.sh` issues two certs on purpose: a 10-year root (`ca.pem`, what devices install) and a
229
+ 390-day leaf (`cert.pem`, what the listener serves). Apple caps server-certificate validity at 398
230
+ days, so a 10-year leaf is exactly what iOS refuses *after* the install dance. Rotate the leaf
231
+ (`--leaf-only`) freely; the installed root is untouched.
232
+
233
+ ### Backends
234
+
235
+ | id | command | notes |
236
+ |---|---|---|
237
+ | `hermes` | `hermes acp` | needs a Hermes install on PATH (`hermes acp --check`) |
238
+ | `qoder` | `qodercli --acp` | needs `qodercli login` first, else `newSession` fails `-32000` |
239
+ | `mock` | `node packages/server/mock/agent.mjs` | offline dev/QA: fake streaming, permission prompts, crash/plan/tool triggers |
240
+
241
+ Adding a backend is one row in `packages/server/src/acp/backends.ts`; the protocol layer never
242
+ changes.
243
+
244
+ ## HERMES_HOME
245
+
246
+ A spawned agent CLI inherits the server's environment, so the server **always sets `HERMES_HOME`
247
+ explicitly**: the row's own home if it names one, otherwise the default — **your real `~/.hermes`**,
248
+ the same `state.db` your gateway and Studio have open. That is the intended production setting: a
249
+ session drives your actual agent, with your config, credentials, memory and session list.
250
+
251
+ An *inherited* `HERMES_HOME` is ignored, so where a session writes is always something you chose —
252
+ on the backend row, or via `AGENTUS_HERMES_HOME`. To keep one session's data separate, name a
253
+ directory in that row's HERMES_HOME; no permission is needed.
254
+
255
+ ## Running it as a service
256
+
257
+ Agentus runs in the foreground by default; a service unit keeps it up across logins and reboots.
258
+ Point the unit at the same `AGENTUS_DATA` you use interactively, or the cockpit will come up with
259
+ a different session list.
260
+
261
+ **macOS (launchd)** — `~/Library/LaunchAgents/org.agentus.cockpit.plist`:
262
+
263
+ ```xml
264
+ <?xml version="1.0" encoding="UTF-8"?>
265
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
266
+ <plist version="1.0">
267
+ <dict>
268
+ <key>Label</key> <string>org.agentus.cockpit</string>
269
+ <key>ProgramArguments</key>
270
+ <array>
271
+ <string>/usr/bin/env</string><string>agentus</string><string>--port</string><string>8787</string>
272
+ </array>
273
+ <key>EnvironmentVariables</key>
274
+ <dict><key>AGENTUS_DATA</key><string>/Users/YOU/.agentus</string></dict>
275
+ <key>RunAtLoad</key> <true/>
276
+ <key>KeepAlive</key> <true/>
277
+ <key>StandardOutPath</key> <string>/tmp/agentus.out.log</string>
278
+ <key>StandardErrorPath</key><string>/tmp/agentus.err.log</string>
279
+ </dict>
280
+ </plist>
281
+ ```
282
+
283
+ ```bash
284
+ launchctl load ~/Library/LaunchAgents/org.agentus.cockpit.plist
285
+ launchctl kickstart -k gui/$(id -u)/org.agentus.cockpit # restart
286
+ ```
287
+
288
+ **Linux (systemd, user unit)** — `~/.config/systemd/user/agentus.service`:
289
+
290
+ ```ini
291
+ [Unit]
292
+ Description=Agentus cockpit
293
+ After=network.target
294
+
295
+ [Service]
296
+ ExecStart=/usr/bin/env agentus --port 8787
297
+ Environment=AGENTUS_DATA=%h/.agentus
298
+ Restart=on-failure
299
+
300
+ [Install]
301
+ WantedBy=default.target
302
+ ```
303
+
304
+ ```bash
305
+ systemctl --user daemon-reload
306
+ systemctl --user enable --now agentus
307
+ journalctl --user -u agentus -f
308
+ ```
309
+
310
+ TODO(verify): the exact `env` resolution of `agentus` inside a launchd/systemd unit depends on
311
+ where npm put the bin (a per-user prefix is not on a service's PATH by default) — use an absolute
312
+ path to the `agentus` bin if the unit fails to start.
313
+
314
+ ## Upgrade
315
+
316
+ ```bash
317
+ npm i -g agentus@latest # or: npx agentus@latest … for a one-off
318
+ ```
319
+
320
+ Your state is untouched — `~/.agentus` (or wherever `AGENTUS_DATA` points) is not inside the
321
+ package, so an upgrade cannot delete it. If you installed into a prefix with
322
+ `AGENTUS_INSTALL_DIR=~/.local`, upgrade with the same prefix. On startup the server adopts existing
323
+ session cookies once instead of logging every device out.
324
+
325
+ ## Uninstall
326
+
327
+ ```bash
328
+ npm rm -g agentus # remove the program (state stays)
329
+ rm -rf ~/.agentus # remove state — SQLite, login, TLS key, settings
330
+ ```
331
+
332
+ If you installed into a per-user prefix, `npm rm -g --prefix ~/.local agentus`. Uninstalling never
333
+ touches `~/.hermes` (your agents' own data) or any working directory.
334
+
335
+ ## Troubleshooting
336
+
337
+ **Port already in use.** `agentus --port 9000`, or find and stop the holder:
338
+ `lsof -nP -iTCP:8787 -sTCP:LISTEN`. Agentus never reads bare `PORT` for exactly this reason.
339
+
340
+ **"No backend" / a session won't start.** The agent CLI is missing or not logged in. Run
341
+ `hermes acp --check`, or `qodercli login` (a `-32000` on `newSession` means qoder is not logged
342
+ in). Check the backend row's command in Settings; the boot log and a 探测 (probe) say whether the
343
+ command resolves.
344
+
345
+ **Login problems.**
346
+
347
+ - Forgot the password: delete `<DATA_DIR>/credentials.json` and restart — the bootstrap from
348
+ `AGENTUS_USERNAME` / `AGENTUS_PASSWORD` applies again (or the defaults).
349
+ - Locked out by the brute-force limiter: **Settings → 登录失败锁定** lists the locked IPs with
350
+ unlock / unlock all.
351
+ - Locked out entirely (can't log in to reach Settings): delete `<DATA_DIR>/sessions.json` to drop
352
+ the session index, or restart the server — the in-memory lockout clears on boot.
353
+
354
+ **"My sessions are gone."** The server is reading a different data directory. Run `agentus --where`
355
+ — it prints the data dir **and which rule chose it** (top of this page). The usual cause is an
356
+ existing checkout whose sessions live in `<repo>/packages/server/.data` while the install now
357
+ defaults to `~/.agentus`. Boot once with `AGENTUS_DATA` set to the directory that actually holds
358
+ them, e.g. `AGENTUS_DATA=~/.agentus agentus` or `AGENTUS_DATA=<repo>/packages/server/.data agentus`.
359
+
360
+ **The page loads but says "reconnecting" forever.** A tunnel is dropping WebSocket `Upgrade`
361
+ requests. The live stream rides on `/ws`; use a tunnel that passes WebSockets through (SakuraFrp
362
+ TCP+auto-HTTPS tunnels are verified working).
363
+
364
+ **Certificate warning on the LAN.** That is the TLS listener's self-signed cert (a bare LAN IP
365
+ cannot carry a trusted cert). Either use the plain-HTTP `:8787` port on the LAN, or install the
366
+ issuer once from `/cert.crt` (see [TLS](#tls)).