dsh-vscode-mode 0.23.0 → 0.23.2
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 +7 -3
- package/lib/client.js +146 -67
- package/lib/client.js.map +2 -2
- package/lib/index.js +167 -41
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/reviewBatch.ts +22 -21
- package/src/client/ui/EditorView.ts +110 -16
- package/src/client/ui/OfficialDiffBar.tsx +18 -3
- package/src/client/ui/SidecarDiffBar.tsx +3 -2
- package/src/client/ui/reviewAllRow.tsx +5 -4
- package/src/reviewRpc.ts +149 -36
- package/src/shared/review.ts +2 -1
- package/src/shared/rpc.ts +1 -1
package/lib/index.js
CHANGED
|
@@ -15969,6 +15969,51 @@ function finishUndo(cwd, request) {
|
|
|
15969
15969
|
}
|
|
15970
15970
|
//#endregion
|
|
15971
15971
|
//#region src/reviewRpc.ts
|
|
15972
|
+
/** 批量写入快照的硬上限:待审积压超过它说明状态异常,宁可拒绝也不做无界写循环。 */
|
|
15973
|
+
const BATCH_LIMIT = 2e3;
|
|
15974
|
+
/** 冷端 Undo 说明:Undo 会校验证据并写回源文件,只能在持有该会话的那一端执行。 */
|
|
15975
|
+
const UNDO_NEEDS_LIVE = "Undo 需要在本端活跃的会话上执行:该会话当前在另一端(Web / 桌面)激活。Keep 不受影响,可在任一端执行;或在本端打开该会话后重试。";
|
|
15976
|
+
/** 冷端待审判定说明:待审库是按工作区的本地存储,判定需要与正在编辑的那一端保持一致。 */
|
|
15977
|
+
const PENDING_NEEDS_LIVE = "待审条目的判定需要在本端活跃的会话上执行:该会话当前在另一端激活,请在那一端处理,或在本端打开该会话后重试。";
|
|
15978
|
+
/**
|
|
15979
|
+
* @private
|
|
15980
|
+
* @author ddj 2026年10月31号
|
|
15981
|
+
* @description: 批量写入用的一次性完整快照。合并口径与列表一致(待审投影优先、同路径官方旧行让位),
|
|
15982
|
+
* 但一次取全,且 items 与 pending 同源,客户端才能把它当成写前不变式。
|
|
15983
|
+
* @param cwd 工作区
|
|
15984
|
+
* @param pending 该会话可见的待审页
|
|
15985
|
+
* @returns 完整快照;待审积压超过上限或分页期间数量漂移时返回错误
|
|
15986
|
+
*/
|
|
15987
|
+
function batchSnapshot(cwd, pending) {
|
|
15988
|
+
const first = reviewPage(cwd);
|
|
15989
|
+
if (first.pending > BATCH_LIMIT) return {
|
|
15990
|
+
ok: false,
|
|
15991
|
+
error: `待审记录过多(${first.pending} 条,上限 ${BATCH_LIMIT}),请先分批处理后重试`
|
|
15992
|
+
};
|
|
15993
|
+
const official = [];
|
|
15994
|
+
let page = first;
|
|
15995
|
+
while (page) {
|
|
15996
|
+
for (const item of page.items) if (item.current && item.decision === "pending") official.push(item);
|
|
15997
|
+
page = page.nextCursor === null ? void 0 : reviewPage(cwd, page.nextCursor);
|
|
15998
|
+
}
|
|
15999
|
+
if (official.length !== first.pending) return {
|
|
16000
|
+
ok: false,
|
|
16001
|
+
error: "待审记录数量不完整,未执行批量操作"
|
|
16002
|
+
};
|
|
16003
|
+
const live = pending.items.filter((item) => !item.settled);
|
|
16004
|
+
const kept = official.filter((row) => !live.some((entry) => pendingSamePath(entry.path, row.currentPath) || pendingSamePath(entry.path, row.path)));
|
|
16005
|
+
const items = [...live.map((entry) => pendingItemOf(entry, cwd)), ...kept];
|
|
16006
|
+
return {
|
|
16007
|
+
ok: true,
|
|
16008
|
+
page: {
|
|
16009
|
+
items,
|
|
16010
|
+
pending: items.length,
|
|
16011
|
+
nextCursor: null,
|
|
16012
|
+
gaps: first.gaps,
|
|
16013
|
+
...first.overview ? { overview: first.overview } : {}
|
|
16014
|
+
}
|
|
16015
|
+
};
|
|
16016
|
+
}
|
|
15972
16017
|
/**
|
|
15973
16018
|
* @public
|
|
15974
16019
|
* @author ddj 2026年10月26号 / 2026年10月28号
|
|
@@ -15979,19 +16024,61 @@ function finishUndo(cwd, request) {
|
|
|
15979
16024
|
* @returns 审查相关处理器表
|
|
15980
16025
|
*/
|
|
15981
16026
|
function reviewHandlers(ctx, registry) {
|
|
15982
|
-
/**
|
|
15983
|
-
|
|
16027
|
+
/**
|
|
16028
|
+
* @private
|
|
16029
|
+
* @author ddj 2026年10月31号
|
|
16030
|
+
* @description: 解析会话作用域,并标明本端是否持有该会话的活跃实例。
|
|
16031
|
+
* 工作区一律由 host 解析,绝不采信调用方 cwd;冷读作用域只带 cwd,不带会话实例。
|
|
16032
|
+
* @param sessionId 请求作用域
|
|
16033
|
+
* @returns 已迁移工作区与活跃标记
|
|
16034
|
+
* @throws 会话不存在、会话无工作区或活跃库迁移未完成
|
|
16035
|
+
*/
|
|
16036
|
+
async function scopeOf(sessionId) {
|
|
15984
16037
|
const scope = await resolveScope(ctx, sessionId);
|
|
15985
16038
|
if ("err" in scope) throw new Error("会话不存在或没有工作区");
|
|
15986
16039
|
await bucketOf(registry, ctx, scope.cwd);
|
|
15987
|
-
return
|
|
16040
|
+
return {
|
|
16041
|
+
cwd: scope.cwd,
|
|
16042
|
+
live: !!scope.session
|
|
16043
|
+
};
|
|
15988
16044
|
}
|
|
15989
|
-
/**
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
16045
|
+
/**
|
|
16046
|
+
* @private
|
|
16047
|
+
* @author ddj 2026年10月31号
|
|
16048
|
+
* @description: 本端是否持有该会话的活跃实例(纯诊断用,不解析工作区)。
|
|
16049
|
+
* @param sessionId 请求作用域
|
|
16050
|
+
* @returns 本端加载了该会话时为 true
|
|
16051
|
+
*/
|
|
16052
|
+
function liveOf(sessionId) {
|
|
16053
|
+
return !!sessionOf(ctx, sessionId);
|
|
16054
|
+
}
|
|
16055
|
+
/**
|
|
16056
|
+
* @private
|
|
16057
|
+
* @author ddj 2026年10月31号
|
|
16058
|
+
* @description: 按本端是否活跃覆写面板能力位。活跃端保持数据库结论;
|
|
16059
|
+
* 冷端关闭依赖会话的操作(Undo、待审判定)并给出跨端原因,Keep 仍按数据库结论放行。
|
|
16060
|
+
* @param capability 数据库推导的能力位
|
|
16061
|
+
* @param live 本端是否持有活跃会话
|
|
16062
|
+
* @param pending 是否为待审投影条目
|
|
16063
|
+
* @returns 面板可用的能力位
|
|
16064
|
+
*/
|
|
16065
|
+
function liveCapabilityOf(capability, live, pending) {
|
|
16066
|
+
if (live) return {
|
|
16067
|
+
...capability,
|
|
16068
|
+
live: true
|
|
16069
|
+
};
|
|
16070
|
+
if (pending) return {
|
|
16071
|
+
keep: false,
|
|
16072
|
+
undo: false,
|
|
16073
|
+
reason: PENDING_NEEDS_LIVE,
|
|
16074
|
+
live: false
|
|
16075
|
+
};
|
|
16076
|
+
return {
|
|
16077
|
+
...capability,
|
|
16078
|
+
undo: false,
|
|
16079
|
+
live: false,
|
|
16080
|
+
reason: capability.undo ? UNDO_NEEDS_LIVE : capability.reason
|
|
16081
|
+
};
|
|
15995
16082
|
}
|
|
15996
16083
|
/**
|
|
15997
16084
|
* @private
|
|
@@ -16004,7 +16091,11 @@ function reviewHandlers(ctx, registry) {
|
|
|
16004
16091
|
*/
|
|
16005
16092
|
function pendingPageOf(cwd, sessionId) {
|
|
16006
16093
|
try {
|
|
16007
|
-
|
|
16094
|
+
const page = loadPage(cwd, sessionId);
|
|
16095
|
+
return page.items.some((item) => item.settled === true) ? {
|
|
16096
|
+
...page,
|
|
16097
|
+
items: page.items.filter((item) => item.settled !== true)
|
|
16098
|
+
} : page;
|
|
16008
16099
|
} catch {
|
|
16009
16100
|
return {
|
|
16010
16101
|
items: [],
|
|
@@ -16016,7 +16107,8 @@ function reviewHandlers(ctx, registry) {
|
|
|
16016
16107
|
}
|
|
16017
16108
|
return {
|
|
16018
16109
|
"edrv.review.list": async (args) => {
|
|
16019
|
-
const cwd = await
|
|
16110
|
+
const { cwd } = await scopeOf(args.sessionId);
|
|
16111
|
+
if (args.all && !args.history && args.path === void 0) return batchSnapshot(cwd, pendingPageOf(cwd, args.sessionId));
|
|
16020
16112
|
const official = reviewPage(cwd, args.cursor, args.history, args.path);
|
|
16021
16113
|
const pending = pendingPageOf(cwd, args.sessionId);
|
|
16022
16114
|
if (args.history) return {
|
|
@@ -16035,63 +16127,97 @@ function reviewHandlers(ctx, registry) {
|
|
|
16035
16127
|
}
|
|
16036
16128
|
};
|
|
16037
16129
|
}
|
|
16130
|
+
const merged = mergedPage(official, pending, cwd);
|
|
16131
|
+
log.debug(`[review] list live=${liveOf(args.sessionId) ? 1 : 0} path=${args.path ?? "(all)"} items=${merged.items.length} pending=${merged.pending} covered=${merged.overview?.coveredPaths.length ?? -1}`);
|
|
16038
16132
|
return {
|
|
16039
16133
|
ok: true,
|
|
16040
|
-
page:
|
|
16134
|
+
page: merged
|
|
16041
16135
|
};
|
|
16042
16136
|
},
|
|
16043
16137
|
"edrv.review.detail": async (args) => {
|
|
16044
|
-
const
|
|
16138
|
+
const scope = await scopeOf(args.sessionId);
|
|
16139
|
+
const cwd = scope.cwd;
|
|
16045
16140
|
if (args.id.startsWith("pending:")) {
|
|
16046
16141
|
const detail = loadDetail(cwd, args.id.slice(8));
|
|
16047
16142
|
if (!detail) return {
|
|
16048
16143
|
ok: false,
|
|
16049
16144
|
error: "待审条目不存在"
|
|
16050
16145
|
};
|
|
16146
|
+
const mapped = pendingDetailOf(detail, new Date(detail.item.updatedAt).toISOString(), cwd);
|
|
16051
16147
|
return {
|
|
16052
16148
|
ok: true,
|
|
16053
|
-
detail:
|
|
16149
|
+
detail: {
|
|
16150
|
+
...mapped,
|
|
16151
|
+
capability: liveCapabilityOf(mapped.capability, scope.live, true)
|
|
16152
|
+
}
|
|
16054
16153
|
};
|
|
16055
16154
|
}
|
|
16056
16155
|
const detail = reviewDetail(cwd, args.id);
|
|
16057
|
-
|
|
16156
|
+
if (!detail) {
|
|
16157
|
+
log.debug(`[review] detail live=${scope.live ? 1 : 0} id=${args.id} found=0`);
|
|
16158
|
+
return {
|
|
16159
|
+
ok: false,
|
|
16160
|
+
error: "官方审查记录不存在"
|
|
16161
|
+
};
|
|
16162
|
+
}
|
|
16163
|
+
const capability = liveCapabilityOf(detail.capability, scope.live, false);
|
|
16164
|
+
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
|
+
return {
|
|
16058
16166
|
ok: true,
|
|
16059
|
-
detail
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16167
|
+
detail: {
|
|
16168
|
+
...detail,
|
|
16169
|
+
capability
|
|
16170
|
+
}
|
|
16063
16171
|
};
|
|
16064
16172
|
},
|
|
16065
16173
|
"edrv.review.decide": async (args) => {
|
|
16066
|
-
const cwd = await liveWorkspace(args.sessionId);
|
|
16067
|
-
if (args.id.startsWith("pending:")) {
|
|
16068
|
-
const result = await applyDecide(ctx.get("fs"), cwd, {
|
|
16069
|
-
sessionId: args.sessionId,
|
|
16070
|
-
id: args.id.slice(8),
|
|
16071
|
-
revision: args.revision,
|
|
16072
|
-
operationId: args.operationId,
|
|
16073
|
-
decision: args.decision,
|
|
16074
|
-
scope: args.scope === "hunk" ? "block" : "file",
|
|
16075
|
-
...args.blockId === void 0 ? {} : { blockId: args.blockId }
|
|
16076
|
-
});
|
|
16077
|
-
if (!result.ok) return {
|
|
16078
|
-
ok: false,
|
|
16079
|
-
error: result.reason
|
|
16080
|
-
};
|
|
16081
|
-
return {
|
|
16082
|
-
ok: true,
|
|
16083
|
-
revision: result.flags.revision
|
|
16084
|
-
};
|
|
16085
|
-
}
|
|
16086
16174
|
try {
|
|
16175
|
+
const scope = await scopeOf(args.sessionId);
|
|
16176
|
+
if (args.id.startsWith("pending:")) {
|
|
16177
|
+
if (!scope.live) return {
|
|
16178
|
+
ok: false,
|
|
16179
|
+
error: PENDING_NEEDS_LIVE
|
|
16180
|
+
};
|
|
16181
|
+
const result = await applyDecide(ctx.get("fs"), scope.cwd, {
|
|
16182
|
+
sessionId: args.sessionId,
|
|
16183
|
+
id: args.id.slice(8),
|
|
16184
|
+
revision: args.revision,
|
|
16185
|
+
operationId: args.operationId,
|
|
16186
|
+
decision: args.decision,
|
|
16187
|
+
scope: args.scope === "hunk" ? "block" : "file",
|
|
16188
|
+
...args.blockId === void 0 ? {} : { blockId: args.blockId }
|
|
16189
|
+
});
|
|
16190
|
+
if (!result.ok) return {
|
|
16191
|
+
ok: false,
|
|
16192
|
+
error: result.reason
|
|
16193
|
+
};
|
|
16194
|
+
return {
|
|
16195
|
+
ok: true,
|
|
16196
|
+
revision: result.flags.revision
|
|
16197
|
+
};
|
|
16198
|
+
}
|
|
16199
|
+
if (args.decision === "rejected") {
|
|
16200
|
+
if (!scope.live) return {
|
|
16201
|
+
ok: false,
|
|
16202
|
+
error: UNDO_NEEDS_LIVE
|
|
16203
|
+
};
|
|
16204
|
+
return {
|
|
16205
|
+
ok: true,
|
|
16206
|
+
...await undoReview(ctx, sessionOf(ctx, args.sessionId), scope.cwd, args)
|
|
16207
|
+
};
|
|
16208
|
+
}
|
|
16209
|
+
const result = decideReview(scope.cwd, args);
|
|
16210
|
+
if (!scope.live) log.info(`[review] 冷端 Keep 官方记录 ${args.id}`);
|
|
16087
16211
|
return {
|
|
16088
16212
|
ok: true,
|
|
16089
|
-
...
|
|
16213
|
+
...result
|
|
16090
16214
|
};
|
|
16091
16215
|
} catch (error) {
|
|
16216
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16217
|
+
log.warn(`[review] decide 失败 id=${args.id} decision=${args.decision}: ${message}`);
|
|
16092
16218
|
return {
|
|
16093
16219
|
ok: false,
|
|
16094
|
-
error:
|
|
16220
|
+
error: message
|
|
16095
16221
|
};
|
|
16096
16222
|
}
|
|
16097
16223
|
}
|