🐛 fix(vscode): 接管时把会话文件里的发送方路径重写为本机路径

Claude-Session: https://claude.ai/code/session_011cEyL6k351U2BzX1Qmygph
This commit is contained in:
2026-08-26 19:04:09 +08:00
parent 07c40897e2
commit 71a91fe7ec
7 changed files with 151 additions and 10 deletions
+11 -10
View File
@@ -171,11 +171,7 @@ export async function handleHandoffStart(panel: KanbanWebviewPanel, issueNumber:
const column = knownColumns.includes(state.column as IssueColumn) ? (state.column as IssueColumn) : 'todo'
const assignees = (issue.assignees ?? []).map(a => a.login)
if (!canStartHandoff({ source: 'gitea', column, handoffAttachmentId: str(state.handoffAttachmentId), assignees }, me)) {
// 区分「不是我的工单」和「已在移交中/已完成」两种拒绝原因,措辞不同。
const notMine = assignees.length > 0 && !(me !== undefined && assignees.includes(me))
toast(panel, 'error', notMine
? `#${issueNumber} 不是你负责的工单,不能移交`
: `#${issueNumber} 已在移交中或已完成,不能再次移交`)
toast(panel, 'error', `#${issueNumber} 不是你负责的工单,或已在移交中 / 已完成,不能移交`)
return
}
@@ -266,7 +262,7 @@ export async function handleHandoffStart(panel: KanbanWebviewPanel, issueNumber:
const outDir = await scratchDir('spx-handoff-out-')
try {
manifest = await stageHandoffBundle(
{ issueNumber, from: me, branch, sessions, profiles },
{ issueNumber, from: me, branch, ...(worktreeAbs && fs.existsSync(worktreeAbs) ? { worktreePath: worktreeAbs } : {}), sessions, profiles },
staging,
{ projectsRoot: claudeProjectsRoot(), codexRoot: defaultCodexSessionsDir() },
)
@@ -453,19 +449,24 @@ export async function handleHandoffAccept(panel: KanbanWebviewPanel, issueNumber
})
}
// ③ 装会话文件
// ③ 装会话文件;发送方与接收方的 worktree 路径不同就顺带把会话里的
// cwd 类路径重写成接收方路径,否则 `codex resume` / MCP 起进程会照着
// 发送方那条本机不存在的路径找目录,直接 ENOENT。
const pathRewrite = manifest.worktreePath && worktreeAbs ? { from: manifest.worktreePath, to: worktreeAbs } : undefined
await installHandoffBundle(extracted, manifest, {
projectsRoot: claudeProjectsRoot(),
codexRoot: defaultCodexSessionsDir(),
workspaceProjectsDir: projectsDirFor(workspaceRoot),
...(worktreeAbs ? { worktreeProjectsDir: projectsDirFor(worktreeAbs) } : {}),
...(pathRewrite ? { pathRewrite } : {}),
})
// ④ 本机字段 + 清共享字段;⑤ 删附件(失败只 warn)
// reviewSessionFileExists 是计算字段,board 只在启动时算一次(annotateReviewSessionFileExists);
// 接管刚把 codex 会话文件装进本机,这里顺手算好一起下发,UI 不用等下次刷新才能点「审查会话」链接。
const reviewSessionFileExists = !!manifest.sessions.reviewSessionId
&& manifest.codex.some(c => c.id.toLowerCase() === manifest.sessions.reviewSessionId!.toLowerCase())
const reviewSessionId = manifest.sessions.reviewSessionId
const reviewSessionFileExists = !!reviewSessionId
&& manifest.codex.some(c => c.id.toLowerCase() === reviewSessionId.toLowerCase())
const local = { sessions: manifest.sessions, profiles: manifest.profiles, worktreePath: worktreeAbs }
await panel.mergeIssueState(issueNumber, handoffAcceptedStateExtra(local))
try {
@@ -482,7 +483,7 @@ export async function handleHandoffAccept(panel: KanbanWebviewPanel, issueNumber
patch: handoffAcceptedUiPatch({ ...local, worktreeExists: !!worktreeAbs, reviewSessionFileExists }),
})
toast(panel, 'success', `已接管 #${issueNumber}(来自 ${manifest.from}${worktreeAbs ? `worktree${worktreeAbs}` : ''}`)
logger.add({ level: 'info', source: 'panel', message: `接管 #${issueNumber} 完成`, details: JSON.stringify({ manifest, worktreeAbs }) })
logger.add({ level: 'info', source: 'panel', message: `接管 #${issueNumber} 完成`, details: JSON.stringify({ manifest, worktreeAbs, pathRewrite }) })
}
catch (err) {
const message = err instanceof Error ? err.message : String(err)