dsh-recall-plugin 1.6.1 → 1.7.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 +21 -0
- package/README.md +4 -3
- package/lib/client.js +74 -13
- package/lib/index.js +5 -1
- package/lib/maintenance.js +145 -145
- package/lib/scripts.posix.js +53 -2
- package/lib/scripts.pwsh.js +73 -3
- package/lib/snapshots.js +300 -216
- package/lib/store.js +33 -1
- package/package.json +1 -1
package/lib/store.js
CHANGED
|
@@ -42,7 +42,12 @@ export function createRuntime(ctx, config) {
|
|
|
42
42
|
gitExe: null,
|
|
43
43
|
posixHomeBase: null,
|
|
44
44
|
homeContainer: null,
|
|
45
|
-
errors: []
|
|
45
|
+
errors: [],
|
|
46
|
+
// 逐消息的快照反馈(issue #7 失败可见性):失败 {failed,error} 或
|
|
47
|
+
// fail-open 跳过 {skipped:[...]},由 snapshot-info 端点下发给客户端
|
|
48
|
+
// 弹 toast。放共享 state 而非 snapshots.js 闭包:端点在 index.js,
|
|
49
|
+
// 与索引/根缓存同层取用。
|
|
50
|
+
snapFeedback: new Map()
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
// 最近错误环形缓冲:Host 侧所有失败原本只进 console.error(宿主进程
|
|
@@ -95,12 +100,39 @@ export function createRuntime(ctx, config) {
|
|
|
95
100
|
const res = await shell.run(spec)
|
|
96
101
|
const out = (res && res.stdout && res.stdout.text) || ''
|
|
97
102
|
if (res && res.exitCode !== 0) {
|
|
103
|
+
// 失败兜底(issue #7):超时/失败的 git 命令可能留下孤儿进程与
|
|
104
|
+
// stale 锁——subprocess 服务的树级终止有竞态窗口,且 git 被硬杀时
|
|
105
|
+
// 不回收 index.lock,残留锁会让后续每条快照持续 fatal。best-effort
|
|
106
|
+
// 清扫后再抛原始错误,清扫自身的失败不得掩盖它。
|
|
107
|
+
await cleanupAfterGitFailure(command)
|
|
98
108
|
const err = ((res && res.stderr && res.stderr.text) || '').trim() || ('exit ' + String(res.exitCode))
|
|
99
109
|
throw new Error(err.slice(0, 1500))
|
|
100
110
|
}
|
|
101
111
|
return out
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
// 从脚本文本提取影子仓库 git-dir:两套模板的 git 命令脚本都以
|
|
115
|
+
// `$g = '<store.git>'` / `g='<store.git>'` 开头(凡带 store 的脚本全遵守
|
|
116
|
+
// 此约定),取首个带引号字面量赋值即得。resolveGitScript 等对 $g 的
|
|
117
|
+
// 非字面量赋值天然不匹配;含单引号的罕见路径会让 psq 的 '' 转义截断
|
|
118
|
+
// 提取结果——清扫脚本对错误路径只是 no-op(杀不到进程、删不到锁),
|
|
119
|
+
// 安全降级为本兜底加入前的行为。
|
|
120
|
+
function extractGitDir(command) {
|
|
121
|
+
const m = String(command).match(/(?:^|\n)[ \t]*(?:\$g|g)[ \t]*=[ \t]*'([^']+)/)
|
|
122
|
+
return m ? m[1] : null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function cleanupAfterGitFailure(command) {
|
|
126
|
+
// 哨兵识别清扫脚本自身:它也定义 $g 且可能失败(如 taskkill 缺失),
|
|
127
|
+
// 不拦住会「清扫失败 → 再清扫」无限递归
|
|
128
|
+
if (!command || String(command).indexOf('RECALL_CLEANUP') >= 0) return
|
|
129
|
+
const gitDir = extractGitDir(command)
|
|
130
|
+
if (!gitDir) return
|
|
131
|
+
try {
|
|
132
|
+
await runShell(scripts.killOrphansScript(gitDir), { timeoutMs: 60000, stdoutMaxBytes: 4096 })
|
|
133
|
+
} catch (error) { /* best-effort:清扫失败不影响原始错误的抛出 */ }
|
|
134
|
+
}
|
|
135
|
+
|
|
104
136
|
async function resolveRoot(sessionId) {
|
|
105
137
|
const key = sessionId ? String(sessionId) : 'fallback'
|
|
106
138
|
const cached = state.roots.get(key)
|