Skip to content

feat(extension): make the local daemon port configurable in the popup - #175

Open
shnpd wants to merge 4 commits into
mainfrom
feat/configurable-daemon-port
Open

feat(extension): make the local daemon port configurable in the popup#175
shnpd wants to merge 4 commits into
mainfrom
feat/configurable-daemon-port

Conversation

@shnpd

@shnpd shnpd commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

摘要

fix #114
扩展原先写死连接 ws://127.0.0.1:52800。本 PR 让用户在 popup 里配置本机 daemon 端口,写入 chrome.storage.local 后 background 自动改 WebSocket URL 并重连。默认端口仍为 52800

未连接时不再展示 [WSTransport] ... 这类技术错误,改为提示确认 daemon 已启动且端口一致;连接开关在未连接时显示为关闭。

改动说明

  • Popup 新增「连接端口」输入,blur / Enter / 关闭弹窗时提交;非法端口只提示、不写入。
  • 端口走 chrome.storage.localbsk_daemon_port),不再使用未接线的 set_port 消息。
  • Background 启动时读取已存端口;监听 storage 变化后 setUrl + disconnect/connect。
  • WSTransport 支持运行时更换 URL(不自动重连)。
  • 隐私政策、架构文档、中英文案同步更新。

ui展示

企业微信20260902-192153@2x image

测试

  • 默认端口 52800 能正常连上 daemon
  • popup 改成有效端口(如 53200)后,扩展会重连到新端口
  • 输入非法端口(abc / 0 / 65536)显示错误,且不写入 storage
  • 清空输入后回落到默认端口
  • 连接开关关闭时改端口,不会强制重连
  • daemon 未启动或端口不一致时,显示「无法连接…」,不显示原始 transport 错误
  • 重新打开 popup,端口输入框显示上次保存的值

shnpd and others added 2 commits September 2, 2026 18:56
The extension was hardwired to 52800; persist a custom loopback port and reconnect when it changes so users can match a non-default daemon.

Co-authored-by: Cursor <cursoragent@cursor.com>
The field was too short to read; grow its height without widening the card.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shnpd
shnpd requested review from Ljy-0827 and iuyo5678 and a lite review from Copilot September 2, 2026 11:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Port parsing and URL construction need to be made stricter/more robust (e.g., avoid parseInt-accepting malformed input and avoid manual URL concatenation that can break IPv6 formatting).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR implements runtime configuration of the extension’s local daemon WebSocket port via the popup UI, persisting the setting in chrome.storage.local and updating the background connection behavior accordingly (addressing issue #114).

Changes:

  • Added a popup “Connection port” input with validation + persistence (bsk_daemon_port), plus friendlier disconnected-state messaging.
  • Updated background to read the stored port at startup and react to storage changes by updating the WebSocket URL and reconnecting.
  • Extended transport to allow runtime URL changes and added/updated docs + i18n + tests.
File summaries
File Description
packages/i18n/src/locales/zh-CN/extension.json Adds zh-CN strings for daemon port UI and unreachable messaging.
packages/i18n/src/locales/en-US/extension.json Adds en-US strings for daemon port UI and unreachable messaging.
docs/architecture.md Documents that the default daemon WS port is configurable via the popup.
apps/extension/src/transport/ws-transport.ts Allows updating transport URL via setUrl() without auto-reconnect.
apps/extension/src/transport/daemon-endpoint.ts New helpers for default port, URL resolution, and input/storage normalization.
apps/extension/src/transport/tests/ws-transport.test.ts Adds coverage for setUrl() behavior.
apps/extension/src/transport/tests/daemon-endpoint.test.ts Adds coverage for daemon endpoint URL/port parsing helpers.
apps/extension/src/lib/popup-bridge.ts Removes unused set_port message variant; port now stored via storage.
apps/extension/src/lib/instance-id.ts Adds get/set helpers and storage key for bsk_daemon_port.
apps/extension/src/lib/tests/instance-id.test.ts Adds tests for daemon port persistence/normalization.
apps/extension/src/entrypoints/popup/use-daemon-port.ts New hook to load/commit daemon port preference via chrome.storage.local.
apps/extension/src/entrypoints/popup/use-connection-state.ts Updates comment to reflect current popup bridge semantics.
apps/extension/src/entrypoints/popup/App.tsx Renders port input, adjusts disconnected messaging, and hides transport errors when disconnected.
apps/extension/src/entrypoints/popup/App.test.tsx Adds tests for disconnected UX and daemon port input behavior.
apps/extension/src/entrypoints/background.ts Reads stored port on startup and reconnects on storage updates.
apps/extension/PRIVACY.zh-CN.md Updates privacy text to note configurable loopback port.
apps/extension/PRIVACY.md Updates privacy text to note configurable loopback port.
Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/extension/src/transport/daemon-endpoint.ts
Comment thread apps/extension/src/entrypoints/background.ts
Comment thread apps/extension/src/transport/daemon-endpoint.ts
Comment thread apps/extension/src/transport/daemon-endpoint.ts
Comment thread apps/extension/src/entrypoints/popup/use-daemon-port.ts
shnpd and others added 2 commits September 3, 2026 19:57
如果有字符就报错,不直接转为数字

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
修复注释不准确

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@iuyo5678

iuyo5678 commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

这个 PR 解决了 #114 中扩展端口无法与 CLI 对齐的问题。建议保留目前的主要实现方向:chrome.storage.local 持久化、storage.onChanged 驱动配置生效,以及 WSTransport.setUrl() 只更新 URL、不自动重连。

合并前建议调整一下连接生命周期和 popup 的状态处理。这样既能保留当前功能,也能减少对现有连接行为的影响。下面是基于当前提交的具体改进建议。

1. 恢复连接开关与错误展示的原有语义

连接开关只表示“是否允许连接”

App.tsx 目前使用:

checked={snapshot.connectionEnabled && !isDisconnected}

connectionEnabled === true、但 daemon 不可达时,开关会显示关闭。此时点击开关发送的仍是 true,Controller 因为状态没有变化直接返回,用户无法通过开关停止自动重连。

建议恢复为:

checked={snapshot.connectionEnabled}

开关表示用户是否启用连接,状态文字表示当前是否已连接。这两个状态可以独立展示,断连期间也应允许用户关闭连接。

保留具体错误,友好提示作为补充

App.tsx 新增的 !isDisconnected 会隐藏所有断连状态下的错误。但协议主版本不兼容、版本过旧也会进入 disconnected,这些情况需要提示升级,不能都显示成“检查 daemon 是否启动、端口是否一致”。

建议本 PR 恢复原有 lastError 展示。新增的连接提示可以只在没有具体错误时显示;更完整的错误分类和文案优化可以单独处理。

收益: 这两处主要通过撤回额外行为修改完成,不需要引入新状态模型,同时保留用户关闭连接和排查协议问题的能力。

2. 让 Controller 统一协调端口切换

background.ts 当前直接执行 setUrl → disconnect → connect。但 disconnect() 又会触发 Controller 的恢复流程,后者也负责清理会话和重连。

这里等待 transport.disconnect() 并不等于等待会话清理完成。两个流程并行后,新连接可能已经建立,旧会话仍在归还标签页或解绑 CDP。

建议增加一个由 ConnectionController 管理的配置切换入口,例如 reconfigureTransport。具体接口名可以按现有风格确定,职责建议如下:

模块 职责
popup 编辑、校验和保存端口偏好
background 接收 storage 变化,归一化配置并交给 Controller
ConnectionController 协调切换、清理、握手取消和重新连接
WSTransport 更新 URL,处理 socket 及其底层重试

继续复用同一个 WSTransport 实例即可,避免重建实例后重新绑定 dispatcher、heartbeat 和其他监听器。URL 的生成仍放在端口模块;Controller 可以通过配置回调调用现有 setUrl(),不必给通用 Transport 接口强行加入 WebSocket 专属配置。

切换过程

记录最新目标配置,进入受控切换状态
→ 暂停其他连接入口,取消旧握手和重试
→ 受控断开旧连接
→ 等待旧会话清理完成
→ 应用最新目标 URL
→ 根据最新 connectionEnabled 决定是否发起连接

实现时建议保证以下行为:

  • 受控切换只有一个执行流程。 主动断开产生的事件不能再启动另一条意外断连恢复流程;如果已有清理正在执行,就复用该清理过程。
  • 重连入口统一经过 Controller。 keepalive、唤醒事件和握手重试都应遵守初始化、切换及清理状态。可以给 keepalive 注入 Controller 的连接请求回调,避免它直接绕过协调过程调用 transport.connect()
  • 连续修改采用最新配置。 清理期间从 A 改成 B、再改成 C,结束后应用 C;同一个已生效或待应用端口不重复触发切换。
  • 用户关闭连接立即影响后续决策。 清理期间关闭开关,结束后保持断开。端口切换本身不要通过临时修改用户的 connectionEnabled 偏好来实现。
  • 旧连接的异步结果失效。 复用现有 generation / abort 思路,防止旧握手或旧连接尝试的完成、失败回调覆盖新状态。
  • 清理未成功就不越过清理阶段连接新端点。 保留失败信息和可重试状态,后续连接请求仍通过同一个协调入口处理。

这里有一个需要特别注意的实现细节:不要让串行协调流程一直等待 connect() 成功。 当前 transport 在目标不可达时可能持续等待重试;如果把整个 await connect() 放入队列,后续换端口和关闭连接会被它堵住。应当协调有限的清理和配置操作,连接尝试则允许被后续配置或关闭操作取消。

同理,如果复用现有恢复逻辑,应区分“清理完成的 Promise”和“包含连接等待的整个恢复 Promise”,避免间接引入相同的阻塞。

收益: 端口切换和现有断连恢复共享协调规则,旧会话清理与新连接之间有明确顺序;同时覆盖快速切换端口、切换时关闭连接和后台唤醒等时序,不需要在各处分别补重连逻辑。

3. 启动读取与运行时更新走同一套配置规则

建议启动时读取端口与连接开关,完成必要监听器的安装后,再允许发起首次连接。初始化期间收到的 storage 变化先记录,后续应用最新值,避免迟到的初始读取覆盖更新。

这里的“初始化完成”指配置和监听器准备完成,不能以首次连接成功为条件#114 的场景本身就是默认端口不可达,用户必须能够在未连接时修改端口。

端口校验也建议统一:

  • 输入时完整校验数字及 1–65535 范围,保留当前“用户主动清空则恢复默认值”的约定。
  • 从 storage 读取和监听变化时使用同一归一化规则;删除配置或遇到非法值时,行为与冷启动一致。
  • 保存前比较归一化后的端口,未变化则不重复写入或重连。

收益: 首次启动、扩展被唤醒和运行时修改不会形成三套不同的配置行为。

4. popup 明确区分草稿、加载和保存状态

use-daemon-port.ts 初始草稿为 "",但 "" 又代表提交默认端口。如果初始读取尚未完成就触发提交,就有覆盖原配置的风险;迟到的读取结果也可能覆盖用户已经输入的内容。

建议 hook 明确维护以下状态,其中 dirty 可以由草稿和已保存值推导,不必全部做成独立 state:

状态 含义
savedPort 最近确认的持久化配置
draft 用户正在编辑的文字
loaded 初始读取是否成功完成
dirty 是否存在尚未提交的编辑
saving 是否正在保存

处理规则建议如下:

  • 未加载完成时禁用编辑和提交;读取失败显示错误,不把空草稿当作默认值写回。
  • 没有修改时不写入 storage。
  • 收到外部 storage 更新时同步 savedPort,只有没有本地未提交编辑时才同步 draft
  • 保存成功后才清除未保存状态;保存失败保留草稿并展示失败。
  • 保存期间短暂禁用编辑和重复提交,可减少旧写入完成覆盖新草稿的时序。

交互上更推荐 “保存按钮 + Enter 提交”。端口修改会触发连接中断,明确提交能让生效时机更清楚,也能让保存失败在 popup 仍打开时被看到。

如果希望保留 blur 保存,也可以复用同一个提交入口。但建议不把“关闭 popup 时保存”作为正确性保证:pagehide 本身没有必达保证,关闭时的回调至多作为尽力提交。相关生命周期说明

另外,“配置保存成功”和“连接成功”应继续分别展示:前者由 storage 写入结果确定,后者由现有 Controller 快照确定。

收益: 避免默认值误写和异步读取覆盖输入;用户能够分辨保存失败与 daemon 不可达,hook 的提交条件也更容易测试。

5. 建议补充的验证

现有测试覆盖了端口解析、保存和单独的 setUrl()。建议重点补齐组件之间的时序测试,用可控制完成时机的 storage、清理和连接 mock 验证以下行为:

场景 预期结果
初始读取尚未完成,关闭或尝试提交 popup 不写入默认端口
初始读取期间收到较新的配置 最终应用新配置
保存相同端口 不断开、不重连
连接开关关闭时修改端口 保存配置,保持断开
会话清理未完成时触发 keepalive、唤醒或握手重试 不提前连接
清理期间连续修改多个端口 最终应用最新配置
切换过程中关闭连接开关 清理结束后仍保持断开
旧端口不可达、连接 Promise 未完成,再换端口或关闭 新操作不被阻塞
旧握手或旧连接尝试较晚完成 不覆盖新状态
清理失败或 storage 写入失败 保留具体错误,不误报成功
断连时操作开关、协议不兼容时打开 popup 能关闭自动连接,能看到协议诊断

修改范围可以集中在 popup/hook、端口配置模块、Controller,以及 background/keepalive 的连接入口接线;CLI 和 daemon 协议不需要随之调整。已有会话清理逻辑可以复用,重点是保证它完成之前不会建立新连接。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Allow configuring daemon port in extension UI | 浏览器扩展支持配置 daemon 端口

3 participants