llm-api-gateway-cli 1.0.4 → 1.0.6

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/README.en.md ADDED
@@ -0,0 +1,346 @@
1
+ # LLM API Gateway CLI — English entry
2
+
3
+ > **Language:** English (this page) · [中文(权威全文)](README.md)
4
+ >
5
+ > [`README.md`](README.md) is the authoritative, always-current document (Chinese, far more detail:
6
+ > every flag, every API route, Docker publishing, troubleshooting).
7
+ > This page is the English entry point: what the tool is, how to install it, and how to run it.
8
+
9
+ A local toolbox for an **OpenAI-compatible LLM gateway** (default `http://127.0.0.1:9000`).
10
+ It gives you five ways to talk to the gateway from your own machine, plus a task mode where the
11
+ model actually reads and writes files in a directory you pick:
12
+
13
+ | Mode | Entry point | What it does | Extra deps |
14
+ | --- | --- | --- | --- |
15
+ | 1 | `gateway-openai` (`cli-openai.js`) | OpenAI-compatible chat against `/v1/chat/completions` | `openai` SDK |
16
+ | 2 | `gateway-anthropic` (`cli-anthropic.js`) | Anthropic-compatible calls against `/v1/messages` | `@anthropic-ai/sdk` |
17
+ | 3 | `gateway-claude-code` (`cli-claude-code.js`) | Pipes through a locally installed `claude` CLI | local `claude` |
18
+ | 4 + 5 | `gateway-web` (`server.js`) · `gateway-task` (`task-server.js`) | Local Web UI: chat, task mode and manual in one page | none |
19
+ | 6 | `gateway-agent` (`cli-agent.js`) | Native agent CLI with tool calls (recommended) | none |
20
+
21
+ Modes 4, 5 and 6 are plain Node.js with no additional dependencies — the two SDKs above are only
22
+ needed by modes 1 and 2. Modes 4 and 5 run on **one port** (`3100`) as one service.
23
+
24
+ **Output language.** Every command-line entry point accepts `--lang en` (or the environment
25
+ variable `LLM_GATEWAY_LANG=en`) and then prints its help, banner, errors, REPL prompts and
26
+ `config` / `setup` output in English. The default is Chinese, and the Web UI and the manual page
27
+ stay Chinese either way — this switch covers the command line only (details under
28
+ [Output language](#output-language-command-line-only) below).
29
+
30
+ ## Requirements
31
+
32
+ - **Node.js >= 18** (`node -v`). The installers check this and stop with a clear message; they never
33
+ install a runtime for you.
34
+ - A gateway you can reach, plus an API key for it. There is no bundled model — this is a client.
35
+
36
+ ## Install
37
+
38
+ Three routes, all producing the same artifact and the same commands.
39
+
40
+ ### A. One-liner script (macOS / Linux / WSL)
41
+
42
+ ```bash
43
+ # Pin a release tag (recommended: the tag never moves)
44
+ curl -fsSL https://raw.githubusercontent.com/boonya-hrgk/llm-api-gateway-cli/<version>/scripts/install.sh | bash
45
+
46
+ # Or track the newest script from main (script and release contract may drift)
47
+ curl -fsSL https://raw.githubusercontent.com/boonya-hrgk/llm-api-gateway-cli/main/scripts/install.sh | bash
48
+
49
+ # Passing arguments through a pipe needs -s --
50
+ curl -fsSL https://raw.githubusercontent.com/boonya-hrgk/llm-api-gateway-cli/<version>/scripts/install.sh \
51
+ | bash -s -- --version v1.0.0 --prefix "$HOME/.llm-api-gateway"
52
+ ```
53
+
54
+ ### B. One-liner script (Windows PowerShell 5.1 / 7)
55
+
56
+ ```powershell
57
+ irm https://raw.githubusercontent.com/boonya-hrgk/llm-api-gateway-cli/<version>/scripts/install.ps1 | iex
58
+
59
+ # Passing arguments needs the scriptblock form
60
+ & ([scriptblock]::Create((irm https://raw.githubusercontent.com/boonya-hrgk/llm-api-gateway-cli/<version>/scripts/install.ps1))) -Version v1.0.0 -DryRun
61
+ ```
62
+
63
+ ### C. npm
64
+
65
+ ```bash
66
+ npm install -g llm-api-gateway-cli
67
+ ```
68
+
69
+ The installers put everything under `~/.llm-api-gateway` (Windows: `$HOME\.llm-api-gateway`), add the
70
+ bin directory to your `PATH`, are idempotent, and need no admin rights. Useful flags:
71
+ `--prefix`, `--no-modify-path`, `--dry-run`, `--force`, `--base` (mirror).
72
+
73
+ ### Already have the sources
74
+
75
+ ```bash
76
+ git clone https://github.com/boonya-hrgk/llm-api-gateway-cli
77
+ cd llm-api-gateway-cli
78
+ npm install
79
+ npm link # symlink, so edits take effect without reinstalling
80
+ ```
81
+
82
+ You can also skip installing entirely inside the repo: `node cli-agent.js`, `node server.js`.
83
+
84
+ ## First-time setup
85
+
86
+ You do **not** need to hand-write `.env` any more. Either run the guided setup:
87
+
88
+ ```bash
89
+ gateway-agent setup # asks for gateway URL, then the key (hidden input)
90
+ gateway-agent setup --base-url http://127.0.0.1:9000 --key sk-xxx # non-interactive
91
+ ```
92
+
93
+ or set the key from the Web UI (`Settings -> gateway key`) after starting the service — the server
94
+ **starts without a key on purpose**, so the page is reachable and you have somewhere to paste it.
95
+
96
+ The key is written to `~/.llm-api-gateway-cli/credentials.json` (owner-readable only) and is
97
+ **never stored in `config.json`**. Sources are resolved in this order, first hit wins:
98
+
99
+ 1. `--key sk-xxx` on the command line
100
+ 2. environment variables: `GATEWAY_KEY`, `SK`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`
101
+ 3. `credentials.json` (what the UI / `config set key` writes)
102
+ 4. `.env` in the install directory, then in the current working directory
103
+
104
+ When the key comes from the environment, `credentials.json` is ignored — the startup banner always
105
+ prints which source is in effect, so you never have to guess.
106
+
107
+ ```bash
108
+ gateway-agent config set baseUrl http://127.0.0.1:9000
109
+ gateway-agent config set model qwen3:8b
110
+ gateway-agent config list # every value and where it comes from
111
+ ```
112
+
113
+ ## Quick start
114
+
115
+ ### Command line
116
+
117
+ ```bash
118
+ gateway-agent -p "explain this stack trace" # one-shot
119
+ gateway-agent -i # interactive REPL
120
+ gateway-agent # bare command: asks chat/task, CLI/web
121
+ ```
122
+
123
+ Bare `llm-api-gateway-cli` (or `gateway-agent`) with no arguments asks two questions — what to do
124
+ (chat / task) and where to run it (terminal / Web UI) — instead of silently picking one. Passing
125
+ `-p`, `-i`, or piping stdin skips the menu.
126
+
127
+ Slash commands are available inside the REPL (`/help` lists them; the CLI REPL has 10 of them).
128
+
129
+ ### Managing MCP servers from the CLI (`/mcp ...`)
130
+
131
+ `/mcp` used to only print the tool-list status. It now also manages the binding and registers your
132
+ own HTTP MCP server (BYO-A) with the same `sk-` key you already use:
133
+
134
+ | Subcommand | What it does |
135
+ | --- | --- |
136
+ | `/mcp` | Overview: what this key has **bound**, what you may **self-add**, what you **registered**, plus the mode and tool count |
137
+ | `/mcp refresh` | Force-reload the tool list (unchanged behaviour) |
138
+ | `/mcp bind +3 -5` | Add `+id` / remove `-id` (the id must be in the gateway's `selectable_ids`; anything else is rejected locally with **no request sent**) |
139
+ | `/mcp bind =2,7` | Set the whole binding (ordered) |
140
+ | `/mcp mode off\|inject\|loop` | Change the mode only — the request body carries `mode` alone, so the binding is left untouched |
141
+ | `/mcp add <name> <URL> [--header "k: v"]...` | Register your own HTTP MCP server: the gateway probes it once and binds it to this key |
142
+ | `/mcp rm <name\|id>` | Delete one of **your own** registrations (asks first; in a non-interactive shell it refuses and asks for the argument) |
143
+
144
+ Three things worth knowing:
145
+
146
+ - **The gateway decides, the CLI never guesses.** Whether self-registration is open comes from
147
+ `self_server.allowed` in `GET /v1/mcp/binding`. While it is closed, `/mcp add` only tells you how to
148
+ open it (set `MCP_ALLOW_SELF_SERVERS=true` in the gateway's `.env` and restart the gateway) and does
149
+ **not** send the request it knows would 403.
150
+ - **Credentials are only ever shown masked.** Query parameters such as `?key=` / `?ak=` and the values
151
+ of `--header` go to the gateway in plaintext but are never printed: every output path (including the
152
+ `GATEWAY_MCP_DEBUG=1` diagnostics and the URL a gateway error echoes back) goes through one masking
153
+ implementation (`maskUrl` / `maskSecrets` in `lib/mcpadmin.js`). Masking is by parameter *name*, so
154
+ do not hide a credential behind a custom name — `?mykey=` is not on the list and shows up as-is.
155
+ - **Every change reloads the list.** `bind` / `mode` / `add` / `rm` refresh the tool list and print the
156
+ new count right away, so "changed it but the list looks stale" cannot happen. "Registered, but the
157
+ probe failed" (row created, server disabled, reason in `probe_error`) is a different outcome from
158
+ "registration failed", and you are told which one you got.
159
+
160
+ > BYO-A only (public http/https addresses): no CLI-side direct connections and no task-page (browser)
161
+ > entry point — those APIs need the `sk-` key while the page only holds an admin JWT, which is a
162
+ > separate decision with its own security questions.
163
+
164
+ #### The same thing in the browser: `/mcp` (the key never reaches the browser)
165
+
166
+ If you would rather not use the terminal, switch to the "MCP" tab in the shell's tab bar (same page,
167
+ no new browser tab; Alt+5), or open **http://127.0.0.1:3100/mcp** directly (the standalone task page
168
+ links there from its sidebar too). It covers the same ground as the CLI
169
+ subcommands: see the servers this key has **bound**, the ones you may **self-add** and the ones you
170
+ **registered**, change the binding by ticking boxes, change the mode (mode only — the binding is left
171
+ untouched), register your own server with a name and a URL, and delete your own registrations after a
172
+ confirmation.
173
+
174
+ - **The scope is your `sk-` key, not the whole gateway.**
175
+ - **The key never reaches the browser:** the page only calls the local `/api/mcp*` endpoints, and the
176
+ server-side hub forwards them to the gateway with the key it already holds (the same pattern as the
177
+ chat page's `/api/chat`). Neither the page source nor the network requests contain the key.
178
+ - **Gated like the task records:** these endpoints are served only while the service listens on
179
+ loopback (`--allow-remote-fs` opens them up), and a missing key produces a readable message instead
180
+ of a 500.
181
+ - **It does not replace the gateway's admin page:** global servers, auditing and statistics stay at
182
+ `http://localhost:9000/#/mcp` (that menu item requires an admin login). Whether self-registration is
183
+ allowed at all remains a deployment-time `.env` switch on the gateway — deliberately not a button
184
+ here.
185
+
186
+ API: `GET /api/mcp` (binding plus tool list), `PATCH /api/mcp/binding` (`{serverIds}` or `{mode}`),
187
+ `POST /api/mcp/servers`, `DELETE /api/mcp/servers/<id>` — pinned by `tests/mcp-ui.test.mjs`.
188
+
189
+ Interactive sessions are persisted: each run writes `~/.llm-api-gateway-cli/sessions/<uuid>.json`
190
+ (sliding 15 days, at most 50 files, 1 MB each), and `--continue` / `--resume <id>` — or `/resume`
191
+ inside the REPL — brings one back with its context and any write that was still waiting for
192
+ approval. These sessions are **separate from the Web UI's tasks**: the Web sidebar lists `tasks/`
193
+ only, so a conversation you had in the terminal never shows up there, and a Web task is never
194
+ offered by `--resume`. Resuming also restores the working directory recorded in the session, so you
195
+ can start it from anywhere (`-C` overrides it).
196
+
197
+ **Reading them in the browser:** the server serves a read-only viewer at
198
+ `http://127.0.0.1:3100/sessions`, or just switch to the sessions tab in the shell's tab bar (same
199
+ page, no new browser tab; Alt+4). The standalone task page links there from its sidebar too.
200
+ It lists the sessions
201
+ in `sessions/` with their working directory, model, message count, token usage and last activity,
202
+ and replays a full transcript — tool calls and reasoning included. It is read-only: no input box,
203
+ and to actually continue a conversation you go back to the terminal. Data comes from
204
+ `GET /api/cli-sessions` and `GET /api/cli-sessions/<id>`, which are gated like the task records:
205
+ loopback only unless you pass `--allow-remote-fs`.
206
+
207
+ ### Output language (command line only)
208
+
209
+ The default output language is Chinese. Add `--lang en`, or set `LLM_GATEWAY_LANG=en`; the flag
210
+ wins when both are given.
211
+
212
+ ```bash
213
+ gateway-agent --help --lang en # English help
214
+ gateway-agent -i --lang en # English REPL: prompts, banner, approval gate
215
+ LLM_GATEWAY_LANG=en gateway-agent -p "…" # same thing through the environment
216
+ node server.js --help --lang en # the server entry accepts it too (help output only)
217
+ ```
218
+
219
+ Recognised values are `zh` / `zh-CN` / `cn` and `en` / `en-US` / `english`. An unrecognised value
220
+ is ignored rather than fatal, and a missing English string falls back to Chinese, so the output is
221
+ never half translated.
222
+
223
+ What switches: `--help` of all five entry points, the startup banner, errors and the missing-key
224
+ hint, the REPL prompts (`/help` and its command table included), the write-approval gate, and the
225
+ `config` / `setup` subcommand output.
226
+
227
+ What does **not** switch (Chinese by design): the **Web UI** (chat, task and settings panels), the
228
+ **manual page** at `/manual` together with the command tables it renders, the `/api/commands`
229
+ payload served to the browser, and the system prompts / tool descriptions sent to the model —
230
+ that last one is model behaviour, a separate decision from interface language.
231
+
232
+ The implementation is `lib/i18n.js` (about 80 lines, no dependencies): language is a process-level
233
+ switch set once by each entry point, and `pick(zh, en)` returns Chinese whenever English is missing.
234
+
235
+ ### Web UI — one page, three tabs
236
+
237
+ ```bash
238
+ gateway-web # or: node server.js
239
+ ```
240
+
241
+ | Page | URL | Notes |
242
+ | --- | --- | --- |
243
+ | Tab shell (recommended entry) | `http://127.0.0.1:3100/` | Chat / Task / Manual switch in one browser tab |
244
+ | Chat panel | `http://127.0.0.1:3100/chat` | Pure conversation, no file system access |
245
+ | Task panel | `http://127.0.0.1:3100/task` | Pick a directory, the model really reads/writes it |
246
+ | Manual | `http://127.0.0.1:3100/manual` | Operation manual (Chinese) generated from the live command tables |
247
+ | CLI sessions (read-only) | `http://127.0.0.1:3100/sessions` | Archive of terminal `gateway-agent -i` conversations: list + full replay, no input box |
248
+
249
+ The shell owns the single top bar (tabs, per-panel actions, theme, reload); panels are loaded
250
+ lazily on first activation and keep their state when you switch away. `Alt+1/2/3` switches tabs.
251
+ Each panel also opens standalone, which is what external systems embed.
252
+
253
+ Useful flags: `--port`, `--host`, `--store <dir>`, `--mode manual|auto|plan`, `--max-steps`,
254
+ `--allow-bash`, `--allow-remote-fs`, `--verbose`, `--lang zh|en` (`node server.js --help`).
255
+
256
+ ### Task mode in one minute
257
+
258
+ 1. Open the Task tab and pick a **working directory** — the model's file tools are confined to it.
259
+ 2. Describe the job, press `Enter`. The model lists directories, reads files and searches content.
260
+ 3. In **manual** mode every write shows an approval card first; click approve/reject (or `/approve`,
261
+ `/reject`). **auto** executes writes directly. **plan** gives the model read-only tools, writes a
262
+ plan document into `docs/` inside your working directory, and waits for you to confirm.
263
+ 4. `/stop` or the Stop button cancels the current run; other tasks keep going in the background.
264
+
265
+ Tools the model can use: `list_dir`, `read_file`, `search_files`, `glob`, `grep`, `write_file`,
266
+ `apply_patch`. `bash` exists but is **off by default** — enable it with `--allow-bash` (dangerous:
267
+ in auto mode commands are not confirmed one by one). The task page has 14 slash commands; the manual
268
+ page lists all of them side by side with the CLI.
269
+
270
+ Task data is stored on disk by the server (not in the browser): `<data dir>/tasks/<id>.json` plus a
271
+ model-side session per task. Default retention is a sliding **15 days / at most 20 tasks**.
272
+
273
+ ## Configuration and storage
274
+
275
+ | Env var | Purpose |
276
+ | --- | --- |
277
+ | `GATEWAY_BASE_URL` | Gateway base URL (default `http://127.0.0.1:9000`) |
278
+ | `GATEWAY_MODEL` | Default model (built-in default `qwen3:8b`) |
279
+ | `GATEWAY_KEY` / `SK` | Gateway API key |
280
+ | `WEB_PORT` / `TASK_WEB_PORT` | Web UI port (default `3100`) |
281
+ | `TASK_MODE` | Default approval mode for new tasks: `manual` / `auto` / `plan` |
282
+ | `TASK_MAX_STEPS` | Max model turns per task |
283
+ | `TASK_STORE_DIR` | Where task records live |
284
+ | `TASK_HISTORY_MAX_CHARS` | History budget sent to the model |
285
+ | `TASK_SESSION_MAX_BYTES` | Cap for one task's model-side session file |
286
+ | `LLM_GATEWAY_DATA_DIR` | Move the whole data root (config + credentials + tasks) |
287
+
288
+ | File | Contents |
289
+ | --- | --- |
290
+ | `~/.llm-api-gateway-cli/config.json` | Settings (never the key) |
291
+ | `~/.llm-api-gateway-cli/credentials.json` | The key, owner-readable only |
292
+ | `~/.llm-api-gateway-cli/tasks/` | Task records + per-task sessions (what the Web UI lists) |
293
+ | `~/.llm-api-gateway-cli/sessions/` | CLI REPL sessions (`--continue` / `--resume`); **not** listed in the Web UI |
294
+
295
+ Precedence everywhere is **command-line flag > environment variable > `.env` > `config.json` >
296
+ built-in default**, and every surface (CLI, REPL, Web UI) reads and writes the same files.
297
+
298
+ ## Embedding and theming
299
+
300
+ Any page can be embedded in another system, and you can hand it the host's background colour so it
301
+ does not look pasted on:
302
+
303
+ ```
304
+ http://127.0.0.1:3100/task?bg=%23f0f4ff light host background
305
+ http://127.0.0.1:3100/task?bg=%23111a2b dark host background
306
+ http://127.0.0.1:3100/task?bg=none back to the page's own palette
307
+ ```
308
+
309
+ The theme is derived from that one colour (panels, borders, text and accent all follow), and the
310
+ theme switcher hides itself because the host now decides.
311
+
312
+ ## Docker
313
+
314
+ A `Dockerfile`, `docker-compose.yml` and `docker.ps1` helper are included: one image containing the
315
+ Web UI, with your project mounted as the agent's working directory and task history on a named
316
+ volume. See the [Chinese README](README.md#docker-容器化部署) for the full walkthrough
317
+ (`.\docker.ps1 up`, publishing to ACR / Docker Hub, troubleshooting).
318
+
319
+ ## Security notes
320
+
321
+ - The API key stays on the server: the browser never receives it (the config endpoint returns a mask
322
+ and its source only).
323
+ - Directory browsing, task records and the settings API are **only served when the service is bound
324
+ to loopback**. Binding `0.0.0.0` requires the explicit `--allow-remote-fs` flag.
325
+ - The agent's sandbox is the working directory you picked: path traversal, absolute paths and
326
+ symlink escapes are rejected, and `bash` is off unless you pass `--allow-bash`.
327
+
328
+ ## Tests
329
+
330
+ ```bash
331
+ npm test # offline suite (no gateway needed)
332
+ npm run test:all # offline + tests that call a real gateway on 127.0.0.1:9000
333
+ ```
334
+
335
+ ## Where to read more
336
+
337
+ - **`/manual`** in the running Web UI — install, first-time setup, task workflow, every slash command
338
+ on all three surfaces (its command tables are generated from `lib/commands.js` and
339
+ `public/task-slash.js`, so they cannot go stale).
340
+ - [`README.md`](README.md) — the authoritative Chinese document: all flags, every API route, storage
341
+ internals, Docker publishing, FAQ.
342
+ - [`docs/`](docs/) — iteration plans and release steps (Chinese).
343
+
344
+ ## License
345
+
346
+ MIT
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # LLM API Gateway CLI 编程工具
2
2
 
3
+ > 🌐 **English entry:** [`README.en.md`](README.en.md) —— 英文入口(是什么 / 怎么装 / 怎么跑)。
4
+ > 本页是**权威全文**(中文,最全):所有参数、全部接口、存储内部细节、Docker 发布与排错。
5
+ > 🈯 **命令行英文输出**:所有 CLI 入口都认 `--lang en`(或环境变量 `LLM_GATEWAY_LANG=en`),不给就是中文;
6
+ > 覆盖范围、优先级与「哪些不变」见下文「命令行输出语言」一节,**Web 界面与手册页仍是中文**。
7
+
3
8
  验证 `llm-api-gateway`(`http://127.0.0.1:9000`)除了 Web 对话页 / SDK 之外,**CLI 方式同样可用**。多个脚本覆盖多种接入方言,其中**原生 Agent(`cli-agent.js`)是终端里干活的推荐方式** —— 自带工具循环,无需安装 Claude Code。
4
9
 
5
10
  | 脚本 | 模式 | 走网关的端点 | 依赖 |
@@ -502,6 +507,7 @@ node task-server.js --mode auto # 默认审批模式:manual / auto
502
507
  | `GET /chat` | 聊天面板(单独打开;老链接 `/index.html` 也是它) |
503
508
  | `GET /task` | 任务面板(单独打开,选目录、模型真读写) |
504
509
  | `GET /manual` | 操作手册面板(安装与启动 + 任务流程 + 三端斜杠指令对照) |
510
+ | `GET /sessions` | **命令行会话的只读存档**(终端 `gateway-agent -i` 的对话:列表 + 全文回放,不能在这里续写) |
505
511
  | `GET /api/commands` | 三端(命令行 / 本机聊天页 / 任务页)指令的机器可读清单,手册页据此生成,不手抄 |
506
512
  | `GET /api/cost?prompt=&completion=&model=` | 费用粗估(与 CLI `/cost` 同一份单价表 `lib/pricing.js`) |
507
513
  | `GET /api/config` | 网关地址、模型、工具清单、上限、密钥是否就绪(只回脱敏值 + 来源 + 密钥文件路径) |
@@ -519,12 +525,14 @@ node task-server.js --mode auto # 默认审批模式:manual / auto
519
525
  | `GET /api/tasks` | 任务列表(顺手清理过期任务,返回 `removed`) |
520
526
  | `POST /api/tasks` | 新建一条任务(id 由页面生成 UUID,服务端校验) |
521
527
  | `GET /api/tasks/<id>` | 读一条任务的完整内容 |
528
+ | `GET /api/cli-sessions` | **命令行会话**(模式六)元信息列表:`dir / ttlDays / maxSessions / sessions[]`(**不含正文**) |
529
+ | `GET /api/cli-sessions/<id>` | 读一条命令行会话的完整 `messages`(只读;id 必须是 UUID) |
522
530
  | `PUT /api/tasks/<id>` | 覆盖保存(改名、换工作目录、写入消息) |
523
531
  | `DELETE /api/tasks/<id>` | 删除一条任务(连带删掉它的会话文件) |
524
532
  | `POST /api/tasks/prune` | 立即按 15 天 / 20 条清理一次(同样连带删会话) |
525
533
 
526
534
  > `/api/config` 里的 `tools` 是**当前真正可用的工具**:`bash` 未由宿主开启时不会出现在列表里。
527
- > `/api/store`、`/api/tasks*` 与 `/api/settings` 和目录浏览一样只在本机监听时开放(开在 `0.0.0.0` 上时一律 403 —— 它们能读本机路径、也能改你的配置)。任务 id 必须是 UUID 形状,服务端按格式校验,`..%2F` 之类的穿越 id 一律 400。
535
+ > `/api/store`、`/api/tasks*`、`/api/cli-sessions*` 与 `/api/settings` 和目录浏览一样只在本机监听时开放(开在 `0.0.0.0` 上时一律 403 —— 它们能读本机路径、也能改你的配置)。任务 id 必须是 UUID 形状,服务端按格式校验,`..%2F` 之类的穿越 id 一律 400。
528
536
 
529
537
  ### 沙箱边界
530
538
 
@@ -572,6 +580,15 @@ node task-server.js --mode auto # 默认审批模式:manual / auto
572
580
  | CLI 会话(模式六的 `--continue`) | `~/.llm-api-gateway-cli/sessions/` |
573
581
  | 配置文件 | `~/.llm-api-gateway-cli/config.json` |
574
582
 
583
+ > **两份「会话」各管各的,互不相通**:网页任务页的侧边栏只列 `tasks/` 里的**任务**(模式五),
584
+ > 命令行 REPL 的对话在另一个目录 `sessions/` 里(模式六),网页侧**没有任何接口读它** ——
585
+ > 所以**终端里聊的内容不会出现在网页任务列表里**,反过来网页任务也不在 `--resume` 的候选里
586
+ > (两者的记录字段本来就不同:任务有标题、更新时间、回复索引,会话只有消息与用量)。
587
+ > 要接着终端那条聊:`gateway-agent -i --continue`(最近一条)或 `--resume <id>`,REPL 里 `/resume` 列最近几条。
588
+ > 想在网页里**看**它:服务起着时打开 **http://127.0.0.1:3100/sessions**(任务页顶栏也有「命令行」入口)——
589
+ > 那一页是**只读回放**(列表 + 全文 + 用量),没有输入框,改不了任何东西;接口是 `GET /api/cli-sessions[ /<id>]`,
590
+ > 与任务记录同一道本机闸门(服务没绑在本机地址且没加 `--allow-remote-fs` 时回 403)。
591
+
575
592
  > 老版本用的是平台数据目录(`%LOCALAPPDATA%` / `~/Library/Application Support` / `$XDG_DATA_HOME`),
576
593
  > 早期版本还会退到**脚本目录下的 `.tasks/`** —— 同一台机器上数据可能在两三个地方,而且会把 `.tasks`
577
594
  > 写进项目目录。现在统一到主目录一处:**启动时如果发现旧位置有数据,会自动复制到新位置**,
@@ -667,6 +684,7 @@ node cli-agent.js -i --resume <会话id> # 切到指定
667
684
  | `--yes` | 自动批准所有**文件写入**(**危险**:模型可直接改文件,慎用)。MCP 调用不需要它 |
668
685
  | `--verbose` | 打印工具结果正文(默认只显示一行摘要) |
669
686
  | `--no-color` | 关闭彩色输出(也遵循 `NO_COLOR`) |
687
+ | `--lang <zh\|en>` | 输出语言(默认 `zh`,也可用 `LLM_GATEWAY_LANG`)—— 帮助 / 横幅 / 报错 / REPL 提示都跟着变,详见「命令行输出语言」 |
670
688
 
671
689
  工具集与模式五完全相同(`list_dir` / `read_file` / `search_files` / `glob` / `grep` / `apply_patch` / `write_file` / `bash`),其中 `bash` **默认关闭**、加 `--allow-bash` 才可用。单个任务默认最多 100 轮模型调用(`--max-steps` 可调)。**写入闸门在终端里,而且只管本机文件**:模型每次写入(`write_file` / `apply_patch`,以及开启后的 `bash`)都会先打印预览框(路径、新建/覆盖、行数与字节数;覆盖已有文件时还打印行级 `- / +` diff),再问你 `y/N`。非交互环境(如管道)**默认拒绝文件写入**,确需自动批准请显式加 `--yes` —— 这样脚本误改文件的风险最小。
672
690
 
@@ -678,7 +696,7 @@ node cli-agent.js -i --resume <会话id> # 切到指定
678
696
  > 「计划模式不给 MCP 工具」这条边界**没有变**(它只做本机只读调研):它是「给不给模型」的问题,
679
697
  > 和「要不要人工批准」是两件事,在代码里也是两个谓词(`isWriteTool` / `isFileWriteTool`)。
680
698
 
681
- **会话落盘**:交互会话默认写到用户主目录下的 `.llm-api-gateway-cli/sessions/`(与任务存储同一个根;主目录不可写时才退到旧位置、再到系统临时目录),滑动保留 15 天、最多 50 条、单文件 1MB 上限。`--continue` 续最近一条、`--resume <id>` 切指定一条;恢复时会连同「待批准的写入」一起还原,进程退出前没批完的写入,回来还能接着批。用 `--no-session` 可完全关掉落盘。
699
+ **会话落盘**:交互会话默认写到用户主目录下的 `.llm-api-gateway-cli/sessions/`(与任务存储同一个根;主目录不可写时才退到旧位置、再到系统临时目录),滑动保留 15 天、最多 50 条、单文件 1MB 上限。`--continue` 续最近一条、`--resume <id>` 切指定一条;恢复时会连同「待批准的写入」一起还原,进程退出前没批完的写入,回来还能接着批。用 `--no-session` 可完全关掉落盘。**这份会话不会出现在网页任务页的列表里**(网页列的是 `tasks/` 里的任务,见上文「存储位置」那条说明)—— 想在网页里看它,打开 `/sessions`(**只读**回放,见「命令行会话(只读)」);要接着聊就在终端 `--continue` / `--resume`;恢复时工作目录沿用会话里记下的那个(`-C` 才覆盖),所以在哪个目录敲都行。
682
700
 
683
701
  **项目记忆**:启动时若工作目录下有 `AGENTS.md` 或 `CLAUDE.md`,会读进来注入 system prompt(上限 4KB,超出截断);`--no-memory` 可关闭。
684
702
 
@@ -686,6 +704,41 @@ node cli-agent.js -i --resume <会话id> # 切到指定
686
704
 
687
705
  > 命令表(`lib/commands.js`)与网关 Web 对话页的斜杠指令**共用一套元数据**(`capability` 能不能跑 / `surfaces` 在哪一端有意义 / `args`);同名命令两端语义一致,这是被两侧的跨端契约断言钉住的。网关 Web 的「恢复默认设置」因此把 `/reset` 让名给 CLI 的「重开会话」、自己改叫 `/defaults`(口径见网关仓库 `docs/SLASH-CLI-ALIGNMENT-20260916.md`)。
688
706
 
707
+ #### `/mcp`:看绑定、改绑与「登记自己的 MCP 服务器」
708
+
709
+ 以前在 CLI 里只能**读** MCP 工具(`/mcp` 打一行清单状态),要改绑定得去管理员后台。现在 `/mcp` 多了一组子命令,用你手里这把 `sk-` 密钥就能自助办:
710
+
711
+ | 子命令 | 作用 |
712
+ | --- | --- |
713
+ | `/mcp` | 总览:本密钥**已绑**什么、**可自助加**哪些、**我自建**了哪些(各带 id 与名字)+ 当前模式与工具数 |
714
+ | `/mcp refresh` | 强制重拉工具清单(既有行为,不变) |
715
+ | `/mcp bind +3 -5` | 改绑:`+id` 加上、`-id` 去掉(id 必须出现在网关给的 `selectable_ids` 里,越权 id **本地就拒绝、不发请求**) |
716
+ | `/mcp bind =2,7` | 直接设定整份绑定(有序) |
717
+ | `/mcp mode off\|inject\|loop` | 只改模式,**不动绑定**(下发体里只有 `mode`) |
718
+ | `/mcp add <名字> <URL> [--header "k: v"]...` | 登记自己的 HTTP MCP 服务器(BYO-A):网关探测一次并自动绑到本密钥 |
719
+ | `/mcp rm <名字\|id>` | 删掉**自己登记**的(先确认;非交互环境不自动删,要求写成参数) |
720
+
721
+ 三点要知道的:
722
+
723
+ - **判据来自网关,CLI 不猜**:能不能自助登记由 `GET /v1/mcp/binding` 的 `self_server.allowed` 决定。没开放时 `/mcp add` 只告诉你开启办法(部署方在网关 `.env` 设 `MCP_ALLOW_SELF_SERVERS=true` 并**重启网关**),**不发那次注定 403 的请求**;
724
+ - **凭据只显示掩码**:地址里的 `?key=` / `?ak=` 这类查询参数与 `--header` 的值**明文只发给网关**,CLI 的所有输出(含 `GATEWAY_MCP_DEBUG=1` 的排障行、以及网关错误里回显的那份 URL)一律走同一把掩码(`lib/mcpadmin.js` 的 `maskUrl` / `maskSecrets`)。所以**别把凭据放在自定义参数名里**(掩码是按名字认的):`?mykey=` 不在名单里,会原样显示;
725
+ - **改完立刻重拉清单**:`bind` / `mode` / `add` / `rm` 成功后马上刷新并打印新的工具数 —— 否则「改成功了但清单还是旧的」看起来就像没生效。登记失败与「登记上了但探测连不通」(置为停用 + `probe_error`)是**两种结局**,分开告诉你。
726
+
727
+ > 只做 **BYO-A**(登记 http/https 公网地址);不做 CLI 直连回传,也不做任务页(浏览器)入口 —— 那些接口要的是 `sk-` 密钥,而页面手里只有管理员 JWT,属于另一件带安全决策的事。
728
+ > 五个子命令的失败语义、掩码形状、403 引导与「管理操作不改运行面」都钉在 `tests/mcpadmin.test.mjs`(56 条,含 `GATEWAY_MCP_DEBUG=1` 那条打印路径与「管理面连接跟着 `ctx.cfg` 走」)。
729
+
730
+ #### 同一件事的网页入口:`/mcp`(密钥不进浏览器)
731
+
732
+ 不想敲终端时,起服务后点首页 Tab 栏的「**MCP**」(同页切换,不开新标签;Alt+5),或直接开 **http://127.0.0.1:3100/mcp**(单独打开任务页时它左侧栏也有入口):
733
+
734
+ - **范围就是你这把 `sk-` 密钥**(不是全局):看已绑定 / 可自助加 / 我自建的,勾选改绑、选模式(只改模式不动绑定)、填名字 + URL 登记自己的服务器、删掉自己登记的(删前确认);
735
+ - **密钥不下发**:页面只打本机的 `/api/mcp*`,由 hub 用服务端保存的密钥代发给网关(与聊天页 `/api/chat` 同一套姿势)——浏览器源码、网络请求里都没有 `sk-`;
736
+ - **闸门与任务记录同级**:只在本机监听时开放(服务绑到 `0.0.0.0` 要显式 `--allow-remote-fs`);没配密钥时给可读提示而不是 500;
737
+ - **它替代不了网关的管理员页面**:全局服务器、审计与统计仍在网关 `http://localhost:9000/#/mcp`(管理员登录才看得见那一项)。**「开不开自助登记」永远是网关 `.env` 的部署期开关**,不会变成这里的一个按钮。
738
+
739
+ > 接口:`GET /api/mcp`(绑定 + 工具清单)、`PATCH /api/mcp/binding`(`{serverIds}` 或 `{mode}`)、`POST /api/mcp/servers`、`DELETE /api/mcp/servers/<id>`;契约钉在 `tests/mcp-ui.test.mjs`。
740
+
741
+
689
742
  任务页(`/task`)另有一组**只影响这个页面、不发模型**的斜杠指令(E 组,与上面两端同一套元数据口径,共 14 条):
690
743
 
691
744
  | 组 | 指令 | 作用 |
@@ -721,6 +774,17 @@ node cli-agent.js -i --resume <会话id> # 切到指定
721
774
 
722
775
  > 本模式与模式三是**两种不同的接入姿态**:模式三让 Claude Code 这个外部 Agent 走网关(验证「网关能当 Anthropic 后端」),模式六是**网关自带 Agent**(验证「不装 Claude Code 也能在终端干活」)。两者共存,按需选用。
723
776
 
777
+ ### 命令行会话(只读):`/sessions`
778
+
779
+ 终端里 `gateway-agent -i` 聊过的东西,可以在网页里翻出来看:**http://127.0.0.1:3100/sessions**,或者直接切首页 Tab 栏的「**命令行**」(同页切换,不开新标签;Alt+4)。单独打开任务页时,它左侧栏也有一个入口。
780
+
781
+ - **它是存档,不是任务**:列的是 `<数据根>/sessions/`(模式六),与网页任务页的 `tasks/` 是两套存储,两边不会互相出现(原因见上文「存储位置」那条说明);
782
+ - **只读**:页面上没有输入框、没有写入接口(只有 `GET /api/cli-sessions` 与 `GET /api/cli-sessions/<id>`),要接着聊得回终端跑页面上给出的 `gateway-agent -i --resume <id>`(一键复制);
783
+ - **能看到什么**:工作目录、模型、消息条数与 token 用量、最后活动时间;点开回放全文,含工具调用与参数、思考过程、以及「这条会话还有待批准的写入」这类挂起态提示;
784
+ - **闸门与任务记录同级**:只在本机监听时开放,服务绑到 `0.0.0.0` 要显式 `--allow-remote-fs`;会话 id 必须是 UUID(防目录穿越),列表里那条滑动 15 天 / 最多 50 条的清理口径与 CLI 完全一致。
785
+
786
+ > 写这一页的直接原因:用户在终端聊完,去网页任务列表里找,找不到(2026-09-22 的真实反馈)。存储布局本身没变,只是补了一个**看**的入口。
787
+
724
788
  ### 与网关任务模式共用的内核
725
789
 
726
790
  模式五(Web)与模式六(CLI)共用同一套实现,避免两处各写一遍后行为漂移:
@@ -740,6 +804,7 @@ node cli-agent.js -i --resume <会话id> # 切到指定
740
804
  - `lib/sessionstore.js`:模式六的会话落盘(`--continue` / `--resume`,TTL 15 天 / 50 条 / 单文件 1MB);
741
805
  - `lib/jsonstore.js`:共享的原子写入(tmp → rename,Windows 上 EPERM/EACCES/EBUSY 重试 5 次),`runstore` 与 `sessionstore` 都引用它;
742
806
  - `lib/config.js`:四个 CLI 共用的配置解析(`--flag > env > .env` 收在 `resolveConfig` 一处)+ 统一的失败出口 `fail(e, { exit })`;
807
+ - `lib/i18n.js`:**命令行输出语言的唯一实现** —— `--lang` > `LLM_GATEWAY_LANG` > `zh` 的解析(`resolveLang`)、进程级开关(`setLang` / `getLang`)、双语取词 `pick(中文, English)`(缺英文回落中文);**只管命令行**,Web 界面与手册页不读它;
743
808
  - `lib/commands.js`:模式六的斜杠命令表(`COMMANDS` / `parseSlash` / `helpText`)+ 审批预览用的极简行级 `diffLines`;
744
809
  - `public/task-slash.js`:任务页的斜杠指令表(14 条)+ `commandRows()`(与 `lib/commands.js` 同字段,供 `/api/commands` 与手册页共用);`public/manual.js` 把接口数据渲染成手册页的对照表 —— 页面里没有第二份手抄清单;
745
810
  - `public/models.js`:设置面板「模型」下拉的唯一实现(两页共用)—— 选项只来自 `/api/models`,并处理「当前值不在列表里」「一个都没拉到」这两种不能丢配置的情况;`public/render.js` 同理是两页共用的渲染实现;
@@ -764,10 +829,13 @@ npm run test:all # 先离线再联网
764
829
  | `tests/config.test.mjs` | 配置解析优先级(`--flag > env > .env > 默认值`)、`--` 透传、非法数字不塞 NaN、`fail(e, { exit:false })` 不杀 REPL |
765
830
  | `tests/sessionstore.test.mjs` | 会话落盘:目录选择、脏文件与 TTL、体积裁剪、`isSafeId` 防穿越、与 `runTurn` 的 `persist` 回调打通、挂起态恢复与续批 |
766
831
  | `tests/commands.test.mjs` | 斜杠命令表逐项(含 `/model` / `/cost` / `/compact` / `/resume`)、`diffLines` 行级 diff、定价表 |
832
+ | `tests/i18n.test.mjs` | **命令行输出语言**(`--lang en` / `LLM_GATEWAY_LANG`):机制(默认 zh、flag 压过环境变量、认不出的值退回默认、英文缺失回落中文)、五个入口的接线与 `--lang` 参数表、命令表的双语与 `commandRows()` 默认仍中文、缺密钥 / 启动器 / MCP 状态行的英文,以及**真起子进程**跑五个入口的 `--help`(英文输出里不许有中文或全角字符);最后钉住 README(中 / 英)与手册页确实写了这个开关 |
767
833
  | `tests/model-select.test.mjs` | 「模型」只可选:默认项在首位、选项只来自接口、**当前值不在列表里时补项并保持选中(不悄悄换成别的模型)**、空列表只剩「默认 + 当前值」、重复模型名去重、`ensure()` 补项(对照真实 `<select>` 赋不存在的值会变空)、`match()` 大小写;以及真服务一侧:`GET /api/models` 拉不到上游时**也回 `default`**、`/models.js` 可取、两个页面都是 `<select>` 且没有 datalist 退路 |
768
834
  | `tests/task-slash.test.mjs` | 任务页指令表(14 条):元数据与自检、解析口径(别名 / 大小写 / 全角斜杠 / `//` 转义 / 未知指令不发给模型)、**空前缀返回全部候选**(敲 `/` 就该看见全貌)、逐条行为、**`/model` 只认列表里的名字**(列表外被拒并列出可选项)、`commandRows()` 字段与 CLI 对齐、fs 闸门 |
769
835
  | `tests/manual.test.mjs` | 操作手册页:`/manual` 与静态资源可访问、正文覆盖操作流程要点、**安装与启动一节(三条路线 + 首次配置 + 启动自检)与 `package.json` / 两份安装脚本 / `lib/common.js`(密钥别名、`.env` 查找顺序、默认网关地址)/ `lib/settings.js`(config.json 拒收密钥)/ `lib/secrets.js`(密钥文件名与落点)交叉一致,且「Web 服务没密钥也能起」与「命令行 agent 缺密钥即退」两半都写明**、`GET /api/commands` 一条不多一条不少地覆盖两张表、`GET /api/cost` 与 `lib/pricing.js` 逐字同口径,以及**「表里每一条都出现在手册里」**的回归闸门(含渲染后的文本断言) |
770
836
  | `tests/mcp-batch.test.mjs` | **MCP ×「一批工具调用」**:模拟网关**真的**执行「带 tool_calls 的 assistant 消息必须被逐条回应」这条协议约束(破了就回 400,与真实网关一致),据此压两个实测踩到的问题——一批里排在被批准调用**后面**的调用原先既不执行也不回应、恢复时又只补一个 ⇒ 下一轮请求 400 打死整条任务;以及 MCP 原先被当「写入类」逐个要批准 ⇒ 非交互 `-p` 下所有 MCP 调用全被拒。同时守住反向边界:本机写入仍然必须批准、计划模式仍然拿不到 MCP 工具 |
837
+ | `tests/mcpadmin.test.mjs` | **MCP 管理面**(`/mcp bind\|mode\|add\|rm`):与清单面相反的性质——管理面失败**必须吵**(原因 + 怎么修)、清单面失败要静默;掩码(查询参数与 `--header` 的值在 stdout/stderr/`GATEWAY_MCP_DEBUG` 里都只有掩码,含网关错误回显的那份 URL)、越权 id 本地就拒且不发请求、`mode` 的 body 不带 `server_ids`、`self_server.allowed=false` 时 `/mcp add` 一次请求都不发、`rm` 先确认(非 TTY 不删)、老网关 502 → 「可能没重启或版本较旧」,以及「管理操作不改 `mcpHeadersForChat()` 的双重执行保护」这条不变量 |
838
+ | `tests/mcp-ui.test.mjs` | **网页 MCP 面板**(`/mcp` + `/api/mcp*`):真 hub + 假网关跑完整链路 —— 列表形状、`mode` 的请求体不带 `server_ids`、非法值与越权 400、登记时 URL 原文发给网关但**回包只有掩码**、探测失败 ≠ 登记失败、删除、403 闸门、没配密钥 409 而不是 500,以及**响应与页面源码里都没有密钥**(只在服务端);最后把 `public/mcp.js` 放进 DOM 垫片真跑一遍(渲染 + 点「保存模式」发出 `{mode}`) |
771
839
  | `tests/modes.test.mjs` | 三种审批模式:计划模式拿不到写入工具、硬写也被挡下且不落盘、自动模式直接执行并标 `auto`、自动模式仍守沙箱、非计划模式不误标 `planReady`、系统提示词按模式分流;以及挂起态落盘:TTL 清理、脏文件容错、id 防穿越、**换模块实例模拟重启后仍能取回待批准** |
772
840
  | `tests/plandoc.test.mjs` | **计划落盘**:概要清洗与日期命名(`docs/YYYYMMDD-<概要>.md`)、`docs/` 不存在时自动创建、同名同内容幂等、同名异内容**只追加「更新」不覆盖**、概要里的 `../` 出不去工作目录、系统提示词按「有没有已落盘计划」分流;以及端到端:真起 hub 服务 + 模拟上游跑一轮计划模式,确认 SSE 发过 `plan_saved`、磁盘上真有那份计划、下一轮自动模式的 system 里带着它的路径、会话文件里记着 `planFile` |
773
841
  | `tests/dom.test.mjs` | JS 引用的 DOM id 是否都在 HTML 中、`dom.xxx` 引用的 key 是否都定义过、服务端每种事件前端是否都处理、CSS 变量与 class 是否都有定义、两个页面的侧边栏是否同构、**两个页面的「模型」都是 `<select>` 且没有 datalist 退路**;以及**切任务不打断**的源码契约(`switchTask` 里不许再出现 `abort`、必须走缓存、落盘按任务走、停止只停当前那条) |
@@ -785,6 +853,38 @@ npm run test:all # 先离线再联网
785
853
  | `tests/secrets.test.mjs` | 免 `.env` 的密钥配置:密钥文件路径(跟数据根走 / `LLM_GATEWAY_SECRET_FILE` 覆盖)、读写往返与覆盖、POSIX `0600` 与「权限被放松」告警、坏文件降级、优先级(`--key`/env > 文件)、`keyRow` 只回掩码、`config set/get/list/unset/path key` 的落点,以及**没有密钥也能起服务** + 接口回 409 `needsKey` + 界面 PUT 一次即生效/手工改文件刷新即生效 |
786
854
  | `tests/config-source.test.mjs` | **配置来源与首次使用引导**:`loadDotEnv` 记下「哪个变量由哪个 `.env` 提供」且真环境变量不算、来源文案四层(内置默认 / 启动参数 / 环境变量 + `.env` 路径 / 配置文件路径)、`summarizeConfigSources` 的「全默认 + 无 config.json = 全新机器」判定、密钥来源说到具体别名、**四个 CLI 也认 `credentials.json`**(本轮修掉的缺陷),以及 CLI/hub 横幅必须带来源、缺密钥时仍退 1 的源码契约 |
787
855
 
856
+ ## 命令行输出语言(`--lang en`)
857
+
858
+ 命令行侧可以切成英文,**默认仍然是中文**(不给语言时与加这个开关之前逐字一致)。两种给法等价,`--lang` 优先:
859
+
860
+ ```bash
861
+ node cli-agent.js --help --lang en # 英文帮助
862
+ gateway-agent -i --lang en # 英文 REPL(提示语、写入批准闸门都变英文)
863
+ LLM_GATEWAY_LANG=en gateway-agent -p "…" # 环境变量写法,与上面等价
864
+ # Windows PowerShell:$env:LLM_GATEWAY_LANG='en'; gateway-agent -i
865
+ node server.js --help --lang en # 起服务的方式也认(只影响这一行帮助)
866
+ ```
867
+
868
+ | 给法 | 优先级 | 说明 |
869
+ | --- | --- | --- |
870
+ | `--lang en` / `--lang zh` | 最高 | `zh-CN`、`en-US`、`english`、`cn` 这类写法都认 |
871
+ | 环境变量 `LLM_GATEWAY_LANG=en` | 次之 | 认不出的值**不报错**,直接退回默认中文 —— 语言不该拦住任何一次运行 |
872
+ | 都不给 | 默认 | `zh` |
873
+
874
+ **覆盖哪些输出**:
875
+
876
+ | 会跟着变 | 不会变(始终中文) |
877
+ | --- | --- |
878
+ | 四个 CLI 入口与 `server.js` 的 `--help` | **Web 界面**:聊天页 / 任务页 / 设置面板 |
879
+ | 启动横幅(网关、模型、密钥来源那几行) | **操作手册页 `/manual`**(包括它的指令表格) |
880
+ | 各种报错、缺密钥提示 | `GET /api/commands` 下发给网页的指令清单 |
881
+ | REPL 提示(`你>`、退出语、`/help` 的指令表) | 发给模型的系统提示词与工具描述(那是**模型行为语言**,不是界面语言) |
882
+ | `config list/get/set/unset/path` 与 `setup` 的输出 | `sendJson` 下发的接口文案(Web 与集成方的契约) |
883
+
884
+ **机制与边界**(`lib/i18n.js`,约 80 行,零依赖):语言是**进程级**开关,只在各入口脚本里 `setLang(resolveLang(args, env))` 一次,其余模块只管 `pick(中文, English)`;**缺英文时自动回落中文**,所以不会出现半中半英或 `undefined`。手册页的表格之所以不受 `server.js --lang en` 影响,是因为 `commandRows()` 的默认语言就是 `zh`,只有显式要英文时才取 `en`。
885
+
886
+ **验证**:`node tests/i18n.test.mjs` —— 机制(15)· 入口接线(14)· 命令表(9)· 其它文案(8)· 真子进程端到端(12)· 文档同步(6),共 **64 条**;离线全量 `npm test` 里也带它。
887
+
788
888
  ## 配置:住在磁盘上,CLI / Web / REPL 同一套
789
889
 
790
890
  **优先级一句话**:`--flag` > 环境变量 > `config.json` > 内置默认;密钥是唯一的例外 —— `--key` / 环境变量 / `.env` > `credentials.json`,而它**永远不进 `config.json`**。
@@ -1149,4 +1249,6 @@ docker run --rm -v llm-api-gateway-data:/data -v "${PWD}:/backup" alpine tar czf
1149
1249
  - **模型名**:按密钥绑定的上游选择 —— Ollama 常用 `qwen3:8b` / `qwen3:8b-nothink`;DeepSeek 常用 `deepseek-v4-pro` / `deepseek-v4-flash`(详见网关 `USEAGE.md`)。
1150
1250
  - **模式六只出思维链没有正文**:思考模型会先把 token 花在 `reasoning_content` 上,`--max-tokens` 给小了就会如此。调大 `--max-tokens` 再试。
1151
1251
  - **想接着上次的会话继续**:`node cli-agent.js -i --continue`(或 `--resume <id>`)。会话默认存在 `~/.llm-api-gateway-cli/sessions`,`--session-dir` 可换地方,`--no-session` 完全关闭落盘。
1252
+ - **刚才命令行聊的,怎么在网页任务列表里找不到**:这是设计如此,不是丢了 —— 网页侧边栏列的是 `tasks/` 里的**任务**(模式五),命令行 REPL 的对话在独立的 `sessions/` 目录(模式六),网页侧没有任何接口读它;`--lang en` 与这件事无关。文件就在 `~/.llm-api-gateway-cli/sessions/<uuid>.json`(一条一个文件),数一下 `ls ~/.llm-api-gateway-cli/sessions`(Windows:`dir %USERPROFILE%\.llm-api-gateway-cli\sessions`)能看到它;接着聊用 `gateway-agent -i --resume <那个 uuid>`,或在 REPL 里敲 `/resume` 从列表里挑。反过来,网页任务也不会出现在 `--resume` 的候选里。
1253
+ - **想在网页里看命令行聊的那些**:起服务后打开 **http://127.0.0.1:3100/sessions**(任务页顶栏「命令行」也是它)。这一页列出 `<数据根>/sessions/` 里的会话(工作目录 / 模型 / 消息数 / token 用量 / 最后活动时间),点开回放全文,含工具调用与思考过程;它**只读**(页面上没有输入框),要接着聊还是回终端 `--resume`。数据源是 `GET /api/cli-sessions` 与 `GET /api/cli-sessions/<id>`,与任务记录同级别:只在本机可访问,服务绑到 `0.0.0.0` 时要显式 `--allow-remote-fs` 才放行。
1152
1254
  - **Web 服务怎么关 / 端口被占**:见上面「[关闭服务](#关闭服务)」。一句话版 —— 同一个终端里 `Ctrl+C`;找不到终端就 `netstat -ano | findstr :3100` 拿 PID 再 `Stop-Process -Id <PID> -Force`(Linux/macOS 用 `lsof -i :3100` 或 `fuser -k 3100/tcp`)。