dsh-vscode-mode 0.23.2 → 0.23.4
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 +5 -3
- package/lib/client.js +391 -17
- package/lib/client.js.map +2 -2
- package/lib/index.js +97 -6
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/missingReview.ts +24 -0
- package/src/client/openReceipt.ts +3 -3
- package/src/client/planBridge.ts +9 -11
- package/src/client/planDirect.ts +266 -0
- package/src/client/planState.ts +20 -2
- package/src/client/ui/EditorView.ts +67 -4
- package/src/missingReview.ts +44 -5
- package/src/officialCapture.ts +11 -0
- package/src/reviewDb.ts +23 -0
- package/src/reviewRpc.ts +32 -2
- package/src/rpc.ts +6 -2
- package/src/shared/rpc.ts +4 -1
package/src/officialCapture.ts
CHANGED
|
@@ -184,6 +184,11 @@ export function installCapture(ctx: Ctx, onSnapshot?: (cwd: string, snapshot: Re
|
|
|
184
184
|
log.warn('官方历史补采不可用:缺少 sessionQuery;后续实时事件仍会采集')
|
|
185
185
|
return
|
|
186
186
|
}
|
|
187
|
+
const source = ctx.get('workspaceChanges')
|
|
188
|
+
if (!source?.summary) {
|
|
189
|
+
log.warn('官方历史补采不可用:缺少 workspaceChanges;后续实时事件仍会采集')
|
|
190
|
+
return
|
|
191
|
+
}
|
|
187
192
|
try {
|
|
188
193
|
// Repair durable history independently: official summaries do not survive Host restart.
|
|
189
194
|
// Each saved snapshot resolves against its own cwd, not the current session directory.
|
|
@@ -193,11 +198,17 @@ export function installCapture(ctx: Ctx, onSnapshot?: (cwd: string, snapshot: Re
|
|
|
193
198
|
}
|
|
194
199
|
// Semantic search omits structural events; only the raw metadata list contains workspace/changes.
|
|
195
200
|
const events = await query.listEvents(session.id)
|
|
201
|
+
let expired = 0
|
|
196
202
|
for (const event of events) {
|
|
197
203
|
if (event.type !== 'workspace/changes' || event.seq < (session.inheritedEventCount ?? 0)) continue
|
|
204
|
+
// Official summaries live only in Host memory, so a restart makes every
|
|
205
|
+
// historical event unreadable. Probe before enqueue: queueing them records
|
|
206
|
+
// one durable gap per turn that can never be repaired or replayed.
|
|
207
|
+
if (!source.summary(session.id, event.seq)) { expired += 1; continue }
|
|
198
208
|
const window = await query.readEvent({ sessionId: session.id, seq: event.seq })
|
|
199
209
|
collector.enqueue(session, window.target)
|
|
200
210
|
}
|
|
211
|
+
if (expired) log.info(`官方历史补采跳过 ${expired} 轮已失效事件:官方 summary 不跨宿主重启保留,实时事件照常采集`)
|
|
201
212
|
} catch (error) { log.warn('官方历史补采失败:' + String(error)) }
|
|
202
213
|
}
|
|
203
214
|
}
|
package/src/reviewDb.ts
CHANGED
|
@@ -205,6 +205,29 @@ export function reviewPage(cwd: string, cursor = 0, history = false, path?: stri
|
|
|
205
205
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* @public
|
|
210
|
+
* @author ddj 2026年10月31号
|
|
211
|
+
* @description: 该文件是否还有未处理的官方待审改动。缺失文件预览据此区分「需要去差异栏 Keep 结案」
|
|
212
|
+
* 与「记录都已处理、可直接关闭标签页」,避免已结案的删除文件继续提示待审。
|
|
213
|
+
* @param cwd Migrated workspace
|
|
214
|
+
* @param path Host path of the file
|
|
215
|
+
* @returns 存在未决官方记录时为 true
|
|
216
|
+
*/
|
|
217
|
+
export function officialPending(cwd: string, path: string): boolean {
|
|
218
|
+
if (typeof path !== 'string') return false
|
|
219
|
+
const db = dbFor(cwd)
|
|
220
|
+
if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name='review_files' AND type='table'").get()) return false
|
|
221
|
+
// Windows 盘符路径按「反斜杠折成正斜杠 + 折大小写」比较(与 hasOfficial 的 SQL 归一化等价);
|
|
222
|
+
// 其余平台按身份精确比较,别把大小写不同的两个文件当成同一个。
|
|
223
|
+
if (path.length > 1 && path[1] === ':') {
|
|
224
|
+
const folded = path.split(String.fromCharCode(92)).join('/').toLowerCase()
|
|
225
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending'"
|
|
226
|
+
+ " AND lower(replace(identity,char(92),'/'))=? LIMIT 1").get(folded)
|
|
227
|
+
}
|
|
228
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending' AND identity=? LIMIT 1").get(path)
|
|
229
|
+
}
|
|
230
|
+
|
|
208
231
|
/** @public @author ddj 2026年10月26号 @param cwd Migrated workspace. @param path Legacy write target. @returns Whether any official version owns the file, including decided history. */
|
|
209
232
|
export function hasOfficial(cwd: string, path: string): boolean {
|
|
210
233
|
// 防御:解析结果若被误当成路径传进来(不透明对象),按「无官方记录」处理,
|
package/src/reviewRpc.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Session-scoped official review RPC. @author ddj 2026年10月26号 */
|
|
2
2
|
import type { RpcHandlerMap } from './shared/rpc.js'
|
|
3
|
-
import type { ReviewItem, ReviewPage } from './shared/review.js'
|
|
3
|
+
import type { ReviewDetail, ReviewItem, ReviewPage } from './shared/review.js'
|
|
4
4
|
import type { PendingPage } from './shared/pendingReview.js'
|
|
5
5
|
import { decideReview, reviewDetail, reviewPage } from './reviewDb.js'
|
|
6
6
|
import { undoReview } from './reviewUndo.js'
|
|
@@ -8,6 +8,7 @@ import { applyDecide, loadDetail, loadPage } from './pending/store.js'
|
|
|
8
8
|
import { PENDING_PREFIX, mergedPage, pendingDetailOf, pendingItemOf, pendingSamePath } from './pendingCompat.js'
|
|
9
9
|
import { bucketOf, resolveScope, sessionOf } from './registry.js'
|
|
10
10
|
import type { Registry } from './registry.js'
|
|
11
|
+
import { resolveTargetIn } from './store.js'
|
|
11
12
|
import type { Ctx } from './store.js'
|
|
12
13
|
import { log } from './log.js'
|
|
13
14
|
|
|
@@ -54,6 +55,35 @@ function batchSnapshot(cwd: string, pending: PendingPage): { ok: true; page: Rev
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* @private
|
|
60
|
+
* @author ddj 2026年10月31号
|
|
61
|
+
* @description: 文件已在磁盘删除时修正官方记录的能力说明。Keep 只写审查库、仍可结案,
|
|
62
|
+
* Undo 需要读写源文件、必然不可用:把「缺少原始字节」这种误导性原因换成可执行说明。
|
|
63
|
+
* 只改文案与 undo 位,不参与判定写入。
|
|
64
|
+
* @param ctx Host 能力
|
|
65
|
+
* @param cwd 工作区
|
|
66
|
+
* @param detail 官方记录详情
|
|
67
|
+
* @param capability 已按冷/热端调整过的能力
|
|
68
|
+
* @returns 修正后的能力;无法确认删除时原样返回
|
|
69
|
+
*/
|
|
70
|
+
async function absenceCapability(ctx: Ctx, cwd: string, detail: ReviewDetail, capability: ReviewDetail['capability']): Promise<ReviewDetail['capability']> {
|
|
71
|
+
if (!capability.keep) return capability
|
|
72
|
+
// 记录本身就是删除(after=false):Undo 要整文件内容,官方差异只有片段
|
|
73
|
+
if (detail.diff.kind === 'text' && !detail.diff.after) {
|
|
74
|
+
return { ...capability, undo: false, reason: '该记录删除了这个文件:Keep 按删除结案;Undo 需要完整文件内容,官方差异只有片段,不可用。' }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 文件已被删除时,Undo 的失败原因是「文件不在」,不是「缺少原始字节」:先查磁盘再谈证据
|
|
78
|
+
const fs = ctx.get('fs')
|
|
79
|
+
if (!fs) return capability
|
|
80
|
+
try {
|
|
81
|
+
const target = await resolveTargetIn(ctx, cwd, detail.item.currentPath)
|
|
82
|
+
if (await fs.stat(target)) return capability
|
|
83
|
+
} catch { return capability }
|
|
84
|
+
return { ...capability, undo: false, reason: '文件已在磁盘删除:Keep 按删除结案;Undo 需要读写该文件,当前不可用。' }
|
|
85
|
+
}
|
|
86
|
+
|
|
57
87
|
/**
|
|
58
88
|
* @public
|
|
59
89
|
* @author ddj 2026年10月26号 / 2026年10月28号
|
|
@@ -176,7 +206,7 @@ export function reviewHandlers(ctx: Ctx, registry: Registry): Partial<RpcHandler
|
|
|
176
206
|
return { ok: false, error: '官方审查记录不存在' }
|
|
177
207
|
}
|
|
178
208
|
// Keep 只写共享库,冷端放行;Undo 依赖会话证据与源文件写回,冷端关闭
|
|
179
|
-
const capability = liveCapabilityOf(detail.capability, scope.live, false)
|
|
209
|
+
const capability = await absenceCapability(ctx, cwd, detail, liveCapabilityOf(detail.capability, scope.live, false))
|
|
180
210
|
log.debug(`[review] detail live=${scope.live ? 1 : 0} id=${args.id} found=1`
|
|
181
211
|
+ ` keep=${capability.keep ? 1 : 0} undo=${capability.undo ? 1 : 0}`)
|
|
182
212
|
return { ok: true, detail: { ...detail, capability } }
|
package/src/rpc.ts
CHANGED
|
@@ -63,7 +63,7 @@ import { settingsDocPath } from './settingsDocPath.js'
|
|
|
63
63
|
import { createTextPages } from './textPages.js'
|
|
64
64
|
import { reviewHandlers } from './reviewRpc.js'
|
|
65
65
|
import { pendingHandlers } from './pendingRpc.js'
|
|
66
|
-
import { fileAbsent, missingReview, missingDecide, missingPath } from './missingReview.js'
|
|
66
|
+
import { fileAbsent, missingReview, missingDecide, missingPath, refusalCodeOf } from './missingReview.js'
|
|
67
67
|
import { pendingCovers } from './pendingCompat.js'
|
|
68
68
|
|
|
69
69
|
/** Process-local cursor signer; reads must stay on the Host filesystem capability. */
|
|
@@ -733,7 +733,11 @@ export function buildHandlers(
|
|
|
733
733
|
const bucket = await bucketOf(registry, ctx, sc.cwd)
|
|
734
734
|
if (!await fileAbsent(ctx, sc.cwd, args.path)) return { ok: true, review: null }
|
|
735
735
|
return { ok: true, review: await missingReview(ctx, sc.session, sc.cwd, bucket, args.path) }
|
|
736
|
-
} catch (error) {
|
|
736
|
+
} catch (error) {
|
|
737
|
+
// 可预期的拒绝带稳定错误码:客户端据此显示「已删除」终态,而不是把错误原文当加载失败重试
|
|
738
|
+
const code = refusalCodeOf(error)
|
|
739
|
+
return { ok: false, error: String(error), ...(code ? { code } : {}) }
|
|
740
|
+
}
|
|
737
741
|
},
|
|
738
742
|
'edrv.missingDecide': async (args) => {
|
|
739
743
|
const sc = await requireSession(ctx, args.sessionId)
|
package/src/shared/rpc.ts
CHANGED
|
@@ -736,10 +736,13 @@ export interface RpcOkMap {
|
|
|
736
736
|
'edrv.uiState.set': object
|
|
737
737
|
}
|
|
738
738
|
|
|
739
|
+
/** 缺失文件预览的稳定拒绝码:客户端据此显示「已删除」终态,而不是把错误原文当加载失败重试。 */
|
|
740
|
+
export type MissingRefusal = 'official-covered' | 'official-decided' | 'no-records' | 'no-evidence' | 'too-large'
|
|
741
|
+
|
|
739
742
|
/** 统一响应:{ok:true, ...payload} 或 {ok:false, error}。 */
|
|
740
743
|
export type RpcResult<M extends RpcMethod> =
|
|
741
744
|
| ({ ok: true } & RpcOkMap[M])
|
|
742
|
-
| { ok: false; error: string; code?: 'file-too-large' | 'file-missing'; reason?: Extract<import('./textPages.js').TextPageResult, { ok: false }>['reason'] }
|
|
745
|
+
| { ok: false; error: string; code?: 'file-too-large' | 'file-missing' | MissingRefusal; reason?: Extract<import('./textPages.js').TextPageResult, { ok: false }>['reason'] }
|
|
743
746
|
|
|
744
747
|
/** 每个方法的 handler 签名(host 分发表与 client 类型检查共用)。 */
|
|
745
748
|
export type RpcHandler<M extends RpcMethod> = (
|