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.
Files changed (3) hide show
  1. package/client.js +0 -1
  2. package/index.js +60 -4
  3. package/package.json +62 -58
package/client.js CHANGED
@@ -2089,4 +2089,3 @@ window.__ModuleLoader__.load({
2089
2089
  },
2090
2090
  });
2091
2091
 
2092
-
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
- const NUDGE_ENABLED = String(process.env.DPC_NUDGE || '').toLowerCase() !== 'off';
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 12s: the window is running a plugin list from before this code.',
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 disabled by DPC_NUDGE=off; the entry stays hidden until the page reloads');
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
- }, 12_000);
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.13",
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
+ }