dsh-github-router 0.3.1 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,56 +1,79 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [0.3.1] - 2026-08-28
9
-
10
- ### Fixed
11
-
12
- - Boot with DSH 0.1.2-alpha.4
13
-
14
- ## [0.3.0] - 2026-08-28
15
-
16
- ### Changed
17
-
18
- - Adapted to DeepSeek Harness 0.1.2-alpha.1
19
-
20
- ## [0.2.1] - 2026-08-28
21
-
22
- ### Changed
23
-
24
- - Declare compatibility for dsh 0.1.1-rc.2
25
-
26
- ## [0.2.0] - 2026-08-21
27
-
28
- ### Changed
29
-
30
- - **Migrated plugin configuration to the framework's plugin-settings mechanism** (requires DSH ≥ 0.1.0-rc.7, which serves every registered settings namespace and dispatches plugin-owned configuration cards). The browser half now registers a `settings.plugin.item` card keyed by the `dsh-github-router` namespace inside Settings → Plugins, bound through `ctx.settingsScope` (shared describe mirror, revision fencing, recovery reads, reconnect invalidation); the plugin-owned `/dsh-github-router/config` web routes and the standalone `settings.section` page are removed. The Host keeps the same `settings.register` seam and adds `applies: 'live'`.
31
- - The token control is now a true write-only field: the configured state comes from the describe mirror's secret slot list (badge: "已配置/configured"), a typed value writes the token, and a blank save clears a configured one.
32
- - Runtime requirements: the settings card needs DSH ≥ 0.1.0-rc.7; the Host-side settings seam alone still works on older compositions (tools resolve the composition config as before).
33
-
34
- ## [0.1.0] - 2026-08-19
35
-
36
- First release.
37
-
38
- ### Added
39
-
40
- - Five read-only model tools: `github_probe`, `github_pr`, `github_issue`, `github_file`, `github_api`
41
- - In-tool route ladder: api.github.com (direct → CONNECT-tunnel proxy) → gh CLI (read-only subcommands) → git protocol (plugin fetch cache + read-only local repo reads) → PR/issue page HTML (strict `react-app.embeddedData` JSON extraction) → user-configured raw mirrors (off by default), with per-part route attribution on every result
42
- - `github_probe` connectivity matrix with per-route timings and a recommended route chain
43
- - TTL response cache with atomic writes and corruption-tolerant reads
44
- - Settings: the `dsh-github-router` namespace on the official settings seam (secret token / `tokenEnv` credential ref, proxy, route switches, cache TTLs, byte caps, granted local repos), an independent Settings page (the dsh-notification `settings.section` mechanism; common fields up top, the long tail in a collapsed "Advanced settings" disclosure), and plugin-owned `/dsh-github-router/config` web routes (the dsh-market pattern) with same-origin enforcement and revision fencing
45
- - `github-router` skill and a system-prompt guidance section (order 118)
46
- - Zero runtime dependencies: zero-dependency CONNECT tunnel for proxied requests; peers resolve from the DSH profile
47
- - 72 offline tests (`node --test`) covering extraction, guards, cache, tunnel, tool wiring, client bundle, config routes, and localization compliance
48
- - Documentation: bilingual README, SECURITY, design notes, and a development guide
49
-
50
- ### Security
51
-
52
- - Read-only by construction: no write/push/comment/mutation verb exists anywhere
53
- - Tokens attach only to api.github.com and are redacted on every wire boundary
54
- - Page HTML is parsed with `JSON.parse` only (never evaluated); a bounded BFS copies a whitelist of fields
55
- - Subprocess calls are argv arrays with fixed flag lists — no shell interpolation
56
- - Configuration writes require same-origin POSTs and are body-capped
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.0] - 2026-09-28
9
+
10
+ ### Changed
11
+
12
+ - Migrated the plugin to the DSH `0.1.7-rc.2` plugin API
13
+ - Configuration is the exported `Config` schema.
14
+ - The configuration page moved to the plugin's row on the Plugins page.
15
+ - A blank token draft no longer clears the stored token.
16
+
17
+ ## [0.3.2] - 2026-09-08
18
+
19
+ ### Fixed
20
+
21
+ - The `gh` route now builds valid arguments for its viewers.
22
+ - The `gh` route now returns the metadata URL field.
23
+ - The proxy transport now decodes chunked responses.
24
+ - The html route now handles the current GitHub pull request page payload.
25
+ - Fallback routes are now entered only when a part could not be fetched.
26
+
27
+ ### Added
28
+
29
+ - Offline regression tests for the `gh` route, chunked decoding, and failover gating.
30
+
31
+ ## [0.3.1] - 2026-08-28
32
+
33
+ ### Fixed
34
+
35
+ - Boot with DSH 0.1.2-alpha.4
36
+
37
+ ## [0.3.0] - 2026-08-28
38
+
39
+ ### Changed
40
+
41
+ - Adapted to DeepSeek Harness 0.1.2-alpha.1
42
+
43
+ ## [0.2.1] - 2026-08-28
44
+
45
+ ### Changed
46
+
47
+ - Declare compatibility for dsh 0.1.1-rc.2
48
+
49
+ ## [0.2.0] - 2026-08-21
50
+
51
+ ### Changed
52
+
53
+ - **Migrated plugin configuration to the framework's plugin-settings mechanism** (requires DSH ≥ 0.1.0-rc.7, which serves every registered settings namespace and dispatches plugin-owned configuration cards). The browser half now registers a `settings.plugin.item` card keyed by the `dsh-github-router` namespace inside Settings → Plugins, bound through `ctx.settingsScope` (shared describe mirror, revision fencing, recovery reads, reconnect invalidation); the plugin-owned `/dsh-github-router/config` web routes and the standalone `settings.section` page are removed. The Host keeps the same `settings.register` seam and adds `applies: 'live'`.
54
+ - The token control is now a true write-only field: the configured state comes from the describe mirror's secret slot list (badge: "已配置/configured"), a typed value writes the token, and a blank save clears a configured one.
55
+ - Runtime requirements: the settings card needs DSH ≥ 0.1.0-rc.7; the Host-side settings seam alone still works on older compositions (tools resolve the composition config as before).
56
+
57
+ ## [0.1.0] - 2026-08-19
58
+
59
+ First release.
60
+
61
+ ### Added
62
+
63
+ - Five read-only model tools: `github_probe`, `github_pr`, `github_issue`, `github_file`, `github_api`
64
+ - In-tool route ladder: api.github.com (direct → CONNECT-tunnel proxy) → gh CLI (read-only subcommands) → git protocol (plugin fetch cache + read-only local repo reads) → PR/issue page HTML (strict `react-app.embeddedData` JSON extraction) → user-configured raw mirrors (off by default), with per-part route attribution on every result
65
+ - `github_probe` connectivity matrix with per-route timings and a recommended route chain
66
+ - TTL response cache with atomic writes and corruption-tolerant reads
67
+ - Settings: the `dsh-github-router` namespace on the official settings seam (secret token / `tokenEnv` credential ref, proxy, route switches, cache TTLs, byte caps, granted local repos), an independent Settings page (the dsh-notification `settings.section` mechanism; common fields up top, the long tail in a collapsed "Advanced settings" disclosure), and plugin-owned `/dsh-github-router/config` web routes (the dsh-market pattern) with same-origin enforcement and revision fencing
68
+ - `github-router` skill and a system-prompt guidance section (order 118)
69
+ - Zero runtime dependencies: zero-dependency CONNECT tunnel for proxied requests; peers resolve from the DSH profile
70
+ - 72 offline tests (`node --test`) covering extraction, guards, cache, tunnel, tool wiring, client bundle, config routes, and localization compliance
71
+ - Documentation: bilingual README, SECURITY, design notes, and a development guide
72
+
73
+ ### Security
74
+
75
+ - Read-only by construction: no write/push/comment/mutation verb exists anywhere
76
+ - Tokens attach only to api.github.com and are redacted on every wire boundary
77
+ - Page HTML is parsed with `JSON.parse` only (never evaluated); a bounded BFS copies a whitelist of fields
78
+ - Subprocess calls are argv arrays with fixed flag lists — no shell interpolation
79
+ - Configuration writes require same-origin POSTs and are body-capped
package/CONTRIBUTING.md CHANGED
@@ -28,16 +28,27 @@ loads at process start).
28
28
 
29
29
  ## Offline peer resolution
30
30
 
31
- The plugin's imports (`@deepseek-ai/dsh-tools`, `dsh-settings`,
32
- `dsh-credentials`, `schemastery`, `cordis`) are peer dependencies and
33
- resolve from the DSH profile in production. To run the tests without a
34
- registry round-trip, create a local junction inside the checkout:
31
+ The plugin's imports (`@deepseek-ai/dsh-tools`, `dsh-credentials`,
32
+ `schemastery`, `cordis`) are peer dependencies and resolve from the DSH install
33
+ in production — the profile's `node_modules/@deepseek-ai` mirrors the checkout
34
+ that ran last. To run the tests without a registry round-trip, create a local
35
+ `node_modules/@deepseek-ai` inside this checkout and junction the packages the
36
+ tests import to the framework copies of the DSH you run:
35
37
 
36
38
  ```powershell
37
- New-Item -ItemType Junction -Path node_modules\@deepseek-ai -Target <dschome>\node_modules\@deepseek-ai\dsh\node_modules\@deepseek-ai
39
+ $dsh = '<path to the DSH checkout you run>'
40
+ New-Item -ItemType Directory -Force node_modules\@deepseek-ai | Out-Null
41
+ foreach ($p in 'schemastery', 'cordis', 'dsh-tools', 'dsh-settings') {
42
+ New-Item -ItemType Junction -Path "node_modules\@deepseek-ai\$p" -Target "$dsh\apps\cli\node_modules\@deepseek-ai\$p"
43
+ }
44
+ New-Item -ItemType Junction -Path node_modules\@deepseek-ai\dsh-credentials -Target "$dsh\packages\credentials\credentials"
38
45
  ```
39
46
 
40
- (`node_modules/` is git-ignored; the junction is a local convenience only.)
47
+ For an npm-installed DSH the framework packages live under
48
+ `<DSH_HOME>\node_modules\@deepseek-ai\dsh\node_modules\@deepseek-ai` instead.
49
+ `schemastery` must be ≥ 3.18.3: `.volatile()`, which every settings field uses,
50
+ does not exist in 3.18.2. (`node_modules/` is git-ignored; the junction is a
51
+ local convenience only.)
41
52
 
42
53
  ## Conventions
43
54
 
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > Read-only GitHub access for a DeepSeek Harness project — load PRs, issues, files, and API data through tools that route internally (API, gh CLI, git protocol, page HTML, mirrors), so an agent never burns turns fighting shell-side TLS or proxy failures.
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
- [![Node.js >= 20.18](https://img.shields.io/badge/Node.js-%3E%3D20.18-brightgreen)](https://nodejs.org/)
8
+ [![Node.js >= 22.19](https://img.shields.io/badge/Node.js-%3E%3D22.19-brightgreen)](https://nodejs.org/)
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-github-router)](https://www.npmjs.com/package/dsh-github-router)
10
10
 
11
11
  A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin bundle that gives agents GitHub reads without shell retries:
@@ -15,13 +15,13 @@ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin bun
15
15
  - **One call, structured outcome** — PRs come back with metadata, discussion, reviews, commits, changed files, and the diff, each part annotated with the route that served it; failures return a route matrix instead of a dozen retried shell commands.
16
16
  - **Connectivity probe** — `github_probe` reports which routes are live from the host in one call, with timings and a recommended route chain.
17
17
  - **Zero runtime dependencies** — peers resolve from the DSH profile; proxied requests travel through the plugin's own CONNECT tunnel (no third-party HTTP stack), so the package installs fully offline.
18
- - **Settings page** — an independent Settings entry ("GitHub Router", like the 通知 section): the `dsh-github-router` settings namespace (secret token, proxy, route switches, cache TTLs, byte caps) with staged edits, save/discard, and override badges.
18
+ - **Framework-declared settings page** — the plugin exports its `Config` schema, so DSH serves the `dsh-github-router` namespace to the browser and the plugin's row on Settings → **Plugins** opens the page (secret token, proxy, route switches, cache TTLs, byte caps) with staged edits, override badges, and a write-only token control.
19
19
  - **Context efficiency** — response caching with per-kind TTLs, byte caps everywhere, list caps with truncation notes, and rate-limit surfacing.
20
20
 
21
21
  ## Requirements
22
22
 
23
- - Node.js >= 20.18
24
- - A DSH profile composed from `@deepseek-ai/dsh-base` (it provides the `tools`, `subprocess`, `skills`, `settings`, and `credentials` services the plugin uses)
23
+ - Node.js >= 22.19
24
+ - DeepSeek Harness **0.1.7-rc.2**, with a profile composed from `@deepseek-ai/dsh-base` (it provides the `tools`, `subprocess`, `skills`, `settings`, and `credentials` services the plugin uses)
25
25
  - Optional: the `gh` CLI (authenticated) for the gh route; `git` for the git route
26
26
 
27
27
  ## Install
@@ -48,12 +48,17 @@ Then **restart the DSH backend** — the host composition loads at process start
48
48
 
49
49
  ## Compatibility
50
50
 
51
- Choose the plugin version that matches your DeepSeek Harness release:
51
+ This plugin tracks the DSH plugin API, which does not keep backward
52
+ compatibility between releases. Pick the plugin version that matches your
53
+ DeepSeek Harness release:
52
54
 
53
55
  | DeepSeek Harness | Install |
54
- | --- | --- |
56
+ |------------------| --- |
57
+ | 0.1.7-rc.2 | `dsh-github-router@0.4.0` or later |
58
+ | 0.1.2 ~ 0.1.6 | `dsh-github-router@0.3.2` |
55
59
  | 0.1.1 or earlier | `dsh-github-router@0.2.1` |
56
- | 0.1.2-alpha or later | the latest `dsh-github-router` |
60
+
61
+ The mapping is recorded in `package.json` under `dsh.compatibility.dshReleases`.
57
62
 
58
63
  ## Usage
59
64
 
@@ -85,14 +90,16 @@ Behavior notes:
85
90
 
86
91
  ## Configuration
87
92
 
88
- Settings → **Plugins** shows the **GitHub Router** card on the configurable
89
- tab (the framework's `settings.plugin.item` card slot keyed by the settings
90
- namespace; requires DSH ≥ 0.1.0-rc.7): edits are staged locally and written
91
- only on save, fields overridden by the user are badged, and blank fields
92
- fall back to the defaults below. The token is a write-only field — a blank
93
- save clears a configured token. The same values can be set in the
94
- composition (profile `cordis.patch.yml`) as the plugin's base config; the
95
- Settings UI overrides per user.
93
+ Settings → **Plugins** → the **dsh-github-router** bundle → its row's
94
+ **Configure** control opens the plugin's page. The framework owns the frame:
95
+ it reads the schema this plugin exports, serves the `dsh-github-router`
96
+ namespace to the browser, and renders the save control. Fields the user
97
+ overrode are badged with a reset control, and clearing a field falls back to
98
+ the defaults below. The token is a write-only field: a blank draft keeps the
99
+ stored token, and the **Clear stored token** control stages the removal. The
100
+ same values can be set in the composition (profile `cordis.patch.yml`) as the
101
+ plugin's base config; the page overrides per user, and a saved value applies to
102
+ the next tool call.
96
103
 
97
104
  | Field | Default | Meaning |
98
105
  | --- | --- | --- |
@@ -122,9 +129,10 @@ Settings UI overrides per user.
122
129
  | Path | Purpose |
123
130
  | ---- | ------- |
124
131
  | `cordis.patch.yml` | Profile patch layer inserting the `dsh-github-router` row |
125
- | `lib/index.js` | Host plugin: settings namespace, five tools, skill, guidance |
126
- | `lib/client.js` | Browser half: the settings card (hand-written factory bundle, no build step) |
127
- | `lib/config.js` | Settings schema, defaults, runtime option resolution |
132
+ | `lib/index.js` | Host plugin: the `Config` schema export, five tools, skill, guidance |
133
+ | `lib/client.js` | Browser half: the row configuration page (hand-written factory bundle, no build step) |
134
+ | `lib/config.js` | Settings schema, defaults, live section reads, runtime option resolution |
135
+ | `lib/settings.js` | Host settings wiring: the plugin-owned page policy and the options thunk |
128
136
  | `lib/net.js`, `lib/tunnel.js` | Route-aware HTTP layer; zero-dependency CONNECT proxy tunnel |
129
137
  | `lib/routes/` | One module per route: `api` (GET-only REST), `gh` (CLI), `git` (protocol), `html` (page parse), `mirror` (raw mirrors) |
130
138
  | `lib/core/` | Per-call runtime assembly and the `pr`/`issue`/`file`/`probe` aggregators |
package/README.zh.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > 为 DeepSeek Harness 项目提供只读的 GitHub 访问——通过内部多路由(API、gh CLI、git 协议、页面 HTML、镜像)的工具加载 PR、issue、文件与 API 数据,agent 不再需要在终端里反复对抗 TLS 或代理故障。
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
- [![Node.js >= 20.18](https://img.shields.io/badge/Node.js-%3E%3D20.18-brightgreen)](https://nodejs.org/)
8
+ [![Node.js >= 22.19](https://img.shields.io/badge/Node.js-%3E%3D22.19-brightgreen)](https://nodejs.org/)
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-github-router)](https://www.npmjs.com/package/dsh-github-router)
10
10
 
11
11
  一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件捆绑包,为 agent 提供无需终端重试的 GitHub 读取能力:
@@ -15,13 +15,12 @@
15
15
  - **一次调用、结构化结果** —— PR 一次返回元数据、讨论、评审、提交、变更文件与 diff,每个部件标注来源路由;失败时返回路由矩阵,而不是十几条重试过的终端命令。
16
16
  - **连通性探针** —— `github_probe` 一次调用报告宿主侧哪些路由存活,附耗时与推荐路由链。
17
17
  - **零运行时依赖** —— peer 依赖由 DSH profile 解析;代理请求走插件自带的 CONNECT 隧道(不依赖第三方 HTTP 栈),因此可以完全离线安装。
18
- - **框架级插件设置卡片** —— 设置 → **Plugins(插件)** 的可配置页中出现「GitHub 路由」卡片(框架的 `settings.plugin.item` 机制,按设置命名空间配对,需 DSH ≥ 0.1.0-rc.7):`dsh-github-router` 设置命名空间(secret token、代理、路由开关、缓存 TTL、字节上限),编辑暂存、保存/放弃、覆盖徽标、token 已配置徽标。
18
+ - **框架级插件设置页** —— 设置 → **Plugins(插件)** → 本插件 bundle 的**行**上的「配置」控件打开插件页面(框架声明式配置:插件导出 `Config` schema,框架以 Loader entry id `dsh-github-router` 作为设置命名空间服务到浏览器、派生表单、校验并落盘写入,需要 DSH 0.1.7-rc.2;`dsh-github-router` 设置命名空间含 secret token、代理、路由开关、缓存 TTL、字节上限)。编辑本地暂存、保存才写入;被用户覆盖的字段带徽标与「恢复默认」控件;token 为只写字段,留空表示保持已存 token,页面提供「清除已保存的 token」控件。
19
19
  - **上下文效率** —— 按类别 TTL 的响应缓存、全局字节上限、带截断注记的列表上限,以及限流余量提示。
20
20
 
21
21
  ## 环境要求
22
22
 
23
- - Node.js >= 20.18
24
- - 由 `@deepseek-ai/dsh-base` 组合的 DSH profile(提供插件使用的 `tools`、`subprocess`、`skills`、`settings`、`credentials` 服务)
23
+ - Node.js >= 22.19
25
24
  - 可选:gh 路由需要已安装且认证的 `gh` CLI;git 路由需要 `git`
26
25
 
27
26
  ## 安装
@@ -50,12 +49,16 @@ dsh plugin --profile web add github:<owner>/dsh-github-router
50
49
 
51
50
  ## 兼容性
52
51
 
52
+ 本插件跟随 DSH 的插件 API 走,而 DSH 在快速迭代,不保证跨版本兼容。
53
53
  请选择与你的 DeepSeek Harness 版本匹配的插件版本:
54
54
 
55
- | DeepSeek Harness | 安装 |
56
- | --- | --- |
57
- | 0.1.1 及更早 | `dsh-github-router@0.2.1` |
58
- | 0.1.2-alpha 及更新 | 最新版 `dsh-github-router` |
55
+ | DeepSeek Harness | 安装 |
56
+ |------------------|-------------------------------|
57
+ | 0.1.7-rc.2 | `dsh-github-router@0.4.0` 或最新 |
58
+ | 0.1.2 ~ 0.1.6 | `dsh-github-router@0.3.2` |
59
+ | 0.1.1 及更早 | `dsh-github-router@0.2.1` |
60
+
61
+ 该映射同时记录在 `package.json` 的 `dsh.compatibility.dshReleases` 中。
59
62
 
60
63
  ## 使用
61
64
 
@@ -87,12 +90,13 @@ github_api { path: "/repos/o/r/commits", query: { per_page: 5 } }
87
90
 
88
91
  ## 配置
89
92
 
90
- 设置 → **Plugins(插件)** → 可配置页中的「GitHub 路由」卡片(注册进
91
- `settings.plugin.item` 槽、以设置命名空间为 key 的框架插件卡片,需要
92
- DSH ≥ 0.1.0-rc.7):编辑内容先本地暂存、点保存才落盘,被用户覆盖的
93
- 字段带标记,留空字段回退到下方默认值;token 为只写字段,留空保存会
94
- 清除已配置的 token。同样的值也可以在组合(profile 的
95
- `cordis.patch.yml`)中作为插件基础配置写入;设置 UI 按用户覆盖。
93
+ 设置 → **Plugins(插件)** → `dsh-github-router` bundle → 该**行**上的
94
+ **配置**控件打开插件页面。页面外框由框架负责:它读取本插件导出的 schema、
95
+ 把 `dsh-github-router` 命名空间服务到浏览器,并渲染保存控件。被用户覆盖的字段带徽标和「恢复默认」控件,清空字段即回退到下方
96
+ 默认值。token 为只写字段:留空表示保持已存 token,「清除已保存的 token」
97
+ 控件才会暂存删除并在保存时写入。同样的值也可以在组合(profile 的
98
+ `cordis.patch.yml`)中作为插件基础配置写入;页面按用户覆盖,且保存的值在
99
+ 下一次工具调用即生效。
96
100
 
97
101
  | 字段 | 默认 | 含义 |
98
102
  | --- | --- | --- |
@@ -122,9 +126,10 @@ DSH ≥ 0.1.0-rc.7):编辑内容先本地暂存、点保存才落盘,被
122
126
  | 路径 | 用途 |
123
127
  | ---- | ---- |
124
128
  | `cordis.patch.yml` | 插入 `dsh-github-router` 行的 profile patch 层 |
125
- | `lib/index.js` | 宿主插件:设置段、五个工具、技能、引导 |
126
- | `lib/client.js` | 浏览器半边:独立设置页(手写工厂包,无构建步骤) |
127
- | `lib/config.js` | 设置 schema、默认值、运行时选项解析 |
129
+ | `lib/index.js` | 宿主插件:导出 `Config` schema、五个工具、技能、引导 |
130
+ | `lib/client.js` | 浏览器半边:行配置页(手写工厂包,无构建步骤) |
131
+ | `lib/config.js` | 设置 schema、默认值、实时读取、运行时选项解析 |
132
+ | `lib/settings.js` | 宿主设置接线:插件自有页面策略与 options thunk |
128
133
  | `lib/net.js`、`lib/tunnel.js` | 路由感知 HTTP 层;零依赖 CONNECT 代理隧道 |
129
134
  | `lib/routes/` | 每条路由一个模块:`api`(GET-only REST)、`gh`(CLI)、`git`(协议)、`html`(页面解析)、`mirror`(raw 镜像) |
130
135
  | `lib/core/` | 每次调用的运行时装配与 `pr`/`issue`/`file`/`probe` 聚合器 |
package/docs/design.md CHANGED
@@ -107,52 +107,58 @@ substitute bytes. When enabled, each configured base yields two candidates
107
107
  per file (raw passthrough and the `github.com` `/raw/` route), tried in
108
108
  order.
109
109
 
110
- ## Settings card
111
-
112
- The Host registers the `dsh-github-router` settings namespace on the
113
- official settings seam (durable document, schema validation, revision
114
- fencing, `applies: 'live'`). The browser half (`lib/client.js`, a
115
- hand-written ModuleLoader factory bundle with no build step) registers one
116
- plugin configuration CARD into the `settings.plugin.item` slot keyed by the
117
- namespace — the framework's mechanism for plugins distributed outside the
118
- repository (DSH ≥ 0.1.0-rc.7): the Settings → Plugins configurable tab
119
- reads which namespaces the Host serves and dispatches the intersection of
120
- that ledger and the registered cards, so the card appears only when the
121
- namespace is registered and served.
110
+ ## Settings page
111
+
112
+ The plugin exports its `Config` schema from the Loader entry (`lib/index.js`
113
+ re-exports `lib/config.js`). DSH reads that schema, serves the entry id
114
+ (`dsh-github-router`, the row `cordis.patch.yml` inserts) as the settings
115
+ namespace, derives the page and its form from the schema, validates and
116
+ persists every write with revision fencing, redacts `role('secret')` fields on
117
+ each wire boundary, and hands the Host half Volatile references for the live
118
+ values. There is no registration call. `lib/settings.js` keeps the two
119
+ plugin-owned pieces: `settings.configure({ auto: false }, ctx.fiber)` (the
120
+ plugin ships its own page, so the framework generates none) and the
121
+ `options()` thunk, which reads the live section per call so a saved value
122
+ applies to the next tool call.
122
123
 
123
124
  ### Framework transport
124
125
 
125
- Since DSH 0.1.0-rc.7 the api-proxy serves every registered settings
126
- namespace (the earlier `WEB_SETTINGS_NAMESPACES` allowlist and its
127
- `settings-not-exposed` answer retired in PR #2404), so the card binds the
128
- namespace through the framework settings transport:
129
- `ctx.settingsScope.bind({ namespace })` returns a `SettingsScope` whose
130
- snapshot carries the redacted resolved section
131
- (`status`/`value`/`base`/`user`/`revision`/`writable`). All consumers
132
- derive from one shared describe mirror, so reads never block activation,
133
- writes carry revision fencing and recovery re-reads, and the mirror
134
- refreshes on `settings/document-updated` and `connection/reset` — a Host
135
- restart no longer strands the form in a failed state. No plugin-owned HTTP
136
- route exists.
137
-
138
- ### Card form
126
+ The browser half (`lib/client.js`, a hand-written ModuleLoader factory bundle
127
+ with no build step) registers one page into the Plugins page's
128
+ `plugins.row.config` slot, keyed `<package name>#<row id>`
129
+ (`dsh-github-router#dsh-github-router`): the bundle's row gains a **Configure**
130
+ control that opens the page, and the entry's `view: 'summary'` answers the
131
+ row's one-liner. The slot is declared and dispatched by the Plugins page, so
132
+ the page exists while the Host serves the namespace and disappears with it
133
+ (`ctx.configForms.whileServed`). Reads and writes ride the shared configuration
134
+ form for the entry, `ctx.configForms.get('dsh-github-router')`, whose snapshot
135
+ carries the redacted resolved section
136
+ (`status`/`value`/`base`/`user`/`revision`/`writable`/`mode`) and whose
137
+ `set`/`unset`/`mutate` queue revision-fenced writes with recovery re-reads. All
138
+ of it derives from one shared describe mirror that refreshes on
139
+ `settings/document-updated` and `connection/reset`, so a Host restart no longer
140
+ strands the form in a failed state. No plugin-owned HTTP route exists.
141
+
142
+ ### Page form
139
143
 
140
144
  Edits are staged locally and written only on save, one field per
141
- `scope.set`/`scope.unset`; after each write the card verifies the user
142
- layer (JSON-shaped deep equality) and keeps drafts that did not land.
143
- Secret fields never ride a response, so the token control is write-only:
144
- its configured state comes from the describe mirror's secret slot list
145
- (the snapshot itself redacts it from every layer), a typed value writes the
146
- token through `settings.mutate` (the one direction secrets cross the wire),
147
- and a blank draft clears a configured one.
148
-
149
- Because the scope writes **scalar fields by name**, the settings schema is
150
- deliberately flat (`routesApi`, `cacheTtlMeta`, …); the Host projects it
145
+ `scope.set`/`scope.unset`; after each write the card verifies the user layer
146
+ (JSON-shaped deep equality) and keeps drafts that did not land. Leaving the
147
+ page drops every staged edit. Secret fields never ride a response, so the token
148
+ control is write-only: its configured state comes from the describe mirror's
149
+ secret-slot list (`ctx.configForms.describe()`, the snapshot itself redacts the
150
+ value from every layer), a typed value writes the token through the shared form
151
+ (the one direction secrets cross the wire), and a blank draft deliberately
152
+ writes nothing — the explicit **Clear stored token** control stages the `unset`
153
+ that removes a configured token.
154
+
155
+ Because the shared form writes **scalar fields by name**, the settings schema
156
+ is deliberately flat (`routesApi`, `cacheTtlMeta`, …); the Host projects it
151
157
  into the nested runtime shape (`routes.api`, `cacheTtlSeconds.meta`) in
152
- `resolveOptions`. The composition layer can still carry the same flat
153
- keys. The card shows the common fields up top (token, proxy, main route
154
- switches) and the long tail (timeouts, retries, cache TTLs, mirrors,
155
- repos, git cache dir) in a collapsed "Advanced settings" disclosure.
158
+ `resolveOptions`. The composition layer can still carry the same flat keys. The
159
+ page shows the common fields up top (token, proxy, main route switches) and the
160
+ long tail (timeouts, retries, cache TTLs, mirrors, repos, git cache dir) in a
161
+ collapsed "Advanced settings" disclosure.
156
162
 
157
163
  ## Cache
158
164
 
@@ -189,9 +195,11 @@ within one host process.
189
195
 
190
196
  ## Known limitations
191
197
 
192
- - **Fallback routes are unexercised while the API is healthy.** The ladder
193
- is failover, not fan-out: when api succeeds, gh/html/git are not called
194
- (saving quota). Forcing a specific route per call is not yet supported.
198
+ - **Fallback routes are failover, not fan-out.** A ladder is entered only
199
+ when a part could not be fetched by the route above it: a successfully
200
+ fetched empty list (an issue with no comments, a PR with no reviews) is
201
+ authoritative and does not trigger gh/html/git, saving quota. Forcing a
202
+ specific route per call is not yet supported.
195
203
  - **Anonymous quota is shared per IP** (60 req/h). A PR aggregation costs
196
204
  up to six calls. Token configuration is the intended mitigation; the
197
205
  cache is the second line.