dsh-panel-control 0.3.13 → 0.3.14
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/client.js +0 -1
- package/index.js +60 -4
- package/package.json +62 -58
package/client.js
CHANGED
package/index.js
CHANGED
|
@@ -44,7 +44,20 @@ const LOG_KEEP_BYTES = 256 * 1024;
|
|
|
44
44
|
const CLIENT_FILE = fileURLToPath(new URL('./client.js', import.meta.url));
|
|
45
45
|
|
|
46
46
|
/** Set `DPC_NUDGE=off` to keep the Host strictly read-only about its own files. */
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The nudge is opt-in, and that default is deliberate.
|
|
49
|
+
*
|
|
50
|
+
* Rewriting `client.js` makes the loader hot-swap this module into the live
|
|
51
|
+
* page. That re-evaluation runs the whole slot reconcile again, and a plugin
|
|
52
|
+
* whose registration is not idempotent then throws on its second
|
|
53
|
+
* `register({ id: 'workspace-control' })` and loses its sidebar entry: healing
|
|
54
|
+
* this plugin broke a neighbour's. A page reload fixes the frozen list without
|
|
55
|
+
* touching anyone else, so the safe path is the banner, and the nudge stays
|
|
56
|
+
* behind DPC_NUDGE=on for people who need it and accept the risk.
|
|
57
|
+
*/
|
|
58
|
+
const NUDGE_ENABLED = ['on', '1', 'true', 'yes'].includes(
|
|
59
|
+
String(process.env.DPC_NUDGE || '').trim().toLowerCase(),
|
|
60
|
+
);
|
|
48
61
|
|
|
49
62
|
/** Set once the nudge below actually rewrote the Client file in this process. */
|
|
50
63
|
let recoveredAt = 0;
|
|
@@ -503,6 +516,32 @@ function inventorySnapshot(scope) {
|
|
|
503
516
|
};
|
|
504
517
|
}
|
|
505
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Write one line per apply that answers the only question a cold start leaves
|
|
521
|
+
* open: did this package even reach the Host's plugin graph?
|
|
522
|
+
*
|
|
523
|
+
* The page can only load what the graph advertises, so "missing from the list"
|
|
524
|
+
* and "listed but never fetched" have completely different fixes. One line,
|
|
525
|
+
* once per apply, settles it from `host.log` alone.
|
|
526
|
+
*/
|
|
527
|
+
function logInventoryOnApply(scope) {
|
|
528
|
+
try {
|
|
529
|
+
const snapshot = inventorySnapshot(scope);
|
|
530
|
+
if (!snapshot.ok) {
|
|
531
|
+
logHost(`graph: unreadable (${String(snapshot.error && snapshot.error.message)})`);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const value = snapshot.value;
|
|
535
|
+
const listed = value.ids.includes(PACKAGE);
|
|
536
|
+
logHost(
|
|
537
|
+
`graph: ${listed ? 'lists' : 'does not list'} ${PACKAGE} · rev ${value.rev} · ` +
|
|
538
|
+
`${value.ids.length} client entr${value.ids.length === 1 ? 'y' : 'ies'}`,
|
|
539
|
+
);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
logHost(`graph: probe failed (${describeError(error)})`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
506
545
|
/**
|
|
507
546
|
* Say it out loud when the page never came back.
|
|
508
547
|
*
|
|
@@ -545,17 +584,33 @@ async function watchFirstClientBoot(startedAt) {
|
|
|
545
584
|
// while developing) must not be reported as "the window never got the code".
|
|
546
585
|
if (newest >= startedAt - 120_000) return;
|
|
547
586
|
logHost(
|
|
548
|
-
'client half did not report within
|
|
587
|
+
'client half did not report within 30s: the window is running a plugin list from before this code ' +
|
|
588
|
+
'(a rebuilt interface can take this long to finish loading; press the refresh button if the entry is missing)',
|
|
549
589
|
);
|
|
550
590
|
// The page will not ask for a script that is not in its frozen list, but it
|
|
551
591
|
// does watch this package's files: one byte-level change to the Client entry
|
|
552
592
|
// makes the loader hand the module to the live page. Flip the trailing
|
|
553
593
|
// newline, so the file stays byte-stable instead of growing on every launch.
|
|
554
594
|
if (!NUDGE_ENABLED) {
|
|
555
|
-
logHost('recovery nudge
|
|
595
|
+
logHost('recovery nudge stayed off (opt in with DPC_NUDGE=on); a page reload is the safe fix');
|
|
556
596
|
return;
|
|
557
597
|
}
|
|
558
598
|
try {
|
|
599
|
+
// The page may have reported while this timer was waiting: never rewrite a
|
|
600
|
+
// file the running page just loaded, because the resulting hot swap is
|
|
601
|
+
// what re-runs everybody's registration.
|
|
602
|
+
const late = await fs.readFile(CLIENT_LOG, 'utf8').catch(() => '');
|
|
603
|
+
const quiet = late
|
|
604
|
+
.split(String.fromCharCode(10))
|
|
605
|
+
.some((line) => {
|
|
606
|
+
if (!line.includes('"kind":"boot"')) return false;
|
|
607
|
+
const stamp = Date.parse(line.slice(0, line.indexOf(' ')));
|
|
608
|
+
return Number.isFinite(stamp) && stamp >= since;
|
|
609
|
+
});
|
|
610
|
+
if (quiet) {
|
|
611
|
+
logHost('recovery nudge skipped: the page reported while the watchdog was waiting');
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
559
614
|
const source = await fs.readFile(CLIENT_FILE, 'utf8');
|
|
560
615
|
const next = source.endsWith(NL + NL) ? source.slice(0, -1) : source + NL;
|
|
561
616
|
await fs.writeFile(CLIENT_FILE, next, 'utf8');
|
|
@@ -607,6 +662,7 @@ export function apply(ctx) {
|
|
|
607
662
|
}
|
|
608
663
|
if (dispose) {
|
|
609
664
|
logHost(`route mounted at ${ROUTE_PATH} (attempt ${attempts})`);
|
|
665
|
+
logInventoryOnApply(scope);
|
|
610
666
|
const startedAt = Date.now();
|
|
611
667
|
// A timer callback that throws is fatal for the Host: contain it here and
|
|
612
668
|
// again inside the async body.
|
|
@@ -614,7 +670,7 @@ export function apply(ctx) {
|
|
|
614
670
|
void watchFirstClientBoot(startedAt).catch((error) =>
|
|
615
671
|
logHost(`client boot watchdog failed: ${describeError(error)}`),
|
|
616
672
|
);
|
|
617
|
-
},
|
|
673
|
+
}, 30_000);
|
|
618
674
|
if (typeof watchdog.unref === 'function') watchdog.unref();
|
|
619
675
|
return;
|
|
620
676
|
}
|
package/package.json
CHANGED
|
@@ -1,58 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-panel-control",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Manage the global panel navigation at the top of the DSH sidebar: reorder, hide and group every entry, with the layout persisted per installation.",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"dsh",
|
|
8
|
-
"dsh-plugin",
|
|
9
|
-
"deepseek-harness",
|
|
10
|
-
"sidebar",
|
|
11
|
-
"panel"
|
|
12
|
-
],
|
|
13
|
-
"exports": {
|
|
14
|
-
".": "./index.js",
|
|
15
|
-
"./client": "./client.js",
|
|
16
|
-
"./icon": "./icon.svg"
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"LICENSE",
|
|
20
|
-
"README.md",
|
|
21
|
-
"assets",
|
|
22
|
-
"client.js",
|
|
23
|
-
"cordis.patch.yml",
|
|
24
|
-
"docs",
|
|
25
|
-
"icon.svg",
|
|
26
|
-
"index.js",
|
|
27
|
-
"screenshots.json",
|
|
28
|
-
"scripts"
|
|
29
|
-
],
|
|
30
|
-
"dsh": {
|
|
31
|
-
"bundle": {
|
|
32
|
-
"patch": "./cordis.patch.yml"
|
|
33
|
-
},
|
|
34
|
-
"client": {
|
|
35
|
-
"platform": "web",
|
|
36
|
-
"immediately": true,
|
|
37
|
-
"inject": []
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"license": "MIT",
|
|
41
|
-
"author": {
|
|
42
|
-
"name": "QIUZAIYOU"
|
|
43
|
-
},
|
|
44
|
-
"repository": {
|
|
45
|
-
"type": "git",
|
|
46
|
-
"url": "git+https://github.com/QIUZAIYOU/dsh-panel-control.git"
|
|
47
|
-
},
|
|
48
|
-
"homepage": "https://github.com/QIUZAIYOU/dsh-panel-control#readme",
|
|
49
|
-
"bugs": {
|
|
50
|
-
"url": "https://github.com/QIUZAIYOU/dsh-panel-control/issues"
|
|
51
|
-
},
|
|
52
|
-
"scripts": {
|
|
53
|
-
"check": "node --check index.js && node --check client.js && node scripts/host-smoke.mjs",
|
|
54
|
-
"smoke": "node scripts/host-smoke.mjs",
|
|
55
|
-
"doctor": "node scripts/profile-doctor.mjs",
|
|
56
|
-
"enable": "node scripts/enable-local.mjs"
|
|
57
|
-
}
|
|
58
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-panel-control",
|
|
3
|
+
"version": "0.3.14",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Manage the global panel navigation at the top of the DSH sidebar: reorder, hide and group every entry, with the layout persisted per installation.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"dsh",
|
|
8
|
+
"dsh-plugin",
|
|
9
|
+
"deepseek-harness",
|
|
10
|
+
"sidebar",
|
|
11
|
+
"panel"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./index.js",
|
|
15
|
+
"./client": "./client.js",
|
|
16
|
+
"./icon": "./icon.svg"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md",
|
|
21
|
+
"assets",
|
|
22
|
+
"client.js",
|
|
23
|
+
"cordis.patch.yml",
|
|
24
|
+
"docs",
|
|
25
|
+
"icon.svg",
|
|
26
|
+
"index.js",
|
|
27
|
+
"screenshots.json",
|
|
28
|
+
"scripts"
|
|
29
|
+
],
|
|
30
|
+
"dsh": {
|
|
31
|
+
"bundle": {
|
|
32
|
+
"patch": "./cordis.patch.yml"
|
|
33
|
+
},
|
|
34
|
+
"client": {
|
|
35
|
+
"platform": "web",
|
|
36
|
+
"immediately": true,
|
|
37
|
+
"inject": []
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"author": {
|
|
42
|
+
"name": "QIUZAIYOU"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/QIUZAIYOU/dsh-panel-control.git"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/QIUZAIYOU/dsh-panel-control#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/QIUZAIYOU/dsh-panel-control/issues"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"check": "node --check index.js && node --check client.js && node scripts/host-smoke.mjs",
|
|
54
|
+
"smoke": "node scripts/host-smoke.mjs",
|
|
55
|
+
"doctor": "node scripts/profile-doctor.mjs",
|
|
56
|
+
"enable": "node scripts/enable-local.mjs"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"registry": "https://registry.npmjs.org/",
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|