Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ab59020
docs(webui): the engine layer has six files, not five (webui-parity 107)
fengzhi09 Oct 1, 2026
1dc559a
test(webui): M2 capability-declaration snapshot vs the real host (eng…
fengzhi09 Oct 1, 2026
8c085fa
test(webui): point the capability snapshot at the engine layer's real…
fengzhi09 Oct 1, 2026
aa5ab47
fix(webui): stop the shell from carrying one session's state into ano…
fengzhi09 Oct 1, 2026
af01e0e
refactor(webui): the plugins and turn-diff routes take the host from …
fengzhi09 Oct 1, 2026
f1842ba
test(webui): make the run-mirror, first-turn-guard and mavis-usage su…
fengzhi09 Oct 1, 2026
726d286
refactor(webui): the plugins and turn-diff routes take the host from …
fengzhi09 Oct 1, 2026
a4fad96
test(webui): make the run-mirror, first-turn-guard and mavis-usage su…
fengzhi09 Oct 1, 2026
e7c0ce9
feat(webui): the five read endpoints ask the engine facade, not the t…
fengzhi09 Oct 1, 2026
e053ae7
feat(webui): the session-tree and export endpoints ask the engine fac…
fengzhi09 Oct 1, 2026
4fb8267
feat(webui): the usage endpoints ask the engine facade, and the deriv…
fengzhi09 Oct 1, 2026
88b9a48
fix(webui): rebase M3-B3 onto M3-B2, register B2's two tmp prefixes, …
fengzhi09 Oct 1, 2026
6bc24bd
feat(webui): the account, model and capability reads ask the engine f…
fengzhi09 Oct 2, 2026
eb2a429
feat(webui): #73 swaps the ACP wire table for the 14-key engine-capab…
fengzhi09 Oct 2, 2026
2baf051
fix(webui): stop two B4 comments describing behaviour the code no lon…
fengzhi09 Oct 2, 2026
edf2b1e
feat(webui): move the session write family behind the engine facade
fengzhi09 Oct 2, 2026
1506cc2
fix(webui): drop whitespace text nodes in markdown tables and dedupe …
fengzhi09 Oct 2, 2026
8cca235
fix(webui): sweep the non-flipping inverted text token off primary su…
fengzhi09 Oct 2, 2026
eecd8c0
feat(webui): move session switch behind the engine facade
fengzhi09 Oct 2, 2026
0cfd51f
Merge main into dev-lhl
fengzhi09 Oct 2, 2026
e4cf052
chore: allowlist the leak-tripwire fixture in model-reads tests
fengzhi09 Oct 2, 2026
3f5b8d2
test(webui): pin session-writes cleanup-orphans test to isolated paths
fengzhi09 Oct 2, 2026
e7df93d
chore: ignore gitleaks fingerprints of deliberate test fixtures
fengzhi09 Oct 2, 2026
62814ff
chore: make the gitleaks fixture allowlists path-only
fengzhi09 Oct 2, 2026
3074010
feat(webui): move interrupt and load endpoints behind the engine facade
fengzhi09 Oct 3, 2026
063a43a
fix(webui): take the plan's 5s abort force-kill bound by product call
fengzhi09 Oct 3, 2026
90cf85e
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
a9af820
docs(webui): add session-switch, interrupt and session-load to the ar…
fengzhi09 Oct 3, 2026
4d904c3
docs(webui): add the missing zh-CN section for the B5 write family
fengzhi09 Oct 3, 2026
fdc3ff2
fix(webui): make webui-only session delete return promptly instead of…
fengzhi09 Oct 3, 2026
dab453d
fix(webui): retire lossy streaming mirrors when the engine transcript…
fengzhi09 Oct 3, 2026
7138b5b
feat(webui): add the streaming-send capability gate and pure stream b…
fengzhi09 Oct 3, 2026
a2223f4
feat(webui): run send on the runtime transport behind the engine facade
fengzhi09 Oct 3, 2026
8b51fdd
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
964c0cf
feat(webui): answer set-mode and set-config-option with structured 50…
fengzhi09 Oct 3, 2026
bdde1eb
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
a112e45
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
245a101
docs(webui): add the streaming-send architecture section, bilingual
fengzhi09 Oct 3, 2026
d9e181d
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
679d0fe
docs(webui): add the streaming-send architecture section, bilingual
fengzhi09 Oct 3, 2026
a078ee6
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
591ccff
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
b529543
fix(local-runtime): make an abandoned migration lease recoverable at …
fengzhi09 Oct 3, 2026
a8e56dc
feat(webui): move model and permission writes behind the engine facade
fengzhi09 Oct 3, 2026
48199c5
Reset dev-lhl to the full local integration line (B9+B10+docs+P13+P14…
fengzhi09 Oct 3, 2026
5661bb9
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
4b5e8d2
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
9fdd8d1
fix(webui): surface truncated acp stderr in failure alerts
fengzhi09 Oct 3, 2026
5427f23
feat(webui): move the provider family behind the engine facade with s…
fengzhi09 Oct 3, 2026
afa995e
fix(webui): acknowledge in-flight messages explicitly instead of echo…
fengzhi09 Oct 3, 2026
6c30484
fix(webui): normalise the expected side of the provider cwd path asse…
fengzhi09 Oct 3, 2026
e68a8df
feat(webui): bridge thinkingEffort as the third config id and gate th…
fengzhi09 Oct 3, 2026
1473183
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
15a3f42
feat(webui): register the acp transport as the first engine capabilit…
fengzhi09 Oct 3, 2026
313286a
fix(webui): keep over-tall code blocks inside their scroll container
fengzhi09 Oct 3, 2026
506c0a9
feat(webui): replace the flat provider form with the desktop-style di…
fengzhi09 Oct 3, 2026
25da27a
docs(webui): document the provider dialog interaction, bilingual
fengzhi09 Oct 3, 2026
38befac
feat(webui): route session deletion through the engine deleteSession …
fengzhi09 Oct 3, 2026
06a0e8e
feat(webui): open the host services window for capability exposure ba…
fengzhi09 Oct 3, 2026
60e5361
feat(webui): register the exec transport in the engine capability reg…
fengzhi09 Oct 3, 2026
64914d7
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
382c10c
fix(webui): escape raw svg tags in markdown output instead of mountin…
fengzhi09 Oct 3, 2026
bc49315
fix(webui): consume the real stream-json events on the exec transport
fengzhi09 Oct 3, 2026
35f1e0c
feat(webui): unlock the session context menu actions backed by the en…
fengzhi09 Oct 3, 2026
2b3a9e4
test(webui): register the PB-1 real-host tmp prefix
fengzhi09 Oct 3, 2026
f9829b3
chore(release-tools): register the mcode-exec-stream- tmp prefix
fengzhi09 Oct 3, 2026
b5bad19
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
33629d7
feat(webui): wire the context-window usage switch to the composer rea…
fengzhi09 Oct 3, 2026
c10736d
feat(webui): unlock the project menu's reveal-in-folder (SB-6)
fengzhi09 Oct 3, 2026
92abe74
feat(webui): make the Shortcuts page state what the browser can do
fengzhi09 Oct 3, 2026
595c06d
feat(webui): plan card reads the account tier, honest cloud placeholders
fengzhi09 Oct 3, 2026
d087f28
feat(webui): wire the usage-and-models model source to the engine (SB-1)
fengzhi09 Oct 3, 2026
43d0b2a
dev-lhl: SB-3/6/2/7/1 + SB-5 + P19 + SB-4 (settings waves, delete-han…
fengzhi09 Oct 3, 2026
c96e7a2
fix(webui): re-read the account after a source switch, and stop the k…
fengzhi09 Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 319 additions & 15 deletions docs/webui.md

Large diffs are not rendered by default.

167 changes: 151 additions & 16 deletions docs/webui.zh-CN.md

Large diffs are not rendered by default.

151 changes: 151 additions & 0 deletions packages/webui/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2477,6 +2477,157 @@ it through the same form the custom-providers UI uses.

---

## Model source (SB-1)

Four endpoints behind the 「用量与模型」 tab's source switcher, the
「使用中」 badge and the MiniMax API key row. Each one is a thin window
over an engine method that already existed
(`packages/local-runtime-v2/src/local/cli-service.ts`:
`getMiniMaxModelSource`, `setMiniMaxModelSource`,
`upsertMiniMaxApiKey`, `testUserModel`) and previously had no route.

**These are NOT the provider catalogue.** `/api/providers*` is webui's
own store of custom BYOK endpoints. This family is the ENGINE's MiniMax
credential state — `minimaxModelSource` and `minimax_api.apiKey` in the
engine's own `config.yaml`. The two sit on adjacent tabs, share a masking
convention, and share no storage and no validation.

**Gate.** The four methods hang off the v2 cli-service's own
`modelProviders` requirement, which is not one of the 14 declared
capability keys, so this family is gated on the LIVE member rather than
on a declaration: `503 engine_host_unavailable` when no runtime is
booted, `501 engine_member_unavailable` when the booted host does not
carry the method. See `server/engine/model-source.js`.

**Masking.** `apiKey` is masked in every response. The mask is the
engine's own (`service/model-system/secret.js`), forwarded unchanged, and
no path in the route can unmask it.

### `GET /api/model-source`

The read the settings tab opens on: the active source, plus the stored
key's masked projection.

**Response 200**
```json
{
"ok": true,
"source": "token_plan",
"apiKey": {
"available": true,
"hasKey": false,
"masked": null,
"testState": null,
"lastTestedAtMs": null
}
}
```

`source` is `token_plan` (the managed Token Plan credential) or
`minimax_api_key` (the user's own BYOK key). `apiKey.available: false`
is NOT `hasKey: false`: the first means the host could not report the
key half at all, the second means it reported that nothing is stored. A
UI that collapsed the two would tell a user with a saved key that they
have none.

- `501 engine_member_unavailable` — the host has no
`getMiniMaxModelSource`.
- `503 engine_host_unavailable` — no runtime booted.
- `502 UNKNOWN_MODEL_SOURCE` — the engine reported a value outside the
two its own type allows. Refused rather than rendered, because the UI
has no way back out of a source it cannot name.

### `PUT /api/model-source`

Switch the active source. The response reports what the engine
PERSISTED, not what was requested, so the badge can never disagree with
the config.

**Request** `{ "source": "token_plan" | "minimax_api_key" }`

**Response 200** `{ "ok": true, "source": "minimax_api_key" }`

- `400 INVALID_MODEL_SOURCE` — missing or unknown `source`. Checked
before the engine is reached, so a typo costs no runtime round trip.
- `400 BAD_FIELD_TYPE` — `source` was not a string.
- `400 NO_API_KEY` — the engine refused the BYOK direction because no
key is stored. This code is the UI's cue to point the user at the key
field rather than to show a failure.

### `PUT /api/model-source/api-key`

Upsert the BYOK key, optionally switching to it in the same call.

**Request** `{ "apiKey": "<raw key>", "saveAndUse": true }`

**The keep-key sentinel.** An absent, empty or whitespace-only `apiKey`
keeps the stored key, calls no engine write, and answers `200` with
`changed: false` plus the current masked status. It exists because the
GET can only return a MASK and the engine rejects a mask submitted as a
key (`INVALID_API_KEY`); a UI that round-tripped its own masked state
would turn every save into a failure. Same convention and same
empty-string spelling as `PUT /api/providers`.

`saveAndUse` is the engine's own flag: it writes the key AND switches
the source to `minimax_api_key` in one transaction, so the tab never
shows a saved key beside a source that was not switched.

**Response 200**
```json
{
"ok": true,
"source": "minimax_api_key",
"apiKey": { "available": true, "hasKey": true, "masked": "sk-a*******6789" },
"changed": true,
"saveAndUse": true
}
```

- `400 BAD_FIELD_TYPE` — `apiKey` was not a string, or `saveAndUse` was
not a boolean. A non-string key is refused rather than treated as the
keep sentinel, which would turn a client's bug into a successful no-op.
- `400 INVALID_API_KEY` — the engine's own refusal (empty or masked).
- `500 engine_error` — a throw with no engine status. Its message is
REPLACED, not forwarded: an exception string from an unrecognised
thrower is the one place a credential could still be echoed.

### `POST /api/model-source/test`

Connectivity probe. **Request** `{ "modelId"?: "MiniMax-M3" }`; the
engine falls back to the first configured MiniMax model when it is
absent, and an unknown id is the engine's own 404.

The probe always runs against the STORED key on the `minimax_api`
provider, and says so in `tested: "stored_key"`. Two limits are the
engine's contract, not this route's: v2's `testUserModel` takes no key
override, so an unsaved key cannot be probed; and the managed Token Plan
credential is not a model-service key, so the Token Plan source has
nothing here to probe with.

**Response 200** — for a completed probe, successful or not:
```json
{
"ok": true,
"success": true,
"providerId": "minimax_api",
"modelId": null,
"tested": "stored_key",
"status": {
"state": "available",
"lastTestedAt": 1700000000000,
"lastErrorCode": null,
"lastErrorMessage": null
}
}
```

`success: false` is a COMPLETED probe of a model that did not answer, so
it stays 200 — the same split `POST /api/providers/test` makes. Only a
refusal to try is a non-200: `503` / `501` from the gate, or the
engine's `400 NO_API_KEY` when nothing is stored to test.

---

## Usage

### `POST /api/usage` and `POST /api/usage-trigger`
Expand Down
133 changes: 133 additions & 0 deletions packages/webui/docs/API.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2263,6 +2263,139 @@ SSE 事件,让每个已连接客户端刷新目录。下一次 `/api/models`

---

## 模型来源(SB-1)

「用量与模型」页的三来源切换头、「使用中」徽标与 MiniMax API Key
一行的四个端点。每一个都是引擎既有方法
(`packages/local-runtime-v2/src/local/cli-service.ts` 的
`getMiniMaxModelSource`、`setMiniMaxModelSource`、
`upsertMiniMaxApiKey`、`testUserModel`)的薄窗口,此前没有 HTTP 出口。

**这一族不是供应商目录。** `/api/providers*` 是 webui 自有存储里的
自定义 BYOK 端点;本族是**引擎**侧的 MiniMax 凭据状态,即引擎自己的
`config.yaml` 里的 `minimaxModelSource` 与 `minimax_api.apiKey`。两者
在界面上相邻、共用掩码约定,但存储与校验完全不共享。

**门控。** 四个方法挂在 v2 cli-service 自己的 `modelProviders` 要求上,
而它不是 14 个已声明能力键之一,因此本族按**活成员**门控而非按声明门控:
没有运行时启动时 `503 engine_host_unavailable`,已启动的宿主不带该方法时
`501 engine_member_unavailable`。见 `server/engine/model-source.js`。

**掩码。** `apiKey` 在任何响应里都是掩码,且是引擎自己的掩码
(`service/model-system/secret.js`)原样透传;本路由没有任何代码路径
能把它还原。

### `GET /api/model-source`

设置页打开时读的那一次:当前来源,加上已存密钥的掩码投影。

**响应 200**
```json
{
"ok": true,
"source": "token_plan",
"apiKey": {
"available": true,
"hasKey": false,
"masked": null,
"testState": null,
"lastTestedAtMs": null
}
}
```

`source` 取 `token_plan`(托管的 Token Plan 凭据)或
`minimax_api_key`(用户自带的 BYOK 密钥)。`apiKey.available: false`
**不等于** `hasKey: false`:前者是宿主根本读不到密钥半边,后者是读到了
"没有存密钥"。把两者合并渲染,会告诉一个已经存了密钥的用户"你没有密钥"。

- `501 engine_member_unavailable` —— 宿主没有 `getMiniMaxModelSource`。
- `503 engine_host_unavailable` —— 没有已启动的运行时。
- `502 UNKNOWN_MODEL_SOURCE` —— 引擎报出了其自身类型之外的值。拒绝而
不是渲染,因为界面无法从一个叫不出名字的来源里退出来。

### `PUT /api/model-source`

切换当前来源。响应报的是引擎**已持久化**的值而不是请求值,因此徽标
不可能与配置不一致。

**请求** `{ "source": "token_plan" | "minimax_api_key" }`

**响应 200** `{ "ok": true, "source": "minimax_api_key" }`

- `400 INVALID_MODEL_SOURCE` —— 缺失或未知的 `source`。在触达引擎之前
就判定,因此一次笔误不会付出一次运行时往返。
- `400 BAD_FIELD_TYPE` —— `source` 不是字符串。
- `400 NO_API_KEY` —— 引擎因为没有存密钥而拒绝了 BYOK 方向。这个码是
界面把用户指向密钥输入框的信号,而不是弹一个失败提示。

### `PUT /api/model-source/api-key`

写入(upsert)BYOK 密钥,可选在同一调用里切到它。

**请求** `{ "apiKey": "<原始密钥>", "saveAndUse": true }`

**保留密钥哨兵。** `apiKey` 缺失、为空或只有空白时保留已存密钥,
不调用任何引擎写,并返回 `200` + `changed: false` + 当前掩码状态。
它存在的原因是:`GET` 只能返回掩码,而引擎把掩码当密钥提交会拒绝
(`INVALID_API_KEY`)——一个把自己读到的掩码回写的前端会让每次保存都
失败。约定与拼写都跟 `PUT /api/providers` 一致(空串即保留)。

`saveAndUse` 是引擎自带的标志:它在一个事务里既写密钥又把来源切成
`minimax_api_key`,因此界面不会出现"密钥已存、来源没切"的中间态。

**响应 200**
```json
{
"ok": true,
"source": "minimax_api_key",
"apiKey": { "available": true, "hasKey": true, "masked": "sk-a*******6789" },
"changed": true,
"saveAndUse": true
}
```

- `400 BAD_FIELD_TYPE` —— `apiKey` 不是字符串,或 `saveAndUse` 不是
布尔值。非字符串密钥被拒绝而不是当作"保留",否则客户端的 bug 会变成
一次"成功但什么都没做"的调用。
- `400 INVALID_API_KEY` —— 引擎自身的拒绝(空值或掩码形态)。
- `500 engine_error` —— 没有引擎状态的抛出。异常消息被**替换**而非
透传:来自不可识别抛出者的异常字符串是唯一仍可能回显凭据的地方。

### `POST /api/model-source/test`

连通性检测。**请求** `{ "modelId"?: "MiniMax-M3" }`;缺省时引擎回落到
第一个已配置的 MiniMax 模型,未知 id 则是引擎自身的 404。

检测始终针对**已保存的密钥**、在 `minimax_api` 供应商上进行,并在
`tested: "stored_key"` 里说明这一点。两条限制来自引擎契约而非本路由的
选择:v2 的 `testUserModel` 不接受密钥覆写,因此未保存的密钥无法被检测;
托管的 Token Plan 凭据也不是模型服务的密钥,Token Plan 来源在这里没有
可检测的对象。

**响应 200** —— 检测跑完即 200,无论成功与否:
```json
{
"ok": true,
"success": true,
"providerId": "minimax_api",
"modelId": null,
"tested": "stored_key",
"status": {
"state": "available",
"lastTestedAt": 1700000000000,
"lastErrorCode": null,
"lastErrorMessage": null
}
}
```

`success: false` 是一次**跑完**的检测且被测模型没有应答,因此仍是 200
——与 `POST /api/providers/test` 的切分一致。只有"拒绝去试"才是非 200:
门控给出的 `503` / `501`,或没有可测密钥时引擎的 `400 NO_API_KEY`。

---

## 用量

### `POST /api/usage` 与 `POST /api/usage-trigger`
Expand Down
52 changes: 52 additions & 0 deletions packages/webui/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ import * as modelRoute from "./routes/model.js";
import * as debugRoute from "./routes/debug.js";
import * as protocolRoute from "./routes/protocol.js";
import * as providersRoute from "./routes/providers.js";
import * as modelSourceRoute from "./routes/model-source.js";
import * as followUpRoute from "./routes/follow-up.js";
import * as gitRoute from "./routes/git.js";
import * as pluginsRoute from "./routes/plugins.js";
import * as turnDiffRoute from "./routes/turn-diff.js";
Expand Down Expand Up @@ -129,6 +131,16 @@ export const OWNED_ROUTES = new Set([
"POST /api/send",
"POST /api/stop",
"POST /api/cmd",
// SB-4 — the follow-up message family. One window over the two engine
// methods that already existed with nothing in front of them
// (`cli-service.ts`: enqueueMessage / steer), which the composer's
// 跟进消息行为 switch was writing to localStorage and never reading.
// Unlike `/api/send` this is NOT fire-and-forget: the response carries
// the engine's own answer (a queue item id and position, or the turn a
// message was steered into), because a message that was not delivered
// has to come back to the input box. See `engine/follow-up.js` for the
// ownership gate and the KNOWN DEBT list.
"POST /api/follow-up",
// Usage / quota.
"POST /api/usage",
"POST /api/usage-trigger",
Expand Down Expand Up @@ -213,6 +225,20 @@ export const OWNED_ROUTES = new Set([
// Preset providers (ticket 02): gallery + one-click enable.
"GET /api/providers/presets",
"POST /api/providers/preset/:id/enable",
// SB-1 — the 「用量与模型」 tab's model-source family. Four windows
// over engine methods that existed all along
// (`cli-service.ts`: getMiniMaxModelSource / setMiniMaxModelSource /
// upsertMiniMaxApiKey / testUserModel) and had no route: the source
// switcher was `useState` plus a comment claiming the backend did not
// exist, and the two key buttons were permanently disabled. The key
// write sits on its own sub-resource so its handler can carry the
// keep-key sentinel (an absent key keeps the stored one) without a
// body flag that would read as "clear my key" by accident. See
// `engine/model-source.js` for the gate and the KNOWN DEBT list.
"GET /api/model-source",
"PUT /api/model-source",
"PUT /api/model-source/api-key",
"POST /api/model-source/test",
// Debug injection (gated by DEBUG_INJECT=1).
"POST /api/debug/inject",
"GET /api/debug/state",
Expand Down Expand Up @@ -539,6 +565,13 @@ export function createHonoApp() {
app.post("/api/cmd", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), chatRoute.handleCmd),
);
// ----- SB-4: follow-up messages -----
// Adjacent to the chat routes because it is a chat action, and
// registered after them so the OWNED_ROUTES order and the registration
// order stay the same list.
app.post("/api/follow-up", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), followUpRoute.handleFollowUp),
);

// ----- Usage / quota -----
// /api/usage and /api/usage-trigger share one handler in the legacy table;
Expand Down Expand Up @@ -754,6 +787,25 @@ export function createHonoApp() {
),
);

// ----- SB-1: model source + MiniMax API key -----
// Registered after the provider family and in the same order as
// `OWNED_ROUTES`. The three literal paths share the `/api/model-source`
// prefix, so the order between them does not shadow anything — but the
// sub-resource (`/api/model-source/api-key`) is a distinct path, not a
// suffix match, and the router resolves it on its own literal.
app.get("/api/model-source", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), modelSourceRoute.handleGetModelSource),
);
app.put("/api/model-source", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), modelSourceRoute.handleSetModelSource),
);
app.put("/api/model-source/api-key", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), modelSourceRoute.handlePutModelSourceApiKey),
);
app.post("/api/model-source/test", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), modelSourceRoute.handleTestModelSource),
);

// ----- Debug injection (gated by DEBUG_INJECT=1) -----
app.post("/api/debug/inject", (c) =>
invokeHandler(c, c.get(CAPTURE_KEY), debugRoute.handleDebugInject),
Expand Down
Loading
Loading