@chenacc/agentbridge 0.2.1 → 0.2.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/.env.example CHANGED
@@ -63,3 +63,8 @@ AGENTBRIDGE_PROJECTS=agentbridge=D:\codex_project\手机远程操控agent
63
63
  # AGENTBRIDGE_NOTIFY_TOKEN=your-provider-token
64
64
  # AGENTBRIDGE_NOTIFY_CHANNEL=clawbot
65
65
  # AGENTBRIDGE_NOTIFY_LINK=1
66
+
67
+ # The bridge reads notify.json from its data directory. If that directory moved
68
+ # (the DSH plugin gives the bridge its own state folder), an older folder can be
69
+ # adopted once instead of re-entering the token by hand:
70
+ # AGENTBRIDGE_NOTIFY_IMPORT=D:\codex_project\agentbridge\.agentbridge
package/README.md CHANGED
@@ -8,23 +8,29 @@
8
8
 
9
9
  **一条命令,把手机变成这台电脑上 Agent 的遥控器。** 微信扫码即用,不用装 App,也不用和电脑连同一个 Wi-Fi。
10
10
 
11
- | 桌面端在干活 | 手机扫一扫就接管 | 手机端同一份对话 |
12
- |---|---|---|
13
- | <img src="./docs/assets/screen-desktop-console.jpg" alt="桌面端控制台" width="330" /> | <img src="./docs/assets/screen-mobile-1.png" alt="手机端控制台 1" width="180" /> | <img src="./docs/assets/screen-mobile-2.png" alt="手机端控制台 2" width="180" /> |
11
+ <img src="./docs/assets/hero-phone.jpg" alt="手机端控制台:语音/文字发指令,Agent 流式回复,与桌面同一份对话" width="240" align="right" />
12
+
13
+ 在电脑上跑着的 Claude Code / Codex / DeepSeek Harness / DSH Desktop 会话,执行 `/phone` 会出现一次性二维码:
14
+
15
+ - 手机扫码后**接管的就是屏幕上那一条对话**,不是新建一个"类似的"会话;
16
+ - 消息、工具调用、审批卡片、模型切换、停止任务,两端看到的是同一份事件流;
17
+ - 电脑始终是唯一执行端,凭据和文件访问不出本机。
14
18
 
15
19
  ```powershell
16
20
  npx @chenacc/agentbridge # 起桥并直接打印二维码
17
21
  ```
18
22
 
19
- 然后在 Claude Code / Codex / DSH Desktop 里输入 `/phone`,扫二维码即可。凭据和文件访问始终留在本机,手机只下发命令。
23
+ 然后在 Claude Code / Codex / DSH Desktop 里输入 `/phone`,扫二维码即可。
20
24
 
21
25
  > 觉得有用就给个 star:[github.com/chenacc1/agentbridge](https://github.com/chenacc1/agentbridge) —— 不点也不影响任何功能,只影响我有没有动力继续写。
22
26
 
27
+ ![桌面端控制台:左侧是 Agent / 项目 / 会话三级导航,右侧是同一段对话与工具活动](./docs/assets/hero-desktop.jpg)
28
+
23
29
  手机与桌面共享同一份可追加事件流,因此消息、工具活动、审批和任务状态始终同步。电脑是唯一的执行端:手机只运行界面,不运行 Agent,也拿不到任何供应商凭据。
24
30
 
25
31
  默认走 Cloudflare 隧道:手机用流量、在外地都能打开;隧道对外开放,因此自动带一道访问密钥门禁。
26
32
 
27
- **当前版本:** 桥 `@chenacc/agentbridge` **0.2.0** · DSH 插件 `@chenacc/dsh-agentbridge` **0.4.0**(两个包各自发布,见文末「版本与发布」)。
33
+ **当前版本:** 桥 `@chenacc/agentbridge` **0.2.1** · DSH 插件 `@chenacc/dsh-agentbridge` **0.4.1**(两个包各自发布,见文末「版本与发布」)。
28
34
 
29
35
  ## 它是怎么工作的
30
36
 
@@ -380,6 +386,9 @@ agentbridge phone --agent deepseek-harness
380
386
  | `AGENTBRIDGE_SESSION_TTL_MS` | `259200000` | 设备会话有效期(72 小时) |
381
387
  | `AGENTBRIDGE_CLAUDE_MODEL` | 供应商默认 | 可选的 Claude / Kimi 模型 ID |
382
388
  | `AGENTBRIDGE_NOTIFY_WEBHOOK` | 未设置 | 通知 Webhook;未设置时该功能静默 |
389
+ | `AGENTBRIDGE_NOTIFY_IMPORT` | 未设置 | 通知设置存在旧数据目录时,指向它一次即可自动迁过来 |
390
+
391
+ > 通知设置(webhook / token / 静默开关)保存在**桥的数据目录**下的 `notify.json`,默认是 `%USERPROFILE%\.agentbridge`。DSH 插件接管网关后数据目录换过位置,老配置如果留在原来的 `.agentbridge` 里,用 `AGENTBRIDGE_NOTIFY_IMPORT=<旧目录>` 启动一次就会自动迁过去;也可以直接在桌面控制台的「通知」面板重填一次。桥启动日志里有一行 `Notifications: pushplus/clawbot` 或 `Notifications: off — no webhook configured in …`,能直接看出它到底读到了哪个文件。
383
392
 
384
393
  **跨网络(CLI 与插件都认):**
385
394
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenacc/agentbridge",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Control local coding agents from a phone through a secure WeChat QR web console.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -65,8 +65,9 @@
65
65
  "verify:responsive": "node scripts/verify-responsive.mjs",
66
66
  "verify:guide": "node scripts/verify-product-guide.mjs",
67
67
  "verify:release": "node scripts/verify-plugin-release.mjs",
68
- "check": "node --check src/server.js && node --check src/cli.js && node --check src/config.js && node --check src/cc-switch-models.js && node --check src/workspaces/workspace-catalog.js && node --check src/conversations/conversation-catalog.js && node --check src/adapters/capabilities.js && node --check src/adapters/claude.js && node --check src/adapters/codex.js && node --check src/adapters/deepseek-harness.js && node --check src/adapters/dsh-desktop.js && node --check src/dsh-desktop-channels.js && node --check src/event-store.js && node --check src/notifier.js && node --check src/attachments.js && node --check src/session-manager.js && node --check integrations/dsh-phone/lib/index.js && node --check src/tunnel.js && node --check src/remote.js && node --check src/adapters/zcode-native.js && node --check public/adapter-capabilities.js && node --check public/workspace-navigation.js && node --check public/markdown.js && node --check public/highlight.js && node --check public/transcript.js && node --check public/app.js && node --check scripts/verify-workspace-ui.mjs && node --check scripts/verify-message-blocks.mjs && node --check scripts/verify-background-scrim.mjs && node --check scripts/verify-model-control.mjs && node --check scripts/verify-notifications.mjs && node --check scripts/verify-attachments.mjs && node --check scripts/verify-features.mjs && node --check scripts/verify-quick-nav.mjs && node --check scripts/verify-questions.mjs && node --check scripts/verify-responsive.mjs && node --check scripts/verify-product-guide.mjs && node --check scripts/verify-plugin-release.mjs",
69
- "verify:remote": "node scripts/verify-remote-tunnel.mjs"
68
+ "check": "node --check src/server.js && node --check src/cli.js && node --check src/config.js && node --check src/cc-switch-models.js && node --check src/workspaces/workspace-catalog.js && node --check src/conversations/conversation-catalog.js && node --check src/adapters/capabilities.js && node --check src/adapters/claude.js && node --check src/adapters/codex.js && node --check src/adapters/deepseek-harness.js && node --check src/adapters/dsh-desktop.js && node --check src/dsh-desktop-channels.js && node --check src/event-store.js && node --check src/notifier.js && node --check src/attachments.js && node --check src/session-manager.js && node --check integrations/dsh-phone/lib/index.js && node --check src/tunnel.js && node --check src/remote.js && node --check src/adapters/zcode-native.js && node --check public/adapter-capabilities.js && node --check public/workspace-navigation.js && node --check public/markdown.js && node --check public/highlight.js && node --check public/transcript.js && node --check public/app.js && node --check scripts/verify-workspace-ui.mjs && node --check scripts/verify-message-blocks.mjs && node --check scripts/verify-background-scrim.mjs && node --check scripts/verify-model-control.mjs && node --check scripts/verify-notifications.mjs && node --check scripts/verify-attachments.mjs && node --check scripts/verify-features.mjs && node --check scripts/verify-quick-nav.mjs && node --check scripts/verify-questions.mjs && node --check scripts/verify-responsive.mjs && node --check scripts/verify-product-guide.mjs && node --check scripts/verify-plugin-release.mjs && node --check scripts/capture-screenshots.mjs",
69
+ "verify:remote": "node scripts/verify-remote-tunnel.mjs",
70
+ "capture:shots": "node scripts/capture-screenshots.mjs"
70
71
  },
71
72
  "dependencies": {
72
73
  "katex": "0.19.1",
package/src/config.js CHANGED
@@ -105,16 +105,52 @@ function parseProjects(raw, workspace) {
105
105
  * end up in a tracked file. The bridge's own data directory is git-ignored, so a
106
106
  * small JSON file there is the honest place for it; environment variables still win
107
107
  * when both are present, which keeps `setx` and one-off experiments working.
108
+ *
109
+ * `legacyFiles` exists because the data directory can move under the user's feet:
110
+ * the DSH plugin started hosting the bridge and gave it its own state directory,
111
+ * which silently left notifications configured in the old one. When the current
112
+ * directory has no settings but an old one does, adopt them once and say so — the
113
+ * alternative is a feature that stops working with no explanation.
108
114
  */
109
- function readNotifyFile(dataDir) {
115
+ function readNotifyFile(dataDir, legacyFiles = []) {
116
+ const own = path.join(dataDir, "notify.json");
117
+ const parsed = readJsonObject(own);
118
+ if (parsed) return parsed;
119
+
120
+ for (const candidate of legacyFiles) {
121
+ const adopted = readJsonObject(candidate);
122
+ if (!adopted) continue;
123
+ try {
124
+ mkdirSync(dataDir, { recursive: true });
125
+ writeFileSync(own, JSON.stringify(adopted, null, 2), "utf8");
126
+ console.error(`Notifications: adopted settings from ${candidate} into ${own}`);
127
+ } catch {
128
+ // A read-only data directory still gets to use the settings for this run.
129
+ }
130
+ return adopted;
131
+ }
132
+ return {};
133
+ }
134
+
135
+ function readJsonObject(file) {
110
136
  try {
111
- const parsed = JSON.parse(readFileSync(path.join(dataDir, "notify.json"), "utf8"));
112
- return parsed && typeof parsed === "object" ? parsed : {};
137
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
138
+ return parsed && typeof parsed === "object" ? parsed : null;
113
139
  } catch {
114
- return {};
140
+ return null;
115
141
  }
116
142
  }
117
143
 
144
+ /** Candidate files a moved data directory may have left behind. */
145
+ export function legacyNotifyPaths(env = process.env, workspace = process.cwd()) {
146
+ const fromEnv = String(env.AGENTBRIDGE_NOTIFY_IMPORT ?? "")
147
+ .split(";")
148
+ .map((entry) => entry.trim())
149
+ .filter(Boolean)
150
+ .map((entry) => (/notify\.json$/i.test(entry) ? path.resolve(entry) : path.join(path.resolve(entry), "notify.json")));
151
+ return [...fromEnv, path.join(path.resolve(workspace), ".agentbridge", "notify.json")];
152
+ }
153
+
118
154
  function pick(env, key, fallback) {
119
155
  const value = String(env[key] ?? "").trim();
120
156
  return value || fallback || null;
@@ -153,7 +189,7 @@ export function loadConfig(env = process.env, workspace = process.cwd()) {
153
189
  const advertisedOrigin = publicOrigin || `http://${firstLanAddress()}:${port}`;
154
190
  const accessKey = (env.AGENTBRIDGE_ACCESS_KEY ?? "").trim() || null;
155
191
  const dataDir = path.resolve(env.AGENTBRIDGE_DATA_DIR ?? path.join(workspace, ".agentbridge"));
156
- const notifyFile = readNotifyFile(dataDir);
192
+ const notifyFile = readNotifyFile(dataDir, legacyNotifyPaths(env, workspace));
157
193
 
158
194
  return {
159
195
  host,
package/src/server.js CHANGED
@@ -994,6 +994,17 @@ export async function startBridge() {
994
994
  console.log(`Remote path: ${bridge.config.remoteProvider} → ${bridge.config.advertisedOrigin}`);
995
995
  }
996
996
  console.log(`WeChat scan target: ${bridge.getCurrentPairing().url}`);
997
+ // Notifications are silent by design, which once hid the fact that the config
998
+ // was being read from a directory that did not have it (the data directory moved
999
+ // when the DSH plugin started hosting the bridge). Say where it looked.
1000
+ if (bridge.notifier.enabled) {
1001
+ console.log(`Notifications: ${bridge.notifier.format}${bridge.notifier.channel ? `/${bridge.notifier.channel}` : ""}${bridge.notifier.muted ? " (muted)" : ""}`);
1002
+ } else {
1003
+ console.log(
1004
+ `Notifications: off — no webhook configured in ${path.join(bridge.config.dataDir, "notify.json")} `
1005
+ + "(set it in the desktop console, or point AGENTBRIDGE_NOTIFY_IMPORT at an older .agentbridge folder)",
1006
+ );
1007
+ }
997
1008
  // Silence here used to mean a QR encoding an address the phone could never reach.
998
1009
  if (new URL(bridge.config.advertisedOrigin).hostname === "127.0.0.1") {
999
1010
  console.warn(