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/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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-recall-plugin",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "DSH 消息撤回插件:在用户消息气泡旁加「撤回」按钮,把项目文件(独立影子 git 仓库快照)与对话历史(官方 fork)一并回退到该消息发送之前。",
5
5
  "type": "module",
6
6
  "repository": {