中文EN

dsh-workspace-api-key

已测通过npmMIT 條件偏離

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.

📦 体积
0.2 MB · 小巧
🔌 需要联网
未声明
✅ 沙箱测试
已通过
🏷️ 已发布版本
18 个

這個插件適合誰

  • 經常開長會話、需要會話管理的人
下载量 / Downloads
0
依赖体积 / node_modules
0.2 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

安装

dsh plugin --profile web add dsh-workspace-api-key

沙箱测试结果

條件偏離 —— 本結果在 DSHIO/T 21 v0.9 條件下產生(內存 1g、CPU 1.5、T4 超時 60s,未記鏡像 digest)。偏離條款:5.1.3(内存 768MB)、5.1.3(CPU 1 核)、5.1.6(T4 超时 90s)、5.1.2(镜像 digest)。按 DSHIO/T 21 §8.2,該判定不得與標準條件結果比較,不得用作認證依據。
阶段结果
T0 静态检查—
T1 安装✓
T2 列装✓
T3 挂载✓
T4 启动—

四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。

README

dsh-workspace-api-key

[English](README.md) | 中文

一个 DeepSeek Harness 插件:为每个工作区——必要时也可以为单个会话——单独绑定一把 DeepSeek API key。

DSH 只从一个全局凭据 ref(默认 DEEPSEEK_API_KEY)解析 DeepSeek key,于是所有工作区的所有会话花的都是同一把 key。这个插件加了一个面板,让单个工作区、甚至单个会话可以用自己的 key,这样一把 key 的花费就只落在你指定的会话上。没有单独配置的层级继续用上层设置:

会话 key  →  工作区 key  →  系统默认

你会得到

  • 一行侧栏入口,叫「API Key 分配」,紧跟在宿主自己的「插件」行下方,点开是全宽页面。
  • 按工作区配置的 key —— 列出 workspaceRegistry 服务里注册的全部工作区,各自一个 key 输入框。
  • 按会话配置的 key —— 每个工作区行可以展开(默认折叠),列出该目录下的会话,每个会话有自己的 key;从上层继承的会话会如实标注层级,而不是假装自己配了 key。
  • 系统默认卡片 —— 当前生效的 ref、值的来源(file、env 等)以及打码后的值。
  • 测试连接 —— 用该 key 真发一次 POST <baseURL>/messages(max_tokens: 1),返回 ok、invalid、quota、rate-limit 或 unreachable;测试成功会顺手清掉失效标记。
  • key 失效提醒 —— 401/403 把绑定的 key 标为 invalid,402 标为 quota,429 标为 rate-limit。当你点进一个生效 key 已失效的会话时,覆盖层会说明情况,并给出去配置、改用上层设置或稍后。
  • 不匹配提醒 —— 如果绑 key 时记下的是某个服务商(比如 deepseek-official),而该会话现在跑在另一个服务商上,点进这个会话时会提醒你,因为那次请求会拿着对方不认的 key 发出去。
  • 绝不悄悄换 key —— 两种提醒都不会改动实际使用的 key,决定权始终在你手上;自动替换会直接破坏「按会话分离计费」这个初衷。
  • 一键回退 —— 清空会话条目回到工作区 key,清空工作区条目回到系统默认。
  • 搜索默认 key 单独一把 —— 「搜索默认 key」卡片把这把 key 绑到 DEEPSEEK_API_KEY_SEARCH,于是搜索可以和聊天分开计费:会话自己的 key 最优先,其次勾了同步的工作区走聊天层级,再次是这把搜索默认 key,最后才是官方搜索页里配的那把。
  • 接管官方搜索 key 设置(先有 key 才接管) —— 启动时若已启用新版 Web 搜索 API,插件把官方 web-deepseek-api / web-search-deepseek 条目的 apiKeyEnv 指到 DEEPSEEK_API_KEY_SEARCH(只改这一个指向,永远不动 key 内容)。此后「设置 → 插件 → 网页搜索」里填的就是本面板这把搜索 key,既不会再顶掉聊天 key,也不存在第二处藏搜索 key 的地方。还没配搜索 key 时,官方条目继续指着聊天那把,所以升级不会让 web_search 直接失效,面板会如实提醒「搜索暂时和聊天共用一把 key」。
  • 每个工作区一个搜索开关 —— 每个工作区行上都有「同步搜索 API」复选框。勾选后,该目录下会话发出的 web_search / web_fetch 请求改用它们聊天时用的那把 key(会话 → 工作区 → 系统默认);不勾选则继续用官方「设置 → 插件 → 网页搜索」里配置的那把 key。会话自己配了 key 时永远优先,所以「工作区同步、会话单独设置」花的是会话那把 key,而不是搜索默认 key。
  • 在失败之前就提醒的搜索告警 —— 配了搜索 key 但没打开「启用新版 Web 搜索 API」,或者新版 API 打开了但它的 key ref 是空的,面板会显示提醒条,点进会话时的覆盖层也会用一句可直接照做的话说明;当生效的搜索通道不是新版 API 时,覆盖层不会拿搜索问题打扰你。
  • 使用说明 —— 面板顶部有「使用说明」,推荐两把 key 分工:聊天默认用 default,搜索默认用 search-default,两份预算互不干扰;里面还画了层级,并列出了会触发提醒的几种情况。

实现方式

宿主半边(lib/index.js):

  • 每个进程包装一次 credentials.resolve(通过 ctx.effect 在卸载时还原),而不是去注册第二个同名服务或第二条 provider 路由——对一个已被占用的名字,cordis 不允许这两者;
  • 从 agents.currentInitiator()?.session 取当前会话的目录与 id:agent loop 把整个回合跑在那个异步作用域里,所以解析凭据时这两个值都拿得到,解析器据此选中「有 key 的最具体层级」;
  • 把该目录与 workspaceRegistry.list() 做规范化后比对(绝对化、去掉尾部分隔符、Windows 折大小写);
  • 由每一层派生出稳定的 ref —— 工作区是 DEEPSEEK_API_KEY_WS_<sha1(规范化路径) 前 16 位十六进制大写>,会话是 DEEPSEEK_API_KEY_SS_<sha1(会话 id) 前 16 位十六进制大写>(会话 id 含 -,不能直接当 ref 名)——通过凭据服务写入 key,并返回这个 ref 的值;
  • 只改写 provider 真正会读的 ref:默认 ref、每个适配器配置的 apiKeyEnv,以及插件自己生成的 ref;其它 ref 原样透传;
  • 把「搜索请求」和「聊天请求」分开——按 ref 分辨,而不是包住 web 服务。官方新版 Search/Fetch API 自己解析 key,用的是 web-deepseek-api.apiKeyEnv 指向的凭据;本插件在启动时(以及面板每次保存搜索 key 时)把它指到插件自有的 DEEPSEEK_API_KEY_SEARCH,于是只看 ref 就能认出「这是搜索在要 key」:聊天永远不会解析这个 ref。0.2.15 起删掉了原先「把 ctx.web.search / ctx.web.fetch 包进 AsyncLocalStorage」的做法——包住 web 服务可能让 web_search / web_fetch 一直挂到工具 30/60 秒预算耗尽(连 http://127.0.0.1:1/ 这种必然立刻失败的地址也一样挂)。规则本身没变:先看会话自己的 key(永远最优先),再看该工作区有没有勾「同步搜索 API」,勾了就依次用工作区 key、系统默认 key;没勾时先用插件面板自己持有的搜索默认 ref(若配了),最后才用搜索插件配置的那个 ref。旧版基于 Messages 的搜索仍然走原来的聊天层级,因为那条路线本身就是一次 messages 请求;
  • 会话列表来自 sessionController 服务(list(),会拆开它 {items: […]} 的信封),控件缺失 / 抛错 / 列表为空时再退到宿主 live 会话服务(ctx.get('sessions').list()),所以那种情况下面板仍能列出会话;侧栏藏起来的两种会话这里也藏:子代理会话(origin: 'subagent' 或带 parentSession)和空会话(建了但一句话都没发过,侧栏只显示当前开着的那个)。两种都不过滤的话,一个只有 1 个真实会话的工作区会列出 13 个;
  • 不是注册工作区的目录,只有侧栏真的会显示它才会被列出来。这件事宿主判不了:「空会话里只有当前开着的那个算数」是浏览器端才知道的事(retainedBy.mainView),所以宿主把这种目录连同每个会话的 blank 标记一起发出去,并用 hiddenBlankDirs 点名「里面全是空会话」的目录,由浏览器半边套用与侧栏一致的判据;
  • /wsk-api/state 会带上插件自身版本与会话探针(available、shape、source、count、withoutCwd、subagents、samples、error,以及工作区路径样本),面板据此说明为什么没有列出会话:宿主半边是旧版本(只刷新了页面而没重启应用)、没有 sessionController、list() 抛错、信封形状不认识,还是会话的 cwd 与任何工作区路径都对不上;
  • 自己的账本(哪一层对应哪个 ref、失效标记、key 是给哪个服务商记的)落在 $DSH_HOME/storages/dsh-workspace-api-key.json;
  • 通过 agent/request-error waterfall 观察失败,但从不返回 {kind: 'retry'}——只记录判定结果并 next() 放行。

浏览器半边(lib/client.js)注册到 sidebar.panellist 槽(order: 1)、main 槽(key 为 workspace-api-key)与 shell.overlay 槽(点进会话时的提醒),和 dsh-skill-mcp-panel、dsh-429-guard 用的是同一套槽。全部是朴素的 React.createElement,没有构建步骤,也不打包任何依赖。它会把宿主行与浏览器端 sessions 存储(ctx.get('sessions').list.getSnapshot(),也就是侧栏自己渲染的数据)合并起来,因此标题取自 displayTitle,浏览器端只认识一部分会话时也不会把宿主行挤掉,宿主列不出会话时面板也照样有会话可列。这个存储由 dsh-api-session-controller 在插件 apply() 之后才提供,所以面板与覆盖层都会为它重试(每 500ms 一次,最多 40 次),而不是首次渲染拿不到就放弃。覆盖层向 /wsk-api/check?sessionId=…&path=… 查询当前占据主视图的会话,每个会话最多问一次,它的按钮只负责把你带到面板——改 key 只能由面板里的操作完成。

HTTP 接口(仅限回环)

方法 · 路径 · 用途

GET · /wsk-api/state · 默认 ref 状态、工作区及其会话、覆盖项、失效标记

GET · /wsk-api/resolve · 当前会话此刻解析到哪一层、哪个 ref

GET · /wsk-api/check · ?sessionId=…&path=… 只读判定:生效层级、key、失效标记、服务商不匹配、needsAttention

POST · /wsk-api/set(搜索) · {scope: 'search-default', key} 绑定面板自己的搜索默认 key;{useDefault: true} 清除

POST · /wsk-api/set · {scope: 'workspace' \ · 'session', path \ · sessionId, title, key, provider?} 绑定 key;{useDefault: true} 或空 key 为清除

POST · /wsk-api/test · {scope, path \ · sessionId \ · ref}(或 {key})真发一次请求并返回判定;invalid 打标记,ok 清标记

POST · /wsk-api/clear-flag · {ref} 或一个目标,清掉失效/配额标记

POST · /wsk-api/search-sync · {path, enabled} —— 打开或关闭某个工作区的「同步搜索 API」

只接受本机回环调用(127.0.0.1、::1、::ffff:127.0.0.1),且 Origin 必须同源或缺失。任何响应都不会包含明文 key,只有掩码。

工作区目标也接受不是注册工作区的目录;这类条目会作为「未分组目录」列在面板里,且只列侧栏真的会显示的目录——一个目录里若只有「刚建好、一句话都没发过」的空会话,侧栏不会显示它(空会话里只有当前打开的那个算数),面板也不会列。

安装

插件被市场收录后,可以直接在「设置 → 插件市场」里一键安装;也可以从 npm 装:

# 用 DSH 自带的 CLI 装进某个 profile(web、tui……)。Desktop 自己的 profile
# 由 Electron 应用独占管理,请在它的界面里安装。
dsh plugin --profile web add dsh-workspace-api-key

这条命令会把参数原样转交给 profile 目录里的 pnpm,等价的手写形式是:

# 在 profile 目录里,例如 ~/.dsh/profiles/web
pnpm add dsh-workspace-api-key          # 或者:npm install dsh-workspace-api-key

锁版本用 `dsh-worksp

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

嵌入徽章

两个徽章各用自己站的标识:左边是**标准**(DSHIO 发布),右边是**检测机构**(deepseekbase 检测)。每次展示都指向标准原文和检验报告。

DSHIO/T 10deepseekbase

Markdown(推荐,两个一起)

[![DSHIO/T 10](https://deepseekbase.com/badge/std.svg)](https://dshio.org/10/) [![deepseekbase](https://deepseekbase.com/badge/dsh-workspace-api-key.svg)](https://deepseekbase.com/zh/plugins/dsh-workspace-api-key/)

注意:这不是认证。DSHIO/T 32 认证规则尚未启用。

版本

npm 上共发布:18,最后更新 2026-11-04

链接

GitHub · Homepage · npm

常見問題

怎麼安裝 dsh-workspace-api-key?
在 DSH 裡執行 dsh plugin --profile web add dsh-workspace-api-key。如果你用的是 DSH Desktop,也可以在插件頁面直接貼套件名。
裝不上怎麼辦?
先確認 DSH 版本夠新(dsh --version),再看上面測試結果區的第一條錯誤。還不行就到 GitHub 倉庫提 issue。
怎麼卸載?
執行 dsh plugin --profile web remove dsh-workspace-api-key。