dsh-xray 0.3.2 → 0.4.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/README.md +16 -0
- package/README.zh.md +16 -0
- package/bin/xray.js +17 -3
- package/docs/demo.svg +22 -0
- package/lib/index.js +24 -1
- package/lib/model.js +14 -7
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
X-ray for your [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — see what's actually loaded, why, and what it costs you.
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
[中文](./README.zh.md)
|
|
10
12
|
|
|
11
13
|
> **Status: 0.2.x — static + runtime imaging.** Static commands work even when dsh cannot boot; `deps`/`health` and the agent tool need the plugin mounted.
|
|
@@ -63,6 +65,20 @@ tool-bash
|
|
|
63
65
|
.disabled: @deepseek-ai/dsh-base → @deepseek-ai/dsh-web-app (winner: @deepseek-ai/dsh-web-app)
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
What every request actually carries — prompt sections observed at assembly, blended with tool schemas:
|
|
69
|
+
|
|
70
|
+
```console
|
|
71
|
+
$ npx dsh-xray cost
|
|
72
|
+
~1625 tokens: 1 tool schema(s) ~121 + 19 prompt section(s) ~1504
|
|
73
|
+
|
|
74
|
+
# prompt sections (observed at last assembly):
|
|
75
|
+
app:web-surface ~248 15.3% ████████
|
|
76
|
+
tool:goal ~184 11.3% ██████
|
|
77
|
+
tool:ralph ~109 6.7% ███
|
|
78
|
+
harness:source ~94 5.8% ███
|
|
79
|
+
...
|
|
80
|
+
```
|
|
81
|
+
|
|
66
82
|
And when a patch row targets an id that doesn't exist (dsh skips it silently), `diff` catches it:
|
|
67
83
|
|
|
68
84
|
```console
|
package/README.zh.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 拍 X 光——看清到底加载了什么、为什么在那、以及它悄悄花掉了你什么。
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
[English](./README.md)
|
|
10
12
|
|
|
11
13
|
> **状态:0.2.x — 静态 + 运行时成像。** 静态命令在 dsh 起不来时照样能用;`deps`/`health` 和 agent 工具需要插件已挂载。
|
|
@@ -63,6 +65,20 @@ tool-bash
|
|
|
63
65
|
.disabled: @deepseek-ai/dsh-base → @deepseek-ai/dsh-web-app (winner: @deepseek-ai/dsh-web-app)
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
每次请求实际携带什么——assembly 时观测到的 prompt sections,与工具 schema 合并计价:
|
|
69
|
+
|
|
70
|
+
```console
|
|
71
|
+
$ npx dsh-xray cost
|
|
72
|
+
~1625 tokens: 1 tool schema(s) ~121 + 19 prompt section(s) ~1504
|
|
73
|
+
|
|
74
|
+
# prompt sections (observed at last assembly):
|
|
75
|
+
app:web-surface ~248 15.3% ████████
|
|
76
|
+
tool:goal ~184 11.3% ██████
|
|
77
|
+
tool:ralph ~109 6.7% ███
|
|
78
|
+
harness:source ~94 5.8% ███
|
|
79
|
+
...
|
|
80
|
+
```
|
|
81
|
+
|
|
66
82
|
patch 行指向不存在的 id 时(dsh 静默跳过),`diff` 能抓到:
|
|
67
83
|
|
|
68
84
|
```console
|
package/bin/xray.js
CHANGED
|
@@ -183,11 +183,25 @@ function cmdCost(args) {
|
|
|
183
183
|
const result = model.contextCost(snap);
|
|
184
184
|
if (args.json) return console.log(JSON.stringify(result, null, 2));
|
|
185
185
|
console.log(
|
|
186
|
-
`~${result.totalTokens} tokens
|
|
186
|
+
`~${result.totalTokens} tokens: ${result.toolCount} tool schema(s) ~${result.toolTokens} + ${result.sectionCount} prompt section(s) ~${result.sectionTokens} (captured ${result.capturedAt})\n`,
|
|
187
187
|
);
|
|
188
|
+
const bar = (share) => '█'.repeat(Math.max(1, Math.round(share / 2)));
|
|
189
|
+
if (result.sections.length) {
|
|
190
|
+
console.log('# prompt sections (observed at last assembly):');
|
|
191
|
+
for (const s of result.sections) {
|
|
192
|
+
console.log(
|
|
193
|
+
`${pad(s.name, 32)} ${pad(`~${s.tokens}`, 8)} ${pad(`${s.share}%`, 7)} ${bar(s.share)}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
console.log();
|
|
197
|
+
} else {
|
|
198
|
+
console.log('# no prompt assembly observed yet — send one agent message first\n');
|
|
199
|
+
}
|
|
200
|
+
console.log('# tool schemas:');
|
|
188
201
|
for (const t of result.tools) {
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
console.log(
|
|
203
|
+
`${pad(t.name, 32)} ${pad(`~${t.tokens}`, 8)} ${pad(`${t.share}%`, 7)} ${bar(t.share)}`,
|
|
204
|
+
);
|
|
191
205
|
}
|
|
192
206
|
}
|
|
193
207
|
|
package/docs/demo.svg
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="446" viewBox="0 0 720 446" font-family="ui-monospace,SFMono-Regular,Menlo,monospace" font-size="12.5">
|
|
2
|
+
<rect width="720" height="446" rx="8" fill="#0d1117" stroke="#30363d"/>
|
|
3
|
+
<circle cx="20" cy="17" r="5.5" fill="#ff5f56"/><circle cx="40" cy="17" r="5.5" fill="#ffbd2e"/><circle cx="60" cy="17" r="5.5" fill="#27c93f"/>
|
|
4
|
+
<text x="360" y="21" fill="#8b949e" text-anchor="middle" font-size="11.5">dsh-xray — composition X-ray for DeepSeek Harness</text>
|
|
5
|
+
<text x="16" y="63" fill="#7ee787">$ npx dsh-xray cost</text>
|
|
6
|
+
<text x="16" y="82" fill="#e6edf3">~1625 tokens: 1 tool schema(s) ~121 + 19 prompt section(s) ~1504</text>
|
|
7
|
+
<text x="16" y="120" fill="#8b949e"># prompt sections (observed at last assembly):</text>
|
|
8
|
+
<text x="16" y="139" fill="#e6edf3">app:web-surface ~248 15.3% ████████</text>
|
|
9
|
+
<text x="16" y="158" fill="#e6edf3">tool:goal ~184 11.3% ██████</text>
|
|
10
|
+
<text x="16" y="177" fill="#e6edf3">tool:ralph ~109 6.7% ███</text>
|
|
11
|
+
<text x="16" y="196" fill="#e6edf3">tool:glob ~98 6% ███</text>
|
|
12
|
+
<text x="16" y="215" fill="#e6edf3">harness:source ~94 5.8% ███</text>
|
|
13
|
+
<text x="16" y="234" fill="#8b949e">…</text>
|
|
14
|
+
<text x="16" y="272" fill="#7ee787">$ npx dsh-xray attribute</text>
|
|
15
|
+
<text x="16" y="291" fill="#8b949e"># 130 rows in profile "web"</text>
|
|
16
|
+
<text x="16" y="310" fill="#e6edf3">hmr @deepseek-ai/dsh-base ← patched by dsh-web-app [disabled]</text>
|
|
17
|
+
<text x="16" y="329" fill="#e6edf3">llm @deepseek-ai/dsh-base</text>
|
|
18
|
+
<text x="16" y="348" fill="#e6edf3">session-query @deepseek-ai/dsh-base ← patched by dsh-web-app</text>
|
|
19
|
+
<text x="16" y="386" fill="#7ee787">$ npx dsh-xray deps</text>
|
|
20
|
+
<text x="16" y="405" fill="#8b949e"># disable-cascade (transitive consumers of each provider):</text>
|
|
21
|
+
<text x="16" y="424" fill="#e6edf3"> Loader → 5 plugin(s): AgentPresets, ClientModuleRegistry, Hmr, …</text>
|
|
22
|
+
</svg>
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const os = require('node:os');
|
|
4
|
-
const { snapshotRegistry, stateName } = require('./collect/runtime.js');
|
|
4
|
+
const { snapshotRegistry, stateName, estimateTokens } = require('./collect/runtime.js');
|
|
5
5
|
const { serviceGraph, health, shadowing, contextCost } = require('./model.js');
|
|
6
6
|
|
|
7
7
|
const name = 'dsh-xray';
|
|
@@ -26,6 +26,7 @@ function apply(ctx) {
|
|
|
26
26
|
const dir = xrayDir();
|
|
27
27
|
const file = path.join(dir, 'runtime.json');
|
|
28
28
|
const transitions = new Map(); // plugin name -> [{state, at}] ring buffer
|
|
29
|
+
let lastAssembly = null; // latest system-prompt assembly observation
|
|
29
30
|
|
|
30
31
|
let timer = null;
|
|
31
32
|
const writeSnapshot = () => {
|
|
@@ -33,6 +34,7 @@ function apply(ctx) {
|
|
|
33
34
|
try {
|
|
34
35
|
const snap = snapshotRegistry(ctx);
|
|
35
36
|
snap.transitions = Object.fromEntries(transitions);
|
|
37
|
+
snap.promptAssembly = lastAssembly;
|
|
36
38
|
fs.mkdirSync(dir, { recursive: true });
|
|
37
39
|
const tmp = `${file}.tmp`;
|
|
38
40
|
fs.writeFileSync(tmp, JSON.stringify(snap, null, 2));
|
|
@@ -59,9 +61,29 @@ function apply(ctx) {
|
|
|
59
61
|
}
|
|
60
62
|
schedule();
|
|
61
63
|
});
|
|
64
|
+
// system-prompt/assemble is an expert waterfall: delegate via next(),
|
|
65
|
+
// then observe the final assembly. Purely observational — the assembly
|
|
66
|
+
// is returned unmodified.
|
|
67
|
+
const disposeAssemble = ctx.on('system-prompt/assemble', async (assembly, _context, next) => {
|
|
68
|
+
const result = await next();
|
|
69
|
+
try {
|
|
70
|
+
lastAssembly = {
|
|
71
|
+
at: Date.now(),
|
|
72
|
+
sections: (result?.sections ?? []).map((s) => ({
|
|
73
|
+
name: s.name,
|
|
74
|
+
tokens: estimateTokens(s.text ?? ''),
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
/* observation must never break assembly */
|
|
79
|
+
}
|
|
80
|
+
schedule();
|
|
81
|
+
return result;
|
|
82
|
+
});
|
|
62
83
|
schedule(); // initial snapshot
|
|
63
84
|
return [
|
|
64
85
|
disposeStatus,
|
|
86
|
+
disposeAssemble,
|
|
65
87
|
() => {
|
|
66
88
|
clearTimeout(timer);
|
|
67
89
|
writeSnapshot(); // final state on unload
|
|
@@ -111,6 +133,7 @@ function apply(ctx) {
|
|
|
111
133
|
async execute(args) {
|
|
112
134
|
const snap = snapshotRegistry(ctx);
|
|
113
135
|
snap.transitions = Object.fromEntries(transitions);
|
|
136
|
+
snap.promptAssembly = lastAssembly;
|
|
114
137
|
if (args.view === 'deps') return serviceGraph(snap);
|
|
115
138
|
if (args.view === 'health') return health(snap);
|
|
116
139
|
if (args.view === 'cost') return contextCost(snap);
|
package/lib/model.js
CHANGED
|
@@ -277,18 +277,25 @@ function shadowing(snap) {
|
|
|
277
277
|
return out;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/** F8: estimated context cost
|
|
280
|
+
/** F8: estimated context cost — tool schemas plus prompt sections. */
|
|
281
281
|
function contextCost(snap) {
|
|
282
282
|
const tools = (snap.tools ?? []).slice().sort((a, b) => b.tokens - a.tokens);
|
|
283
|
-
const
|
|
283
|
+
const toolTokens = tools.reduce((sum, t) => sum + t.tokens, 0);
|
|
284
|
+
const sections = (snap.promptAssembly?.sections ?? [])
|
|
285
|
+
.slice()
|
|
286
|
+
.sort((a, b) => b.tokens - a.tokens);
|
|
287
|
+
const sectionTokens = sections.reduce((sum, s) => sum + s.tokens, 0);
|
|
288
|
+
const total = toolTokens + sectionTokens;
|
|
289
|
+
const share = (n) => (total ? Math.round((n / total) * 1000) / 10 : 0);
|
|
284
290
|
return {
|
|
285
291
|
totalTokens: total,
|
|
292
|
+
toolTokens,
|
|
293
|
+
sectionTokens,
|
|
286
294
|
toolCount: tools.length,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
})),
|
|
295
|
+
sectionCount: sections.length,
|
|
296
|
+
tools: tools.map((t) => ({ name: t.name, tokens: t.tokens, share: share(t.tokens) })),
|
|
297
|
+
sections: sections.map((s) => ({ name: s.name, tokens: s.tokens, share: share(s.tokens) })),
|
|
298
|
+
promptObservedAt: snap.promptAssembly?.at ?? null,
|
|
292
299
|
capturedAt: snap.capturedAt,
|
|
293
300
|
};
|
|
294
301
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-xray",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "X-ray for your DeepSeek Harness — see what's actually loaded, why, and what it costs you.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"bin",
|
|
24
24
|
"cordis.patch.yml",
|
|
25
25
|
"README.md",
|
|
26
|
-
"README.zh.md"
|
|
26
|
+
"README.zh.md",
|
|
27
|
+
"docs"
|
|
27
28
|
],
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"yaml": "^2.6.0"
|