Files
qiming/qimingclaw/crates/agent-electron-client/docs/review-reg-and-autoreconnect.md

7.9 KiB
Raw Permalink Blame History

Code Review: reg 与自动重连逻辑

审查范围App.tsx 自动重连、auth.ts syncConfigToServer、processHandlers.ts Lanproxy 启动
结论:逻辑正确,注释与行为一致;有少量可改进点。


1. 已做对的部分

1.1 先 reg 成功再启动服务

  • savedKey 路径syncConfigToServer()await,只有 result 为真时才调用 startServicesSequentially(),不会在 reg 失败时启动服务。
  • 配置来源syncConfigToServer 内用本次 reg 的 response.serverHost / response.serverPort 调用 saveServerConfig()lanproxy 启动时从 settings 读到的已是本次 reg 写入的最新值。

1.2 reg 返回可能变化的处理

  • syncConfigToServer 在 reg 成功后将本次返回的 serverHost/serverPort 写回配置,并返回 response调用方在 reg 成功后再启动,符合「用本次最新结果」的语义。
  • 注释在 App.tsx 与 auth.ts 中已说明「reg 返回可能会变化,先写配置再启动」。

1.3 失败与提示

  • reg 失败(result 为 null时只打日志 + notification不启动服务行为与注释一致。
  • 异常被 try/catch 捕获,避免未处理 Promise rejection。

1.4 代码质量

  • 当前修改未引入新的 lint 报错。
  • 类型与现有风格一致(仅个别 as any 为既有写法)。

2. 可改进点(非必须)

2.1 「向导刚完成」路径未再调 reg

  • 现状setupJustCompleted.current === true 时直接 startServicesSequentially(...),不调用 syncConfigToServer
  • 原因:向导完成时通常刚执行过登录,loginAndRegister 已调过 reg 并执行 saveServerConfig,配置已是最新。
  • 建议:若希望与「重新打开客户端」完全统一(始终先 reg 再启动),可在该分支也先 await syncConfigToServer({ suppressToast: true }),成功后再 startServicesSequentially;否则保持现状即可,在注释中注明「向导刚完成时假定登录流程已执行 reg直接使用当前配置启动」。

2.2 自动重连中的 catch 未对用户提示

  • 现状autoReconnectcatchconsole.error('[App] 自动重连失败:', error),用户无 toast。
  • 建议:若希望网络异常等未预期错误也有提示,可在 catch 里补一次 notification.warning(与 result 为 null 时的文案区分开,例如「自动重连异常,请稍后重试或手动启动服务」),避免重复可复用现有「自动重连失败」的 key 或 message。

2.3 类型收紧(低优先级)

  • startServicesSequentiallyagentConfiglpConfig 使用 as any,后续可改为具体类型(如从 shared 或 api 类型中抽取),便于后续扩展 reg 返回字段时的类型安全。

3. 与 processHandlers 的衔接

  • Lanproxy 启动仍从主进程 readSetting('lanproxy_config') / lanproxy.server_host / lanproxy.server_port 读配置;这些值由渲染进程侧 saveServerConfig 经 IPC 写入顺序为「reg 成功 → syncConfigToServer 写配置 → 再启动服务」,因此主进程读到的已是本次 reg 写入的最新配置,无需改 processHandlers。

4. 已修复:账号切换后 lanproxy 使用旧 clientKey2026-03-12

问题

场景一:不退出直接切换账号

handleLoginhandleStartService('lanproxy') 时传入新账号的 clientKey,但主进程 startLanproxyProcess 检测到 ctx.lanproxy.running === true(旧进程仍在),直接返回 success: true 而不重启 → 旧 key 继续使用 → 服务端认为是旧账号 → 新账号会话显示「客户端离线」。

场景二:退出登录后用新账号登录

handleLogout 会停止所有服务agent/fileServer/lanproxy/mcpProxy/computerServer并清除 auth 信息。用户用新账号登录时 handleLogin 执行 loginAndRegister → 写入新 configKey/savedKey → 启动服务。由于 logout 已停掉所有服务lanproxy 的 ctx.lanproxy.running === false,新进程用新 clientKey 正常启动。此场景在修复前已能正常工作。

修复

processHandlers.tsstartLanproxyProcess 中,将「已运行则跳过」改为「已运行则异步停止(等待进程退出)再用本次传入的 config 重启」:

if (ctx.lanproxy.running) {
  // 切换账号后 clientKey 会变化,必须用新配置重启,不能跳过。
  // 否则旧进程继续使用旧 clientKey 导致「本地显示已联通、会话显示离线」。
  log.info('[Lanproxy] 已在运行,先停止再用新配置重启');
  await ctx.lanproxy.stopAsync();
}

stopAsync() 发送 SIGTERM 并等待进程 exit 事件5s 超时后 SIGKILL确保端口/资源释放后再启动新进程。

修改点

  • src/main/processManager.ts:新增 stopAsync() 方法(含 stdio 监听器清理,防止 Windows 句柄泄漏)
  • src/main/ipc/processHandlers.tsstartLanproxyProcess 内的 running 守卫改用 await stopAsync()
场景 修复前 修复后
不退出直接切换 lanproxy 用旧 key新账号离线 先停旧进程再用新 config 启动
退出后用新账号登录 logout 已停服务,正常启动 未运行,直接启动
任何路径调用 lanproxy:start 已运行时跳过 始终应用本次传入的配置

5. 总结

项目 状态
reg 成功后才启动服务 正确
使用本次 reg 返回的配置 正确(通过 saveServerConfig 写再读)
注释与行为一致
失败分支与提示 正确catch 可考虑补用户提示
向导完成路径 合理,若需统一可先 reg 再启动
账号切换时先停旧服务 已修复2026-03-12
手动启动单个服务时先 reg 已补充2026-03-12

结论:当前实现满足「等待 reg 成功返回后才启动服务」且「使用 reg 可能变化后的最新配置」;账号切换场景已通过先停后启修复;手动启动单个服务场景已补充先 reg 再启动。


6. 手动启动单个服务时补充 reg 调用2026-03-12

问题

场景:用户在 ClientPage 点击某个服务的「启动」按钮(非「启动全部」、非登录流程)。

此前 handleStartService(svc.key) 直接启动目标服务,不调用 reg。这意味着:

  • 如果后端 serverHost/serverPort 发生过变化如服务端重新分配端口lanproxy 启动时读到的是旧值。
  • 与「登录」和「启动全部」行为不一致——它们都在启动服务前/后调用 syncConfigToServer()

修复

新增 handleStartServiceManual 包装函数UI 按钮 onClick 改为调用此函数:

const handleStartServiceManual = async (key: string) => {
  // 先 reg确保 lanproxy 等服务启动时使用最新的后端返回数据
  try {
    await syncConfigToServer({ suppressToast: true });
  } catch (e) {
    console.error('[ClientPage] 手动启动服务前 reg 同步失败:', e);
  }
  await handleStartService(key);
  onAuthChange?.();
};

时序对比

场景 reg 时机 启动时机
登录流程 (handleLogin) loginAndRegister() + 启动非代理服务后 syncConfigToServer() reg 后启动 lanproxy
启动全部 (handleStartAll) 启动非代理服务后 syncConfigToServer() reg 后启动 lanproxy
手动启动单个 (handleStartServiceManual) syncConfigToServer() reg 后启动目标服务

三个场景均保证 reg 在服务启动之前完成lanproxy 读到的 serverIp/serverPort 为 reg 返回的最新值。

修改点

  • src/renderer/components/pages/ClientPage.tsx:新增 handleStartServiceManual,单个服务「启动」按钮 onClick 改用此函数