dsh-job-progress 0.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/LICENSE +21 -0
- package/README.md +332 -0
- package/README.zh.md +297 -0
- package/assets/logo.png +0 -0
- package/assets/screenshot.png +0 -0
- package/cordis.patch.yml +8 -0
- package/lib/client.js +490 -0
- package/lib/dsh-progress.mjs +241 -0
- package/lib/index.js +362 -0
- package/package.json +61 -0
- package/test/preflight-client.mjs +121 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-job-progress contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.png" alt="dsh-job-progress" width="150" />
|
|
4
|
+
|
|
5
|
+
# dsh-job-progress
|
|
6
|
+
|
|
7
|
+
**How far along is it?** — a floating ball in the session that owns the job answers that.
|
|
8
|
+
|
|
9
|
+
A DeepSeek Harness plugin showing live progress for long-running background jobs:
|
|
10
|
+
downloads, model conversions, renders, batch jobs — anything that runs in the background.
|
|
11
|
+
|
|
12
|
+
[](./LICENSE)
|
|
13
|
+
[](#compatibility)
|
|
14
|
+
[](#compatibility)
|
|
15
|
+
[](#features)
|
|
16
|
+
[](#contributing)
|
|
17
|
+
[](https://github.com/Rice00/dsh-job-progress/stargazers)
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
<sub>The ball floats over the conversation, badges the running count, and expands into the task panel.</sub>
|
|
22
|
+
|
|
23
|
+
[Features](#features) · [Install](#install) · [Quick start](#quick-start) · [Protocol](#the-progress-protocol) · [Verify](#verify-the-install) · [Troubleshooting](#troubleshooting) · [**中文**](./README.zh.md)
|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
| | |
|
|
32
|
+
|---|---|
|
|
33
|
+
| 🟢 **Floating ball** | Appears only in sessions that have something to watch. The badge counts running tasks (`9+` above nine). |
|
|
34
|
+
| 🖱️ **Draggable** | Put it wherever you like; the position survives page reloads. |
|
|
35
|
+
| 📊 **Live panel** | One row per job: state dot, label, progress bar, `%`, `done/total`, speed, ETA, elapsed time. |
|
|
36
|
+
| 🧹 **Clear finished** | Deletes this session's finished progress files. Running entries are never touched. |
|
|
37
|
+
| 🤝 **No cooperation needed** | Jobs come from the registry, so an unreported job still shows up — just without a bar. |
|
|
38
|
+
| 🔒 **Session-scoped** | A session only ever sees its own jobs. Other sessions' work never leaks in. |
|
|
39
|
+
| 🧩 **Any job kind** | Shell commands, ComfyUI renders, anything that registers a background job. |
|
|
40
|
+
| 🔑 **Zero config** | No credentials, no tokens, no network calls: it reads the job registry and its own files. |
|
|
41
|
+
| 📦 **No build step** | The client plugin is a hand-written module-loader module; no bundler output to keep in sync. |
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
### From a local folder (recommended)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/Rice00/dsh-job-progress.git
|
|
49
|
+
dsh plugin --profile <profile> add link:/abs/path/to/dsh-job-progress # the checkout from above
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The bundle patch inserts one row (`job-progress`) into the profile. Then **restart that profile** —
|
|
53
|
+
host plugin modules are cached in-process, so a running harness will not pick the row up.
|
|
54
|
+
|
|
55
|
+
`link:` is a live link: **edits to the source folder take effect immediately** (after a restart for
|
|
56
|
+
host code, after a refresh for UI code), but the folder must not be moved afterwards. To copy the
|
|
57
|
+
files instead, use `file:/abs/path/to/dsh-job-progress` — then later edits need a re-install.
|
|
58
|
+
|
|
59
|
+
### From GitHub or npm
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
dsh plugin --profile <profile> add github:Rice00/dsh-job-progress
|
|
63
|
+
dsh plugin --profile <profile> add dsh-job-progress # once published to npm
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### For an AI assistant (copy-paste)
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Please install the DSH plugin dsh-job-progress for me:
|
|
70
|
+
|
|
71
|
+
1) Install it into the web profile, from GitHub:
|
|
72
|
+
dsh plugin --profile web add github:Rice00/dsh-job-progress
|
|
73
|
+
or from a local checkout (absolute path of the folder):
|
|
74
|
+
dsh plugin --profile web add link:<absolute-path>
|
|
75
|
+
2) Restart that profile — host plugin modules are cached in-process, so the new row is
|
|
76
|
+
only picked up on boot. (UI-only changes just need a browser refresh.)
|
|
77
|
+
3) Verify:
|
|
78
|
+
node <absolute-path>/test/preflight-client.mjs → must print "ALL PASS (12)"
|
|
79
|
+
the host log must contain: job-progress: mounted, progress root …
|
|
80
|
+
On Windows the host log is under %APPDATA%\DSH Desktop\logs\host\.
|
|
81
|
+
If DSH starts normally and no new errors appear in the renderer console, you are done.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
Nothing to configure — run something in the background and the ball appears:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node download.mjs https://example.com/model.safetensors # run_in_background
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
To get a progress bar, have the producer report numbers (one file, any language):
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import { track } from 'dsh-job-progress/progress';
|
|
96
|
+
|
|
97
|
+
const t = track({ label: 'model.safetensors', total: 66000000 });
|
|
98
|
+
t.update(bytesSoFar); // speed + ETA are measured for you
|
|
99
|
+
t.phase('verifying');
|
|
100
|
+
t.finish('done'); // or t.finish('failed', 'sha256 mismatch')
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or straight from a shell, no import needed:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node <plugin>/lib/dsh-progress.mjs set --label model.safetensors --done 12 --total 100
|
|
107
|
+
node <plugin>/lib/dsh-progress.mjs done --key model.safetensors
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## How it works
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
producer (your script) host plugin client plugin (browser)
|
|
114
|
+
track({ label, total }) ──▶ reads <DSH_HOME>/job-progress/ ──▶ polls jobProgress/snapshot
|
|
115
|
+
writes <key>.json <DSH_SESSION_ID>/*.json every 2 s and renders the
|
|
116
|
+
+ registry snapshots ball, badge and panel
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The ball's position and the per-session "already cleared" list live in `localStorage`.
|
|
120
|
+
|
|
121
|
+
## The progress protocol
|
|
122
|
+
|
|
123
|
+
A producer writes one JSON file per task into the session's progress directory:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
<DSH_HOME>/job-progress/<DSH_SESSION_ID>/<key>.json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Both environment values already exist in every agent shell call, so a producer never has to be
|
|
130
|
+
told a path.
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"label": "anima_preview_5B.safetensors",
|
|
135
|
+
"done": 4187599360,
|
|
136
|
+
"total": 9972879360,
|
|
137
|
+
"unit": "bytes",
|
|
138
|
+
"speed": 13107200,
|
|
139
|
+
"eta": 440,
|
|
140
|
+
"phase": "download",
|
|
141
|
+
"status": "running",
|
|
142
|
+
"note": "",
|
|
143
|
+
"jobId": "bash-3",
|
|
144
|
+
"updatedAt": 1758000000000
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| field | meaning |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `label` | shown in the panel; also how an entry is matched to a registry job |
|
|
151
|
+
| `done` / `total` | units completed; `total: 0` renders as "running, unknown size" |
|
|
152
|
+
| `unit` | `bytes` (default, rendered KiB/MiB/GiB) or `count` (rendered raw) |
|
|
153
|
+
| `speed` / `eta` | optional; nothing is invented when they are absent |
|
|
154
|
+
| `phase` | free text; `merging` and `verifying` get built-in labels |
|
|
155
|
+
| `status` | `running` \| `done` \| `failed` |
|
|
156
|
+
| `jobId` | optional; pins the entry to a registry job instead of matching by label |
|
|
157
|
+
| `updatedAt` | ms-epoch heartbeat |
|
|
158
|
+
|
|
159
|
+
**Heartbeat.** While `status` is `running`, refresh `updatedAt` at least every ~15 s. A live entry
|
|
160
|
+
that stops refreshing is treated as gone, because a producer that stopped writing is
|
|
161
|
+
indistinguishable from one that crashed. `done` / `failed` entries stay visible for two minutes.
|
|
162
|
+
|
|
163
|
+
**Atomic writes.** Write `<file>.tmp`, then rename over the target; a reader must never observe a
|
|
164
|
+
half-written record.
|
|
165
|
+
|
|
166
|
+
### CLI reference
|
|
167
|
+
|
|
168
|
+
| command | effect |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `set --label <name> [--done N] [--total N] [--unit bytes\|count] [--phase P]` | create or continue a record |
|
|
171
|
+
| `done --key <key> [--note "..."]` | mark it finished |
|
|
172
|
+
| `failed --key <key> --note "..."` | mark it failed |
|
|
173
|
+
| `dir` | print the resolved progress directory |
|
|
174
|
+
| `clear [--key <key>]` | delete this session's progress files |
|
|
175
|
+
|
|
176
|
+
## Clearing
|
|
177
|
+
|
|
178
|
+
**Clear finished** deletes every finished progress file of this session; running entries are never
|
|
179
|
+
touched.
|
|
180
|
+
|
|
181
|
+
Registry jobs cannot be deleted — they are read-only projections — so terminal job rows are
|
|
182
|
+
recorded in a per-session ignore list and stop appearing. That key includes the job's `startedAt`,
|
|
183
|
+
because job ids are `<kind>-N` counted per process and a restart would otherwise let a stale ignore
|
|
184
|
+
entry hide a brand-new job.
|
|
185
|
+
|
|
186
|
+
The host refuses a session id containing a path separator or `..`, and requires the session to
|
|
187
|
+
actually exist: the id doubles as a directory name, so this is a path-traversal fence, not a
|
|
188
|
+
formality. If the host does not answer, the UI reports the failure and changes nothing — it never
|
|
189
|
+
claims a clear that did not happen.
|
|
190
|
+
|
|
191
|
+
## Compatibility
|
|
192
|
+
|
|
193
|
+
| | |
|
|
194
|
+
|---|---|
|
|
195
|
+
| **DSH** | tested on `0.1.5-rc.2`; works in the browser GUI and in the web host embedded in the Desktop app |
|
|
196
|
+
| **Profile** | any profile that carries the web UI (`web`, and `desktop` when the same row is added there) |
|
|
197
|
+
| **Runtime** | Node 22+ (the plugin adds no dependencies of its own) |
|
|
198
|
+
| **Job kinds** | anything registered in `ctx.jobs`, regardless of kind |
|
|
199
|
+
| **Requirements** | no credentials, no tokens, no network access |
|
|
200
|
+
|
|
201
|
+
## Verify the install
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 1) the client plugin: module contract, slot registration, one render pass
|
|
205
|
+
node test/preflight-client.mjs # → ALL PASS (12)
|
|
206
|
+
|
|
207
|
+
# 2) the host plugin mounted (Windows Desktop app)
|
|
208
|
+
Select-String -Path "$env:APPDATA\DSH Desktop\logs\host\dsh-*.log" -Pattern 'job-progress'
|
|
209
|
+
# → dsh-job-progress: mounted, progress root …\.dsh\job-progress
|
|
210
|
+
|
|
211
|
+
# 3) the row is in the profile
|
|
212
|
+
dsh --profile <profile> --dump-config | Select-String 'job-progress'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Then start a background job in any session and hover the ball.
|
|
216
|
+
|
|
217
|
+
## Troubleshooting
|
|
218
|
+
|
|
219
|
+
| symptom | cause | fix |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| The ball never appears | the session has no background jobs and no progress files | start a background job, or write a progress file |
|
|
222
|
+
| UI changes seem ignored | the client plugin is fetched on page load | refresh the page (F5) |
|
|
223
|
+
| Changes to `lib/index.js` seem ignored | host plugin modules are cached in-process | restart the profile |
|
|
224
|
+
| `Clear failed: host not ready` | the running host predates the `clear` endpoint | restart the profile |
|
|
225
|
+
| Something renders as a squircle | the app's global `corner-shape` | see "For plugin authors" below |
|
|
226
|
+
| "Did the row even load?" | check the host log | `dsh-job-progress: mounted, progress root …` |
|
|
227
|
+
| A task disappeared before it finished | the producer stopped writing for 15 s (crashed or killed) | make sure the producer keeps the heartbeat, or set `DSH_PROGRESS` writers accordingly |
|
|
228
|
+
|
|
229
|
+
<details>
|
|
230
|
+
<summary><b>Design notes — why there is a file protocol at all</b></summary>
|
|
231
|
+
|
|
232
|
+
Two properties of the harness shaped this design. Both were verified against the shipped packages,
|
|
233
|
+
not assumed:
|
|
234
|
+
|
|
235
|
+
1. **The job registry carries no progress.** `ctx.jobs` records an id, kind, label, status
|
|
236
|
+
(`running → stopping → completed | killed | failed`) and timestamps — no field a producer could
|
|
237
|
+
fill with a percentage.
|
|
238
|
+
2. **Job output cannot be sampled.** `ShellProcess.readOutput` is *incremental* (consecutive reads
|
|
239
|
+
never repeat output) and `ctx.jobs.read()` marks a terminal job as reported. A panel that polled
|
|
240
|
+
job output to parse percentages would steal output the model is about to read and swallow its
|
|
241
|
+
completion notice. This plugin never calls `read()`; it reads only **registry snapshots** (pure
|
|
242
|
+
projections) plus its own files.
|
|
243
|
+
|
|
244
|
+
So progress has to be reported by whatever does the work, while job discovery stays automatic.
|
|
245
|
+
|
|
246
|
+
</details>
|
|
247
|
+
|
|
248
|
+
<details>
|
|
249
|
+
<summary><b>For plugin authors — circles in this GUI</b></summary>
|
|
250
|
+
|
|
251
|
+
The app sets **`corner-shape: superellipse(1.5)` globally**. Under that setting *every*
|
|
252
|
+
`border-radius` — including `50%` — paints as a **squircle**, not as a circular arc. A computed
|
|
253
|
+
`border-radius: 50%` is therefore not evidence that something renders as a circle. The app's own
|
|
254
|
+
stylesheets opt specific elements back with `corner-shape: round` (spinners, dots, switch thumbs).
|
|
255
|
+
Add that declaration when you need a true circle:
|
|
256
|
+
|
|
257
|
+
```css
|
|
258
|
+
border-radius: 50%;
|
|
259
|
+
corner-shape: round; /* omit it and you get a squircle — which may well be what you want */
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
This plugin deliberately keeps the squircle: it matches the GUI's own design language.
|
|
263
|
+
|
|
264
|
+
</details>
|
|
265
|
+
|
|
266
|
+
<details>
|
|
267
|
+
<summary><b>Development</b></summary>
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
node test/preflight-client.mjs # no install, no browser needed
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The client plugin runs in the browser, where its errors land in a renderer console that is not
|
|
274
|
+
written to disk — so a broken client plugin looks exactly like a plugin that never loaded. The
|
|
275
|
+
preflight loads `lib/client.js` through a stubbed module loader and exercises the factory, `apply`,
|
|
276
|
+
slot registration and one render pass against a stub React. It caught a missing `module`/`exports`
|
|
277
|
+
declaration and a render-gate regression while this plugin was being written.
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
cordis.patch.yml bundle patch: inserts the `job-progress` row
|
|
281
|
+
package.json manifest: bundle patch + web client plugin
|
|
282
|
+
lib/client.js client plugin: ball, drag, panel, clear
|
|
283
|
+
lib/index.js host plugin: registry snapshots + progress dir + jobProgress/clear
|
|
284
|
+
lib/dsh-progress.mjs producer side: protocol, helper, CLI
|
|
285
|
+
test/preflight-client.mjs 12 checks for the client plugin
|
|
286
|
+
assets/ logo and screenshot used by this README
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The host plugin registers a Typert Remote service (`jobProgress`) reached through the standard
|
|
290
|
+
`/api` gateway as `jobProgress/snapshot` and `jobProgress/clear`. Set `debug: true` on the
|
|
291
|
+
`job-progress` row to have the snapshot response carry which registries were found and how many
|
|
292
|
+
entries and jobs were matched.
|
|
293
|
+
|
|
294
|
+
</details>
|
|
295
|
+
|
|
296
|
+
## Uninstall
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
dsh plugin --profile <profile> remove dsh-job-progress
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Progress files written by producers stay under `<DSH_HOME>/job-progress/` — delete them yourself if
|
|
303
|
+
you no longer need them. The plugin never deletes anything outside that directory.
|
|
304
|
+
|
|
305
|
+
## Roadmap
|
|
306
|
+
|
|
307
|
+
- [ ] Jobs owned by subagents (today a session sees its own owner's jobs only)
|
|
308
|
+
- [ ] Move the icon and any large assets to a host route instead of an inlined data URI
|
|
309
|
+
- [ ] Richer phases, so a producer can announce arbitrary stages with labels
|
|
310
|
+
- [ ] More translations of this README
|
|
311
|
+
|
|
312
|
+
## Contributing
|
|
313
|
+
|
|
314
|
+
[Issues](https://github.com/Rice00/dsh-job-progress/issues) and pull requests are welcome. Before
|
|
315
|
+
opening a PR:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
node test/preflight-client.mjs # must print ALL PASS
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Keep the client plugin dependency-free (React only) and run the preflight after any edit — it exists
|
|
322
|
+
because client-side mistakes are otherwise invisible.
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
[MIT](./LICENSE)
|
|
327
|
+
|
|
328
|
+
<div align="center">
|
|
329
|
+
|
|
330
|
+
MIT License © dsh-job-progress contributors
|
|
331
|
+
|
|
332
|
+
</div>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.png" alt="dsh-job-progress" width="150" />
|
|
4
|
+
|
|
5
|
+
# dsh-job-progress
|
|
6
|
+
|
|
7
|
+
**跑到哪了?**——让我们快速查询当前进度(为每个急哭的人设计)。
|
|
8
|
+
|
|
9
|
+
一个 DeepSeek Harness 插件:为长时间运行的后台任务显示实时进度。下载、模型转换、渲染、批量任务,都算。
|
|
10
|
+
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
[](#兼容性)
|
|
13
|
+
[](#兼容性)
|
|
14
|
+
[](#功能)
|
|
15
|
+
[](#参与贡献)
|
|
16
|
+
[](https://github.com/Rice00/dsh-job-progress/stargazers)
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
<sub>小球浮在对话上方,角标显示正在跑的数量,点开就是任务面板。</sub>
|
|
21
|
+
|
|
22
|
+
[功能](#功能) · [安装](#安装) · [快速上手](#快速上手) · [协议](#进度协议) · [验证](#验证安装) · [常见问题](#常见问题) · [**English**](./README.md)
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 功能
|
|
29
|
+
|
|
30
|
+
| | |
|
|
31
|
+
|---|---|
|
|
32
|
+
| 🟢 **悬浮小球** | 只在有任务可看的会话里出现。角标是正在跑的任务数(超过 9 显示 `9+`)。 |
|
|
33
|
+
| 🖱️ **可以拖** | 摆哪儿都行,位置刷新页面后还在。 |
|
|
34
|
+
| 📊 **实时面板** | 一行一个任务:状态点、名称、进度条、百分比、`已完成/总量`、速度、剩余时间、已用时间。 |
|
|
35
|
+
| 🧹 **清除已完成** | 删掉本会话已完成的进度文件,正在跑的绝不动。 |
|
|
36
|
+
| 🤝 **不靠自觉** | 任务列表来自作业登记表,所以没上报进度的任务照样会出现,只是没有进度条。 |
|
|
37
|
+
| 🔒 **会话隔离** | 一个会话只能看到自己的任务,别的会话的东西不会漏进来。 |
|
|
38
|
+
| 🧩 **不限任务类型** | shell 命令、ComfyUI 渲染,任何登记了后台任务的都行。 |
|
|
39
|
+
| 🔑 **零配置** | 不要凭据、不要令牌、不联网:它只读作业登记表和自己写的文件。 |
|
|
40
|
+
| 📦 **无需构建** | 前端插件是手写的模块加载器模块,没有打包产物要同步。 |
|
|
41
|
+
|
|
42
|
+
## 安装
|
|
43
|
+
|
|
44
|
+
### 本地目录(推荐)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/Rice00/dsh-job-progress.git
|
|
48
|
+
dsh plugin --profile <profile> add link:/abs/path/to/dsh-job-progress # 指向上面检出的目录
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
bundle 补丁会往 profile 里插一行(`job-progress`)。**然后重启这个 profile**——宿主插件模块在进程内缓存,运行中的 harness 不会自动读到新行。
|
|
52
|
+
|
|
53
|
+
`link:` 是活链接:**改源码立刻生效**(宿主代码重启后生效,界面代码刷新后生效),但装完之后不能挪动这个目录。想连文件一起复制走,用 `file:/abs/path/to/dsh-job-progress`,代价是以后每次改都得重新装一遍。
|
|
54
|
+
|
|
55
|
+
### 从 GitHub 或 npm 安装
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
dsh plugin --profile <profile> add github:Rice00/dsh-job-progress
|
|
59
|
+
dsh plugin --profile <profile> add dsh-job-progress # 发布到 npm 之后
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 交给 AI 助手(直接复制)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
请帮我安装 DSH 插件 dsh-job-progress:
|
|
66
|
+
|
|
67
|
+
1) 装进 web profile,两种来源任选:
|
|
68
|
+
从 GitHub:
|
|
69
|
+
dsh plugin --profile web add github:Rice00/dsh-job-progress
|
|
70
|
+
或从本地检出(填这个文件夹的绝对路径):
|
|
71
|
+
dsh plugin --profile web add link:<绝对路径>
|
|
72
|
+
2) 重启该 profile——宿主插件模块在进程内缓存,新行只在启动时读。
|
|
73
|
+
(只改界面的话,刷新浏览器就够了。)
|
|
74
|
+
3) 验证:
|
|
75
|
+
node <绝对路径>/test/preflight-client.mjs → 应输出 "ALL PASS (12)"
|
|
76
|
+
宿主日志里应出现: job-progress: mounted, progress root …
|
|
77
|
+
Windows 下宿主日志在 %APPDATA%\DSH Desktop\logs\host\。
|
|
78
|
+
只要 DSH 能正常启动、渲染进程控制台没有新报错,就算装好了。
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 快速上手
|
|
82
|
+
|
|
83
|
+
没有任何要配置的东西——后台跑点活,小球就出来了:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
node download.mjs https://example.com/model.safetensors # 后台运行
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
想让进度条动起来,让干活的那边报几个数就行(一个文件,什么语言都行):
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { track } from 'dsh-job-progress/progress';
|
|
93
|
+
|
|
94
|
+
const t = track({ label: 'model.safetensors', total: 66000000 });
|
|
95
|
+
t.update(bytesSoFar); // 速度和剩余时间会自动算
|
|
96
|
+
t.phase('verifying');
|
|
97
|
+
t.finish('done'); // 或 t.finish('failed', 'sha256 校验失败')
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
不想改代码,直接在 shell 里报也行:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
node <plugin>/lib/dsh-progress.mjs set --label model.safetensors --done 12 --total 100
|
|
104
|
+
node <plugin>/lib/dsh-progress.mjs done --key model.safetensors
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 工作原理
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
产出方(你的脚本) 宿主插件 前端插件(浏览器)
|
|
111
|
+
track({ label, total }) ──▶ 读 <DSH_HOME>/job-progress/ ──▶ 每 2 秒取一次 jobProgress/snapshot
|
|
112
|
+
写 <key>.json <DSH_SESSION_ID>/*.json 渲染小球、角标和面板
|
|
113
|
+
+ 作业登记表快照
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
小球的位置和"哪些已经清掉了"记在 `localStorage` 里。
|
|
117
|
+
|
|
118
|
+
## 进度协议
|
|
119
|
+
|
|
120
|
+
一个任务一个 JSON 文件,写在会话自己的进度目录下:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
<DSH_HOME>/job-progress/<DSH_SESSION_ID>/<key>.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
这两个环境变量在每次 agent shell 调用里都已经存在,所以产出方不需要任何人告诉它路径。
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"label": "anima_preview_5B.safetensors",
|
|
131
|
+
"done": 4187599360,
|
|
132
|
+
"total": 9972879360,
|
|
133
|
+
"unit": "bytes",
|
|
134
|
+
"speed": 13107200,
|
|
135
|
+
"eta": 440,
|
|
136
|
+
"phase": "download",
|
|
137
|
+
"status": "running",
|
|
138
|
+
"note": "",
|
|
139
|
+
"jobId": "bash-3",
|
|
140
|
+
"updatedAt": 1758000000000
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| 字段 | 含义 |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `label` | 面板上显示的名字;也是按名字匹配作业登记表的依据 |
|
|
147
|
+
| `done` / `total` | 已完成量;`total: 0` 显示成"正在跑,总量未知" |
|
|
148
|
+
| `unit` | `bytes`(默认,按 KiB/MiB/GiB 显示)或 `count`(原样显示) |
|
|
149
|
+
| `speed` / `eta` | 可选;没有就不编 |
|
|
150
|
+
| `phase` | 自由文本;`merging`、`verifying` 有内置译文 |
|
|
151
|
+
| `status` | `running` \| `done` \| `failed` |
|
|
152
|
+
| `jobId` | 可选;直接钉住某个作业,而不是按名字匹配 |
|
|
153
|
+
| `updatedAt` | 心跳,毫秒时间戳 |
|
|
154
|
+
|
|
155
|
+
**心跳。** `status` 是 `running` 时,至少要每 15 秒刷新一次 `updatedAt`。停止刷新的活条目会被当作已经没了——一个不再写文件的产出方,和一个崩掉的产出方,从外面看不出区别。`done` / `failed` 的条目会多留两分钟。
|
|
156
|
+
|
|
157
|
+
**原子写入。** 先写 `<file>.tmp` 再改名覆盖目标文件;读的人永远不该看到半截记录。
|
|
158
|
+
|
|
159
|
+
### 命令行
|
|
160
|
+
|
|
161
|
+
| 命令 | 作用 |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `set --label <名字> [--done N] [--total N] [--unit bytes\|count] [--phase P]` | 新建或续写一条记录 |
|
|
164
|
+
| `done --key <key> [--note "..."]` | 标记完成 |
|
|
165
|
+
| `failed --key <key> --note "..."` | 标记失败 |
|
|
166
|
+
| `dir` | 打印实际用的进度目录 |
|
|
167
|
+
| `clear [--key <key>]` | 删除本会话的进度文件 |
|
|
168
|
+
|
|
169
|
+
## 清除
|
|
170
|
+
|
|
171
|
+
**清除已完成**会删掉本会话所有已完成的进度文件,正在跑的一律不动。
|
|
172
|
+
|
|
173
|
+
作业登记表里的任务删不掉——它只是只读投影——所以已经结束的任务行会记进本会话的忽略名单,从此不再出现。名单的键里带了任务的 `startedAt`:作业 id 是按进程数出来的 `<kind>-N`,否则重启之后一条旧的忽略记录会把一个全新的任务也藏掉。
|
|
174
|
+
|
|
175
|
+
会话 id 里带路径分隔符或 `..` 的,宿主直接拒掉,而且要求会话真的存在。这个 id 同时是目录名,所以这是防路径穿越的闸门,不是走形式。宿主不答应的时候,界面会报失败、什么都不改——不会出现"看起来清掉了、文件还在"。
|
|
176
|
+
|
|
177
|
+
## 兼容性
|
|
178
|
+
|
|
179
|
+
| | |
|
|
180
|
+
|---|---|
|
|
181
|
+
| **DSH** | 在 `0.1.5-rc.2` 上实测;浏览器 GUI 和桌面应用内嵌的 web 宿主都能用 |
|
|
182
|
+
| **Profile** | 任何带 web 界面的 profile(`web`;同一行加到 `desktop` 也可以) |
|
|
183
|
+
| **运行时** | Node 22+(插件本身不带任何依赖) |
|
|
184
|
+
| **作业类型** | 登记进 `ctx.jobs` 的任何作业,不限类型 |
|
|
185
|
+
| **额外要求** | 不要凭据、不要令牌、不需要联网 |
|
|
186
|
+
|
|
187
|
+
## 验证安装
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# 1) 前端插件:模块契约、插槽注册、跑一遍渲染
|
|
191
|
+
node test/preflight-client.mjs # → ALL PASS (12)
|
|
192
|
+
|
|
193
|
+
# 2) 宿主插件挂上了没(Windows 桌面应用)
|
|
194
|
+
Select-String -Path "$env:APPDATA\DSH Desktop\logs\host\dsh-*.log" -Pattern 'job-progress'
|
|
195
|
+
# → dsh-job-progress: mounted, progress root …\.dsh\job-progress
|
|
196
|
+
|
|
197
|
+
# 3) profile 里有没有这一行
|
|
198
|
+
dsh --profile <profile> --dump-config | Select-String 'job-progress'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
然后在任意会话里起一个后台任务,把鼠标移到小球上。
|
|
202
|
+
|
|
203
|
+
## 常见问题
|
|
204
|
+
|
|
205
|
+
| 现象 | 原因 | 怎么办 |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| 小球根本不出现 | 这个会话没有后台任务,也没有进度文件 | 起一个后台任务,或者写一个进度文件 |
|
|
208
|
+
| 改了界面像是没生效 | 前端插件在页面加载时才取 | 刷新页面(F5) |
|
|
209
|
+
| 改了 `lib/index.js` 像是没生效 | 宿主插件模块在进程内缓存 | 重启 profile |
|
|
210
|
+
| `清除失败:宿主未就绪` | 正在跑的宿主比 `clear` 接口还老 | 重启 profile |
|
|
211
|
+
| 有东西渲染成了方圆形 | 应用全局的 `corner-shape` | 见下面"给插件作者" |
|
|
212
|
+
| "这一行到底加载上没有?" | 查宿主日志 | `dsh-job-progress: mounted, progress root …` |
|
|
213
|
+
| 任务还没完就不见了 | 产出方超过 15 秒没写心跳(崩了或被杀了) | 让产出方保持心跳 |
|
|
214
|
+
|
|
215
|
+
<details>
|
|
216
|
+
<summary><b>设计说明——为什么要搞一套文件协议</b></summary>
|
|
217
|
+
|
|
218
|
+
这个设计是被 harness 的两个事实定下来的。两条都对着已发布的包验证过,不是猜的:
|
|
219
|
+
|
|
220
|
+
1. **作业登记表里没有进度。** `ctx.jobs` 只有 id、类型、名称、状态(`running → stopping → completed | killed | failed`)和时间戳,没有任何字段能让产出方填一个百分比进去。
|
|
221
|
+
2. **作业的产出读不得。** `ShellProcess.readOutput` 是**增量**的(连着读两次不会重复给你同样的内容),而 `ctx.jobs.read()` 会把已结束的作业标记为"已上报"。一个靠轮询作业输出来抠百分比的插件,会偷走模型马上要读的输出,还会吞掉它的完成通知。本插件从不调用 `read()`,只读**登记表快照**(纯投影)和自己的文件。
|
|
222
|
+
|
|
223
|
+
所以进度只能由干活的那边报,而任务的发现过程保持自动。
|
|
224
|
+
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
<details>
|
|
228
|
+
<summary><b>给插件作者——这个 GUI 里的圆</b></summary>
|
|
229
|
+
|
|
230
|
+
应用全局设了 **`corner-shape: superellipse(1.5)`**。在这个设置下,*任何* `border-radius`——包括 `50%`——都画成**方圆形**,而不是圆弧。所以 `border-radius: 50%` 的计算值不能证明它渲染出来是个圆。应用自己的样式表里,需要真圆的地方(转圈、圆点、开关滑块)都用 `corner-shape: round` 单独改了回去。要真圆就补上这行:
|
|
231
|
+
|
|
232
|
+
```css
|
|
233
|
+
border-radius: 50%;
|
|
234
|
+
corner-shape: round; /* 不写就是方圆形——也可能正是你想要的 */
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
本插件是故意留着方圆形的:它和应用本身的设计语言一致。
|
|
238
|
+
|
|
239
|
+
</details>
|
|
240
|
+
|
|
241
|
+
<details>
|
|
242
|
+
<summary><b>开发</b></summary>
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
node test/preflight-client.mjs # 不用装东西,不用开浏览器
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
前端插件跑在浏览器里,出错只会落到渲染进程的控制台,而那个控制台不写盘——所以"前端插件写坏了"和"插件压根没加载"看起来一模一样。preflight 用一个假的模块加载器把 `lib/client.js` 装进来,对着桩 React 跑一遍工厂函数、`apply`、插槽注册和一次渲染。它在这个插件开发过程中抓到过一次漏写 `module`/`exports` 声明,和一次渲染门槛的回归。
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
cordis.patch.yml bundle 补丁:插入 `job-progress` 这一行
|
|
252
|
+
package.json 清单:bundle 补丁 + web 前端插件
|
|
253
|
+
lib/client.js 前端插件:小球、拖拽、面板、清除
|
|
254
|
+
lib/index.js 宿主插件:作业登记表快照 + 进度目录 + jobProgress/clear
|
|
255
|
+
lib/dsh-progress.mjs 产出方:协议、辅助函数、命令行
|
|
256
|
+
test/preflight-client.mjs 前端插件的 12 项检查
|
|
257
|
+
assets/ 本 README 用的 logo 和截图
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
宿主插件注册了一个 Typert Remote 服务(`jobProgress`),走标准的 `/api` 网关,接口是 `jobProgress/snapshot` 和 `jobProgress/clear`。给 `job-progress` 这一行加上 `debug: true`,快照返回里会带上找到了哪些登记表、匹配到多少条记录和作业。
|
|
261
|
+
|
|
262
|
+
</details>
|
|
263
|
+
|
|
264
|
+
## 卸载
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
dsh plugin --profile <profile> remove dsh-job-progress
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
产出方写在 `<DSH_HOME>/job-progress/` 下的进度文件不会跟着删——不需要了就自己清。插件从不删这个目录以外的任何东西。
|
|
271
|
+
|
|
272
|
+
## 后续计划
|
|
273
|
+
|
|
274
|
+
- [ ] 子代理名下的作业(现在一个会话只看得到自己 owner 的作业)
|
|
275
|
+
- [ ] 图标和大资源改走宿主路由,不再内联成 data URI
|
|
276
|
+
- [ ] 更丰富的阶段,让产出方能自己命名任意阶段
|
|
277
|
+
- [ ] 这份 README 的更多语言版本
|
|
278
|
+
|
|
279
|
+
## 参与贡献
|
|
280
|
+
|
|
281
|
+
欢迎提 [issue](https://github.com/Rice00/dsh-job-progress/issues) 和 PR。开 PR 之前:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
node test/preflight-client.mjs # 必须 ALL PASS
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
前端插件请保持零依赖(只用 React),改完记得跑一遍 preflight——它存在的理由就是前端的问题平时看不见。
|
|
288
|
+
|
|
289
|
+
## 许可证
|
|
290
|
+
|
|
291
|
+
本项目采用 [MIT](./LICENSE) 许可。
|
|
292
|
+
|
|
293
|
+
<div align="center">
|
|
294
|
+
|
|
295
|
+
MIT License © dsh-job-progress contributors
|
|
296
|
+
|
|
297
|
+
</div>
|
package/assets/logo.png
ADDED
|
Binary file
|
|
Binary file
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# dsh-job-progress bundle patch — inserts the `job-progress` row into the profile.
|
|
2
|
+
#
|
|
3
|
+
# The row carries no required configuration: the progress directory defaults to
|
|
4
|
+
# `<DSH home>/job-progress` and the panel polls the host on its own. A user layer
|
|
5
|
+
# may override this row by id (`job-progress`) — the same-id row wins.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: job-progress
|
|
8
|
+
name: 'dsh-job-progress'
|