dsh-plugin-discourse 1.0.1 → 1.1.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/README.md +82 -17
- package/cordis.patch.yml +7 -1
- package/index.js +1179 -30
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +5 -4
- package/scripts/login-test.mjs +272 -0
- package/scripts/smoke.mjs +18 -0
- package/scripts/verify-package.mjs +1 -0
package/README.md
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
Discourse administration and agent tools for the [DeepSeek Harness](https://github.com/deepseek-ai).
|
|
7
7
|
|
|
8
|
-
One Host plugin registers
|
|
9
|
-
forum, read topics and profiles, publish posts,
|
|
10
|
-
staff account would do in the web UI
|
|
8
|
+
One Host plugin registers 17 model-facing tools over the Discourse REST API: an agent can search a
|
|
9
|
+
forum, read topics and profiles, publish posts, carry out the moderator and administrator work a
|
|
10
|
+
staff account would do in the web UI, and sign in through the user's browser with a scoped user API
|
|
11
|
+
key.
|
|
11
12
|
|
|
12
13
|
```
|
|
13
14
|
User → DSH agent → discourse_* tools → Discourse REST API → forum
|
|
@@ -17,6 +18,7 @@ User → DSH agent → discourse_* tools → Discourse REST API → forum
|
|
|
17
18
|
|
|
18
19
|
| Tool | Group | What it does | Key needed |
|
|
19
20
|
|---|---|---|---|
|
|
21
|
+
| `discourse_login` | auth | Start browser authorization, report the session, finish an older payload page, cancel a pending attempt, or sign out | no |
|
|
20
22
|
| `discourse_search` | read | Search topics, posts, and users | no |
|
|
21
23
|
| `discourse_topics` | read | List a feed (`latest`/`new`/`unread`/`top`) or one category | no |
|
|
22
24
|
| `discourse_topic` | read | Read a topic and a slice of its posts | no |
|
|
@@ -111,6 +113,11 @@ it at your forum, add an override to your **profile's** `cordis.patch.yml` (`$DS
|
|
|
111
113
|
readOnly: true
|
|
112
114
|
timeoutMs: 20000
|
|
113
115
|
maxItems: 12
|
|
116
|
+
authMode: auto
|
|
117
|
+
loginScopes: read,write,session_info
|
|
118
|
+
loginClientId: dsh-plugin-discourse
|
|
119
|
+
loginApplicationName: DeepSeek Harness
|
|
120
|
+
loginTimeoutMs: 600000
|
|
114
121
|
```
|
|
115
122
|
|
|
116
123
|
A loader override replaces the row's `config` wholesale, so restate every key you want to keep.
|
|
@@ -123,6 +130,11 @@ A loader override replaces the row's `config` wholesale, so restate every key yo
|
|
|
123
130
|
| `readOnly` | `false` | Blocks every mutating tool when `true`. |
|
|
124
131
|
| `timeoutMs` | `20000` | Per-request deadline, 1000–120000. |
|
|
125
132
|
| `maxItems` | `12` | Default listing size, 1–50. |
|
|
133
|
+
| `authMode` | `auto` | `auto` uses a stored browser session for normal calls and `apiKey` for staff/admin calls; `api-key` ignores the session; `user` requires it for every call. |
|
|
134
|
+
| `loginScopes` | `read,write,session_info` | Comma-separated scopes requested by browser sign-in. Narrow or extend this to match the forum's `allow_user_api_key_scopes`. |
|
|
135
|
+
| `loginClientId` | `dsh-plugin-discourse` | Client id Discourse records with the authorized key. |
|
|
136
|
+
| `loginApplicationName` | `DeepSeek Harness` | Application name shown on the forum authorization page. |
|
|
137
|
+
| `loginTimeoutMs` | `600000` | Browser sign-in deadline, 30000–1800000. Discourse's device code lives 10 minutes. |
|
|
126
138
|
|
|
127
139
|
Anonymous reads work out of the box against the default forum, so you can try `discourse_topics`
|
|
128
140
|
before configuring anything.
|
|
@@ -144,11 +156,54 @@ export DISCOURSE_API_USERNAME="staff-bot"
|
|
|
144
156
|
|
|
145
157
|
Row config wins over the environment when both are present.
|
|
146
158
|
|
|
159
|
+
### Browser sign-in
|
|
160
|
+
|
|
161
|
+
`discourse_login` obtains a **user API key** for the signed-in forum account, so the plugin can act as
|
|
162
|
+
that user without an admin key in the profile. On current Discourse releases it uses the device
|
|
163
|
+
authorization flow (`Auth API` version 4):
|
|
164
|
+
|
|
165
|
+
1. `discourse_login` with `action: "start"` returns a verification URL and an eight-character code.
|
|
166
|
+
2. Open the URL, sign in to the forum if needed, and confirm the code.
|
|
167
|
+
3. `discourse_login` with `action: "status"` reports the signed-in account.
|
|
168
|
+
|
|
169
|
+
The tool also accepts `waitSeconds` on `status` (0–120) when the caller wants to wait for the browser
|
|
170
|
+
step before reporting. `action: "cancel"` withdraws a pending attempt without touching a stored
|
|
171
|
+
session.
|
|
172
|
+
|
|
173
|
+
Forums older than the device flow get the classic `/user-api-key/new` page instead. The plugin asks
|
|
174
|
+
for the encrypted payload that page shows after approval, and
|
|
175
|
+
`discourse_login` with `action: "complete"` and the pasted `payload` finishes the sign-in. Neither
|
|
176
|
+
path needs an `auth_redirect`, a callback listener, or a change to
|
|
177
|
+
`allowed_user_api_auth_redirects`.
|
|
178
|
+
|
|
179
|
+
The authorized key is stored as the `discourse/session` record in the DSH credential store — never in
|
|
180
|
+
`cordis.patch.yml` and never in a tool result. `discourse_login` with `action: "logout"` revokes it at
|
|
181
|
+
the forum when possible and deletes the local record. `readOnly` still permits sign-in and sign-out:
|
|
182
|
+
they manage authentication, not forum content.
|
|
183
|
+
|
|
184
|
+
`authMode` decides which credential a request uses:
|
|
185
|
+
|
|
186
|
+
- `auto` (default) — normal reads and posts use the stored session; staff/admin tools keep using
|
|
187
|
+
`apiKey` when one is configured, so moderation and administration still work.
|
|
188
|
+
- `api-key` — ignore any stored session and always use `apiKey`.
|
|
189
|
+
- `user` — require the stored session for every call; requests fail with a sign-in hint when it is
|
|
190
|
+
missing or expired.
|
|
191
|
+
|
|
192
|
+
A user API key is limited to the account's own permissions and the scopes granted on the consent
|
|
193
|
+
page. It cannot call admin endpoints such as `/admin/users.json`; those tools need `apiKey` (or fail
|
|
194
|
+
with the forum's 403). A forum that does not allow `write` in `allow_user_api_key_scopes` needs
|
|
195
|
+
`loginScopes: read,session_info`.
|
|
196
|
+
|
|
147
197
|
## Safety
|
|
148
198
|
|
|
149
|
-
- `readOnly: true`
|
|
199
|
+
- `readOnly: true` blocks every content-mutating tool before any forum request is built. Browser
|
|
200
|
+
sign-in and sign-out remain available because they manage the credential rather than content.
|
|
201
|
+
- `discourse_login` stores the user API key in the credential store and never prints it. Use
|
|
202
|
+
`action: "logout"` to revoke it at the forum and remove the local record.
|
|
150
203
|
- `discourse_moderate_user` and the site-settings tools require an admin-scoped key; a key without
|
|
151
|
-
those scopes receives the forum's 403 response, and the tool reports it instead of retrying.
|
|
204
|
+
those scopes receives the forum's 403 response, and the tool reports it instead of retrying. A
|
|
205
|
+
browser session does not widen a user key into an admin key; `authMode: auto` therefore prefers a
|
|
206
|
+
configured `apiKey` for staff and admin calls.
|
|
152
207
|
- Failures carry the HTTP status and Discourse's own error text, so a wrong id or a missing scope is
|
|
153
208
|
visible in the transcript.
|
|
154
209
|
- Writes should be exercised with a staging forum first — `discourse_delete_post` with
|
|
@@ -160,16 +215,20 @@ The package is plain ESM with no runtime dependencies; `index.js` is the whole p
|
|
|
160
215
|
|
|
161
216
|
```bash
|
|
162
217
|
node --check index.js # syntax
|
|
163
|
-
npm test # register every tool
|
|
218
|
+
npm test # register every tool, validate definitions, and exercise browser login offline
|
|
164
219
|
node scripts/smoke.mjs --live # additionally call read-only tools against the forum
|
|
220
|
+
node scripts/login-test.mjs # browser login flow against a stubbed forum and credential store
|
|
165
221
|
npm run verify # build the npm tarball and check its file list
|
|
166
222
|
node scripts/doctor.mjs --profile desktop # inspect a profile for a stale local link
|
|
167
223
|
```
|
|
168
224
|
|
|
169
225
|
`scripts/smoke.mjs` drives `apply()` with a stub context, so it exercises the real registration path
|
|
170
|
-
without starting a Harness. `scripts/
|
|
171
|
-
|
|
172
|
-
|
|
226
|
+
without starting a Harness. `scripts/login-test.mjs` drives the same `apply()` path with a stub
|
|
227
|
+
credential store, authorization seam, and forum: it starts the classic page, completes it with an RSA
|
|
228
|
+
payload, reads the status, and signs out, then repeats the device-code path. `scripts/verify-package.mjs`
|
|
229
|
+
packs the package with `npm pack` and fails when a runtime file is missing from `files`, when the
|
|
230
|
+
manifest loses `dsh.bundle`, or when a locale loses its display metadata. `prepack` runs the smoke and
|
|
231
|
+
login tests, so a broken package cannot be published.
|
|
173
232
|
|
|
174
233
|
## Release
|
|
175
234
|
|
|
@@ -182,7 +241,7 @@ push to `main` or a `v*` tag, and every pull request, verifies the package, and
|
|
|
182
241
|
2. Commit, then tag and push:
|
|
183
242
|
|
|
184
243
|
```bash
|
|
185
|
-
git tag -a v1.0
|
|
244
|
+
git tag -a v1.1.0 -m "dsh-plugin-discourse 1.1.0"
|
|
186
245
|
git push origin main --follow-tags
|
|
187
246
|
```
|
|
188
247
|
|
|
@@ -199,22 +258,28 @@ Before the first release, add a **Trusted Publisher** on npmjs.com (package page
|
|
|
199
258
|
| Environment | *(leave empty)* |
|
|
200
259
|
| Allowed action | `npm publish` |
|
|
201
260
|
|
|
202
|
-
|
|
203
|
-
(`id-token: write`) for publish rights and attaches provenance.
|
|
204
|
-
|
|
205
|
-
|
|
261
|
+
With the trusted publisher in place the workflow needs no long-lived credential: it exchanges the
|
|
262
|
+
GitHub OIDC token (`id-token: write`) for publish rights and attaches provenance. Until that setting
|
|
263
|
+
exists, the publish job falls back to the repository's `NPM_TOKEN` secret, and it fails with a
|
|
264
|
+
diagnostic when neither credential is available. npm now requires an interactive 2FA challenge for
|
|
265
|
+
token and trusted-publisher management, so the one-time setup happens in the browser; after it, tagged
|
|
266
|
+
releases are fully automatic.
|
|
206
267
|
|
|
207
268
|
## 中文说明
|
|
208
269
|
|
|
209
|
-
- 这是一个 **Host 插件**,向 DSH 智能体提供
|
|
270
|
+
- 这是一个 **Host 插件**,向 DSH 智能体提供 17 个 Discourse 工具(登录 / 读取 / 发帖 / 版主 / 管理员)。
|
|
271
|
+
- 浏览器授权登录:先调用 `discourse_login`(`action: "start"`),插件会返回论坛授权页地址和 8 位验证码;在浏览器中登录论坛并确认验证码后,再用 `action: "status"` 查看登录结果。中途放弃可用 `action: "cancel"`。新版 Discourse 使用设备授权码流程;旧版论坛会显示加密 payload,把该 payload 交给 `action: "complete"` 即可完成。两种方式都不需要配置 `auth_redirect`,也不需要修改 `allowed_user_api_auth_redirects`。
|
|
272
|
+
- 登录得到的用户 API Key 保存在 DSH 凭据存储的 `discourse/session` 记录中,不会写进 `cordis.patch.yml`,也不会出现在工具结果里;`action: "logout"` 会尽量在论坛撤销它并删除本地记录。
|
|
273
|
+
- `authMode: auto`(默认)下,普通读取和发帖使用已登录的会话,版主/管理员工具继续使用 `apiKey`;改为 `api-key` 可完全忽略会话,改为 `user` 则所有请求都必须先登录。
|
|
274
|
+
- 用户 API Key 只拥有该账号的权限,不能调用 `/admin/*` 管理接口;管理工具仍需 `apiKey`。如果论坛不允许 `write` scope,请把 `loginScopes` 调成 `read,session_info`。
|
|
210
275
|
- 安装:优先用 Web UI 的 *设置 → 插件 → 安装 bundle*,填入 `dsh-plugin-discourse`(npm)或 `github:AkarinLiu/dsh-plugin-discourse`;安装器会先替换旧的本地链接。
|
|
211
276
|
- 命令行:`dsh plugin --profile desktop add dsh-plugin-discourse` 只是把参数转交给 pnpm,安装后还需在插件页选中该 bundle。
|
|
212
277
|
- Windows 从本地克隆切到 npm 时报 `ERR_PNPM_EPERM ... rename ...dsh-plugin-discourse...`:这是 profile 里遗留的目录联接;运行 `node scripts/doctor.mjs --profile desktop --fix` 后再安装(脚本只删链接,不动源码),然后重启 Harness。
|
|
213
278
|
- 发布:推送 `v*` 标签会触发 `.github/workflows/publish.yml`。首次发布前在 npmjs.com 的包设置 → *Trusted Publishing* 添加 GitHub Actions(仓库 `AkarinLiu/dsh-plugin-discourse`、工作流 `publish.yml`、环境留空、允许 `npm publish`),之后不需要 `NPM_TOKEN`,由 OIDC 发布并生成 provenance。
|
|
214
279
|
- 配置:在 profile 的 `cordis.patch.yml` 中加一条 `id: discourse` 的覆盖,把 `baseUrl` 改成你的论坛并填入 `apiKey`(覆盖会整体替换 `config`,请把其它键一并写全)。
|
|
215
|
-
- 不需要 API Key 即可试用只读工具(默认指向 `https://meta.discourse.org
|
|
280
|
+
- 不需要 API Key 即可试用只读工具(默认指向 `https://meta.discourse.org`),也可以直接用浏览器登录获得用户会话。
|
|
216
281
|
- 密钥建议通过环境变量 `DISCOURSE_API_KEY` / `DISCOURSE_API_USERNAME` 提供,避免写进配置文件。
|
|
217
|
-
- 不需要写入权限时把 `readOnly` 设为 `true
|
|
282
|
+
- 不需要写入权限时把 `readOnly` 设为 `true`,所有写操作会被插件直接拒绝;登录和退出仍可使用。
|
|
218
283
|
|
|
219
284
|
## License
|
|
220
285
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Inserts the Discourse tool bundle into the current DSH profile.
|
|
2
2
|
# Change `baseUrl` to your forum and point `apiKey` at an admin-scoped key
|
|
3
|
-
# before enabling writes.
|
|
3
|
+
# before enabling writes. Browser sign-in stores a scoped user API key in the
|
|
4
|
+
# DSH credential store, so no key is required in this file for `discourse_login`.
|
|
4
5
|
- insert:
|
|
5
6
|
- id: discourse
|
|
6
7
|
name: dsh-plugin-discourse
|
|
@@ -11,3 +12,8 @@
|
|
|
11
12
|
readOnly: false
|
|
12
13
|
timeoutMs: 20000
|
|
13
14
|
maxItems: 12
|
|
15
|
+
authMode: auto
|
|
16
|
+
loginScopes: read,write,session_info
|
|
17
|
+
loginClientId: dsh-plugin-discourse
|
|
18
|
+
loginApplicationName: DeepSeek Harness
|
|
19
|
+
loginTimeoutMs: 600000
|