dsh-workspace-api-key 0.2.12 → 0.2.13
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 +3 -3
- package/README.zh.md +3 -3
- package/lib/index.js +58 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ session key → workspace key → system default
|
|
|
22
22
|
- **No silent substitution** — neither warning ever changes which key is used. The decision stays with you, which is the whole point: an automatically swapped key would defeat the billing separation.
|
|
23
23
|
- **One-click revert** — clearing a session entry falls back to the workspace key, clearing a workspace entry falls back to the system default.
|
|
24
24
|
- **A search default key of its own** — the `搜索默认 key` card binds a key to `DEEPSEEK_API_KEY_SEARCH`, so search can be billed apart from chat: a session's own key wins, then a synced workspace's chat cascade, then this key, then whatever the official search page stores.
|
|
25
|
-
- **Takes over the official search key field** — on startup, while the new Web Search API is on, the plugin points the official `web-deepseek-api` / `web-search-deepseek` entry at `DEEPSEEK_API_KEY_SEARCH` (only that `apiKeyEnv` pointer, never a key value). The `设置 → 插件 → 网页搜索` field then edits the same search key this panel does, so it can no longer overwrite your chat key, and there is no second place where a search key can hide.
|
|
25
|
+
- **Takes over the official search key field, once there is a key to put there** — on startup, while the new Web Search API is on, the plugin points the official `web-deepseek-api` / `web-search-deepseek` entry at `DEEPSEEK_API_KEY_SEARCH` (only that `apiKeyEnv` pointer, never a key value). The `设置 → 插件 → 网页搜索` field then edits the same search key this panel does, so it can no longer overwrite your chat key, and there is no second place where a search key can hide. Until you save a search key, the official entry keeps pointing at the chat key, so upgrading never breaks `web_search`; the panel says so instead of failing silently.
|
|
26
26
|
- **A search switch per workspace** — every workspace row carries a `同步搜索 API` checkbox. Ticked, the `web_search` / `web_fetch` calls made by sessions in that directory use the key they would use for chat (`session → workspace → system default`); unticked, they keep the key the official 设置 → 插件 → 网页搜索 page manages. A session with its own key always wins, so "sync on, session configured separately" spends the session key, not the search key.
|
|
27
27
|
- **Search warnings that arrive before the failure** — when a search key is configured but `启用新版 Web 搜索 API` is off, or when the new API is on but its key ref is empty, the panel shows a warning banner and the entering-session overlay says it too, in one line you can act on. The overlay stays quiet about search problems when the search provider is not the one in use.
|
|
28
28
|
- **A usage guide** — `使用说明` at the top of the panel recommends two keys: a chat default (`default`) and a search default (`search-default`), so the two budgets stay separate; it also draws the cascade and lists the situations that trigger a warning.
|
|
@@ -79,7 +79,7 @@ The command forwards its arguments to pnpm inside the profile directory, so the
|
|
|
79
79
|
pnpm add dsh-workspace-api-key # or: npm install dsh-workspace-api-key
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
Pin a version with `dsh-workspace-api-key@0.2.
|
|
82
|
+
Pin a version with `dsh-workspace-api-key@0.2.13`, or install straight from the repository with `dsh-workspace-api-key@github:COH2357/dsh-workspace-api-key` (a git-hosted plugin is built from source on install).
|
|
83
83
|
|
|
84
84
|
Then add `"dsh-workspace-api-key"` to `dsh.profile.bundles` in that profile's `package.json` and restart DSH. For local development, use a link instead:
|
|
85
85
|
|
|
@@ -90,7 +90,7 @@ Then add `"dsh-workspace-api-key"` to `dsh.profile.bundles` in that profile's `p
|
|
|
90
90
|
## Tests
|
|
91
91
|
|
|
92
92
|
```sh
|
|
93
|
-
node test/host.test.mjs #
|
|
93
|
+
node test/host.test.mjs # 175 assertions
|
|
94
94
|
node test/client.test.mjs # 173 assertions
|
|
95
95
|
```
|
|
96
96
|
|
package/README.zh.md
CHANGED
|
@@ -22,7 +22,7 @@ DSH 只从一个全局凭据 ref(默认 `DEEPSEEK_API_KEY`)解析 DeepSeek k
|
|
|
22
22
|
- **绝不悄悄换 key** —— 两种提醒都不会改动实际使用的 key,决定权始终在你手上;自动替换会直接破坏「按会话分离计费」这个初衷。
|
|
23
23
|
- **一键回退** —— 清空会话条目回到工作区 key,清空工作区条目回到系统默认。
|
|
24
24
|
- **搜索默认 key 单独一把** —— 「搜索默认 key」卡片把这把 key 绑到 `DEEPSEEK_API_KEY_SEARCH`,于是搜索可以和聊天分开计费:会话自己的 key 最优先,其次勾了同步的工作区走聊天层级,再次是这把搜索默认 key,最后才是官方搜索页里配的那把。
|
|
25
|
-
- **接管官方搜索 key
|
|
25
|
+
- **接管官方搜索 key 设置(先有 key 才接管)** —— 启动时若已启用新版 Web 搜索 API,插件把官方 `web-deepseek-api` / `web-search-deepseek` 条目的 `apiKeyEnv` 指到 `DEEPSEEK_API_KEY_SEARCH`(只改这一个指向,永远不动 key 内容)。此后「设置 → 插件 → 网页搜索」里填的就是本面板这把搜索 key,既不会再顶掉聊天 key,也不存在第二处藏搜索 key 的地方。还没配搜索 key 时,官方条目继续指着聊天那把,所以升级不会让 `web_search` 直接失效,面板会如实提醒「搜索暂时和聊天共用一把 key」。
|
|
26
26
|
- **每个工作区一个搜索开关** —— 每个工作区行上都有「同步搜索 API」复选框。勾选后,该目录下会话发出的 `web_search` / `web_fetch` 请求改用它们聊天时用的那把 key(会话 → 工作区 → 系统默认);不勾选则继续用官方「设置 → 插件 → 网页搜索」里配置的那把 key。会话自己配了 key 时永远优先,所以「工作区同步、会话单独设置」花的是会话那把 key,而不是搜索默认 key。
|
|
27
27
|
- **在失败之前就提醒的搜索告警** —— 配了搜索 key 但没打开「启用新版 Web 搜索 API」,或者新版 API 打开了但它的 key ref 是空的,面板会显示提醒条,点进会话时的覆盖层也会用一句可直接照做的话说明;当生效的搜索通道不是新版 API 时,覆盖层不会拿搜索问题打扰你。
|
|
28
28
|
- **使用说明** —— 面板顶部有「使用说明」,推荐两把 key 分工:聊天默认用 `default`,搜索默认用 `search-default`,两份预算互不干扰;里面还画了层级,并列出了会触发提醒的几种情况。
|
|
@@ -79,7 +79,7 @@ dsh plugin --profile web add dsh-workspace-api-key
|
|
|
79
79
|
pnpm add dsh-workspace-api-key # 或者:npm install dsh-workspace-api-key
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
锁版本用 `dsh-workspace-api-key@0.2.
|
|
82
|
+
锁版本用 `dsh-workspace-api-key@0.2.13`;直接从仓库装则用 `dsh-workspace-api-key@github:COH2357/dsh-workspace-api-key`(git 来源的插件会在安装时现场构建)。
|
|
83
83
|
|
|
84
84
|
然后把 `"dsh-workspace-api-key"` 加进该 profile `package.json` 的 `dsh.profile.bundles`,重启 DSH。本地开发用 link 代替:
|
|
85
85
|
|
|
@@ -90,7 +90,7 @@ pnpm add dsh-workspace-api-key # 或者:npm install dsh-workspace-api
|
|
|
90
90
|
## 测试
|
|
91
91
|
|
|
92
92
|
```sh
|
|
93
|
-
node test/host.test.mjs #
|
|
93
|
+
node test/host.test.mjs # 175 条断言
|
|
94
94
|
node test/client.test.mjs # 173 条断言
|
|
95
95
|
```
|
|
96
96
|
|
package/lib/index.js
CHANGED
|
@@ -269,6 +269,17 @@ function searchDefaultRefOf(web) {
|
|
|
269
269
|
return official === DEFAULT_API_KEY_REF ? SEARCH_DEFAULT_REF : official
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
/**
|
|
273
|
+
* 「搜索没有自己的 key 时退回哪一把」——官方搜索页当前指向的 ref。
|
|
274
|
+
*
|
|
275
|
+
* 例外:如果那个 ref 是插件自己改上去的 `DEEPSEEK_API_KEY_SEARCH`,而它还是空的
|
|
276
|
+
* (用户还没配搜索默认 key),就退回聊天 ref——否则升级完、还没配搜索 key 时,
|
|
277
|
+
* 所有 web_search 都会因为「ref 是空的」直接失败。
|
|
278
|
+
*/
|
|
279
|
+
function fallbackSearchRef(web) {
|
|
280
|
+
return web.searchKeyEnv === SEARCH_DEFAULT_REF ? DEFAULT_API_KEY_REF : web.searchKeyEnv
|
|
281
|
+
}
|
|
282
|
+
|
|
272
283
|
/**
|
|
273
284
|
* 把官方两套搜索插件的 `apiKeyEnv` 都指向同一个 ref。
|
|
274
285
|
*
|
|
@@ -301,22 +312,35 @@ async function bindOfficialSearchRef(ctx, ref) {
|
|
|
301
312
|
}
|
|
302
313
|
|
|
303
314
|
/**
|
|
304
|
-
*
|
|
315
|
+
* 启动时把官方「网页搜索」页的 key 指向拉回一致。
|
|
305
316
|
*
|
|
306
317
|
* 官方页那个 Key 输入框写的永远是「当前 API 段的 apiKeyEnv 指向的 ref」。用户要求
|
|
307
|
-
* 「直接禁用官方页面里的搜索 key
|
|
318
|
+
* 「直接禁用官方页面里的搜索 key 设置」——所以插件要把它指到自己的 `DEEPSEEK_API_KEY_SEARCH`:
|
|
308
319
|
* 从那以后官方页填的就是插件这把搜索 key,既不会误改聊天 key,也不再存在第二把搜索 key。
|
|
309
320
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
321
|
+
* 但接管有个前提:搜索默认 key 已经存在。官方那条 ref 指向一把空 key 会让**所有搜索直接失败**,
|
|
322
|
+
* 所以这里分两种情况:
|
|
323
|
+
* - 官方指着搜索 ref、但那把 key 还没有 → 退回聊天那把,搜索继续可用(等用户配了再接管);
|
|
324
|
+
* - 官方指着聊天那把、而搜索 key 已经配好 → 接管过去。
|
|
325
|
+
* 只改 apiKeyEnv 指向,不动任何 key 的值;settings 不可写就静默跳过。
|
|
312
326
|
*/
|
|
313
|
-
async function
|
|
327
|
+
async function reconcileOfficialSearchRef(ctx) {
|
|
314
328
|
const web = readWebSettings(ctx)
|
|
315
329
|
if (web.apiChannel !== true) return
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
330
|
+
const official = officialSearchRef(web)
|
|
331
|
+
if (official !== SEARCH_DEFAULT_REF && official !== DEFAULT_API_KEY_REF) return
|
|
332
|
+
const owned = (await describeKey(ctx, SEARCH_DEFAULT_REF)).configured === true
|
|
333
|
+
if (official === SEARCH_DEFAULT_REF && !owned) {
|
|
334
|
+
const bound = await bindOfficialSearchRef(ctx, DEFAULT_API_KEY_REF)
|
|
335
|
+
if (bound.ok !== true) log('restore official search ref failed: %s', bound.reason)
|
|
336
|
+
else if (bound.changed.length > 0) log('search default key is empty; official search key falls back to %s', DEFAULT_API_KEY_REF)
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
if (official === DEFAULT_API_KEY_REF && owned) {
|
|
340
|
+
const bound = await bindOfficialSearchRef(ctx, SEARCH_DEFAULT_REF)
|
|
341
|
+
if (bound.ok !== true) log('adopt official search ref failed: %s', bound.reason)
|
|
342
|
+
else if (bound.changed.length > 0) log('adopted official search key settings: %s', bound.changed.join(', '))
|
|
343
|
+
}
|
|
320
344
|
}
|
|
321
345
|
|
|
322
346
|
/**
|
|
@@ -336,7 +360,7 @@ async function searchRefFor(ctx, state, { sessionId, cwd, web, searchDefault })
|
|
|
336
360
|
// 没同步:搜索默认 key(官方 ref 或插件自有的 ref)优先,其次才是官方搜索页配的那把。
|
|
337
361
|
const ownRef = searchDefaultRefOf(w)
|
|
338
362
|
const configured = searchDefault ?? (await describeKey(ctx, ownRef)).configured === true
|
|
339
|
-
return configured === true ? ownRef : w
|
|
363
|
+
return configured === true ? ownRef : fallbackSearchRef(w)
|
|
340
364
|
}
|
|
341
365
|
|
|
342
366
|
// ───────────────── 会话列表(宿主 sessionController + live 兜底) ─────────────────
|
|
@@ -878,15 +902,28 @@ async function searchWarnings(ctx, state, web) {
|
|
|
878
902
|
})
|
|
879
903
|
}
|
|
880
904
|
|
|
881
|
-
// 2) 开了新版 API
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
905
|
+
// 2) 开了新版 API,却没有搜索默认 key:
|
|
906
|
+
// 聊天 key 还在 → 搜索暂时沿用聊天那把(能跑,但计费混在一起,值得提醒);
|
|
907
|
+
// 聊天 key 也没了 → 搜索会直接报缺少凭据。
|
|
908
|
+
if (web.apiChannel && web.apiLiteralKey === undefined && !searchDefaultConfigured) {
|
|
909
|
+
const chatConfigured = (await describeKey(ctx, DEFAULT_API_KEY_REF)).configured === true
|
|
910
|
+
if (chatConfigured) {
|
|
911
|
+
out.push({
|
|
912
|
+
code: 'search-default-missing',
|
|
913
|
+
level: 'warn',
|
|
914
|
+
title: '搜索默认 key 还没配置:搜索暂时沿用聊天那把 key',
|
|
915
|
+
detail: `现在搜索读的是 ${DEFAULT_API_KEY_REF}(和聊天共用一把),所以搜索与聊天的花费算在一起。`
|
|
916
|
+
+ `在本面板的「搜索默认 key」里保存一把后,插件会自动把官方配置指过去,两边就分开了。`,
|
|
917
|
+
})
|
|
918
|
+
} else {
|
|
919
|
+
out.push({
|
|
920
|
+
code: 'search-key-missing',
|
|
921
|
+
level: 'bad',
|
|
922
|
+
title: '新版 Web 搜索 API 已启用,但搜索 key 没有配置',
|
|
923
|
+
detail: `搜索会用 ${web.apiKeyEnv} 去取 key,而它当前是空的:任何 web_search 都会失败。`
|
|
924
|
+
+ '到「设置 → 插件 → 网页搜索」里填一把,或在本面板的「搜索默认 key」里保存一把。',
|
|
925
|
+
})
|
|
926
|
+
}
|
|
890
927
|
}
|
|
891
928
|
|
|
892
929
|
// 2b) 面板里配了「搜索默认 key」,但新版 API 没开 → 这把 key 也不会被读到。
|
|
@@ -1225,7 +1262,7 @@ export function apply(ctx) {
|
|
|
1225
1262
|
installWebScope(ctx)
|
|
1226
1263
|
installResolver(ctx, state)
|
|
1227
1264
|
// 「直接禁用官方页面里的搜索 key 设置」:把官方 apiKeyEnv 指到插件自有 ref(异步、失败不影响启动)。
|
|
1228
|
-
void
|
|
1265
|
+
void reconcileOfficialSearchRef(ctx)
|
|
1229
1266
|
|
|
1230
1267
|
// ── 2. REST API(loopback 可信)──────────────────────────────────────
|
|
1231
1268
|
let webServer
|
|
@@ -1571,7 +1608,7 @@ async function workspaceRow(ctx, state, { id, path: dir, title, kind }, rows, cl
|
|
|
1571
1608
|
: DEFAULT_API_KEY_REF
|
|
1572
1609
|
: searchDefaultConfigured
|
|
1573
1610
|
? searchDefaultRefOf(web)
|
|
1574
|
-
: web
|
|
1611
|
+
: fallbackSearchRef(web)
|
|
1575
1612
|
: web.searchKeyEnv
|
|
1576
1613
|
const searchKeyMasked = web.apiChannel ? maskKey((await resolveKey(ctx, searchKeyRef))?.value) : undefined
|
|
1577
1614
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-workspace-api-key",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "A DeepSeek Harness plugin that binds a separate DeepSeek API key to each workspace, or to a single session, so the spend of one key stays with the sessions you choose.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|