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/lib/index.js
CHANGED
|
@@ -7649,6 +7649,25 @@ function reviewPage(cwd, cursor = 0, history = false, path) {
|
|
|
7649
7649
|
}))]
|
|
7650
7650
|
};
|
|
7651
7651
|
}
|
|
7652
|
+
/**
|
|
7653
|
+
* @public
|
|
7654
|
+
* @author ddj 2026年10月31号
|
|
7655
|
+
* @description: 该文件是否还有未处理的官方待审改动。缺失文件预览据此区分「需要去差异栏 Keep 结案」
|
|
7656
|
+
* 与「记录都已处理、可直接关闭标签页」,避免已结案的删除文件继续提示待审。
|
|
7657
|
+
* @param cwd Migrated workspace
|
|
7658
|
+
* @param path Host path of the file
|
|
7659
|
+
* @returns 存在未决官方记录时为 true
|
|
7660
|
+
*/
|
|
7661
|
+
function officialPending(cwd, path) {
|
|
7662
|
+
if (typeof path !== "string") return false;
|
|
7663
|
+
const db = dbFor(cwd);
|
|
7664
|
+
if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name='review_files' AND type='table'").get()) return false;
|
|
7665
|
+
if (path.length > 1 && path[1] === ":") {
|
|
7666
|
+
const folded = path.split(String.fromCharCode(92)).join("/").toLowerCase();
|
|
7667
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending' AND lower(replace(identity,char(92),'/'))=? LIMIT 1").get(folded);
|
|
7668
|
+
}
|
|
7669
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending' AND identity=? LIMIT 1").get(path);
|
|
7670
|
+
}
|
|
7652
7671
|
/** @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. */
|
|
7653
7672
|
function hasOfficial(cwd, path) {
|
|
7654
7673
|
if (typeof path !== "string") return false;
|
|
@@ -7926,20 +7945,31 @@ function installCapture(ctx, onSnapshot) {
|
|
|
7926
7945
|
log.warn("官方历史补采不可用:缺少 sessionQuery;后续实时事件仍会采集");
|
|
7927
7946
|
return;
|
|
7928
7947
|
}
|
|
7948
|
+
const source = ctx.get("workspaceChanges");
|
|
7949
|
+
if (!source?.summary) {
|
|
7950
|
+
log.warn("官方历史补采不可用:缺少 workspaceChanges;后续实时事件仍会采集");
|
|
7951
|
+
return;
|
|
7952
|
+
}
|
|
7929
7953
|
try {
|
|
7930
7954
|
if (session.header?.cwd) {
|
|
7931
7955
|
await loadBucket(ctx, session.header.cwd);
|
|
7932
7956
|
await repairPaths(session.header.cwd, resolvePath);
|
|
7933
7957
|
}
|
|
7934
7958
|
const events = await query.listEvents(session.id);
|
|
7959
|
+
let expired = 0;
|
|
7935
7960
|
for (const event of events) {
|
|
7936
7961
|
if (event.type !== "workspace/changes" || event.seq < (session.inheritedEventCount ?? 0)) continue;
|
|
7962
|
+
if (!source.summary(session.id, event.seq)) {
|
|
7963
|
+
expired += 1;
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7937
7966
|
const window = await query.readEvent({
|
|
7938
7967
|
sessionId: session.id,
|
|
7939
7968
|
seq: event.seq
|
|
7940
7969
|
});
|
|
7941
7970
|
collector.enqueue(session, window.target);
|
|
7942
7971
|
}
|
|
7972
|
+
if (expired) log.info(`官方历史补采跳过 ${expired} 轮已失效事件:官方 summary 不跨宿主重启保留,实时事件照常采集`);
|
|
7943
7973
|
} catch (error) {
|
|
7944
7974
|
log.warn("官方历史补采失败:" + String(error));
|
|
7945
7975
|
}
|
|
@@ -16015,6 +16045,39 @@ function batchSnapshot(cwd, pending) {
|
|
|
16015
16045
|
};
|
|
16016
16046
|
}
|
|
16017
16047
|
/**
|
|
16048
|
+
* @private
|
|
16049
|
+
* @author ddj 2026年10月31号
|
|
16050
|
+
* @description: 文件已在磁盘删除时修正官方记录的能力说明。Keep 只写审查库、仍可结案,
|
|
16051
|
+
* Undo 需要读写源文件、必然不可用:把「缺少原始字节」这种误导性原因换成可执行说明。
|
|
16052
|
+
* 只改文案与 undo 位,不参与判定写入。
|
|
16053
|
+
* @param ctx Host 能力
|
|
16054
|
+
* @param cwd 工作区
|
|
16055
|
+
* @param detail 官方记录详情
|
|
16056
|
+
* @param capability 已按冷/热端调整过的能力
|
|
16057
|
+
* @returns 修正后的能力;无法确认删除时原样返回
|
|
16058
|
+
*/
|
|
16059
|
+
async function absenceCapability(ctx, cwd, detail, capability) {
|
|
16060
|
+
if (!capability.keep) return capability;
|
|
16061
|
+
if (detail.diff.kind === "text" && !detail.diff.after) return {
|
|
16062
|
+
...capability,
|
|
16063
|
+
undo: false,
|
|
16064
|
+
reason: "该记录删除了这个文件:Keep 按删除结案;Undo 需要完整文件内容,官方差异只有片段,不可用。"
|
|
16065
|
+
};
|
|
16066
|
+
const fs = ctx.get("fs");
|
|
16067
|
+
if (!fs) return capability;
|
|
16068
|
+
try {
|
|
16069
|
+
const target = await resolveTargetIn(ctx, cwd, detail.item.currentPath);
|
|
16070
|
+
if (await fs.stat(target)) return capability;
|
|
16071
|
+
} catch {
|
|
16072
|
+
return capability;
|
|
16073
|
+
}
|
|
16074
|
+
return {
|
|
16075
|
+
...capability,
|
|
16076
|
+
undo: false,
|
|
16077
|
+
reason: "文件已在磁盘删除:Keep 按删除结案;Undo 需要读写该文件,当前不可用。"
|
|
16078
|
+
};
|
|
16079
|
+
}
|
|
16080
|
+
/**
|
|
16018
16081
|
* @public
|
|
16019
16082
|
* @author ddj 2026年10月26号 / 2026年10月28号
|
|
16020
16083
|
* @description: 审查 RPC 处理器。工作区一律由 host 解析,绝不采信调用方 cwd:
|
|
@@ -16160,7 +16223,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16160
16223
|
error: "官方审查记录不存在"
|
|
16161
16224
|
};
|
|
16162
16225
|
}
|
|
16163
|
-
const capability = liveCapabilityOf(detail.capability, scope.live, false);
|
|
16226
|
+
const capability = await absenceCapability(ctx, cwd, detail, liveCapabilityOf(detail.capability, scope.live, false));
|
|
16164
16227
|
log.debug(`[review] detail live=${scope.live ? 1 : 0} id=${args.id} found=1 keep=${capability.keep ? 1 : 0} undo=${capability.undo ? 1 : 0}`);
|
|
16165
16228
|
return {
|
|
16166
16229
|
ok: true,
|
|
@@ -16470,6 +16533,29 @@ function pendingHandlers(ctx, registry) {
|
|
|
16470
16533
|
/** Missing historical files: frozen, whole-file deletion reviews. */
|
|
16471
16534
|
const frozen = /* @__PURE__ */ new Map();
|
|
16472
16535
|
const locks = /* @__PURE__ */ new Set();
|
|
16536
|
+
/**
|
|
16537
|
+
* @public
|
|
16538
|
+
* @author ddj 2026年10月31号
|
|
16539
|
+
* @description: 给缺失文件预览的可预期拒绝挂稳定错误码。客户端据此显示终态说明,
|
|
16540
|
+
* 而不是把内部错误原文当「加载失败」反复重试;也不让上游把 SQLite 报错原文透给编辑器。
|
|
16541
|
+
* @param code 稳定错误码(official-covered / no-records / no-evidence / too-large)
|
|
16542
|
+
* @param message 可读原因
|
|
16543
|
+
* @returns 带 code 的 Error
|
|
16544
|
+
*/
|
|
16545
|
+
function refusal(code, message) {
|
|
16546
|
+
return Object.assign(new Error(message), { code });
|
|
16547
|
+
}
|
|
16548
|
+
/**
|
|
16549
|
+
* @public
|
|
16550
|
+
* @author ddj 2026年10月31号
|
|
16551
|
+
* @description: 从任意错误里取回缺失文件预览的稳定拒绝码;不是本模块抛出的错误一律返回 undefined。
|
|
16552
|
+
* @param error 捕获到的错误
|
|
16553
|
+
* @returns 稳定拒绝码或 undefined
|
|
16554
|
+
*/
|
|
16555
|
+
function refusalCodeOf(error) {
|
|
16556
|
+
const code = error?.code;
|
|
16557
|
+
return code === "official-covered" || code === "official-decided" || code === "no-records" || code === "no-evidence" || code === "too-large" ? code : void 0;
|
|
16558
|
+
}
|
|
16473
16559
|
/** @private @author ddj 2026年10月28号 @param target Resolved absolute path. @returns Platform file identity. */
|
|
16474
16560
|
function fileKey(target) {
|
|
16475
16561
|
const normalized = resolve(target).replace(/\\/g, "/");
|
|
@@ -16520,13 +16606,16 @@ async function fileAbsent(ctx, cwd, path) {
|
|
|
16520
16606
|
async function missingReview(ctx, session, cwd, bucket, path) {
|
|
16521
16607
|
const abs = targetPathOf(ctx, await resolveTargetIn(ctx, cwd, path));
|
|
16522
16608
|
if (!abs) throw new Error("无法解析目标文件路径");
|
|
16523
|
-
if (hasOfficial(cwd, path) || hasOfficial(cwd, abs))
|
|
16609
|
+
if (hasOfficial(cwd, path) || hasOfficial(cwd, abs)) {
|
|
16610
|
+
if (!officialPending(cwd, abs) && !officialPending(cwd, path)) throw refusal("official-decided", "该文件已在磁盘删除,官方审查记录均已处理:可直接关闭该标签页");
|
|
16611
|
+
throw refusal("official-covered", "该文件已有官方审查记录(磁盘上已删除):请在差异栏对该待审条目执行 Keep 结案;整文件删除预览只适用于待审历史记录");
|
|
16612
|
+
}
|
|
16524
16613
|
if (!await fileAbsent(ctx, cwd, abs)) throw new Error("文件已重新出现,请刷新");
|
|
16525
16614
|
const records = await fileRecords(ctx, cwd, bucket, abs);
|
|
16526
16615
|
const latest = records.at(-1);
|
|
16527
|
-
if (!records.some((record) => !record.archived && !recordResolved(record))) throw
|
|
16528
|
-
if (!latest || typeof latest.after !== "string") throw
|
|
16529
|
-
if (Buffer.byteLength(latest.after, "utf8") > 8388608) throw
|
|
16616
|
+
if (!records.some((record) => !record.archived && !recordResolved(record))) throw refusal("no-records", "磁盘上已没有该文件,且没有待审历史记录:可直接关闭该标签页");
|
|
16617
|
+
if (!latest || typeof latest.after !== "string") throw refusal("no-evidence", "磁盘上已没有该文件,且缺少可靠的完整历史快照,无法展示或恢复;不会用局部差异拼接文件");
|
|
16618
|
+
if (Buffer.byteLength(latest.after, "utf8") > 8388608) throw refusal("too-large", "磁盘上已没有该文件,且历史快照过大,无法整文件预览");
|
|
16530
16619
|
for (const [token, item] of frozen) if (item.expires < Date.now()) frozen.delete(token);
|
|
16531
16620
|
if (frozen.size >= 256) throw new Error("缺失文件预览过多,请稍后重试");
|
|
16532
16621
|
const view = {
|
|
@@ -17316,9 +17405,11 @@ function buildHandlers(ctx, registry, searcher = newSearcher(ctx), contentSearch
|
|
|
17316
17405
|
review: await missingReview(ctx, sc.session, sc.cwd, bucket, args.path)
|
|
17317
17406
|
};
|
|
17318
17407
|
} catch (error) {
|
|
17408
|
+
const code = refusalCodeOf(error);
|
|
17319
17409
|
return {
|
|
17320
17410
|
ok: false,
|
|
17321
|
-
error: String(error)
|
|
17411
|
+
error: String(error),
|
|
17412
|
+
...code ? { code } : {}
|
|
17322
17413
|
};
|
|
17323
17414
|
}
|
|
17324
17415
|
},
|