@gecho-ai/gecho-bridge 1.1.37 → 1.1.38-beta.1

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.
Files changed (104) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/README.md +54 -6
  3. package/README.zh.md +59 -6
  4. package/action-result.js +53 -0
  5. package/bridge-service.js +187 -0
  6. package/bridge-startup.js +336 -0
  7. package/dist/mcp-client.cjs +176 -15
  8. package/dist/server.cjs +1129 -24
  9. package/distribution-skills/amazon/publish.json +11 -0
  10. package/distribution-skills/amazon-product/publish.json +11 -0
  11. package/distribution-skills/amazon-reviews/publish.json +11 -0
  12. package/distribution-skills/amazon-search/publish.json +11 -0
  13. package/distribution-skills/facebook-reels-search/SKILL.md +289 -0
  14. package/distribution-skills/facebook-reels-search/_meta.json +5 -0
  15. package/distribution-skills/facebook-reels-search/publish.json +31 -0
  16. package/distribution-skills/tiktok-influencer/publish.json +11 -0
  17. package/distribution-skills/tiktok-insight/publish.json +11 -0
  18. package/distribution-skills/tiktok-product/publish.json +11 -0
  19. package/distribution-skills/tiktok-search/publish.json +11 -0
  20. package/distribution-skills/tiktok-shop/publish.json +11 -0
  21. package/distribution-skills/tiktok-shop-search/publish.json +11 -0
  22. package/distribution-skills/tiktok-video/publish.json +11 -0
  23. package/distribution-skills/tiktok-video-search/publish.json +11 -0
  24. package/distribution-skills/x/publish.json +11 -0
  25. package/distribution-skills/x-post-detail/publish.json +11 -0
  26. package/distribution-skills/x-search/publish.json +11 -0
  27. package/distribution-skills/youtube-shorts-search/SKILL.md +289 -0
  28. package/distribution-skills/youtube-shorts-search/_meta.json +5 -0
  29. package/distribution-skills/youtube-shorts-search/publish.json +31 -0
  30. package/distribution-skills-zh-CN/amazon/SKILL.md +398 -0
  31. package/distribution-skills-zh-CN/amazon/_meta.json +5 -0
  32. package/distribution-skills-zh-CN/amazon/publish.json +31 -0
  33. package/distribution-skills-zh-CN/amazon/skillhub-publish.json +1 -0
  34. package/distribution-skills-zh-CN/amazon-product/SKILL.md +299 -0
  35. package/distribution-skills-zh-CN/amazon-product/_meta.json +5 -0
  36. package/distribution-skills-zh-CN/amazon-product/publish.json +31 -0
  37. package/distribution-skills-zh-CN/amazon-product/skillhub-publish.json +1 -0
  38. package/distribution-skills-zh-CN/amazon-reviews/SKILL.md +300 -0
  39. package/distribution-skills-zh-CN/amazon-reviews/_meta.json +5 -0
  40. package/distribution-skills-zh-CN/amazon-reviews/publish.json +31 -0
  41. package/distribution-skills-zh-CN/amazon-reviews/skillhub-publish.json +1 -0
  42. package/distribution-skills-zh-CN/amazon-search/SKILL.md +300 -0
  43. package/distribution-skills-zh-CN/amazon-search/_meta.json +5 -0
  44. package/distribution-skills-zh-CN/amazon-search/publish.json +31 -0
  45. package/distribution-skills-zh-CN/amazon-search/skillhub-publish.json +1 -0
  46. package/distribution-skills-zh-CN/facebook-reels-search/SKILL.md +289 -0
  47. package/distribution-skills-zh-CN/facebook-reels-search/_meta.json +5 -0
  48. package/distribution-skills-zh-CN/facebook-reels-search/publish.json +31 -0
  49. package/distribution-skills-zh-CN/facebook-reels-search/skillhub-publish.json +1 -0
  50. package/distribution-skills-zh-CN/tiktok-influencer/SKILL.md +308 -0
  51. package/distribution-skills-zh-CN/tiktok-influencer/_meta.json +5 -0
  52. package/distribution-skills-zh-CN/tiktok-influencer/publish.json +31 -0
  53. package/distribution-skills-zh-CN/tiktok-influencer/skillhub-publish.json +1 -0
  54. package/distribution-skills-zh-CN/tiktok-insight/SKILL.md +362 -0
  55. package/distribution-skills-zh-CN/tiktok-insight/_meta.json +5 -0
  56. package/distribution-skills-zh-CN/tiktok-insight/publish.json +31 -0
  57. package/distribution-skills-zh-CN/tiktok-insight/skillhub-publish.json +6 -0
  58. package/distribution-skills-zh-CN/tiktok-product/SKILL.md +298 -0
  59. package/distribution-skills-zh-CN/tiktok-product/_meta.json +5 -0
  60. package/distribution-skills-zh-CN/tiktok-product/publish.json +31 -0
  61. package/distribution-skills-zh-CN/tiktok-product/skillhub-publish.json +1 -0
  62. package/distribution-skills-zh-CN/tiktok-search/SKILL.md +470 -0
  63. package/distribution-skills-zh-CN/tiktok-search/_meta.json +5 -0
  64. package/distribution-skills-zh-CN/tiktok-search/publish.json +31 -0
  65. package/distribution-skills-zh-CN/tiktok-search/skillhub-publish.json +1 -0
  66. package/distribution-skills-zh-CN/tiktok-shop/SKILL.md +359 -0
  67. package/distribution-skills-zh-CN/tiktok-shop/_meta.json +5 -0
  68. package/distribution-skills-zh-CN/tiktok-shop/publish.json +31 -0
  69. package/distribution-skills-zh-CN/tiktok-shop/skillhub-publish.json +1 -0
  70. package/distribution-skills-zh-CN/tiktok-shop-search/SKILL.md +299 -0
  71. package/distribution-skills-zh-CN/tiktok-shop-search/_meta.json +5 -0
  72. package/distribution-skills-zh-CN/tiktok-shop-search/publish.json +31 -0
  73. package/distribution-skills-zh-CN/tiktok-shop-search/skillhub-publish.json +1 -0
  74. package/distribution-skills-zh-CN/tiktok-video/SKILL.md +380 -0
  75. package/distribution-skills-zh-CN/tiktok-video/_meta.json +5 -0
  76. package/distribution-skills-zh-CN/tiktok-video/publish.json +31 -0
  77. package/distribution-skills-zh-CN/tiktok-video/skillhub-publish.json +1 -0
  78. package/distribution-skills-zh-CN/tiktok-video-search/SKILL.md +306 -0
  79. package/distribution-skills-zh-CN/tiktok-video-search/_meta.json +5 -0
  80. package/distribution-skills-zh-CN/tiktok-video-search/publish.json +31 -0
  81. package/distribution-skills-zh-CN/tiktok-video-search/skillhub-publish.json +1 -0
  82. package/distribution-skills-zh-CN/x/SKILL.md +360 -0
  83. package/distribution-skills-zh-CN/x/_meta.json +5 -0
  84. package/distribution-skills-zh-CN/x/publish.json +31 -0
  85. package/distribution-skills-zh-CN/x/skillhub-publish.json +1 -0
  86. package/distribution-skills-zh-CN/x-post-detail/SKILL.md +299 -0
  87. package/distribution-skills-zh-CN/x-post-detail/_meta.json +5 -0
  88. package/distribution-skills-zh-CN/x-post-detail/publish.json +31 -0
  89. package/distribution-skills-zh-CN/x-post-detail/skillhub-publish.json +1 -0
  90. package/distribution-skills-zh-CN/x-search/SKILL.md +299 -0
  91. package/distribution-skills-zh-CN/x-search/_meta.json +5 -0
  92. package/distribution-skills-zh-CN/x-search/publish.json +31 -0
  93. package/distribution-skills-zh-CN/x-search/skillhub-publish.json +1 -0
  94. package/distribution-skills-zh-CN/youtube-shorts-search/SKILL.md +289 -0
  95. package/distribution-skills-zh-CN/youtube-shorts-search/_meta.json +5 -0
  96. package/distribution-skills-zh-CN/youtube-shorts-search/publish.json +31 -0
  97. package/distribution-skills-zh-CN/youtube-shorts-search/skillhub-publish.json +1 -0
  98. package/mcp-client.js +118 -10
  99. package/openclaw.plugin.json +2 -2
  100. package/package.json +23 -5
  101. package/scheduled-task-scheduler.js +261 -0
  102. package/scheduled-task-store.js +268 -0
  103. package/scheduled-task.js +244 -0
  104. package/server.js +420 -19
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@gecho-ai/gecho-bridge",
3
- "version": "1.1.37",
4
- "description": "TikTok Search Tool for OpenClaw"
3
+ "version": "1.1.38",
4
+ "description": "Browser research tools for OpenClaw via the Gecho Chrome Extension"
5
5
  }
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Gecho Bridge 🚀
2
2
 
3
- **Gecho Bridge connects AI clients such as OpenClaw, Hermes, Trae, and Claude Code to a live Chrome browser through MCP and the Gecho Chrome extension.** Use it to search TikTok videos, collect structured metadata, save JSON results, and run async product, trend, competitor, and content insight workflows.
3
+ **Gecho Bridge connects AI clients such as OpenClaw, Hermes, Trae, and Claude Code to a live Chrome browser through MCP and the Gecho Chrome extension.** Use it to search TikTok videos, YouTube Shorts, Facebook Reels, X posts, and Amazon products, collect structured metadata, save JSON results, and run async product, trend, competitor, and content insight workflows.
4
4
 
5
5
  ## ⚠️ Critical Prerequisite: Read Before Use
6
6
 
7
- Gecho Bridge cannot work from the README, Skill page, or MCP config alone. Before the first TikTok search or insight job, all 3 items below are required:
7
+ Gecho Bridge cannot work from the README, Skill page, or MCP config alone. Before the first platform search or insight job, all 3 items below are required:
8
8
 
9
9
  1. **Configure Gecho Bridge MCP** in your AI client.
10
10
  2. **Install the Gecho Chrome extension** and log in to your Gecho account.
11
- 3. **Log in to TikTok web in Chrome** and keep the logged-in TikTok tab open.
11
+ 3. **Log in to the target platform in Chrome** and keep the logged-in platform tab open.
12
12
 
13
- If any of these are missing, TikTok search and insight tools may fail even if the Skill or plugin is installed.
13
+ If any of these are missing, the selected platform tool may fail even if the Skill or plugin is installed.
14
14
 
15
15
  ## 🚀 3-Step Quick Start
16
16
 
@@ -125,6 +125,49 @@ A new team Skill also requires at least one team category ID. For an existing Sk
125
125
 
126
126
  Official documentation: [CLI installation](https://skillhub.cn/install/skillhub.md), [community publishing](https://skillhub.cn/ai/release.md), [enterprise publishing](https://skillhub.cn/enterprise/dashboard/publish), and [enterprise API keys](https://skillhub.cn/enterprise/dashboard/keys).
127
127
 
128
+ ### WorkBuddy Skill Packaging
129
+
130
+ WorkBuddy uses a separate Skill package format, so a Tencent SkillHub staging copy must not be uploaded directly.
131
+ The repository creates Git-ignored copies under `tmp/workbuddy-publish/`, adds
132
+ `display_name`, `display_name_en`, `description_zh`, `description_en`, `version`, and `author`, and packages
133
+ each Skill using WorkBuddy's official `skills/<skill-name>/SKILL.md` layout.
134
+
135
+ ```bash
136
+ # Create WorkBuddy-only staging copies
137
+ npm run skillhub:workbuddy:stage
138
+
139
+ # Build all English and Chinese ZIPs and validate their structure
140
+ npm run skillhub:workbuddy:dry-run
141
+
142
+ # Prepare one Chinese Skill
143
+ npm run skillhub:workbuddy:dry-run -- \
144
+ --skill tiktok-video-search --locale zh-CN
145
+ ```
146
+
147
+ ZIP files are written to `tmp/workbuddy-publish/archives/` (each must be no larger than 3MB) for manual upload to the
148
+ [WorkBuddy Open Platform](https://open.workbuddy.cn/). The script has no login or submit command and never uses
149
+ Tencent SkillHub `skh_...` or `sk-ent-...` credentials. See the official [WorkBuddy Skill documentation](https://open.workbuddy.cn/docs/skill).
150
+
151
+ ### WorkBuddy Open Platform Connector
152
+
153
+ WorkBuddy's open platform Connector publication is separate from Skill publication. The Connector ZIP contains the
154
+ platform metadata and the MCP startup configuration; it is not one of the Skill ZIPs above.
155
+
156
+ ```bash
157
+ # Build and validate the Connector ZIP for the current package version
158
+ npm run skillhub:workbuddy:connector:dry-run
159
+ ```
160
+
161
+ The uploadable file is generated at:
162
+
163
+ ```text
164
+ tmp/workbuddy-connector/gecho-bridge-workbuddy-v1.1.37.zip
165
+ ```
166
+
167
+ It uses the public `@gecho-ai/gecho-bridge@1.1.37` npm package as a local Stdio MCP server. The generated ZIP contains
168
+ `connector-meta.json`, `mcp.json`, `icon.svg`, and a concise Connector Skill. It is only staged locally; this command
169
+ does not upload or submit a review request. See the official [WorkBuddy Connector documentation](https://open.workbuddy.cn/docs/connector).
170
+
128
171
  ### Option 2: One-Click Setup in Hermes (Hermes Skill Hub)
129
172
  You can quickly add the service to Hermes and restart it with the following commands:
130
173
  ```bash
@@ -167,7 +210,7 @@ After the environment is configured and your AI client has restarted, you can di
167
210
  ### ✅ Self-Check Before First Use
168
211
  1. `gecho-bridge` MCP is configured, or the `@gecho-ai/gecho-bridge-bundle` plugin is installed.
169
212
  2. The [Gecho browser extension](https://chromewebstore.google.com/detail/pjkaeenpekolahdbccjfenjcmanemlbj?utm_source=item-share-cb) is installed.
170
- 3. TikTok is open in Chrome and the account is logged in.
213
+ 3. The target platform is open in Chrome and the account is logged in (TikTok, YouTube, Facebook, X, or Amazon as applicable).
171
214
  4. The Gecho extension is logged in to a Gecho account and online.
172
215
  5. The TikTok page is not stuck or left on a CAPTCHA page.
173
216
 
@@ -185,8 +228,9 @@ Focused single-tool Skills are also available:
185
228
  - **Amazon**: `amazon-search`, `amazon-product`, `amazon-reviews`.
186
229
  - **TikTok**: `tiktok-video-search` for keyword-based video search; `tiktok-video` for a specific video’s detail, comments, and replies; `tiktok-influencer`, `tiktok-insight`, `tiktok-product`, and `tiktok-shop-search` for focused workflows.
187
230
  - **X**: `x-search` and `x-post-detail`.
231
+ - **Short-form video**: `youtube-shorts-search` for YouTube Shorts and `facebook-reels-search` for Facebook Reels.
188
232
 
189
- Use `tiktok-video-search` when you want to discover videos by keyword. Use `tiktok-video` when you already have a video URL and need its detail data. If you want the full TikTok research workflow, use `tiktok-search`.
233
+ Use `tiktok-video-search`, `youtube-shorts-search`, or `facebook-reels-search` when you want to discover short-form videos by keyword. Use `tiktok-video` when you already have a video URL and need its detail data. If you want the full TikTok research workflow, use `tiktok-search`.
190
234
 
191
235
  ### 🔍 Basic Search (`tiktok_search`)
192
236
  Suitable for quickly retrieving and collecting video data.
@@ -237,6 +281,8 @@ If installation is successful, you should see `Status: loaded` and `MCP servers:
237
281
  ### 2. Note about the local background service
238
282
  - Gecho Bridge automatically starts a local service on demand so the MCP client can talk to the browser extension.
239
283
  - This service only listens on `127.0.0.1` and is expected to stay available while you use the plugin.
284
+ - You can also manage it independently with `npm run server:start`, `npm run server:status`, and `npm run server:stop`.
285
+ - To run it after user login, use `npx gecho-bridge-server install`; remove the startup entry with `npx gecho-bridge-server uninstall`.
240
286
  - If Chrome or the extension was restarted and requests start failing, first run `openclaw gateway restart`, then try again.
241
287
 
242
288
  ### 3. Error: Extension not connected
@@ -285,6 +331,8 @@ npm run server
285
331
  - **Client layer** (`mcp-client.js`): The standard MCP STDIO integration layer, responsible for communicating with clients such as OpenClaw, Hermes, and Trae, and declaring the Tools specification.
286
332
  - **Service layer** (`server.js`): The local resident service layer, responsible for communicating with the browser extension via WebSocket and executing the actual scraping and persistence. (Supports a Lazy Start mechanism and launches only when needed.)
287
333
 
334
+ See [`docs/scheduled-flywheel-bridge.md`](docs/scheduled-flywheel-bridge.md) for scheduled flywheel APIs, standalone Bridge operation, and user-level login startup.
335
+
288
336
  ## License
289
337
 
290
338
  MIT
package/README.zh.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Gecho Bridge 🚀
2
2
 
3
- **Gecho Bridge 通过 MCP 和 Gecho Chrome 扩展,把 OpenClaw、Hermes、Trae、Claude Code 等 AI 客户端连接到真实 Chrome 浏览器。** 你可以用它搜索 TikTok 视频、采集结构化元数据、保存 JSON 结果,并运行异步的产品、趋势、竞品和内容洞察工作流。
3
+ **Gecho Bridge 通过 MCP 和 Gecho Chrome 扩展,把 OpenClaw、Hermes、Trae、Claude Code 等 AI 客户端连接到真实 Chrome 浏览器。** 你可以用它搜索 TikTok 视频、YouTube Shorts、Facebook Reels、X 帖子和 Amazon 商品,采集结构化元数据、保存 JSON 结果,并运行异步的产品、趋势、竞品和内容洞察工作流。
4
4
 
5
5
  ## ⚠️ 重要前置:使用前必看
6
6
 
7
- Gecho Bridge 不能只靠 README、Skill 页面或 MCP 配置单独运行。首次搜索 TikTok 或运行洞察任务前,必须同时满足下面 3 个条件:
7
+ Gecho Bridge 不能只靠 README、Skill 页面或 MCP 配置单独运行。首次搜索平台内容或运行洞察任务前,必须同时满足下面 3 个条件:
8
8
 
9
9
  1. **在 AI 客户端中配置 Gecho Bridge MCP**。
10
10
  2. **安装 Gecho Chrome 扩展**,并在扩展中登录 Gecho 账号。
11
- 3. **在 Chrome 中登录 TikTok 网页版**,并保持已登录的 TikTok 标签页打开。
11
+ 3. **在 Chrome 中登录目标平台**,并保持已登录的平台标签页打开。
12
12
 
13
- 如果任一条件缺失,即使 Skill 或 Plugin 已安装,TikTok 搜索和洞察功能也可能无法正常运行。
13
+ 如果任一条件缺失,即使 Skill 或 Plugin 已安装,所选平台的搜索功能也可能无法正常运行。
14
14
 
15
15
  ## 🚀 3 步快速上手
16
16
 
@@ -125,6 +125,54 @@ npm run skillhub:tencent:publish -- \
125
125
 
126
126
  官方文档:[CLI 安装](https://skillhub.cn/install/skillhub.md)、[公共发布](https://skillhub.cn/ai/release.md)、[企业发布控制台](https://skillhub.cn/enterprise/dashboard/publish)、[企业 API Key](https://skillhub.cn/enterprise/dashboard/keys)。
127
127
 
128
+ ### WorkBuddy Skill 发布准备
129
+
130
+ WorkBuddy 使用独立的 Skill 包格式,不能直接上传腾讯 SkillHub 副本。项目会从源 Skill 创建被 Git 忽略的
131
+ `tmp/workbuddy-publish/` 副本,补齐 `display_name`、`display_name_en`、`description_zh`、
132
+ `description_en`、`version` 和 `author`,并按 WorkBuddy 官方要求使用
133
+ `skills/<skill-name>/SKILL.md` 目录结构打包。
134
+
135
+ ```bash
136
+ # 只生成 WorkBuddy 临时副本
137
+ npm run skillhub:workbuddy:stage
138
+
139
+ # 生成全部中英文 ZIP,并进行本地结构校验
140
+ npm run skillhub:workbuddy:dry-run
141
+
142
+ # 只准备一个中文 Skill
143
+ npm run skillhub:workbuddy:dry-run -- \
144
+ --skill tiktok-video-search --locale zh-CN
145
+ ```
146
+
147
+ 输出的 ZIP 位于 `tmp/workbuddy-publish/archives/`(单个不超过 3MB),可在 [WorkBuddy 开放平台](https://open.workbuddy.cn/)
148
+ 手动上传。脚本没有登录或自动提交功能,不会使用腾讯 SkillHub 的 `skh_...` 或 `sk-ent-...` Key。
149
+ WorkBuddy 官方格式参考:[Skill 文档](https://open.workbuddy.cn/docs/skill)。
150
+
151
+ ### Aily SkillHub 发布
152
+
153
+ Aily 当前通过网页上传 Skill 压缩包。项目提供了浏览器自动化脚本,复用已登录的 Edge,
154
+ 从 `tmp/manual-skill-packages/manifest.tsv` 读取压缩包,并使用每个 Skill 的
155
+ `publish.json` 中 `platforms.aily-skillhub.displayName` 自动填写展示名称。
156
+
157
+ 首次使用需要完全退出 Edge 后,以远程调试端口启动,再在该窗口登录 Aily:
158
+
159
+ ```bash
160
+ /Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge \
161
+ --remote-debugging-port=9222
162
+ ```
163
+
164
+ 然后执行:
165
+
166
+ ```bash
167
+ npm run skill:package:manual
168
+ npm run skillhub:aily:dry-run -- --skill tiktok-shop-search --locale zh-CN
169
+ npm run skillhub:aily:publish -- --skill tiktok-shop-search --locale zh-CN
170
+ ```
171
+
172
+ 脚本只修改网页表单,不修改源 Skill,也不保存浏览器凭证;发布结果写入
173
+ `tmp/aily-browser-publish/results.json`。完整参数和批量发布方式见
174
+ [`scripts/README.md`](scripts/README.md)。
175
+
128
176
  ### 方式二:在 Hermes 中一键配置 (Hermes Skill Hub)
129
177
  你可以通过以下命令将服务快捷添加到 Hermes 并重启:
130
178
  ```bash
@@ -167,7 +215,7 @@ claude mcp add gecho-bridge -- npx -y @gecho-ai/gecho-bridge@latest
167
215
  ### ✅ 首次使用前自检
168
216
  1. 已配置 `gecho-bridge` MCP,或已安装 `@gecho-ai/gecho-bridge-bundle` Plugin。
169
217
  2. 已安装 [Gecho 浏览器扩展](https://chromewebstore.google.com/detail/pjkaeenpekolahdbccjfenjcmanemlbj?utm_source=item-share-cb)。
170
- 3. Chrome 中已打开 TikTok 并登录账号。
218
+ 3. Chrome 中已打开目标平台并登录账号(按任务选择 TikTok、YouTube、Facebook、X 或 Amazon)。
171
219
  4. Gecho 扩展已登录 Gecho 账号并处于在线状态。
172
220
  5. TikTok 页面未卡住或未停在验证码页面。
173
221
 
@@ -185,8 +233,9 @@ Gecho 提供四个聚合 Skill:
185
233
  - **Amazon**:`amazon-search`、`amazon-product`、`amazon-reviews`。
186
234
  - **TikTok**:`tiktok-video-search` 用于按关键词搜索视频;`tiktok-video` 用于指定视频的详情、评论和回复;另有 `tiktok-influencer`、`tiktok-insight`、`tiktok-product` 和 `tiktok-shop-search`。
187
235
  - **X**:`x-search` 和 `x-post-detail`。
236
+ - **短视频**:`youtube-shorts-search` 用于 YouTube Shorts,`facebook-reels-search` 用于 Facebook Reels。
188
237
 
189
- 需要按关键词发现视频时使用 `tiktok-video-search`;已有视频 URL、需要获取视频详情时使用 `tiktok-video`。需要完整 TikTok 研究工作流时,使用 `tiktok-search`。
238
+ 需要按关键词发现短视频时使用 `tiktok-video-search`、`youtube-shorts-search` 或 `facebook-reels-search`;已有视频 URL、需要获取视频详情时使用 `tiktok-video`。需要完整 TikTok 研究工作流时,使用 `tiktok-search`。
190
239
 
191
240
  ### 🔍 基础搜索 (`tiktok_search`)
192
241
  适用于快速检索和收集视频数据。
@@ -237,6 +286,8 @@ openclaw plugins info @gecho-ai/gecho-bridge-bundle
237
286
  ### 2. 关于本地后台服务的说明
238
287
  - Gecho Bridge 会在需要时自动拉起本地服务,用来让 MCP 客户端和浏览器扩展通信。
239
288
  - 这个服务只监听 `127.0.0.1`,在你使用插件期间保持可用属于正常行为。
289
+ - 也可以独立管理服务:`npm run server:start`、`npm run server:status`、`npm run server:stop`。
290
+ - 如需用户登录后自动运行,可执行 `npx gecho-bridge-server install`;卸载启动项执行 `npx gecho-bridge-server uninstall`。
240
291
  - 如果你重启了 Chrome 或扩展,随后请求开始失败,先执行一次 `openclaw gateway restart`,再重试。
241
292
 
242
293
  ### 3. 报错:提示扩展未连接
@@ -285,6 +336,8 @@ npm run server
285
336
  - **Client 层** (`mcp-client.js`):标准 MCP STDIO 接入层,负责与 OpenClaw/Hermes/Trae 等客户端通信,并声明 Tools 规范。
286
337
  - **Service 层** (`server.js`):本地常驻服务层,负责与浏览器扩展通过 WebSocket 通信,执行实际抓取与落盘。(支持 Lazy Start 机制,仅在需要时自动拉起)。
287
338
 
339
+ 自动飞轮的定时任务接口、Bridge 独立运行和用户级开机启动说明见 [`docs/scheduled-flywheel-bridge.md`](docs/scheduled-flywheel-bridge.md)。
340
+
288
341
  ## License
289
342
 
290
343
  MIT
@@ -0,0 +1,53 @@
1
+ const ACTION_RESULT_METADATA_FIELDS = ["meta", "errorCode", "errorMessage"];
2
+
3
+ function isRecord(value) {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+
7
+ function hasOwn(value, key) {
8
+ return Object.prototype.hasOwnProperty.call(value, key);
9
+ }
10
+
11
+ function getActionResultMetadata(result) {
12
+ const metadata = {};
13
+ if (!isRecord(result)) return metadata;
14
+
15
+ for (const field of ACTION_RESULT_METADATA_FIELDS) {
16
+ if (hasOwn(result, field)) metadata[field] = result[field];
17
+ }
18
+ return metadata;
19
+ }
20
+
21
+ function createActionResultEnvelope(message) {
22
+ const source = isRecord(message) ? message : {};
23
+ return {
24
+ data: source.data,
25
+ ...getActionResultMetadata(source)
26
+ };
27
+ }
28
+
29
+ function normalizeActionResult(result) {
30
+ if (!isRecord(result) || !hasOwn(result, "data")) return { data: result };
31
+ return {
32
+ data: result.data,
33
+ ...getActionResultMetadata(result)
34
+ };
35
+ }
36
+
37
+ function isHardActionResultFailure(result) {
38
+ if (!isRecord(result)) return false;
39
+
40
+ const hasFailure = ["errorCode", "errorMessage"].some((field) => {
41
+ return hasOwn(result, field) && result[field] !== undefined && result[field] !== null && result[field] !== "";
42
+ });
43
+ if (!hasFailure) return false;
44
+
45
+ return String(result.meta?.status || "").toLowerCase() !== "partial";
46
+ }
47
+
48
+ module.exports = {
49
+ createActionResultEnvelope,
50
+ getActionResultMetadata,
51
+ isHardActionResultFailure,
52
+ normalizeActionResult
53
+ };
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const http = require("node:http");
6
+ const path = require("node:path");
7
+ const { spawn } = require("node:child_process");
8
+ const packageJson = require("./package.json");
9
+ const { getDefaultDataDir } = require("./data-dir");
10
+ const {
11
+ getStartupStatus,
12
+ installStartup,
13
+ uninstallStartup
14
+ } = require("./bridge-startup");
15
+
16
+ const HTTP_PORT = Number(process.env.GECHO_HTTP_PORT || 18793);
17
+ const SERVICE_BASE_URL = `http://127.0.0.1:${HTTP_PORT}`;
18
+ const SERVICE_PATH = path.join(__dirname, "server.js");
19
+ const SERVICE_PROTOCOL_VERSION = 2;
20
+ const START_TIMEOUT_MS = Math.max(1000, Number(process.env.GECHO_SERVICE_START_TIMEOUT_MS || 10000));
21
+ const POLL_INTERVAL_MS = 200;
22
+
23
+ function requestJson(requestPath, method = "GET") {
24
+ return new Promise((resolve) => {
25
+ const request = http.request(`${SERVICE_BASE_URL}${requestPath}`, { method }, (response) => {
26
+ let body = "";
27
+ response.setEncoding("utf8");
28
+ response.on("data", (chunk) => { body += chunk; });
29
+ response.on("end", () => {
30
+ let parsed = {};
31
+ try { parsed = JSON.parse(body || "{}"); } catch (_error) {}
32
+ resolve({
33
+ ok: response.statusCode >= 200 && response.statusCode < 300,
34
+ statusCode: response.statusCode || 0,
35
+ body: parsed
36
+ });
37
+ });
38
+ });
39
+ request.on("error", () => resolve({ ok: false, statusCode: 0, body: {} }));
40
+ request.setTimeout(800, () => {
41
+ request.destroy();
42
+ resolve({ ok: false, statusCode: 0, body: {} });
43
+ });
44
+ request.end();
45
+ });
46
+ }
47
+
48
+ function expectedDataDir() {
49
+ return path.resolve(process.env.GECHO_DATA_DIR || getDefaultDataDir());
50
+ }
51
+
52
+ function isCompatibleService(info) {
53
+ if (!info || info.status !== "ok") return false;
54
+ if (Number(info.serviceProtocolVersion || 0) !== SERVICE_PROTOCOL_VERSION) return false;
55
+ if (info.packageVersion && info.packageVersion !== packageJson.version) return false;
56
+ return !info.dataDir || path.resolve(String(info.dataDir)) === expectedDataDir();
57
+ }
58
+
59
+ async function getServiceInfo() {
60
+ const response = await requestJson("/ping");
61
+ return response.ok ? response.body : null;
62
+ }
63
+
64
+ async function waitForService({ running = true, timeoutMs = START_TIMEOUT_MS } = {}) {
65
+ const startedAt = Date.now();
66
+ while (Date.now() - startedAt <= timeoutMs) {
67
+ const info = await getServiceInfo();
68
+ if (running ? isCompatibleService(info) : !info) return info;
69
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
70
+ }
71
+ return null;
72
+ }
73
+
74
+ async function stopService() {
75
+ const info = await getServiceInfo();
76
+ if (!info) return { status: "not running" };
77
+
78
+ await requestJson("/shutdown", "POST");
79
+ const stopped = await waitForService({ running: false });
80
+ if (stopped === null && Number.isInteger(Number(info.pid)) && Number(info.pid) > 0) {
81
+ try { process.kill(Number(info.pid), "SIGTERM"); } catch (_error) {}
82
+ await waitForService({ running: false, timeoutMs: 3000 });
83
+ }
84
+ return { status: "stopped", pid: info.pid || null };
85
+ }
86
+
87
+ async function startService() {
88
+ const current = await getServiceInfo();
89
+ if (isCompatibleService(current)) {
90
+ return { status: "already running", pid: current.pid || null };
91
+ }
92
+ if (current) await stopService();
93
+
94
+ const child = spawn(process.execPath, [SERVICE_PATH], {
95
+ cwd: __dirname,
96
+ env: process.env,
97
+ detached: true,
98
+ stdio: "ignore",
99
+ windowsHide: true
100
+ });
101
+ child.unref();
102
+ const started = await waitForService();
103
+ if (!started) {
104
+ const error = new Error("Bridge service did not become ready");
105
+ error.code = "BRIDGE_SERVICE_START_FAILED";
106
+ throw error;
107
+ }
108
+ return { status: "started", pid: started.pid || child.pid || null };
109
+ }
110
+
111
+ async function printStatus() {
112
+ const info = await getServiceInfo();
113
+ if (info) {
114
+ process.stdout.write(`${JSON.stringify(info)}\n`);
115
+ return true;
116
+ }
117
+ process.stdout.write(`${JSON.stringify({ status: "not running", url: SERVICE_BASE_URL })}\n`);
118
+ return false;
119
+ }
120
+
121
+ function printUsage() {
122
+ process.stdout.write([
123
+ "Usage: gecho-bridge-server <start|status|stop|restart|install|uninstall|startup-status>",
124
+ "",
125
+ " start Start the standalone local Bridge service",
126
+ " status Print Bridge health and scheduler status",
127
+ " stop Stop the local Bridge service",
128
+ " restart Restart the local Bridge service",
129
+ " install Install a user-level login startup entry",
130
+ " uninstall Remove the user-level login startup entry",
131
+ " startup-status Print login startup configuration status",
132
+ ""
133
+ ].join("\n"));
134
+ }
135
+
136
+ async function main(argv = process.argv.slice(2)) {
137
+ const command = String(argv[0] || "start").trim().toLowerCase();
138
+ if (command === "status") {
139
+ const running = await printStatus();
140
+ process.exitCode = running ? 0 : 1;
141
+ return;
142
+ }
143
+ if (command === "start") {
144
+ process.stdout.write(`${JSON.stringify(await startService())}\n`);
145
+ return;
146
+ }
147
+ if (command === "stop") {
148
+ process.stdout.write(`${JSON.stringify(await stopService())}\n`);
149
+ return;
150
+ }
151
+ if (command === "restart") {
152
+ await stopService();
153
+ process.stdout.write(`${JSON.stringify(await startService())}\n`);
154
+ return;
155
+ }
156
+ if (command === "install") {
157
+ process.stdout.write(`${JSON.stringify(installStartup())}\n`);
158
+ return;
159
+ }
160
+ if (command === "uninstall") {
161
+ process.stdout.write(`${JSON.stringify(uninstallStartup())}\n`);
162
+ return;
163
+ }
164
+ if (command === "startup-status") {
165
+ const status = getStartupStatus();
166
+ process.stdout.write(`${JSON.stringify(status)}\n`);
167
+ process.exitCode = status.configured ? 0 : 1;
168
+ return;
169
+ }
170
+ printUsage();
171
+ process.exitCode = 2;
172
+ }
173
+
174
+ if (require.main === module) {
175
+ main().catch((error) => {
176
+ console.error(`gecho-bridge-server: ${error.message || error}`);
177
+ process.exitCode = 1;
178
+ });
179
+ }
180
+
181
+ module.exports = {
182
+ getServiceInfo,
183
+ isCompatibleService,
184
+ main,
185
+ startService,
186
+ stopService
187
+ };