DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Status — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
S

dsh-status

Status

将每个根会话的回合状态发布到一个 JSON 文档中,macOS 灯光守护进程会将其绘制为红绿灯。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:eustace030616-hub/traffic-light#a90f81161452b6c0722ac0f4850abf5493ccbdc4
README兼容性版本

兼容性与来源证明

Status 以 dsh-status 发布,当前版本为 0.8.4。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/10/15

版本

0.8.4stable
2026/10/15
0.8.3stable
2026/10/8
0.7.14stable
2026/10/2
最新版
0.8.4
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/15
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

README

traffic light

A DSH plugin and a macOS light daemon. The plugin publishes what every session is doing to one small JSON file; the daemon reads that file and draws a traffic light above every window. Two processes, one document, no socket between them.

  • The plugin. lib/ — a DSH plugin, installed from the plugin page.
  • The daemon. mac/ — DSHLight.app, universal and ad-hoc signed. It only ever reads.

Install

The plugin — paste this into DSH's plugin page (sidebar → Plugins) and restart DSH:

github:eustace030616-hub/traffic-light

The daemon — nothing to install: it ships inside the same package and the plugin starts it. To run it on its own, clone and open the shipped bundle:

git clone https://github.com/eustace030616-hub/traffic-light.git ~/traffic-light
open ~/traffic-light/bin/DSHLight.app

Usage

You seeIt means
darkrest (idle): nothing is pending, which is not a failure
yellow, steadya session is working (working) — including any subagents it has delegated to
yellow, pulsinga session is blocked on you (asking) — 30% → 100% over 1.3 s
greena turn finished and you have not read it (waiting)
redthe feed itself cannot be trusted — gone, unreadable or stale
red, pulsingthe balance is under the threshold, or the provider will not cover a call
You doIt does
double-clickswitch between DSH and the application you came from
single clicksays what the light means, beside it, at once — it drifts up and leaves after a second or so, longer for a longer sentence
right-clickopen the list: Appearance, Account, Quit the light
dragdock to the nearer screen edge, at whatever height you drop it

A green settles by itself while DSH is in front, because being here is what the reminder was asking for.

Appearance ▸                    Account ▸
  Size   ●━━━━━━━━━━  20 pt       CNY          12.62
  Gap    ●━━━━━━━━━━   8 pt       ─────────────
  Rest   ●━━━━━━━━━━  28%         Top up now
  Lit    ●━━━━━━━━━━  85%         updated 2m ago
  ─────────────
  Reset to default

The sliders apply as they are dragged and are remembered; Reset to default restores 20 / 8 / 28% / 85%. The account is one line per currency that holds something, with the platform's own top-up page below it. Click a currency line — the ↻ is the hint — to ask for a fresh balance. The list stays open, and the figure is rewritten in place when the answer lands.

How the data moves

One file, one direction:

DSH Desktop                                  the daemon (DSHLight)
  agent/pre-step       a prompt was accepted   reads the document every 0.25 s
  agent/turn-stopping  the ordinary turn end   never fetches, holds no credential
  agent/status         idle | running
  approval/request     blocked on you
  user-questions/…     blocked on you
        │                                             ▲
        ▼                                             │
  lib/index.js — one document per change, and the     │
  same document re-stamped every 2 s (the heartbeat)  │
        │                                             │
        ▼                                             │
  ~/Library/Application Support/dsh-status/state.json ┘
        written to a temp file and renamed into place

  the daemon's one write, for a balance on demand:
  click a currency line → touch <statePath>.refresh → served on the next heartbeat
  • Many sessions, one light. Every root session travels in meta.sessions; the daemon aggregates blocked > unread finish > working > rest.
  • Several harnesses: not yet. Each publisher writes the whole document, so two would overwrite each other, and the daemon is a singleton (flock) reading one path. A lock per path is what would have to move first.
  • Anything else that speaks the contract: write the document yourself, keep updatedAt fresh, point the daemon at it with --state-file PATH. No code in the daemon changes.

The document

~/Library/Application Support/dsh-status/state.json — cat it, or follow it with DSHLight --print.

{ "version": 1, "state": "working", "sessionId": "session-1a2b3c4d", "title": null,
  "updatedAt": 1790000000000, "changedAt": 1790000000000, "heartbeatMs": 2000,
  "reason": "prompt", "meta": { "cwd": "/Users/you/project" } }
FieldMeaning
versionContract version. Bumped only for a breaking change.
stateidle | working | waiting | asking.
sessionIdSession being reported on, or null.
titleSession title — always null for now (see Known gaps).
updatedAtWhen the publisher was last heard from. Re-stamped every heartbeatMs even when nothing changes — that is what makes staleness work, and why it must never be read as a change time.
changedAtWhen this state was last asserted. The heartbeat leaves it alone, so this — not updatedAt — is what an acknowledgement may be compared against.
heartbeatMsThe publisher's cadence, so the daemon need not hardcode a staleness rule.
reasonWhich event wrote this: init, session-start, prompt, turn-end, agent-idle, agent-running, approval, question, answered, dispose. Debugging only.
metaExtension channel: additive, and unknown keys must be ignored.
meta.sessionsEvery session the publisher watches: { id, state, reason, changedAt, agents? }, where agents is how many subagents of that session are working. Live sessions (asking, working) come first and are never crowded out; a session that has not moved in twelve hours is forgotten.
meta.account{ fetchedAt, intervalMs, isAvailable, balances: [{ currency, total, granted, toppedUp }] }, or a reason when the lookup failed. Figures are strings; never a credential.

New information goes into meta, keys are only ever added, and a reader that meets a state it has never heard of rests rather than alarms — so a new state or key cannot make an older build cry wolf.

What moves the light

TransitionTrigger
→ idleplugin load, a session coming up, dispose
→ workingagent/pre-step with messages, or agent/status: running
→ waitingagent/turn-stopping, or agent/status: idle
→ askingapproval/request or user-questions/request, counted until the last is answered
ignoreda subagent's turn end: a child finishing is not the session finishing. A child's work does count — the session is working while one of its subagents is, and the snapshot says how many (agents) — and an ask is charged to its parent session

Only the agent's status covers every ending: a turn you stop is aborted, and turn-stopping is never dispatched for it — which is why a stopped session used to keep the light yellow until DSH restarted.

Configuration

Every key is optional and validated by hand; an unusable value falls back to the default (lib/config.js).

KeyDefaultNotes
statePath~/Library/Application Support/dsh-status/state.jsonmust be absolute
heartbeatMs2000floor of 250
launchtruestart the daemon on mount, stop it on dispose
lightArgs[]extra daemon arguments, e.g. --open / --ack-app for a browser-hosted DSH
balancetruethe only request this package makes
balanceMs300000floor of 60000

The daemon

npm test                  # 68 checks — no DSH, no network, no key
npm run ship              # rebuild bin/ from the Swift (npm test fails if it drifts)
./mac/build.sh --run      # follow the light in a terminal
open build/DSHLight.app   # draw it by hand
pkill -f DSHLight         # stop it
FlagEffect
--state-file PATHwhich document to read (default: the published path)
--printfollow in the terminal instead of drawing a window
--interval SECONDSpoll interval, default 0.25
--open PATHwhat the double-click opens, default /Applications/DSH Desktop.app
--ack-app BUNDLE-IDanother application whose return settles a green; repeatable
--no-ackkeep green until the next prompt instead of settling on return
--level floating|status|screensaverhow high the window sits, default screensaver
--size POINTSlens size for this run, overriding the size slider
--low-balance POINTSwhen a balance turns the light red and breathing, default 8, in the currency holding the most; 0 turns it off

Worth knowing:

  • The body is the menu's own material at half strength, with a 0.15 black wash and a corner radius taken from its narrow side; the ring around a lens is light grey, never black.
  • The menu bar's 22 points are always reserved, never followed: visibleFrame reports the whole screen in both bar states, and riding the bar produced a laggy light that overlapped it.
  • Red is never about the agent. A feed that cannot be trusted is red and steady; a balance running out is red and breathing; an account that cannot be read at all is a dim row in the list, never a lens.
  • The daemon holds no key and fetches nothing; every account figure comes from the publisher, and the only thing it writes is a refresh request.
  • One light per user — an exclusive flock for the life of the process.
  • The double-click restores the application, not the window or tab — as far as public API reaches.
  • No Apple account is needed: ad-hoc signed, and a package-manager install sets no quarantine flag.

Secrets

  • The API key is resolved at request time through the harness's credential seam — inject is the only route that works — and lives in one local for the length of one request. Nothing stores it, and a rotated key reaches the next request with no restart.
  • A failure is a code, never the provider's message: DeepSeek's own 401 echoes part of the key it rejected, so a response body is read only when the answer was a good one.
  • The key never reaches the published document, a log line, or a child process's arguments, and the daemon has no credential at all. The tests inject a canary and assert it appears nowhere.
  • Working on it, the same rule: never print a credential. Describe one by existence, source and length, and rotate through ctx.credentials.set(ref, …) if one escapes.

Known gaps

  • title is always null: the session title plainly exists, but which accessor exposes it is unconfirmed, and a wrong guess would be worse than null.

MIT — see LICENSE.