Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ It is the same trick `scripts/check.ts` already uses for the client bundle, at f
- `src/google-bridge.ts` — the Google models, served through the plugin's own adapter on the real `llm` service. It is the ONLY module that imports pi at runtime, and its route is `opencode-google` so the `google` name stays free for a real deployment.
- `src/opencode-paths.ts` — where an OpenCode installation keeps its files, ported from `~/dev/opencode`'s `global-roots.ts` (the directory) and `database-path.ts` (the filename). It takes `home`, `env` and `channel` as parameters and reads neither the environment nor the filesystem, which is what makes the Windows and Linux layouts testable from here. Two facts it encodes that are easy to get wrong: the directory is XDG on EVERY platform — macOS and Windows use `~/.local/share` too, and `os.homedir()` is the only platform-dependent call — and the FILENAME is not constant, being `$OPENCODE_DB`, else `opencode.db` on a stable channel, else `opencode-<channel>.db`. The database sits under `data` while the config sits under `config`, so one root cannot serve both. It is the foundation of the opt-in reader: `go-discovery.ts`'s `resolveOpencodeStoreKey` is what imports it, and nothing else does.
- `src/opencode-store.ts` — reading the credentials an OpenCode installation already holds, through `node:sqlite`. Two rules shape it: nothing throws (a missing file, a foreign schema and `:memory:` all answer `undefined`), and a value never leaves the return value — no log line, no error message, no client bundle. `node:sqlite` is host-only, which is what keeps the module on the host side of the bundle rule. It is reached only through `resolveOpencodeStoreKey`, which is the last step of every key-source policy and is off unless `readOpencodeStore` is on. Its second reader, `inspectOpencodeStore`, answers the _other_ question — "is there a credential here, and whose is it?" — with a sanitized summary (email, org, kind, expiry) and never the token, which is what lets the settings card show the evidence beside the permission.
- **What the plugin page shows is `docs/detail.md`, not the README.** The page renders one document: the Markdown `meta.detail` in `locale/<language>.json` names, else the whole `README.md`. This package declared nothing for years, so its page carried 461 lines of engineering notes above the settings card. It now declares a 19-line page per language, opening with `docs/media/hero.svg` — and the image is served from the package, so `files` has to ship it (the detail-image route reads package-relative paths). `test/plugin-detail.test.ts` holds the contract: a declared detail in every locale, a path that exists AND is shipped, an image that does the same, under 40 lines, and links to where the rest lives.
- **What the plugin page shows is `docs/detail.md`, not the README.** The page renders one document: the Markdown `meta.detail` in `locale/<language>.json` names, else the whole `README.md`. This package declared nothing for years, so its page carried 461 lines of engineering notes above the settings card. It now declares a page per language that is **one image and nothing else** — a UI preview of the card and the meter (`docs/media/preview.svg`, `.zh.svg`) — which is the shape the upstream plugins use, because the header above the card already carries the title and the one-sentence description. The image is served from the package, so `files` has to ship it (the detail-image route reads package-relative paths). `test/plugin-detail.test.ts` holds the contract: a declared detail in every locale, a path that exists AND is shipped, an image that does the same, and a page that is the picture and nothing else.
- `src/opencode-store-contract.ts` · `src/opencode-store-service.ts` — the inspection seam: the sanitized summary type both sides share, and the host service that produces it. The contract is pure data (no `node:`, no context), so the client bundle may import it; the service is the half that touches `node:sqlite` and the filesystem, and it rewrites the store path to `~`-relative before answering — a home directory is a fact about the machine, not about the plugin. `inspect` is declared through the `Object.defineProperty` marker rather than `@Remote`, for the reason `usage.ts` documents.
- **The login stack is not in this tree.** It lives on the `v2` bookmark, and the map used to describe it here for years after it left — nine modules a reader could not find. What this tree carries is the API-key path only; a login grant is the other half, and reviving it is a days-scale port rather than a rebase (`v2` is 163 commits behind, and its `src/index.ts` exports a module the tree no longer has).

Expand Down
19 changes: 1 addition & 18 deletions docs/detail.md
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
![OpenCode Patch — session affinity, gateway headers and live Go quota](./media/hero.svg)

OpenCode's own CLI and this plugin make the same requests to the same gateway. The difference is what the gateway is told about them, and what the composer shows while they run: this patch restores the request identity the official CLI sends, keeps one session across turns, and puts the live Go quota beside the input box.

## What you get

| Surface | What it does |
| :-- | :-- |
| Gateway requests | Sends the official CLI's `User-Agent`, origin headers and workspace project tag, so Cloudflare's WAF checks pass |
| Sessions | One session id per conversation, derived and reused, so Zen and Go stop treating every turn as a stranger |
| Quota | The Go panel — 5-hour, weekly and monthly rings, the monthly allowance per model — and the Zen session spend |
| Routing | Models served on another protocol are routed to the endpoint that speaks it, instead of being refused |

## Where the rest lives

- The full reference — configuration, every control, the model tables — is in [README.md](https://github.com/viztor/dsh-opencode-patch#readme).
- The meter's numbers, and what each row means, are in [docs/quota-meter.md](https://github.com/viztor/dsh-opencode-patch/blob/main/docs/quota-meter.md).
- The endpoints, the headers and the measurements behind them are in [docs/redirect-planes.md](https://github.com/viztor/dsh-opencode-patch/blob/main/docs/redirect-planes.md).
![The settings card, and the Go meter above the composer](./media/preview.svg)
19 changes: 1 addition & 18 deletions docs/detail.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
![OpenCode 补丁——会话保持、网关请求头与实时 Go 额度](./media/hero.svg)

OpenCode 自己的 CLI 和这个插件向同一个网关发的是同一批请求,差别在于网关被告知了什么、以及运行的时候输入框旁边显示了什么:本补丁恢复官方 CLI 的请求身份、让同一段对话共用一个会话、并把实时 Go 额度放在输入框旁。

## 你能得到什么

| 界面 | 作用 |
| :-- | :-- |
| 网关请求 | 发送官方 CLI 的 `User-Agent`、来源请求头和工作区项目标识,使 Cloudflare WAF 校验通过 |
| 会话 | 每段对话一个会话 id,推导并复用,Zen 与 Go 不再把每一轮都当成陌生人 |
| 额度 | Go 面板——5 小时、每周、每月三个环,以及按模型的月度额度——和 Zen 的会话消耗 |
| 路由 | 走另一种协议的模型会被送到说那种协议的端点,而不是被拒绝 |

## 其余内容在哪儿

- 完整参考——配置、每个控件、模型表——在 [README.zh-CN.md](https://github.com/viztor/dsh-opencode-patch/blob/main/README.zh-CN.md)。
- 计量表上每个数字、每一行在说什么,在 [docs/quota-meter.zh-CN.md](https://github.com/viztor/dsh-opencode-patch/blob/main/docs/quota-meter.zh-CN.md)。
- 端点、请求头以及它们背后的实测,在 [docs/redirect-planes.md](https://github.com/viztor/dsh-opencode-patch/blob/main/docs/redirect-planes.md)。
![设置卡片,以及输入框上方的 Go 计量表](./media/preview.zh.svg)
51 changes: 51 additions & 0 deletions docs/media/preview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading