Repository navigation
bug: no GitHub card in the Plugins panel on DSH 0.2.1 — the browser half still requires the removed credentials remote #29
Description
Summary
On DSH 0.2.1-rc.3 the host half works — gh_repo, gh_file, the whole read surface answers — but the GitHub card never appears in the Plugins panel, and nothing is logged. The browser half never registers on plugins.item.
The 0.7.x client entry still requires the browser-side credentials remote (remote.credentials in inject). DSH 0.2.1 removed it: @deepseek-ai/dsh-credentials ships no ./remote export and no other package provides that namespace. In 0.2.x a pending client entry is a fatal web-boot failure, so the published entry can take the boot down; the obvious defensive patch — probe ctx.get('remote.credentialsController') and return early — makes the card vanish silently instead. I hit the second one first and spent a while hunting a conflict that wasn't there: disabling every other UI plugin changes nothing, because the code path never reaches ctx.slots.register.
Two more contracts moved underneath, and they matter as much as the remote:
plugins.itemnow hands the entry aformshaped{ state, mutate }(formFor()in@deepseek-ai/dsh-client-ui-plugin-manager), not thegetSnapshot()/subscribe()face the 0.7.x controller assumes — a card that did mount would throw onform.subscribe.settings.describe()serves a namespace only when its Config exposes at least one.volatile()field (volatileForm()in@deepseek-ai/dsh-settings).dsh-github's Config has none, so even a correctly mounted card getsform === undefined, and the token is not editable from the UI at all.
Related
- feat: GitHub token settings card in the Plugins settings page #6 introduced this card on
plugins.item; the surface is right, the contracts under it moved. - fix(client): register the browser bundle under the package name #24 / fix(client): register the bundle under the package name #25 fixed the bundle id in the same wrapper area — this is the next contract along the same boundary.
- 0.7.13/0.7.14: lib/client.js ships as unbundled ESM — the next host restart fails every client plugin in the batch ("Failed to load plugins") #12 /
lib/client.jsships unbundled ESM —tscin the build step overwrites the browser bundle, so the plugin crashes DSH on load #13 were the raw-ESM wrapper.
Environment
@perrylink/dsh-github0.7.27 (npm latest;mainis the same tree)- DSH Desktop 0.2.1-rc.3 (bundled Node 24.21.0), Windows 11 x64, zh-CN
- A profile with the bundle in
dsh.profile.bundlesand itsdsh-githubrow at patch root
Steps to reproduce
- Install 0.7.27 into a 0.2.1-rc.3 desktop profile and restart.
- Open the sidebar Plugins panel, Official group.
- Inspect the slot: client
Slots.listSubTreewithroot: "plugins.item"→ occupants aresubagent,web-search,agent-loop,shell, and nodsh-github.
Expected behavior
A GitHub card in the Official group whose page carries the token control.
Actual behavior
No card, no renderer error. plugins.item has four occupants. The host side is fine: gh_repo answers no GitHub token found (source: config/credentials/env/gh) — the tool ran and failed only on the token.
Root cause
The browser half still injects the removed remote:
export const inject = ['slots', 'locale', 'connection', 'remote', 'remote.credentials']@deepseek-ai/dsh-credentials@0.2.1-rc.3 exports only ., ./types, ./src/* and ./package.json — no ./remote — and no other shipped package provides a credentials remote namespace, so remote.credentials never resolves on the client. Everything else follows from that.
Even with the remote handled, the card's own wiring is stale: it reads form.getSnapshot() / form.subscribe(), while the page now passes { state, mutate }:
// dsh-client-ui-plugin-manager, PluginManagerPage
const formFor = (id) => {
if (!configurations?.some((view) => view.ns === id)) return void 0
const form = props.configForm(id)
return { state: form.getSnapshot(), mutate: (ops, revision) => form.mutate(ops, revision) }
}and the namespace only exists when the Config qualifies:
// dsh-settings, describe()
const form = volatileForm(schema)
if (form === undefined) return []
// …
return [{ ns: entry.options.id, /* … */ }]volatileForm() returns undefined unless some field (or an ancestor) carries meta.volatile, which dsh-github's Config never sets.
Proposed fix
- Drop the credentials-remote dependency and read/write the page-supplied
form.state.githubTokenthroughform.mutate. - Add
Config.githubTokenas a.volatile()field plus aconfigmember totokenSource, tried first inauto, so the host resolves the token from configuration without a restart. - Read that field through its volatile reference on every operation — the value arrives as
{ get() }, and reading per call is what makes a token saved on the page apply live. - Render with
SettingsForm+SettingsSecretField, so the card follows the standard settings layout and theme tokens, with a short PAT guide (classic →repo; fine-grained → Contents / Issues / Pull requests).
I have this running on my own profile against 0.2.1-rc.3 and can send a PR if the direction is acceptable.
One caveat worth deciding on together: the token then lives in the profile configuration as plain text, which is the seam this plugin deliberately avoided. Writing through ctx.credentials from the browser would be the better home, but 0.2.1 exposes no browser remote for it — happy to follow whatever direction you prefer.
Workaround
Set the token through $DSH_HOME/.credentials.yaml, GITHUB_TOKEN, or gh auth login. The tools work; only the UI is missing.
中文摘要
在 DSH 0.2.1-rc.3 上,dsh-github 0.7.27 的宿主半边正常(gh_repo 等工具都能调),但「插件」面板里始终没有 GitHub 卡,控制台也没有报错。
原因:浏览器半边仍把 remote.credentials 写进 inject。这个 remote 在 0.2.1 已经没了(@deepseek-ai/dsh-credentials 不再导出 ./remote,也没有别的包提供该命名空间),客户端条目因此一直 pending;0.2.x 把 pending 的客户端条目当成致命启动失败,而「探测不到就提前 return」的防御补丁会退化成卡静默消失。这也解释了为什么停用其他 UI 插件毫无变化——代码根本没走到 ctx.slots.register。
另外两处契约也变了:plugins.item 现在传的 form 是 { state, mutate },不是旧控制器假设的 getSnapshot()/subscribe();而 settings.describe() 只对「Config 里至少有一个 .volatile() 字段」的命名空间出表单——dsh-github 的 Config 一个都没有,所以即使卡挂载成功,拿到的 form 也是 undefined,token 在界面上根本没法编辑。
修复方向:去掉 credentials remote 依赖,改用页面给的 form 读写;新增 .volatile() 的 githubToken 字段与 config 令牌来源;按 volatile 引用的方式每次操作读取;界面改用 SettingsForm + SettingsSecretField 并附 PAT 指引。我在自己的 profile 上已经跑通,方向认可的话可以提 PR。一个需要一起定的取舍:这样 token 会以明文落在 profile 配置里,而这个插件原本刻意避开了这一点。
Activity
fix(client): restore the GitHub card and add an editable token field (#…
chore(release): 0.7.28 — restore the GitHub card on DSH 0.2.1 (#29, #30)