dsh-loadout 0.12.4 → 0.13.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/CHANGELOG.md +41 -1
- package/README.en.md +40 -2
- package/README.md +45 -5
- package/client.js +434 -32
- package/index.js +217 -5
- package/package.json +3 -2
- package/scripts/client-smoke.mjs +303 -10
- package/scripts/selftest.mjs +150 -0
- package/scripts/agent-lookup-probe.mjs +0 -24
- package/scripts/agent-probe.mjs +0 -25
- package/scripts/attribution-probe.mjs +0 -91
- package/scripts/cmd-probe.mjs +0 -30
- package/scripts/dock-probe.mjs +0 -53
- package/scripts/slots-probe.mjs +0 -59
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Offline probe: does the package-name heuristic actually recover the plugins
|
|
3
|
-
* that own the host-registered tools? Prints the derived index and the mapping
|
|
4
|
-
* for a list of real tool names, so the heuristic can be checked before the app
|
|
5
|
-
* is restarted.
|
|
6
|
-
*
|
|
7
|
-
* ELECTRON_RUN_AS_NODE=1 "DeepSeek Harness.exe" scripts/attribution-probe.mjs
|
|
8
|
-
*/
|
|
9
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
10
|
-
import { homedir } from 'node:os'
|
|
11
|
-
import { join } from 'node:path'
|
|
12
|
-
|
|
13
|
-
const BUILTIN_PREFIXES = new Set([
|
|
14
|
-
'todo', 'web', 'kit', 'agent', 'work', 'read', 'write', 'edit', 'glob', 'grep',
|
|
15
|
-
'ask', 'present', 'skill', 'workflow', 'subagent', 'send', 'list', 'interrupt',
|
|
16
|
-
'job', 'schedule', 'create', 'get', 'update', 'exit', 'compress', 'decompress',
|
|
17
|
-
'search', 'load', 'working',
|
|
18
|
-
])
|
|
19
|
-
|
|
20
|
-
const NAME_NOISE = new Set([
|
|
21
|
-
'core', 'tools', 'tool', 'client', 'server', 'shared', 'common', 'plugin', 'plugins',
|
|
22
|
-
'dsh', 'ui', 'web', 'harness', 'extension', 'preset', 'settings', 'sidebar', 'runtime',
|
|
23
|
-
])
|
|
24
|
-
|
|
25
|
-
const SELF = 'dsh-loadout'
|
|
26
|
-
|
|
27
|
-
const REAL_GLOBAL_TOOLS = [
|
|
28
|
-
'kit_list', 'kit_apply', 'kit_create', 'kit_set_tools', 'kit_set_default', 'kit_delete',
|
|
29
|
-
'modlens_read_image',
|
|
30
|
-
'weknora_list_knowledge_bases', 'weknora_search', 'weknora_read_document', 'weknora_ask',
|
|
31
|
-
'hindsight_sync_status', 'hindsight_diagnose', 'hindsight_search_knowledge_pages',
|
|
32
|
-
'hindsight_list_knowledge_pages', 'hindsight_read_knowledge_page', 'hindsight_reflect',
|
|
33
|
-
'hindsight_capture_initiative', 'hindsight_ingest_document',
|
|
34
|
-
'working_directory',
|
|
35
|
-
'vision_describe', 'vision_bootstrap', 'vision_materialize', 'vision_ground', 'vision_detect',
|
|
36
|
-
'vision_crop', 'vision_present', 'vision_pixel_diff', 'vision_colors', 'vision_ocr',
|
|
37
|
-
'vision_long_screenshot_ocr', 'vision_trace', 'vision_extract_foreground', 'vision_html_screenshot',
|
|
38
|
-
'list_mcp_resources', 'list_mcp_resource_templates', 'read_mcp_resource',
|
|
39
|
-
'compress', 'decompress', 'search_context',
|
|
40
|
-
'acp_status', 'acp_cache', 'acp_read_original_image',
|
|
41
|
-
'load_workspace_dependencies',
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
function profilePackages() {
|
|
45
|
-
const root = join(homedir(), '.dsh', 'profiles')
|
|
46
|
-
const names = new Set()
|
|
47
|
-
for (const entry of readdirSync(root)) {
|
|
48
|
-
const file = join(root, entry, 'package.json')
|
|
49
|
-
if (!existsSync(file)) continue
|
|
50
|
-
const parsed = JSON.parse(readFileSync(file, 'utf8'))
|
|
51
|
-
for (const name of Object.keys(parsed.dependencies ?? {})) names.add(name)
|
|
52
|
-
for (const bundle of parsed?.dsh?.profile?.bundles ?? []) if (typeof bundle === 'string') names.add(bundle)
|
|
53
|
-
}
|
|
54
|
-
return [...names]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function buildIndex(packages) {
|
|
58
|
-
const index = new Map()
|
|
59
|
-
for (const packageName of packages) {
|
|
60
|
-
const base = packageName.replace(/^@[^/]+\//, '')
|
|
61
|
-
if (base === SELF) continue
|
|
62
|
-
const stripped = base.replace(/^dsh-/, '').replace(/^plugin-/, '')
|
|
63
|
-
const tokens = [stripped, ...stripped.split('-')]
|
|
64
|
-
.map((token) => token.toLowerCase())
|
|
65
|
-
.filter((token) => token.length >= 3 && !NAME_NOISE.has(token) && !BUILTIN_PREFIXES.has(token))
|
|
66
|
-
for (const token of tokens) if (!index.has(token)) index.set(token, { id: token, title: stripped, packageName: base })
|
|
67
|
-
}
|
|
68
|
-
return index
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const packages = profilePackages()
|
|
72
|
-
const index = buildIndex(packages)
|
|
73
|
-
console.log(`profile packages: ${packages.length}`)
|
|
74
|
-
console.log(`tokens: ${[...index.keys()].sort().join(', ')}`)
|
|
75
|
-
console.log('')
|
|
76
|
-
const grouped = new Map()
|
|
77
|
-
const orphan = []
|
|
78
|
-
for (const toolName of REAL_GLOBAL_TOOLS) {
|
|
79
|
-
const cut = toolName.indexOf('_')
|
|
80
|
-
const prefix = cut > 0 ? toolName.slice(0, cut).toLowerCase() : ''
|
|
81
|
-
const owner = prefix === '' || BUILTIN_PREFIXES.has(prefix) ? undefined : index.get(prefix)
|
|
82
|
-
if (owner === undefined) { orphan.push(toolName); continue }
|
|
83
|
-
if (!grouped.has(owner.title)) grouped.set(owner.title, [])
|
|
84
|
-
grouped.get(owner.title).push(toolName)
|
|
85
|
-
}
|
|
86
|
-
for (const [title, tools] of [...grouped.entries()].sort()) {
|
|
87
|
-
console.log(`${title} (${tools.length})`)
|
|
88
|
-
console.log(` ${tools.join(', ')}`)
|
|
89
|
-
}
|
|
90
|
-
console.log('')
|
|
91
|
-
console.log(`built-in / unattributed (${orphan.length}): ${orphan.join(', ')}`)
|
package/scripts/cmd-probe.mjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What can a client plugin do with the command system, and how is a session id
|
|
3
|
-
* exposed to a session-scoped slot?
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync } from 'node:fs'
|
|
6
|
-
|
|
7
|
-
const BASE = 'C:/Users/JungleYXu/AppData/Local/Programs/DeepSeek Harness/resources/app.asar/dsh/node_modules/@deepseek-ai'
|
|
8
|
-
|
|
9
|
-
function show(rel, pattern, width = 260, limit = 6) {
|
|
10
|
-
let text
|
|
11
|
-
try {
|
|
12
|
-
text = readFileSync(`${BASE}/${rel}`, 'utf8')
|
|
13
|
-
} catch (error) {
|
|
14
|
-
console.log(`skip ${rel}: ${error.code}`)
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
console.log(`\n##### ${rel}`)
|
|
18
|
-
let index = text.search(pattern)
|
|
19
|
-
let shown = 0
|
|
20
|
-
while (index !== -1 && shown < limit) {
|
|
21
|
-
console.log('...' + text.slice(Math.max(0, index - 80), index + width).replace(/\s+/g, ' '))
|
|
22
|
-
index = text.slice(index + 1).search(pattern)
|
|
23
|
-
if (index !== -1) index += 1
|
|
24
|
-
shown += 1
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
show('dsh-client-ui-commands/lib/client.js', /submitCommand|runCommand|executeCommand/)
|
|
29
|
-
show('dsh-client-commands/lib/index.js', /^\s*(run|execute|dispatch|submit)\(|exports\.(run|execute|dispatch|submit)/, 200, 8)
|
|
30
|
-
show('dsh-client-ui-conversation/lib/client.js', /inject: \(sessionId\) =>/, 700, 2)
|
package/scripts/dock-probe.mjs
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Look at how the shipped bundles use the composer-area slots, so the kit bar
|
|
3
|
-
* can plug into a real surface with real props.
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync, readdirSync } from 'node:fs'
|
|
6
|
-
|
|
7
|
-
const BASE = 'C:/Users/JungleYXu/AppData/Local/Programs/DeepSeek Harness/resources/app.asar/dsh/node_modules/@deepseek-ai'
|
|
8
|
-
|
|
9
|
-
function files(pkg) {
|
|
10
|
-
const out = []
|
|
11
|
-
const walk = (dir, depth) => {
|
|
12
|
-
if (depth > 3) return
|
|
13
|
-
let names = []
|
|
14
|
-
try {
|
|
15
|
-
names = readdirSync(dir)
|
|
16
|
-
} catch {
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
for (const name of names) {
|
|
20
|
-
if (name === 'node_modules') continue
|
|
21
|
-
const full = `${dir}/${name}`
|
|
22
|
-
if (name.endsWith('.js') || name.endsWith('.d.ts')) out.push(full)
|
|
23
|
-
else if (!name.includes('.')) walk(full, depth + 1)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
walk(`${BASE}/${pkg}`, 0)
|
|
27
|
-
return out
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const targets = ['composer.dock', 'conversation.composer', 'session.header.utilities', 'shell.overlay']
|
|
31
|
-
const pkgs = readdirSync(BASE).filter((n) => n.startsWith('dsh-client-ui'))
|
|
32
|
-
|
|
33
|
-
for (const pkg of pkgs) {
|
|
34
|
-
for (const file of files(pkg)) {
|
|
35
|
-
let text
|
|
36
|
-
try {
|
|
37
|
-
text = readFileSync(file, 'utf8')
|
|
38
|
-
} catch {
|
|
39
|
-
continue
|
|
40
|
-
}
|
|
41
|
-
for (const target of targets) {
|
|
42
|
-
let index = text.indexOf(target)
|
|
43
|
-
let shown = 0
|
|
44
|
-
while (index !== -1 && shown < 2) {
|
|
45
|
-
const start = Math.max(0, index - 320)
|
|
46
|
-
console.log(`\n=== ${pkg}/${file.split('/').slice(-2).join('/')} :: ${target}`)
|
|
47
|
-
console.log(text.slice(start, index + 320).replace(/\s+/g, ' '))
|
|
48
|
-
index = text.indexOf(target, index + 1)
|
|
49
|
-
shown += 1
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
package/scripts/slots-probe.mjs
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Find real slot names in the shipped client bundles.
|
|
3
|
-
* Electron's fs is asar-aware, so archive-internal paths start with '/'.
|
|
4
|
-
*/
|
|
5
|
-
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
|
6
|
-
|
|
7
|
-
const ROOT = '/dsh/node_modules/@deepseek-ai'
|
|
8
|
-
const found = new Map()
|
|
9
|
-
|
|
10
|
-
function safeReaddir(dir) {
|
|
11
|
-
try {
|
|
12
|
-
return readdirSync(dir, { withFileTypes: false })
|
|
13
|
-
} catch {
|
|
14
|
-
try {
|
|
15
|
-
return readdirSync(dir)
|
|
16
|
-
} catch {
|
|
17
|
-
return []
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const packages = safeReaddir(ROOT).filter((name) => name.startsWith('dsh-client-ui') || name.startsWith('dsh-client-'))
|
|
23
|
-
console.log(`packages: ${packages.length}`)
|
|
24
|
-
|
|
25
|
-
const pattern = /["']((?:shell|conversation|settings|sidebar|composer|workspace|agent|home|chat|message|session)\.[a-zA-Z.]+)["']/g
|
|
26
|
-
|
|
27
|
-
function scan(dir, depth) {
|
|
28
|
-
if (depth > 3) return
|
|
29
|
-
for (const entry of safeReaddir(dir)) {
|
|
30
|
-
const full = `${dir}/${entry}`
|
|
31
|
-
let isDir = false
|
|
32
|
-
try {
|
|
33
|
-
isDir = statSync(full).isDirectory()
|
|
34
|
-
} catch {
|
|
35
|
-
continue
|
|
36
|
-
}
|
|
37
|
-
if (isDir) {
|
|
38
|
-
if (entry === 'node_modules') continue
|
|
39
|
-
scan(full, depth + 1)
|
|
40
|
-
continue
|
|
41
|
-
}
|
|
42
|
-
if (!entry.endsWith('.js')) continue
|
|
43
|
-
let text
|
|
44
|
-
try {
|
|
45
|
-
text = readFileSync(full, 'utf8')
|
|
46
|
-
} catch {
|
|
47
|
-
continue
|
|
48
|
-
}
|
|
49
|
-
for (const match of text.matchAll(pattern)) {
|
|
50
|
-
const slot = match[1]
|
|
51
|
-
if (slot.split('.').length < 2) continue
|
|
52
|
-
found.set(slot, (found.get(slot) ?? 0) + 1)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
for (const name of packages) scan(`${ROOT}/${name}`, 0)
|
|
58
|
-
|
|
59
|
-
for (const [slot, count] of [...found.entries()].sort()) console.log(`${slot}\t${count}`)
|