yunti-browser-runtime 0.1.0 → 0.1.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/README.md +39 -9
- package/docs/EXECUTION_PLAN.md +117 -29
- package/docs/INSTALL.md +15 -12
- package/docs/PROJECT_STATUS.md +45 -30
- package/docs/PUBLISHING_BLOCKERS.md +16 -13
- package/docs/SECURITY.md +6 -4
- package/extension/manifest.json +1 -1
- package/extension/popup.css +16 -0
- package/extension/popup.html +7 -4
- package/extension/popup.js +4 -2
- package/mcp/http-server.js +38 -13
- package/mcp/server.js +18 -8
- package/package.json +1 -1
- package/scripts/doctor.js +6 -3
- package/scripts/print-config.js +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,8 @@ yunti-browser-runtime package-extension
|
|
|
59
59
|
http://127.0.0.1:48887
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
bridge
|
|
62
|
+
默认本地 bridge 只监听 `127.0.0.1`,不需要配置 token。需要额外加固本机访问时,
|
|
63
|
+
可以显式启用 token:
|
|
63
64
|
|
|
64
65
|
```bash
|
|
65
66
|
YUNTI_BROWSER_BRIDGE_TOKEN="$(openssl rand -hex 24)" npm run bridge
|
|
@@ -78,6 +79,30 @@ npm run doctor
|
|
|
78
79
|
npm run doctor:json
|
|
79
80
|
```
|
|
80
81
|
|
|
82
|
+
## 复制给 Agent 的安装引导
|
|
83
|
+
|
|
84
|
+
把下面这段话复制给你正在使用的 Agent,让它一步步引导你安装和验证:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
请帮我安装并接入 Yunti Browser Runtime。它是一个本地浏览器运行时,让你通过 MCP 操作我本机 Chrome/Edge 页面。
|
|
88
|
+
|
|
89
|
+
请按步骤引导我完成,不要跳步:
|
|
90
|
+
|
|
91
|
+
1. 确认我本机有 Node.js 22+。
|
|
92
|
+
2. 执行:npm install -g yunti-browser-runtime
|
|
93
|
+
3. 执行:yunti-browser-runtime print-config -- --agent 当前Agent名称 --human
|
|
94
|
+
4. 根据输出,把 MCP server 配置加入当前 Agent 的 MCP 配置。
|
|
95
|
+
5. 执行:yunti-browser-runtime bridge
|
|
96
|
+
6. 引导我打开 Chrome/Edge 的扩展管理页,开启开发者模式,手动加载扩展目录:
|
|
97
|
+
$(npm root -g)/yunti-browser-runtime/extension
|
|
98
|
+
7. 告诉我扩展里默认 bridge URL 是 http://127.0.0.1:48887,本地默认不需要 token,也不需要保存设置。
|
|
99
|
+
8. 打开任意 http/https 页面并刷新。
|
|
100
|
+
9. 执行:yunti-browser-runtime doctor
|
|
101
|
+
10. 如果 doctor 正常,再调用 yunti_list_browser_targets 或 yunti_get_tool_usage_hints 验证你能看到浏览器页面。
|
|
102
|
+
|
|
103
|
+
注意:Chrome 扩展不能由 npm 静默安装,必须由我手动在浏览器扩展页加载。你要一步步告诉我点哪里、复制什么、检查什么。
|
|
104
|
+
```
|
|
105
|
+
|
|
81
106
|
## 安装浏览器扩展
|
|
82
107
|
|
|
83
108
|
开发加载方式:
|
|
@@ -87,10 +112,11 @@ npm run doctor:json
|
|
|
87
112
|
3. 点击“加载已解压的扩展程序”。
|
|
88
113
|
4. 选择本项目的 `extension/` 目录。
|
|
89
114
|
5. 打开任意 `http` 或 `https` 页面。
|
|
90
|
-
6.
|
|
91
|
-
7. 刷新目标页面,确认页面已连接。
|
|
115
|
+
6. 刷新目标页面,确认页面已连接。
|
|
92
116
|
|
|
93
|
-
|
|
117
|
+
扩展默认使用 `http://127.0.0.1:48887`,本地安装不需要 token,也不需要保存设置。
|
|
118
|
+
扩展 popup 只用于自定义 bridge URL、页面匹配或可选 token。扩展只负责把页面注册到
|
|
119
|
+
本地 bridge,并执行 Agent 发来的浏览器动作。
|
|
94
120
|
|
|
95
121
|
也可以生成 zip 包用于分发或归档:
|
|
96
122
|
|
|
@@ -239,7 +265,10 @@ YUNTI_E2E=1 npm run test:e2e
|
|
|
239
265
|
|
|
240
266
|
### bridge token 是什么?
|
|
241
267
|
|
|
242
|
-
bridge token
|
|
268
|
+
默认本地安装不需要 bridge token。bridge 只监听 `127.0.0.1`,扩展和 MCP server
|
|
269
|
+
可以直接连接,减少首次使用心智。需要加固共享机器或自定义部署时,可设置
|
|
270
|
+
`YUNTI_BROWSER_BRIDGE_TOKEN`,这时 MCP server、extension 和 doctor 必须使用同一个
|
|
271
|
+
token。
|
|
243
272
|
|
|
244
273
|
### 会读取 cookie 或密码吗?
|
|
245
274
|
|
|
@@ -249,8 +278,9 @@ bridge token 是本地 HTTP bridge 的访问凭证。MCP server、extension 和
|
|
|
249
278
|
|
|
250
279
|
调用 `yunti_list_browser_targets` 获取最新浏览器路由,再用返回的 `browserSessionId` 继续操作。
|
|
251
280
|
|
|
252
|
-
##
|
|
281
|
+
## 发布状态与后续事项
|
|
253
282
|
|
|
254
|
-
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
283
|
+
- `yunti-browser-runtime@0.1.2` 已发布到官方 npm registry。
|
|
284
|
+
- `0.1.2` 将扩展 popup 的 token 移入高级设置,并补充可复制给 Agent 的安装引导。
|
|
285
|
+
- 发布后验证命令:`npm run release:verify-published`。
|
|
286
|
+
- 如要上架浏览器扩展商店,需要再次审查 broad host permissions。
|
package/docs/EXECUTION_PLAN.md
CHANGED
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
| P4.16 | 已完成 | 正式发布脚本内置门禁 |
|
|
55
55
|
| P4.17 | 已完成 | npm 登录预检门禁 |
|
|
56
56
|
| P4.18 | 已完成 | npm 发布后验证命令 |
|
|
57
|
-
| P4.19 |
|
|
57
|
+
| P4.19 | 已完成 | npm 正式发布执行 |
|
|
58
|
+
| P5.1 | 已完成 | 本地默认免 bridge token |
|
|
59
|
+
| P5.2 | 已完成 | 扩展 popup 与安装引导简化 |
|
|
58
60
|
|
|
59
61
|
## P0.1 bridge token + CORS 收紧
|
|
60
62
|
|
|
@@ -1151,8 +1153,9 @@ release gate、npm package 内容检查和 extension zip 内容检查。
|
|
|
1151
1153
|
|
|
1152
1154
|
### 验收记录
|
|
1153
1155
|
|
|
1154
|
-
-
|
|
1155
|
-
|
|
1156
|
+
- 发布前,`npm run release:whoami` 曾在未登录状态返回 `ENEEDAUTH`,验证缺少 npm
|
|
1157
|
+
登录时会提前失败。
|
|
1158
|
+
- 登录后,`npm run release:whoami` 返回 `xuanzhu`,并在正式发布前通过预检。
|
|
1156
1159
|
- `release:publish` 已内置登录预检;登录前不会进入正式 npm publish。
|
|
1157
1160
|
|
|
1158
1161
|
## P4.18 npm 发布后验证命令
|
|
@@ -1186,9 +1189,10 @@ metadata 与当前 `package.json` 一致。
|
|
|
1186
1189
|
|
|
1187
1190
|
### 验收记录
|
|
1188
1191
|
|
|
1189
|
-
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
+
- 发布前,`npm run release:verify-published` 曾返回非零退出码并输出结构化未发布报告,
|
|
1193
|
+
且失败路径不包含 npm 本机 debug log 路径。
|
|
1194
|
+
- 发布后,`npm run release:verify-published` 返回 0,确认
|
|
1195
|
+
`yunti-browser-runtime@0.1.0` 已发布到 `https://registry.npmjs.org/`。
|
|
1192
1196
|
- `node --check scripts/check-published-package.js` 通过。
|
|
1193
1197
|
- `npm run release:check` 通过:`npm run check` 已覆盖
|
|
1194
1198
|
`scripts/check-published-package.js`,npm package contents check 确认 tarball
|
|
@@ -1198,7 +1202,7 @@ metadata 与当前 `package.json` 一致。
|
|
|
1198
1202
|
|
|
1199
1203
|
## P4.19 npm 正式发布执行
|
|
1200
1204
|
|
|
1201
|
-
|
|
1205
|
+
状态:已完成(2026-07-03)
|
|
1202
1206
|
|
|
1203
1207
|
执行摘要:
|
|
1204
1208
|
|
|
@@ -1206,8 +1210,8 @@ metadata 与当前 `package.json` 一致。
|
|
|
1206
1210
|
- 已按用户确认执行 `npm run release:publish`。
|
|
1207
1211
|
- `release:publish` 已通过内置 `release:prepublish`、`release:whoami` 和全部本地
|
|
1208
1212
|
发布门禁;本次测试覆盖 59 项,58 项通过,1 项真实浏览器 smoke 按配置跳过。
|
|
1209
|
-
-
|
|
1210
|
-
|
|
1213
|
+
- 正式发布进入 `npm publish --registry=https://registry.npmjs.org/`,tarball 包含
|
|
1214
|
+
40 个文件。
|
|
1211
1215
|
- npm registry 返回 `E403`:当前账号发布包需要双因素认证 OTP,或使用开启
|
|
1212
1216
|
bypass 2FA 的 granular access token。
|
|
1213
1217
|
- 已使用临时 npm token 重试 `npm run release:publish`;本地发布门禁再次通过,但
|
|
@@ -1217,7 +1221,11 @@ metadata 与当前 `package.json` 一致。
|
|
|
1217
1221
|
但重新执行 `npm run release:publish` 仍在 `npm publish` 阶段返回相同 `E403`。
|
|
1218
1222
|
- 已用新的 npm token 覆盖项目级 `.npmrc` 后再次确认 `npm whoami` 返回 `xuanzhu`;
|
|
1219
1223
|
`npm run release:publish` 仍通过本地门禁并在 `npm publish` 阶段返回相同 `E403`。
|
|
1220
|
-
-
|
|
1224
|
+
- 已用具备发布权限的 npm token 覆盖项目级 `.npmrc` 后再次执行
|
|
1225
|
+
`npm run release:publish`,正式发布成功:`yunti-browser-runtime@0.1.0` 已发布到
|
|
1226
|
+
`https://registry.npmjs.org/`。
|
|
1227
|
+
- `npm run release:verify-published` 通过,确认 registry 上的 name、version、
|
|
1228
|
+
repository、homepage、bugs 和 tarball URL 与本地 `package.json` 匹配。
|
|
1221
1229
|
|
|
1222
1230
|
### 目标
|
|
1223
1231
|
|
|
@@ -1234,11 +1242,98 @@ metadata 与当前 `package.json` 一致。
|
|
|
1234
1242
|
repository、homepage、bugs 和 tarball URL 与本地 `package.json` 匹配。
|
|
1235
1243
|
- 发布结果同步到 `docs/PROJECT_STATUS.md`。
|
|
1236
1244
|
|
|
1237
|
-
###
|
|
1245
|
+
### 验收记录
|
|
1246
|
+
|
|
1247
|
+
- `npm run release:publish` 成功发布 `yunti-browser-runtime@0.1.0`。
|
|
1248
|
+
- 发布前门禁通过:metadata URL 检查、公开文档残留检查、Markdown 链接检查、版本一致性、
|
|
1249
|
+
CLI smoke、print-config smoke、doctor smoke、`npm run check`、`npm test`、npm
|
|
1250
|
+
package contents check 和 extension zip contents check。
|
|
1251
|
+
- `npm test` 覆盖 59 项测试:58 项通过,1 项真实浏览器 smoke 按配置跳过。
|
|
1252
|
+
- `npm run release:verify-published` 返回 0。
|
|
1253
|
+
- `npm view yunti-browser-runtime@0.1.0 ... --registry=https://registry.npmjs.org/`
|
|
1254
|
+
返回已发布版本和正确 metadata。
|
|
1255
|
+
|
|
1256
|
+
## P5.1 本地默认免 bridge token
|
|
1257
|
+
|
|
1258
|
+
状态:已完成(2026-07-03)
|
|
1259
|
+
|
|
1260
|
+
实现摘要:
|
|
1261
|
+
|
|
1262
|
+
- 默认 `127.0.0.1` 本地 bridge 不再自动生成随机 token,也不要求 extension、doctor
|
|
1263
|
+
或 MCP proxy 携带 `x-yunti-browser-token`。
|
|
1264
|
+
- 用户显式设置 `YUNTI_BROWSER_BRIDGE_TOKEN` 时,bridge 继续强制校验
|
|
1265
|
+
`x-yunti-browser-token` 或 `Authorization: Bearer <token>`。
|
|
1266
|
+
- bridge 绑定到非 loopback host 时必须设置 `YUNTI_BROWSER_BRIDGE_TOKEN`,避免把
|
|
1267
|
+
本地免 token 默认值误用于远程或局域网部署。
|
|
1268
|
+
- `npm run doctor` 默认显示 token not required;只有 bridge 明确要求 token 且请求
|
|
1269
|
+
未授权时才提示设置 token。
|
|
1270
|
+
- `npm run print-config` 默认不再要求用户配置 token,仅保留需要加固时的可选说明。
|
|
1271
|
+
- 真实浏览器 smoke test 改为覆盖默认免 token 路径。
|
|
1272
|
+
- package 和 extension 版本同步提升到 `0.1.1`。
|
|
1273
|
+
|
|
1274
|
+
### 目标
|
|
1275
|
+
|
|
1276
|
+
降低本地安装和首次使用心智:用户只需要启动 bridge、加载扩展、确认 bridge URL,
|
|
1277
|
+
不需要复制或理解 bridge token。安全边界仍保持本地 loopback 默认,显式 token 用于
|
|
1278
|
+
共享机器、非本地绑定或额外加固场景。
|
|
1279
|
+
|
|
1280
|
+
### 验收标准
|
|
1281
|
+
|
|
1282
|
+
- 默认 `npm run bridge` 启动的 `127.0.0.1` bridge 不要求 token。
|
|
1283
|
+
- 未配置 token 时,extension 可以注册页面,doctor 可以读取完整 health。
|
|
1284
|
+
- 配置 `YUNTI_BROWSER_BRIDGE_TOKEN` 后,未携带 token 的受保护 HTTP route 仍返回 401。
|
|
1285
|
+
- `YUNTI_BROWSER_BRIDGE_HOST` 绑定非 loopback 地址且未配置 token 时启动失败。
|
|
1286
|
+
- README、INSTALL、SECURITY、PROJECT_STATUS 同步新的默认使用路径。
|
|
1287
|
+
- `npm run release:check` 通过。
|
|
1288
|
+
- `npm run release:publish` 发布 `yunti-browser-runtime@0.1.1`,并通过
|
|
1289
|
+
`npm run release:verify-published` 验证。
|
|
1290
|
+
|
|
1291
|
+
### 验收记录
|
|
1292
|
+
|
|
1293
|
+
- `npm test` 通过:61 项测试,60 项通过,1 项真实浏览器 smoke 按配置跳过。
|
|
1294
|
+
- 新增测试覆盖本地默认免 token、显式 token 仍强制校验、非 loopback 无 token
|
|
1295
|
+
启动失败。
|
|
1296
|
+
- `npm run check` 通过。
|
|
1297
|
+
- `npm run release:check` 通过。
|
|
1298
|
+
- `npm run release:publish` 成功发布 `yunti-browser-runtime@0.1.1`。
|
|
1299
|
+
- `npm run release:verify-published` 返回 0。
|
|
1300
|
+
|
|
1301
|
+
## P5.2 扩展 popup 与安装引导简化
|
|
1238
1302
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1303
|
+
状态:已完成(2026-07-03)
|
|
1304
|
+
|
|
1305
|
+
实现摘要:
|
|
1306
|
+
|
|
1307
|
+
- 扩展 popup 首屏不再展示 Bridge Token 输入框,避免用户误以为本地默认安装需要填写
|
|
1308
|
+
token。
|
|
1309
|
+
- Bridge Token 移入“高级设置”,并标注为可选;如果用户已有 token 配置,高级设置会
|
|
1310
|
+
自动展开。
|
|
1311
|
+
- popup 默认状态文案改为“默认无需设置,打开或刷新 http/https 页面即可”。
|
|
1312
|
+
- README 增加“复制给 Agent 的安装引导”,让用户可以把一段话交给 Agent,由 Agent
|
|
1313
|
+
分步骤带用户完成 npm 安装、MCP 配置、扩展加载和 doctor 验证。
|
|
1314
|
+
- README / INSTALL 明确:加载扩展后默认不需要保存设置,不需要 token。
|
|
1315
|
+
- package 和 extension 版本同步提升到 `0.1.2`。
|
|
1316
|
+
|
|
1317
|
+
### 目标
|
|
1318
|
+
|
|
1319
|
+
让用户在安装扩展后尽量立即可用:默认无需填写 token、无需保存 popup 设置;需要
|
|
1320
|
+
自定义 bridge URL、页面匹配或 token 时再进入高级/设置路径。
|
|
1321
|
+
|
|
1322
|
+
### 验收标准
|
|
1323
|
+
|
|
1324
|
+
- popup 首屏不出现 Bridge Token 输入框。
|
|
1325
|
+
- popup 仍保留可选 token 配置能力。
|
|
1326
|
+
- README 包含可复制给 Agent 的安装引导词。
|
|
1327
|
+
- README / INSTALL 不再要求默认本地安装时点击 popup 保存 token 或保存设置。
|
|
1328
|
+
- `npm run release:check` 通过。
|
|
1329
|
+
- `npm run release:publish` 发布 `yunti-browser-runtime@0.1.2`,并通过
|
|
1330
|
+
`npm run release:verify-published` 验证。
|
|
1331
|
+
|
|
1332
|
+
### 验收记录
|
|
1333
|
+
|
|
1334
|
+
- `npm run release:check` 通过。
|
|
1335
|
+
- `npm run release:publish` 成功发布 `yunti-browser-runtime@0.1.2`。
|
|
1336
|
+
- `npm run release:verify-published` 返回 0。
|
|
1242
1337
|
|
|
1243
1338
|
## 每阶段完成后的固定检查
|
|
1244
1339
|
|
|
@@ -1261,18 +1356,11 @@ rg "/U[s]ers|C[o]deg|x[y]y|y[b]m100" README.md docs skills package.json
|
|
|
1261
1356
|
|
|
1262
1357
|
## 当前下一步
|
|
1263
1358
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
-
|
|
1267
|
-
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
-
|
|
1271
|
-
-
|
|
1272
|
-
相同 `E403`。
|
|
1273
|
-
- 已用新的 npm token 覆盖项目级 `.npmrc` 复验,认证仍成功但 publish 仍返回相同
|
|
1274
|
-
`E403`。
|
|
1275
|
-
- 下一步使用当前 npm OTP 执行 `npm run release:publish -- --otp=<6-digit-code>`,
|
|
1276
|
-
或改用具备 publish 权限且允许 bypass 2FA 的 granular access token。
|
|
1277
|
-
- 发布后执行 `npm run release:verify-published` 确认 npm registry 版本和 metadata。
|
|
1278
|
-
- 浏览器扩展如需上架商店,发布前还需重新审查 broad host permissions。
|
|
1359
|
+
P0.1-P5.2 已完成,`yunti-browser-runtime@0.1.2` 已发布到官方 npm registry。
|
|
1360
|
+
|
|
1361
|
+
- 发布后验证已通过:`npm run release:verify-published`。
|
|
1362
|
+
- 本地默认使用不再需要 bridge token;需要加固时可显式设置
|
|
1363
|
+
`YUNTI_BROWSER_BRIDGE_TOKEN`。
|
|
1364
|
+
- 扩展 popup 默认不需要用户保存设置;token 已移入高级可选项。
|
|
1365
|
+
- 后续如要上架浏览器扩展商店,发布前还需重新审查 broad host permissions。
|
|
1366
|
+
- 后续版本开发前,先在本文档新增下一阶段目标、范围和验收标准。
|
package/docs/INSTALL.md
CHANGED
|
@@ -27,8 +27,8 @@ Use a custom port when needed:
|
|
|
27
27
|
YUNTI_BROWSER_BRIDGE_PORT=48999 npm run bridge
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
bridge
|
|
31
|
-
|
|
30
|
+
默认本地 bridge 只监听 `127.0.0.1`,不需要配置 token。需要额外加固本机访问、
|
|
31
|
+
或自定义部署时,可以显式启用 token:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
YUNTI_BROWSER_BRIDGE_TOKEN="$(openssl rand -hex 24)" npm run bridge
|
|
@@ -54,12 +54,14 @@ Development loading:
|
|
|
54
54
|
2. Enable developer mode.
|
|
55
55
|
3. Choose "Load unpacked".
|
|
56
56
|
4. Select the project `extension/` directory.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
59
|
-
|
|
57
|
+
5. Open or refresh any `http` or `https` page.
|
|
58
|
+
6. Click the extension icon only if you want to confirm status or customize
|
|
59
|
+
settings.
|
|
60
60
|
|
|
61
|
-
The extension
|
|
62
|
-
|
|
61
|
+
The extension defaults to `http://127.0.0.1:48887`. Local installs do not need a
|
|
62
|
+
token and do not require saving popup settings. The extension registers browser
|
|
63
|
+
pages with the local bridge. It does not create agent conversations or depend on
|
|
64
|
+
a remote workspace.
|
|
63
65
|
|
|
64
66
|
To create a distributable zip:
|
|
65
67
|
|
|
@@ -117,9 +119,10 @@ npm run print-config -- --agent claude-code --human
|
|
|
117
119
|
```
|
|
118
120
|
|
|
119
121
|
Add the printed `mcpServers.yunti-browser-runtime` block to Claude Code's MCP
|
|
120
|
-
configuration.
|
|
121
|
-
|
|
122
|
-
token in the
|
|
122
|
+
configuration. The default local loopback setup does not require a bridge token.
|
|
123
|
+
If using a bridge token, set `YUNTI_BROWSER_BRIDGE_TOKEN` in the same environment
|
|
124
|
+
Claude Code uses to launch the MCP server, and save the same token in the
|
|
125
|
+
extension popup.
|
|
123
126
|
|
|
124
127
|
#### Cursor
|
|
125
128
|
|
|
@@ -176,8 +179,8 @@ runs the unit test suite, and verifies the npm package contents with
|
|
|
176
179
|
## Common Recovery
|
|
177
180
|
|
|
178
181
|
- If tools say no browser tab is connected, refresh the target page.
|
|
179
|
-
- If doctor reports `authorized: false
|
|
180
|
-
save the same token in the extension popup.
|
|
182
|
+
- If doctor reports `authorized: false` with `authRequired: true`, set
|
|
183
|
+
`YUNTI_BROWSER_BRIDGE_TOKEN` and save the same token in the extension popup.
|
|
181
184
|
- If an old `browserSessionId` fails, call `yunti_list_browser_targets` again
|
|
182
185
|
and use the latest returned session.
|
|
183
186
|
- If a parameter error appears, call `yunti_get_tool_usage_hints` with the
|
package/docs/PROJECT_STATUS.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Current Phase
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Patch release `yunti-browser-runtime@0.1.2` is complete. The local loopback
|
|
6
|
+
bridge no longer requires a token by default, the extension popup no longer
|
|
7
|
+
shows token as a first-run field, and the published package is verified with
|
|
8
|
+
`npm run release:verify-published`.
|
|
8
9
|
|
|
9
10
|
## Current State
|
|
10
11
|
|
|
@@ -20,15 +21,18 @@ authentication OTP.
|
|
|
20
21
|
- A staged execution plan exists at `docs/EXECUTION_PLAN.md`.
|
|
21
22
|
- Tool prefix is `yunti_*`.
|
|
22
23
|
- MCP local mode defaults to `userId=local`.
|
|
23
|
-
-
|
|
24
|
-
|
|
24
|
+
- Local loopback bridge HTTP routes do not require a token by default; setting
|
|
25
|
+
`YUNTI_BROWSER_BRIDGE_TOKEN` re-enables `x-yunti-browser-token` checks.
|
|
26
|
+
- Binding the bridge to a non-loopback host requires
|
|
27
|
+
`YUNTI_BROWSER_BRIDGE_TOKEN`.
|
|
25
28
|
- Bridge CORS now echoes only local debug origins and browser extension origins
|
|
26
29
|
by default, with `YUNTI_BROWSER_BRIDGE_ALLOW_ORIGINS` as an override.
|
|
27
|
-
- Extension popup can save the local bridge URL, token, and page match
|
|
30
|
+
- Extension popup can save the local bridge URL, optional token, and page match
|
|
31
|
+
patterns.
|
|
28
32
|
- Browser sessions now track `lastSeenAt`, `lastActivatedAt`, `expiresAt`, and
|
|
29
33
|
`staleReason`; extension polling refreshes heartbeat and expired sessions are
|
|
30
34
|
cleaned up with recovery guidance.
|
|
31
|
-
- `npm run doctor` now checks Node version, bridge reachability,
|
|
35
|
+
- `npm run doctor` now checks Node version, bridge reachability, auth mode,
|
|
32
36
|
registered sessions, active session, extension presence, MCP server path, and
|
|
33
37
|
skill path; it emits JSON plus a human-readable summary.
|
|
34
38
|
- `npm run print-config` prints MCP configuration for Codex, Claude Code,
|
|
@@ -39,6 +43,9 @@ authentication OTP.
|
|
|
39
43
|
- `npm run release:dry-run` and `npm run release:publish` pin the official npm
|
|
40
44
|
registry at `https://registry.npmjs.org/`, avoiding accidental publication to
|
|
41
45
|
a locally configured mirror registry.
|
|
46
|
+
- `yunti-browser-runtime@0.1.2` is published on the official npm registry.
|
|
47
|
+
- Extension popup keeps Bridge Token under optional advanced settings; default
|
|
48
|
+
local installs do not require saving popup settings.
|
|
42
49
|
- `npm run test:e2e` provides an opt-in real-browser Playwright smoke test for
|
|
43
50
|
extension loading, page registration, MCP target listing, snapshot, click/fill,
|
|
44
51
|
and CDP `Runtime.evaluate`.
|
|
@@ -98,16 +105,15 @@ authentication OTP.
|
|
|
98
105
|
|
|
99
106
|
## Open Work
|
|
100
107
|
|
|
101
|
-
- Follow `docs/EXECUTION_PLAN.md`
|
|
108
|
+
- Follow `docs/EXECUTION_PLAN.md` when defining the next post-0.1.1 phase.
|
|
102
109
|
- P4.1 release-readiness docs and permission review is complete.
|
|
103
110
|
- P4.2 Agent integration examples are complete.
|
|
104
111
|
- P4.3 npm publishing URL confirmation is complete with the GitHub repository
|
|
105
112
|
`https://github.com/dingguangyi0/yunti-browser-runtime`; package metadata
|
|
106
113
|
points to that repository/homepage/issues URL set and all three URLs return
|
|
107
114
|
HTTP 200.
|
|
108
|
-
- `npm view yunti-browser-runtime`
|
|
109
|
-
|
|
110
|
-
name.
|
|
115
|
+
- `npm view yunti-browser-runtime@0.1.0` returns the published `0.1.0` package
|
|
116
|
+
metadata from `https://registry.npmjs.org/`.
|
|
111
117
|
- Latest release gate checks passed after documenting P4.3: public-doc residue
|
|
112
118
|
check, `npm run check`, `npm test`, and `npm pack --dry-run`.
|
|
113
119
|
- P4.4 release gate scripting is complete: `npm run release:check` now runs
|
|
@@ -217,19 +223,16 @@ authentication OTP.
|
|
|
217
223
|
- P4.18 post-publish verification command is complete:
|
|
218
224
|
`release:verify-published` checks the published npm package name, version,
|
|
219
225
|
repository, homepage, bugs URL, and tarball URL against local `package.json`.
|
|
220
|
-
- Latest P4.18 validation: `release:verify-published`
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
verifier.
|
|
225
|
-
- P4.19 formal npm publish execution is in progress: `npm run release:publish`
|
|
226
|
+
- Latest P4.18 validation: after publish, `npm run release:verify-published`
|
|
227
|
+
returns 0 and confirms the published name, version, repository, homepage,
|
|
228
|
+
bugs URL, and tarball URL.
|
|
229
|
+
- P4.19 formal npm publish execution is complete: `npm run release:publish`
|
|
226
230
|
passed metadata checks, release gates, `npm run release:whoami`, `npm run
|
|
227
|
-
check`, `npm test`, npm package contents validation,
|
|
228
|
-
|
|
229
|
-
- Latest P4.19 validation: official npm publish
|
|
230
|
-
`
|
|
231
|
-
`
|
|
232
|
-
granular access token with bypass 2FA enabled.
|
|
231
|
+
check`, `npm test`, npm package contents validation, extension zip validation,
|
|
232
|
+
and official npm publish.
|
|
233
|
+
- Latest P4.19 validation: official npm publish succeeded for
|
|
234
|
+
`yunti-browser-runtime@0.1.0`; the published tarball URL is
|
|
235
|
+
`https://registry.npmjs.org/yunti-browser-runtime/-/yunti-browser-runtime-0.1.0.tgz`.
|
|
233
236
|
- A temporary npm token publish retry also reached official npm publish after
|
|
234
237
|
passing the full local release gate, but npm returned the same `E403`; the
|
|
235
238
|
token does not satisfy npm's publish-time bypass 2FA requirement.
|
|
@@ -238,10 +241,23 @@ authentication OTP.
|
|
|
238
241
|
`.npmrc` is ignored by git to avoid committing local npm credentials.
|
|
239
242
|
- A second npm token written to the project-local `.npmrc` also authenticated
|
|
240
243
|
successfully as `xuanzhu`, then failed at `npm publish` with the same `E403`.
|
|
241
|
-
-
|
|
242
|
-
`npm run release:publish
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
- A publish-capable npm token written to the project-local `.npmrc` authenticated
|
|
245
|
+
successfully as `xuanzhu`; `npm run release:publish` then succeeded, followed
|
|
246
|
+
by successful `npm run release:verify-published`.
|
|
247
|
+
- P5.1 local default auth simplification is complete: default `127.0.0.1`
|
|
248
|
+
bridge usage no longer needs a token; explicit `YUNTI_BROWSER_BRIDGE_TOKEN`
|
|
249
|
+
still enforces token checks; non-loopback binding without a token fails fast.
|
|
250
|
+
- Latest P5.1 validation: `npm test` passed 61 tests with 60 passing and 1
|
|
251
|
+
real-browser smoke skipped by configuration; `npm run check` and
|
|
252
|
+
`npm run release:check` passed; `npm run release:publish` published
|
|
253
|
+
`yunti-browser-runtime@0.1.1`; `npm run release:verify-published` returned 0.
|
|
254
|
+
- P5.2 extension popup and onboarding simplification is complete: Bridge Token
|
|
255
|
+
is now optional under advanced settings, README contains a copyable Agent
|
|
256
|
+
installation prompt, and default extension loading no longer asks users to
|
|
257
|
+
save popup settings.
|
|
258
|
+
- Latest P5.2 validation: `npm run release:check` passed; `npm run
|
|
259
|
+
release:publish` published `yunti-browser-runtime@0.1.2`; `npm run
|
|
260
|
+
release:verify-published` returned 0.
|
|
245
261
|
|
|
246
262
|
## Known Risks
|
|
247
263
|
|
|
@@ -251,9 +267,8 @@ authentication OTP.
|
|
|
251
267
|
long-tail tools can still receive the same treatment before a later release.
|
|
252
268
|
- Extension broad host permissions are now documented, but should still be
|
|
253
269
|
re-reviewed before any store-distributed release.
|
|
254
|
-
- Package repository/homepage/bugs metadata is publicly reachable
|
|
255
|
-
|
|
256
|
-
access token.
|
|
270
|
+
- Package repository/homepage/bugs metadata is publicly reachable and published
|
|
271
|
+
npm metadata now matches local `package.json`.
|
|
257
272
|
- Tool names and descriptions must stay clear enough for agents to choose the
|
|
258
273
|
right route without relying on hidden model knowledge.
|
|
259
274
|
|
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
This document tracks release blockers that require external confirmation before
|
|
4
4
|
publishing `yunti-browser-runtime`.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Current Status
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
No active npm publish blockers as of 2026-07-03.
|
|
9
|
+
|
|
10
|
+
`yunti-browser-runtime@0.1.2` is published on the official npm registry and
|
|
11
|
+
verified with `npm run release:verify-published`.
|
|
12
|
+
|
|
13
|
+
## Resolved Blocker: npm 2FA Publish Token
|
|
14
|
+
|
|
15
|
+
Status: resolved on 2026-07-03.
|
|
9
16
|
|
|
10
17
|
`npm run release:publish` now passes local release gates and npm login preflight,
|
|
11
18
|
then reaches `npm publish --registry=https://registry.npmjs.org/`. The registry
|
|
12
|
-
|
|
19
|
+
initially rejected the publish with `E403` because the logged-in account requires
|
|
13
20
|
two-factor authentication for package publishing.
|
|
14
21
|
|
|
15
22
|
A temporary npm token retry also passed the local release gate and reached
|
|
@@ -23,22 +30,18 @@ The local `.npmrc` is ignored by git and must not be committed.
|
|
|
23
30
|
A second token written to the project-root `.npmrc` also authenticated
|
|
24
31
|
successfully as `xuanzhu`, then failed at `npm publish` with the same `E403`.
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
npm run release:publish -- --otp=<6-digit-code>
|
|
30
|
-
```
|
|
33
|
+
A publish-capable npm token written to the project-root `.npmrc` authenticated
|
|
34
|
+
successfully as `xuanzhu`, and `npm run release:publish` then succeeded for
|
|
35
|
+
`yunti-browser-runtime@0.1.0`.
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
alternative, configure a granular npm access token with package publish
|
|
34
|
-
permission and bypass 2FA enabled, then rerun `npm run release:publish`.
|
|
35
|
-
|
|
36
|
-
After publish succeeds, run:
|
|
37
|
+
Verification:
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
40
|
npm run release:verify-published
|
|
40
41
|
```
|
|
41
42
|
|
|
43
|
+
The verification command returns 0 and confirms the published package metadata.
|
|
44
|
+
|
|
42
45
|
## Resolved Blocker: P4.3 Package URLs
|
|
43
46
|
|
|
44
47
|
Status: resolved on 2026-07-02 with
|
package/docs/SECURITY.md
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
The first release is local single-user software:
|
|
6
6
|
|
|
7
7
|
- bridge binds to `127.0.0.1` by default;
|
|
8
|
-
- bridge routes require
|
|
8
|
+
- local loopback bridge routes do not require a token by default;
|
|
9
|
+
- setting `YUNTI_BROWSER_BRIDGE_TOKEN` enables `x-yunti-browser-token` checks
|
|
10
|
+
for bridge routes, and binding to a non-loopback host requires a token;
|
|
9
11
|
- CORS only echoes local debug origins and browser-extension origins by default;
|
|
10
12
|
- extension talks to the local bridge;
|
|
11
13
|
- MCP tools default to `userId=local`;
|
|
@@ -24,7 +26,7 @@ The extension uses browser permissions required for automation:
|
|
|
24
26
|
|
|
25
27
|
- `tabs` for tab inventory and activation;
|
|
26
28
|
- `debugger` for CDP commands;
|
|
27
|
-
- `storage` for local bridge URL, token, user, and page-match settings;
|
|
29
|
+
- `storage` for local bridge URL, optional token, user, and page-match settings;
|
|
28
30
|
- `webRequest` for sanitized network observations;
|
|
29
31
|
- `activeTab` for visible-tab capture fallback paths.
|
|
30
32
|
|
|
@@ -52,5 +54,5 @@ Users should only load the extension from a trusted local checkout.
|
|
|
52
54
|
- Remote multi-user isolation layer.
|
|
53
55
|
- Per-tool allow/deny policy.
|
|
54
56
|
|
|
55
|
-
Remote deployment must be designed separately and
|
|
56
|
-
|
|
57
|
+
Remote deployment must be designed separately and must not use the local
|
|
58
|
+
no-token loopback default without an authentication and ownership model.
|
package/extension/manifest.json
CHANGED
package/extension/popup.css
CHANGED
|
@@ -34,6 +34,22 @@ label {
|
|
|
34
34
|
font-size: 12px;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
details {
|
|
38
|
+
display: grid;
|
|
39
|
+
gap: 6px;
|
|
40
|
+
color: #334155;
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
summary {
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
details label {
|
|
50
|
+
margin-top: 6px;
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
input,
|
|
38
54
|
textarea {
|
|
39
55
|
width: 100%;
|
package/extension/popup.html
CHANGED
|
@@ -14,14 +14,17 @@
|
|
|
14
14
|
Bridge URL
|
|
15
15
|
<input id="bridgeUrl" autocomplete="off" />
|
|
16
16
|
</label>
|
|
17
|
-
<label>
|
|
18
|
-
Bridge Token
|
|
19
|
-
<input id="bridgeToken" autocomplete="off" type="password" />
|
|
20
|
-
</label>
|
|
21
17
|
<label>
|
|
22
18
|
页面匹配
|
|
23
19
|
<textarea id="platformMatches" rows="3"></textarea>
|
|
24
20
|
</label>
|
|
21
|
+
<details id="advancedSettings">
|
|
22
|
+
<summary>高级设置</summary>
|
|
23
|
+
<label>
|
|
24
|
+
Bridge Token(可选)
|
|
25
|
+
<input id="bridgeToken" autocomplete="off" type="password" />
|
|
26
|
+
</label>
|
|
27
|
+
</details>
|
|
25
28
|
<button id="save">保存设置</button>
|
|
26
29
|
<button id="refresh">刷新状态</button>
|
|
27
30
|
</main>
|
package/extension/popup.js
CHANGED
|
@@ -2,6 +2,7 @@ const statusEl = document.querySelector("#status")
|
|
|
2
2
|
const bridgeUrlEl = document.querySelector("#bridgeUrl")
|
|
3
3
|
const bridgeTokenEl = document.querySelector("#bridgeToken")
|
|
4
4
|
const platformMatchesEl = document.querySelector("#platformMatches")
|
|
5
|
+
const advancedSettingsEl = document.querySelector("#advancedSettings")
|
|
5
6
|
document.querySelector("#save").addEventListener("click", save)
|
|
6
7
|
document.querySelector("#refresh").addEventListener("click", refresh)
|
|
7
8
|
|
|
@@ -17,7 +18,7 @@ async function refresh() {
|
|
|
17
18
|
setStatus(
|
|
18
19
|
state.activeSession
|
|
19
20
|
? `页面已连接\n${state.activeSession.title || state.activeSession.url}`
|
|
20
|
-
: "
|
|
21
|
+
: "默认无需设置。请打开或刷新任意 http/https 页面。"
|
|
21
22
|
)
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -35,12 +36,13 @@ async function save() {
|
|
|
35
36
|
return
|
|
36
37
|
}
|
|
37
38
|
renderSettings(state.settings || {})
|
|
38
|
-
setStatus("
|
|
39
|
+
setStatus("设置已保存。请刷新目标页面完成重新注册。")
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function renderSettings(settings) {
|
|
42
43
|
bridgeUrlEl.value = settings.bridgeUrl || "http://127.0.0.1:48887"
|
|
43
44
|
bridgeTokenEl.value = settings.bridgeToken || ""
|
|
45
|
+
advancedSettingsEl.open = Boolean(settings.bridgeToken)
|
|
44
46
|
platformMatchesEl.value = Array.isArray(settings.platformMatches)
|
|
45
47
|
? settings.platformMatches.join("\n")
|
|
46
48
|
: "*"
|
package/mcp/http-server.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import http from "node:http"
|
|
2
|
-
import { randomUUID } from "node:crypto"
|
|
3
2
|
import {
|
|
4
3
|
BridgeHub,
|
|
5
4
|
DEFAULT_SESSION_TTL_MS,
|
|
@@ -88,6 +87,11 @@ function isAllowedOrigin(origin, allowOrigins) {
|
|
|
88
87
|
return allowOrigins.some((pattern) => originMatchesPattern(origin, pattern))
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
function isLoopbackHost(host) {
|
|
91
|
+
const value = String(host || "").trim().toLowerCase()
|
|
92
|
+
return value === "127.0.0.1" || value === "localhost" || value === "::1" || value === "[::1]"
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
function bridgeCorsHeaders(req, allowOrigins) {
|
|
92
96
|
const origin = String(req.headers.origin || "")
|
|
93
97
|
const headers = {
|
|
@@ -117,20 +121,33 @@ function bridgeRequestToken(req) {
|
|
|
117
121
|
return match ? normalizeBridgeToken(match[1]) : ""
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
function isAuthorizedBridgeRequest(req, bridgeToken) {
|
|
124
|
+
function isAuthorizedBridgeRequest(req, bridgeToken, authRequired = true) {
|
|
125
|
+
if (!authRequired) return true
|
|
121
126
|
const token = normalizeBridgeToken(bridgeToken)
|
|
122
127
|
return Boolean(token && bridgeRequestToken(req) === token)
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
function
|
|
130
|
+
function authInfo(authRequired) {
|
|
131
|
+
return {
|
|
132
|
+
required: Boolean(authRequired),
|
|
133
|
+
header: BRIDGE_TOKEN_HEADER,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function healthWithAuth(value, authRequired) {
|
|
138
|
+
return {
|
|
139
|
+
...value,
|
|
140
|
+
authorized: true,
|
|
141
|
+
auth: authInfo(authRequired),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function limitedHealth(authRequired) {
|
|
126
146
|
return {
|
|
127
147
|
ok: true,
|
|
128
148
|
name: "yunti-browser-runtime-bridge",
|
|
129
149
|
authorized: false,
|
|
130
|
-
auth:
|
|
131
|
-
required: true,
|
|
132
|
-
header: BRIDGE_TOKEN_HEADER,
|
|
133
|
-
},
|
|
150
|
+
auth: authInfo(authRequired),
|
|
134
151
|
}
|
|
135
152
|
}
|
|
136
153
|
|
|
@@ -173,7 +190,13 @@ export async function startBridgeServer({
|
|
|
173
190
|
const configuredBridgeToken = normalizeBridgeToken(
|
|
174
191
|
bridgeToken ?? process.env.YUNTI_BROWSER_BRIDGE_TOKEN
|
|
175
192
|
)
|
|
176
|
-
const
|
|
193
|
+
const authRequired = Boolean(configuredBridgeToken)
|
|
194
|
+
if (!authRequired && !isLoopbackHost(host)) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
"YUNTI_BROWSER_BRIDGE_TOKEN is required when binding the bridge to a non-loopback host"
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
const activeBridgeToken = configuredBridgeToken
|
|
177
200
|
const hub = new BridgeHub({ sessionTtlMs })
|
|
178
201
|
const cleanupTimer = setInterval(() => {
|
|
179
202
|
hub.cleanupExpiredSessions()
|
|
@@ -182,7 +205,7 @@ export async function startBridgeServer({
|
|
|
182
205
|
const server = http.createServer(async (req, res) => {
|
|
183
206
|
try {
|
|
184
207
|
const url = new URL(req.url || "/", `http://${host}:${port}`)
|
|
185
|
-
const authorized = isAuthorizedBridgeRequest(req, activeBridgeToken)
|
|
208
|
+
const authorized = isAuthorizedBridgeRequest(req, activeBridgeToken, authRequired)
|
|
186
209
|
|
|
187
210
|
if (req.method === "OPTIONS") {
|
|
188
211
|
const origin = String(req.headers.origin || "")
|
|
@@ -195,7 +218,9 @@ export async function startBridgeServer({
|
|
|
195
218
|
req,
|
|
196
219
|
res,
|
|
197
220
|
200,
|
|
198
|
-
authorized
|
|
221
|
+
authorized
|
|
222
|
+
? healthWithAuth(hub.health({ userId: url.searchParams.get("userId") }), authRequired)
|
|
223
|
+
: limitedHealth(authRequired),
|
|
199
224
|
{ allowOrigins }
|
|
200
225
|
)
|
|
201
226
|
return
|
|
@@ -209,7 +234,7 @@ export async function startBridgeServer({
|
|
|
209
234
|
{
|
|
210
235
|
ok: false,
|
|
211
236
|
error: `missing or invalid ${BRIDGE_TOKEN_HEADER}`,
|
|
212
|
-
auth:
|
|
237
|
+
auth: authInfo(authRequired),
|
|
213
238
|
},
|
|
214
239
|
{ allowOrigins }
|
|
215
240
|
)
|
|
@@ -320,7 +345,7 @@ export async function startBridgeServer({
|
|
|
320
345
|
if (mode === "proxy") {
|
|
321
346
|
clearInterval(cleanupTimer)
|
|
322
347
|
server.close()
|
|
323
|
-
return { mode: "proxy", host, port, bridgeToken: configuredBridgeToken, allowOrigins, hub: null, server: null }
|
|
348
|
+
return { mode: "proxy", host, port, bridgeToken: configuredBridgeToken, authRequired, allowOrigins, hub: null, server: null }
|
|
324
349
|
}
|
|
325
|
-
return { mode: "owner", host, port, bridgeToken: activeBridgeToken, allowOrigins, hub, server }
|
|
350
|
+
return { mode: "owner", host, port, bridgeToken: activeBridgeToken, authRequired, allowOrigins, hub, server }
|
|
326
351
|
}
|
package/mcp/server.js
CHANGED
|
@@ -215,12 +215,17 @@ export async function runStdio() {
|
|
|
215
215
|
`[yunti-browser-runtime] bridge ${bridge.mode} on http://${bridge.host}:${bridge.port}`
|
|
216
216
|
)
|
|
217
217
|
if (bridge.mode === "owner") {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
if (bridge.authRequired) {
|
|
219
|
+
console.error(
|
|
220
|
+
`[yunti-browser-runtime] bridge token header ${BRIDGE_TOKEN_HEADER}: ${bridge.bridgeToken}`
|
|
221
|
+
)
|
|
222
|
+
} else {
|
|
223
|
+
console.error("[yunti-browser-runtime] bridge auth disabled for local loopback")
|
|
224
|
+
console.error("[yunti-browser-runtime] set YUNTI_BROWSER_BRIDGE_TOKEN to require a local token")
|
|
225
|
+
}
|
|
221
226
|
} else if (!bridge.bridgeToken) {
|
|
222
227
|
console.error(
|
|
223
|
-
`[yunti-browser-runtime] proxy mode
|
|
228
|
+
`[yunti-browser-runtime] proxy mode will connect without a bridge token; set YUNTI_BROWSER_BRIDGE_TOKEN if the running bridge requires one`
|
|
224
229
|
)
|
|
225
230
|
}
|
|
226
231
|
}
|
|
@@ -249,12 +254,17 @@ export async function runBridgeOnly() {
|
|
|
249
254
|
`[yunti-browser-runtime] bridge ${bridge.mode} on http://${bridge.host}:${bridge.port}`
|
|
250
255
|
)
|
|
251
256
|
if (bridge.mode === "owner") {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
if (bridge.authRequired) {
|
|
258
|
+
console.error(
|
|
259
|
+
`[yunti-browser-runtime] bridge token header ${BRIDGE_TOKEN_HEADER}: ${bridge.bridgeToken}`
|
|
260
|
+
)
|
|
261
|
+
} else {
|
|
262
|
+
console.error("[yunti-browser-runtime] bridge auth disabled for local loopback")
|
|
263
|
+
console.error("[yunti-browser-runtime] set YUNTI_BROWSER_BRIDGE_TOKEN to require a local token")
|
|
264
|
+
}
|
|
255
265
|
} else if (!bridge.bridgeToken) {
|
|
256
266
|
console.error(
|
|
257
|
-
`[yunti-browser-runtime] proxy mode
|
|
267
|
+
`[yunti-browser-runtime] proxy mode will connect without a bridge token; set YUNTI_BROWSER_BRIDGE_TOKEN if the running bridge requires one`
|
|
258
268
|
)
|
|
259
269
|
}
|
|
260
270
|
await new Promise(() => {})
|
package/package.json
CHANGED
package/scripts/doctor.js
CHANGED
|
@@ -58,12 +58,14 @@ async function checkBridge() {
|
|
|
58
58
|
`${bridgeUrl}/health?userId=${encodeURIComponent(routeUserId)}`
|
|
59
59
|
)
|
|
60
60
|
const authorized = health.json?.authorized !== false
|
|
61
|
+
const authRequired = health.json?.auth?.required !== false && Boolean(health.json?.auth?.required)
|
|
61
62
|
const sessions = Array.isArray(health.json?.sessions) ? health.json.sessions : []
|
|
62
63
|
const activeSessionId = health.json?.activeSessionId || null
|
|
63
64
|
return {
|
|
64
65
|
ok: health.status === 200 && authorized,
|
|
65
66
|
reachable: health.status === 200,
|
|
66
67
|
authorized,
|
|
68
|
+
authRequired,
|
|
67
69
|
status: health.status,
|
|
68
70
|
url: bridgeUrl,
|
|
69
71
|
tokenConfigured: Boolean(bridgeToken),
|
|
@@ -81,6 +83,7 @@ async function checkBridge() {
|
|
|
81
83
|
ok: false,
|
|
82
84
|
reachable: false,
|
|
83
85
|
authorized: false,
|
|
86
|
+
authRequired: Boolean(bridgeToken),
|
|
84
87
|
url: bridgeUrl,
|
|
85
88
|
tokenConfigured: Boolean(bridgeToken),
|
|
86
89
|
tokenHeader: bridgeTokenHeader,
|
|
@@ -110,8 +113,8 @@ function buildNextSteps(checks) {
|
|
|
110
113
|
if (!checks.bridge.reachable) {
|
|
111
114
|
steps.push("Start the local bridge with npm run bridge.")
|
|
112
115
|
}
|
|
113
|
-
if (checks.bridge.reachable && !checks.bridge.authorized) {
|
|
114
|
-
steps.push("Set YUNTI_BROWSER_BRIDGE_TOKEN to the
|
|
116
|
+
if (checks.bridge.reachable && checks.bridge.authRequired && !checks.bridge.authorized) {
|
|
117
|
+
steps.push("Set YUNTI_BROWSER_BRIDGE_TOKEN to the token used by the running bridge.")
|
|
115
118
|
steps.push("Save the same token in the extension popup.")
|
|
116
119
|
}
|
|
117
120
|
if (checks.bridge.ok && !checks.bridge.extensionConnected) {
|
|
@@ -131,7 +134,7 @@ function humanSummary(report) {
|
|
|
131
134
|
`Yunti Browser Runtime doctor: ${report.ok ? "OK" : "needs attention"}`,
|
|
132
135
|
`- Node: ${report.checks.node.version} (${report.checks.node.ok ? "ok" : "requires >=22"})`,
|
|
133
136
|
`- Bridge: ${report.checks.bridge.reachable ? report.checks.bridge.url : "not reachable"}`,
|
|
134
|
-
`- Token: ${report.checks.bridge.authorized ? "valid" : "missing or invalid"}`,
|
|
137
|
+
`- Token: ${report.checks.bridge.authRequired ? report.checks.bridge.authorized ? "valid" : "missing or invalid" : "not required for local loopback"}`,
|
|
135
138
|
`- Sessions: ${report.checks.bridge.visibleSessionCount} visible, active ${report.checks.bridge.activeSessionId || "none"}`,
|
|
136
139
|
`- Extension: ${report.checks.bridge.extensionConnected ? "connected" : "not detected"}`,
|
|
137
140
|
`- MCP server: ${report.checks.mcpServer.ok ? "found" : "missing"}`,
|
package/scripts/print-config.js
CHANGED
|
@@ -77,7 +77,7 @@ function buildConfig(agent) {
|
|
|
77
77
|
tokenConfigured: bridgeTokenConfigured,
|
|
78
78
|
tokenInstruction: bridgeTokenConfigured
|
|
79
79
|
? "Token is configured in the current environment; copy the same secret into your agent and extension settings without committing it."
|
|
80
|
-
: "
|
|
80
|
+
: "Local loopback bridge auth is disabled by default; set YUNTI_BROWSER_BRIDGE_TOKEN only when you want to require a local token.",
|
|
81
81
|
},
|
|
82
82
|
skill: {
|
|
83
83
|
sourcePath: skillPath,
|