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/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
- /** @private @author ddj 2026年10月28号 @param sessionId 请求作用域 @returns 已迁移工作区(允许跨实例冷读) */
15983
- async function workspace(sessionId) {
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 scope.cwd;
16040
+ return {
16041
+ cwd: scope.cwd,
16042
+ live: !!scope.session
16043
+ };
15988
16044
  }
15989
- /** @private @author ddj 2026年10月28号 @param sessionId 请求作用域 @returns 本端 live 会话的工作区;冷读作用域一律拒绝写入 */
15990
- async function liveWorkspace(sessionId) {
15991
- const scope = await resolveScope(ctx, sessionId);
15992
- if ("err" in scope || !scope.session) throw new Error("会话不存在或没有工作区");
15993
- await bucketOf(registry, ctx, scope.cwd);
15994
- return scope.cwd;
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
- return loadPage(cwd, sessionId);
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 workspace(args.sessionId);
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: mergedPage(official, pending, cwd)
16134
+ page: merged
16041
16135
  };
16042
16136
  },
16043
16137
  "edrv.review.detail": async (args) => {
16044
- const cwd = await workspace(args.sessionId);
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: pendingDetailOf(detail, new Date(detail.item.updatedAt).toISOString(), cwd)
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
- return detail ? {
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
- ok: false,
16062
- error: "官方审查记录不存在"
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
- ...args.decision === "rejected" ? await undoReview(ctx, sessionOf(ctx, args.sessionId), cwd, args) : decideReview(cwd, args)
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: error instanceof Error ? error.message : String(error)
16220
+ error: message
16095
16221
  };
16096
16222
  }
16097
16223
  }