@climber47/dsh-step-clock 0.1.0 → 0.1.1
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 +1 -1
- package/README.zh.md +2 -1
- package/lib/client.js +42 -10
- package/lib/index.js +6 -1
- package/package.json +1 -1
- package/src/client/index.js +41 -10
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ A dsh bundle, mounted as one inserted row:
|
|
|
63
63
|
- `cordis.patch.yml` inserts the `step-clock` row.
|
|
64
64
|
- `lib/index.js` is the (intentionally empty) host half. A bundle's row resolves the package root, so the package must be importable; this plugin has no host behaviour.
|
|
65
65
|
- `lib/client.js` is the browser half, in the `window.__ModuleLoader__.load({ id, factory })` registration shape a client bundle must have. React is taken from the module loader via `require('react')` rather than bundled.
|
|
66
|
-
- It registers
|
|
66
|
+
- It registers one additive entry in `conversation.input.dock` (`replaceRisk: none`, so every shipped todo / goal / queue entry is untouched) and injects its stylesheet by creating a tagged `<style>` element, removed when the plugin unloads.
|
|
67
67
|
- The shipped bundle is generated from `src/client/` by `npm run build`, so the readable source and the artifact cannot drift; `npm test` rebuilds and drives the bundle through its real loader contract.
|
|
68
68
|
|
|
69
69
|
It reads only facts the engine already publishes — the Chat timeline snapshot and the live running-call list — and polls nothing itself.
|
package/README.zh.md
CHANGED
|
@@ -63,7 +63,8 @@ dsh plugin --profile web add @climber47/dsh-step-clock
|
|
|
63
63
|
- `cordis.patch.yml` 插入 `step-clock` 行。
|
|
64
64
|
- `lib/index.js` 是(有意为空的)宿主半边。bundle 的 insert 行会解析包根,所以包必须可被导入;本插件没有宿主行为。
|
|
65
65
|
- `lib/client.js` 是浏览器半边,采用客户端 bundle 必须的 `window.__ModuleLoader__.load({ id, factory })` 注册形状。React 通过 `require('react')` 从模块加载器取得,不打包进产物。
|
|
66
|
-
-
|
|
66
|
+
- 它只注册**一个纯新增**条目:输入框上方的 `conversation.input.dock`(`replaceRisk: none`,不会动自带的 todo / goal / queue 条目)。样式通过创建一个带标记的 `<style>` 元素注入,插件卸载时移除。
|
|
67
|
+
- 它只声明**真实存在**的服务(`slots`、`timer`)。客户端插件没有样式服务——`styles` 只存在于动态插件沙箱里。声明一个无人提供的服务会让 Cordis 无限期等待,插件会显示"已加载"、不报任何错,却永远不渲染。
|
|
67
68
|
- 发布的 bundle 由 `npm run build` 从 `src/client/` 生成,因此可读源码与产物不会漂移;`npm test` 会先重新构建,再把 bundle 喂进它真实的加载器契约里跑行为测试。
|
|
68
69
|
|
|
69
70
|
它只读取引擎**已经发布**的事实——Chat 时间线快照与运行中调用列表——自身不做任何轮询。
|
package/lib/client.js
CHANGED
|
@@ -267,7 +267,7 @@ function StepClock(props) {
|
|
|
267
267
|
if (typeof began === 'number' && typeof ended === 'number' && ended >= began) {
|
|
268
268
|
if (record.seenTurn !== candidate.turn || record.seenStep !== last.step) {
|
|
269
269
|
record.final = record.pending
|
|
270
|
-
record.pending = { turn: candidate.turn, step: last.step, ms: ended - began
|
|
270
|
+
record.pending = { turn: candidate.turn, step: last.step, ms: ended - began }
|
|
271
271
|
record.seenTurn = candidate.turn
|
|
272
272
|
record.seenStep = last.step
|
|
273
273
|
}
|
|
@@ -300,7 +300,9 @@ function StepClock(props) {
|
|
|
300
300
|
say = '已提交,正在等待模型响应,已等待 ' + humanDuration(elapsedMs)
|
|
301
301
|
where = '第 ' + stepNumber + ' 步'
|
|
302
302
|
}
|
|
303
|
-
|
|
303
|
+
// An unknown anchor must not render as `0:00`, which would read as
|
|
304
|
+
// "just started" when in fact the start is not known.
|
|
305
|
+
clock = anchor === null ? '--:--' : clockOf(elapsedMs)
|
|
304
306
|
} else if (shown !== null) {
|
|
305
307
|
// No step is running (yet). Keep reporting the previous step, so the gap
|
|
306
308
|
// between a Turn closing and the next step starting — and a fresh page load
|
|
@@ -346,13 +348,44 @@ function StepClock(props) {
|
|
|
346
348
|
}
|
|
347
349
|
|
|
348
350
|
/**
|
|
349
|
-
*
|
|
351
|
+
* Cordis services this plugin waits for before applying.
|
|
350
352
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
*
|
|
353
|
+
* Only real services belong here. Cordis parks a plugin whose declared service
|
|
354
|
+
* is missing and **waits indefinitely** — it does not throw — so adding a name
|
|
355
|
+
* that no provider offers silently produces a plugin that loads, reports no
|
|
356
|
+
* error, and never runs. `slots` owns registration; `timer` supplies the
|
|
357
|
+
* ticking interval.
|
|
358
|
+
*
|
|
359
|
+
* Notably absent: there is no `styles` service. Stylesheet insertion is done
|
|
360
|
+
* directly on the DOM below, which is what the shipped client plugins do.
|
|
361
|
+
*/
|
|
362
|
+
const inject = ['slots', 'timer']
|
|
363
|
+
|
|
364
|
+
/** Style-tag identity, so re-mounting a second copy reuses one tag. */
|
|
365
|
+
const STYLE_TAG = '@climber47/dsh-step-clock/step-clock.css'
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Insert the plugin's stylesheet once, returning its disposer.
|
|
369
|
+
*
|
|
370
|
+
* A client plugin has no stylesheet service to hand this to: the style element
|
|
371
|
+
* is created and owned by this plugin, tagged so a second instance reuses it,
|
|
372
|
+
* and removed when the plugin unloads.
|
|
373
|
+
* @returns a disposer removing the tag, or a no-op outside a browser.
|
|
354
374
|
*/
|
|
355
|
-
|
|
375
|
+
function insertStyles() {
|
|
376
|
+
if (typeof document === 'undefined') return function () {}
|
|
377
|
+
if (document.querySelector('style[data-plugin-css="' + STYLE_TAG + '"]') !== null) {
|
|
378
|
+
return function () {}
|
|
379
|
+
}
|
|
380
|
+
const tag = document.createElement('style')
|
|
381
|
+
tag.dataset.plugin = '@climber47/dsh-step-clock'
|
|
382
|
+
tag.dataset.pluginCss = STYLE_TAG
|
|
383
|
+
tag.textContent = CSS
|
|
384
|
+
document.head.appendChild(tag)
|
|
385
|
+
return function () {
|
|
386
|
+
tag.remove()
|
|
387
|
+
}
|
|
388
|
+
}
|
|
356
389
|
|
|
357
390
|
/**
|
|
358
391
|
* Register the dock entry.
|
|
@@ -362,9 +395,7 @@ const inject = ['slots', 'styles', 'timer']
|
|
|
362
395
|
* `id` is added beside the shipped entries rather than replacing any of them.
|
|
363
396
|
*/
|
|
364
397
|
function apply(ctx) {
|
|
365
|
-
ctx.effect(
|
|
366
|
-
return ctx.styles.insert(CSS)
|
|
367
|
-
})
|
|
398
|
+
ctx.effect(insertStyles)
|
|
368
399
|
const mount = function (slot, id, order, label) {
|
|
369
400
|
ctx.slots.inject(slot, function () {
|
|
370
401
|
return ctx.slots.register({ name: slot, id: id, order: order }, function (props) {
|
|
@@ -382,6 +413,7 @@ function apply(ctx) {
|
|
|
382
413
|
//#endregion
|
|
383
414
|
exports.StepClock = StepClock;
|
|
384
415
|
exports.CSS = CSS;
|
|
416
|
+
exports.insertStyles = insertStyles;
|
|
385
417
|
exports.apply = apply;
|
|
386
418
|
exports.inject = inject;
|
|
387
419
|
return module.exports;
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
* @module @climber47/dsh-step-clock
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* No host services are required.
|
|
18
|
+
*
|
|
19
|
+
* The browser half declares its own (`slots`, `timer`); see
|
|
20
|
+
* `src/client/index.js`. Nothing here should be read as describing it.
|
|
21
|
+
*/
|
|
17
22
|
export const inject = []
|
|
18
23
|
|
|
19
24
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@climber47/dsh-step-clock",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Live per-step elapsed-time clock for the DeepSeek Harness web GUI: shows which step is running, which tools it is running, and how long it has been going.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek",
|
package/src/client/index.js
CHANGED
|
@@ -231,7 +231,7 @@ export function StepClock(props) {
|
|
|
231
231
|
if (typeof began === 'number' && typeof ended === 'number' && ended >= began) {
|
|
232
232
|
if (record.seenTurn !== candidate.turn || record.seenStep !== last.step) {
|
|
233
233
|
record.final = record.pending
|
|
234
|
-
record.pending = { turn: candidate.turn, step: last.step, ms: ended - began
|
|
234
|
+
record.pending = { turn: candidate.turn, step: last.step, ms: ended - began }
|
|
235
235
|
record.seenTurn = candidate.turn
|
|
236
236
|
record.seenStep = last.step
|
|
237
237
|
}
|
|
@@ -264,7 +264,9 @@ export function StepClock(props) {
|
|
|
264
264
|
say = '已提交,正在等待模型响应,已等待 ' + humanDuration(elapsedMs)
|
|
265
265
|
where = '第 ' + stepNumber + ' 步'
|
|
266
266
|
}
|
|
267
|
-
|
|
267
|
+
// An unknown anchor must not render as `0:00`, which would read as
|
|
268
|
+
// "just started" when in fact the start is not known.
|
|
269
|
+
clock = anchor === null ? '--:--' : clockOf(elapsedMs)
|
|
268
270
|
} else if (shown !== null) {
|
|
269
271
|
// No step is running (yet). Keep reporting the previous step, so the gap
|
|
270
272
|
// between a Turn closing and the next step starting — and a fresh page load
|
|
@@ -310,13 +312,44 @@ export function StepClock(props) {
|
|
|
310
312
|
}
|
|
311
313
|
|
|
312
314
|
/**
|
|
313
|
-
*
|
|
315
|
+
* Cordis services this plugin waits for before applying.
|
|
314
316
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
317
|
+
* Only real services belong here. Cordis parks a plugin whose declared service
|
|
318
|
+
* is missing and **waits indefinitely** — it does not throw — so adding a name
|
|
319
|
+
* that no provider offers silently produces a plugin that loads, reports no
|
|
320
|
+
* error, and never runs. `slots` owns registration; `timer` supplies the
|
|
321
|
+
* ticking interval.
|
|
322
|
+
*
|
|
323
|
+
* Notably absent: there is no `styles` service. Stylesheet insertion is done
|
|
324
|
+
* directly on the DOM below, which is what the shipped client plugins do.
|
|
325
|
+
*/
|
|
326
|
+
export const inject = ['slots', 'timer']
|
|
327
|
+
|
|
328
|
+
/** Style-tag identity, so re-mounting a second copy reuses one tag. */
|
|
329
|
+
const STYLE_TAG = '@climber47/dsh-step-clock/step-clock.css'
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Insert the plugin's stylesheet once, returning its disposer.
|
|
333
|
+
*
|
|
334
|
+
* A client plugin has no stylesheet service to hand this to: the style element
|
|
335
|
+
* is created and owned by this plugin, tagged so a second instance reuses it,
|
|
336
|
+
* and removed when the plugin unloads.
|
|
337
|
+
* @returns a disposer removing the tag, or a no-op outside a browser.
|
|
318
338
|
*/
|
|
319
|
-
|
|
339
|
+
function insertStyles() {
|
|
340
|
+
if (typeof document === 'undefined') return function () {}
|
|
341
|
+
if (document.querySelector('style[data-plugin-css="' + STYLE_TAG + '"]') !== null) {
|
|
342
|
+
return function () {}
|
|
343
|
+
}
|
|
344
|
+
const tag = document.createElement('style')
|
|
345
|
+
tag.dataset.plugin = '@climber47/dsh-step-clock'
|
|
346
|
+
tag.dataset.pluginCss = STYLE_TAG
|
|
347
|
+
tag.textContent = CSS
|
|
348
|
+
document.head.appendChild(tag)
|
|
349
|
+
return function () {
|
|
350
|
+
tag.remove()
|
|
351
|
+
}
|
|
352
|
+
}
|
|
320
353
|
|
|
321
354
|
/**
|
|
322
355
|
* Register the dock entry.
|
|
@@ -326,9 +359,7 @@ export const inject = ['slots', 'styles', 'timer']
|
|
|
326
359
|
* `id` is added beside the shipped entries rather than replacing any of them.
|
|
327
360
|
*/
|
|
328
361
|
export function apply(ctx) {
|
|
329
|
-
ctx.effect(
|
|
330
|
-
return ctx.styles.insert(CSS)
|
|
331
|
-
})
|
|
362
|
+
ctx.effect(insertStyles)
|
|
332
363
|
const mount = function (slot, id, order, label) {
|
|
333
364
|
ctx.slots.inject(slot, function () {
|
|
334
365
|
return ctx.slots.register({ name: slot, id: id, order: order }, function (props) {
|