dsh-panel-control 0.4.0 → 0.7.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 +45 -15
- package/client.js +727 -30
- package/docs/discussion-bundle-evaluated-twice.md +100 -0
- package/docs/sidebar-layout.md +40 -0
- package/index.js +191 -15
- package/package.json +2 -2
- package/scripts/host-smoke.mjs +45 -2
- package/scripts/layout-smoke.mjs +48 -0
- package/scripts/read-page-trace.mjs +104 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Read the page-side trace this plugin leaves in the renderer's localStorage.
|
|
4
|
+
*
|
|
5
|
+
* The Host never hears from the Client half when the runner does not apply it,
|
|
6
|
+
* so the trace is written by the bundle's own module scope and picked up here,
|
|
7
|
+
* offline, straight out of Chromium's Local Storage LevelDB. No browser, no
|
|
8
|
+
* DevTools, no waiting on a person to read a window title.
|
|
9
|
+
*
|
|
10
|
+
* Usage: node scripts/read-page-trace.mjs [--out <file>]
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import os from 'node:os';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
|
|
16
|
+
const KEY = 'dsh.panel-control.trace';
|
|
17
|
+
const ROAMING = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
18
|
+
const PROFILE = path.join(ROAMING, '@deepseek-ai', 'dsh-desktop', 'Local Storage', 'leveldb');
|
|
19
|
+
const OUT = (() => {
|
|
20
|
+
const at = process.argv.indexOf('--out');
|
|
21
|
+
return at !== -1 ? process.argv[at + 1] : path.join(process.env.USERPROFILE || os.homedir(), '.dsh', 'panel-control', 'page-trace.log');
|
|
22
|
+
})();
|
|
23
|
+
|
|
24
|
+
function printable(text) {
|
|
25
|
+
return text.replace(/[^\x20-\x7e\u4e00-\u9fff]/g, ' ').replace(/ {2,}/g, ' ').trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const found = [];
|
|
29
|
+
let files = [];
|
|
30
|
+
try {
|
|
31
|
+
files = fs.readdirSync(PROFILE).filter((name) => /\.(log|ldb)$/.test(name));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('cannot read', PROFILE, error.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const name of files) {
|
|
38
|
+
const buf = fs.readFileSync(path.join(PROFILE, name));
|
|
39
|
+
// keys are UTF-8, values are UTF-16LE: read the key to confirm the entry, then
|
|
40
|
+
// decode the UTF-16LE value that carries the stage list.
|
|
41
|
+
const keyAt = buf.toString('latin1').indexOf(KEY);
|
|
42
|
+
const needle = Buffer.from('#ml=', 'utf16le');
|
|
43
|
+
let value = '';
|
|
44
|
+
let at = buf.indexOf(needle);
|
|
45
|
+
while (at !== -1) {
|
|
46
|
+
const slice = buf.subarray(Math.max(0, at - 800), at + 1600);
|
|
47
|
+
value += printable(slice.toString('utf16le')) + '\n';
|
|
48
|
+
at = buf.indexOf(needle, at + 1);
|
|
49
|
+
}
|
|
50
|
+
if (keyAt !== -1 || value) found.push({ name, keyAt, value: value.trim() });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The full boot manifest sits under its own key; it is the only place that says
|
|
54
|
+
// whether the page was handed the same module twice.
|
|
55
|
+
const bootNeedle = Buffer.from('"dupes":', 'utf16le');
|
|
56
|
+
const boots = [];
|
|
57
|
+
for (const name of files) {
|
|
58
|
+
const buf = fs.readFileSync(path.join(PROFILE, name));
|
|
59
|
+
let at = buf.indexOf(bootNeedle);
|
|
60
|
+
while (at !== -1) {
|
|
61
|
+
const text = printable(buf.subarray(Math.max(0, at - 120), at + 9000).toString('utf16le')).trim();
|
|
62
|
+
if (text) boots.push(text);
|
|
63
|
+
at = buf.indexOf(bootNeedle, at + 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Small always-fresh record: "DPCPAGE|" is written on every stage and on every
|
|
68
|
+
// page error, so at least one copy stays in the live (uncompressed) log file.
|
|
69
|
+
const pageNeedle = Buffer.from('DPCPAGE|', 'utf16le');
|
|
70
|
+
const notes = [];
|
|
71
|
+
for (const name of files) {
|
|
72
|
+
const buf = fs.readFileSync(path.join(PROFILE, name));
|
|
73
|
+
let at = buf.indexOf(pageNeedle);
|
|
74
|
+
while (at !== -1) {
|
|
75
|
+
const text = printable(buf.subarray(at, at + 4000).toString('utf16le'));
|
|
76
|
+
if (text) notes.push(`${name}: ${text}`);
|
|
77
|
+
at = buf.indexOf(pageNeedle, at + 1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const lines = [];
|
|
81
|
+
lines.push(`read ${new Date().toISOString()} from ${PROFILE}`);
|
|
82
|
+
lines.push(`files: ${files.join(' ')}`);
|
|
83
|
+
if (found.length === 0) {
|
|
84
|
+
lines.push(`no "${KEY}" entry: the bundle's module scope never ran in that page`);
|
|
85
|
+
} else {
|
|
86
|
+
for (const item of found) {
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push(`== ${item.name} (key at ${item.keyAt})`);
|
|
89
|
+
lines.push(item.value || '(key present, value unreadable)');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
fs.mkdirSync(path.dirname(OUT), { recursive: true });
|
|
93
|
+
// BOOT KEY: "dsh.panel-control.trace.boot"
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push('== page note');
|
|
96
|
+
if (notes.length === 0) lines.push('none');
|
|
97
|
+
else lines.push(notes.join('\n---\n'));
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push('== boot dump');
|
|
100
|
+
if (boots.length === 0) lines.push('no boot dump: the bundle never described one');
|
|
101
|
+
else lines.push(boots.join('\n---\n'));
|
|
102
|
+
fs.writeFileSync(OUT, lines.join('\n') + '\n', 'utf8');
|
|
103
|
+
console.log(lines.join('\n'));
|
|
104
|
+
console.log('\nwritten to', OUT);
|