@zmainer/dsh-wx-bridge 1.0.15 → 1.0.16
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/lib/kernel/acp-preset-shim.mjs +33 -3
- package/lib/kernel/bridge.mjs +36 -5
- package/package.json +1 -1
|
@@ -100,9 +100,35 @@ const EDITS = [
|
|
|
100
100
|
]
|
|
101
101
|
|
|
102
102
|
/** 由 dsh 入口(<runtime>/lib/bin.js)推出已装 dsh-acp 的文件路径。 */
|
|
103
|
+
/**
|
|
104
|
+
* dsh-acp 的候选路径(2026-09-24:只认旧布局导致预设静默失效)。
|
|
105
|
+
* 三种真实布局:
|
|
106
|
+
* A. 旧桌面/CLI:`<root>/lib/bin.js` + `<root>/node_modules/@deepseek-ai/dsh-acp`
|
|
107
|
+
* B. **新版桌面(hoisted)**:`<app>/node_modules/@deepseek-ai/dsh/lib/bin.js`
|
|
108
|
+
* + `<app>/node_modules/@deepseek-ai/dsh-acp` ← 与 dsh 在同一个 scope 目录下(兄弟)
|
|
109
|
+
* C. 单包自带:`<pkg>/node_modules/@deepseek-ai/dsh-acp`
|
|
110
|
+
* @param {string} runtimeBin dsh 入口(bin.js 绝对路径)
|
|
111
|
+
* @returns {string[]} 候选(按可能性排序;第一个存在的即命中)
|
|
112
|
+
*/
|
|
113
|
+
export function acpPackageCandidates(runtimeBin) {
|
|
114
|
+
if (!runtimeBin) return []
|
|
115
|
+
const lib = dirname(runtimeBin) // .../lib
|
|
116
|
+
const pkg = resolve(lib, '..') // .../dsh(包根)或运行时根
|
|
117
|
+
const parent = resolve(pkg, '..') // .../@deepseek-ai(hoisted scope)或 node_modules
|
|
118
|
+
const nm = resolve(parent, '..') // .../node_modules
|
|
119
|
+
return [
|
|
120
|
+
resolve(pkg, 'node_modules', '@deepseek-ai', 'dsh-acp', 'lib', 'index.js'), // C
|
|
121
|
+
resolve(parent, 'dsh-acp', 'lib', 'index.js'), // B(新版桌面)
|
|
122
|
+
resolve(parent, '@deepseek-ai', 'dsh-acp', 'lib', 'index.js'),
|
|
123
|
+
resolve(nm, '@deepseek-ai', 'dsh-acp', 'lib', 'index.js'), // A
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 命中第一个存在的候选;都不存在时返回首选路径(调用方用 acpPackageCandidates 出诊断)。 */
|
|
103
128
|
export function acpPackageFile(runtimeBin) {
|
|
104
|
-
|
|
105
|
-
|
|
129
|
+
const cands = acpPackageCandidates(runtimeBin)
|
|
130
|
+
for (const c of cands) { try { if (existsSync(c)) return c } catch {} }
|
|
131
|
+
return cands[0] || ''
|
|
106
132
|
}
|
|
107
133
|
|
|
108
134
|
/** 该文件是否已经支持预设(原生支持或已打过补丁)。 */
|
|
@@ -121,7 +147,11 @@ export function acpPresetSupported(file) {
|
|
|
121
147
|
export function ensureAcpPresetSupport(o) {
|
|
122
148
|
const log = o.log || (() => {})
|
|
123
149
|
const file = acpPackageFile(o.runtimeBin)
|
|
124
|
-
if (!file || !existsSync(file))
|
|
150
|
+
if (!file || !existsSync(file)) {
|
|
151
|
+
// 把候选清单一起带出去:区分「真的没装 dsh-acp」与「路径没找对」(2026-09-24 的坑)
|
|
152
|
+
return { status: 'missing', file, candidates: acpPackageCandidates(o.runtimeBin),
|
|
153
|
+
detail: '未找到已装的 dsh-acp;已试路径见 candidates(新版桌面常见于 <app>/node_modules/@deepseek-ai/dsh-acp)' }
|
|
154
|
+
}
|
|
125
155
|
|
|
126
156
|
let src
|
|
127
157
|
try { src = readFileSync(file, 'utf8') } catch (e) { return { status: 'error', file, detail: String(e?.message ?? e) } }
|
package/lib/kernel/bridge.mjs
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* 运行时入口 dshBin(5 级解析,见 resolveDshBin):--dsh-bin/BRIDGE_DSH_BIN/DSH_BIN →
|
|
12
12
|
* <dataDir>/host-runtime.json(宿主自证)→ PATH/npm 全局 → 桌面安装目录 → 运行中进程嗅探
|
|
13
13
|
*/
|
|
14
|
-
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, renameSync, statSync, readdirSync } from 'node:fs'
|
|
14
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, renameSync, statSync, readdirSync, copyFileSync } from 'node:fs'
|
|
15
15
|
import { spawn, execFileSync } from 'node:child_process'
|
|
16
16
|
import { AcpHost } from './acp.mjs'
|
|
17
|
-
import { ensureAcpPresetSupport, acpPresetSupported, acpPackageFile } from './acp-preset-shim.mjs'
|
|
17
|
+
import { ensureAcpPresetSupport, acpPresetSupported, acpPackageFile, acpPackageCandidates } from './acp-preset-shim.mjs'
|
|
18
18
|
import { randomUUID, randomBytes, createHash, createDecipheriv } from 'node:crypto'
|
|
19
19
|
import { zstdDecompressSync } from 'node:zlib'
|
|
20
20
|
import { homedir } from 'node:os'
|
|
@@ -302,11 +302,25 @@ function loadPromptText() {
|
|
|
302
302
|
const cfg = JSON.parse(readFileSync(cfgFile, 'utf8'))
|
|
303
303
|
if (typeof cfg.prompt === 'string') return cfg.prompt.trim()
|
|
304
304
|
} catch {}
|
|
305
|
+
// 契约文件不存在时:从包内模板复制一份到数据目录(面板/手改都行)并**出声**。
|
|
306
|
+
// 静默缺席的后果是「通道没有任何固定身份」,而现象看起来跟"预设没生效"一模一样(2026-09-24 实况)。
|
|
307
|
+
if (!existsSync(CONTRACT_FILE)) {
|
|
308
|
+
try {
|
|
309
|
+
const tpl = join(HERE, 'second-brain-contract.txt')
|
|
310
|
+
mkdirSync(STATE_DIR, { recursive: true })
|
|
311
|
+
if (existsSync(tpl)) {
|
|
312
|
+
copyFileSync(tpl, CONTRACT_FILE)
|
|
313
|
+
log('contract-template-copied', { to: CONTRACT_FILE })
|
|
314
|
+
}
|
|
315
|
+
} catch (e) { log('contract-template-copy-failed', { error: String(e?.message ?? e) }) }
|
|
316
|
+
log('contract-missing', { file: CONTRACT_FILE,
|
|
317
|
+
note: '通道没有固定身份契约;如需「先检索 vault / 落盘铁律」这类前缀,请编辑该文件或在配置里给 vault' })
|
|
318
|
+
}
|
|
305
319
|
try {
|
|
306
320
|
return readFileSync(CONTRACT_FILE, 'utf8').trim()
|
|
307
321
|
.replace(/\{\{vault\}\}/g, VAULT_PATH)
|
|
308
322
|
.replace(/\{\{cwd\}\}/g, CWD)
|
|
309
|
-
.replace(/\{\{model\}\}/g, process.env.BRIDGE_MODEL_LABEL || 'DeepSeek')
|
|
323
|
+
.replace(/\{\{model\}\}/g, process.env.BRIDGE_MODEL_VAL || process.env.BRIDGE_MODEL_LABEL || 'DeepSeek')
|
|
310
324
|
} catch { return '' }
|
|
311
325
|
}
|
|
312
326
|
let CONTRACT = loadPromptText()
|
|
@@ -1027,8 +1041,12 @@ async function reportSessionToHost(sessionId, cwd, isRetry) {
|
|
|
1027
1041
|
})
|
|
1028
1042
|
const j = await r.json().catch(() => null)
|
|
1029
1043
|
log('attach-report', { session: sessionId, ok: !!(j && j.ok), workspace: (j && j.workspacePath) || null, error: (j && j.error) || null })
|
|
1030
|
-
// 会话文件刚落盘时宿主可能还读不到 →
|
|
1031
|
-
|
|
1044
|
+
// 会话文件刚落盘时宿主可能还读不到 → **只**补一次(幂等;再不行还有宿主侧 10 分钟的兜底扫描)。
|
|
1045
|
+
// 2026-09-24 修:原来补试路径也会再排一次补试,失败即每 4 秒无限重试、把日志刷爆。
|
|
1046
|
+
if (!(j && j.ok)) {
|
|
1047
|
+
if (!isRetry) setTimeout(() => { void reportSessionToHost(sessionId, cwd, true) }, 4000)
|
|
1048
|
+
else log('attach-report-give-up', { session: sessionId, error: (j && j.error) || null, note: '补试一次仍失败,交给宿主侧周期扫描' })
|
|
1049
|
+
}
|
|
1032
1050
|
} catch (e) { log('attach-report-failed', { session: sessionId, error: String(e?.message ?? e) }) }
|
|
1033
1051
|
}
|
|
1034
1052
|
|
|
@@ -1656,6 +1674,19 @@ async function main() {
|
|
|
1656
1674
|
}, null, 1))
|
|
1657
1675
|
process.exit(0)
|
|
1658
1676
|
}
|
|
1677
|
+
if (args.includes('--selftest-shim')) {
|
|
1678
|
+
const bin = resolveAcpBin()
|
|
1679
|
+
const cands = acpPackageCandidates(bin)
|
|
1680
|
+
const r = ensureAcpPresetSupport({ runtimeBin: bin, log, dryRun: true })
|
|
1681
|
+
console.log('[selftest-shim] ' + JSON.stringify({
|
|
1682
|
+
acpBin: bin,
|
|
1683
|
+
candidates: cands.map((c) => ({ path: c, exists: existsSync(c) })),
|
|
1684
|
+
status: r.status, file: r.file || null, detail: r.detail || null, backup: r.backup || null,
|
|
1685
|
+
supportedNow: r.file ? acpPresetSupported(r.file) : false,
|
|
1686
|
+
contractFile: CONTRACT_FILE, contractExists: existsSync(CONTRACT_FILE), contractHash: CONTRACT_HASH,
|
|
1687
|
+
}, null, 1))
|
|
1688
|
+
process.exit(0)
|
|
1689
|
+
}
|
|
1659
1690
|
if (args.includes('--selftest-files')) {
|
|
1660
1691
|
const K = 'u-selftest'
|
|
1661
1692
|
state.pendingFiles = {}
|