yuanflow-cli 0.1.22 → 0.1.23
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 +25 -0
- package/package.json +1 -1
- package/skills/yuanflow-skill/README.md +4 -0
- package/skills/yuanflow-skill/SKILL.md +20 -5
- package/skills/yuanflow-skill//350/207/252/345/252/222/344/275/223/346/265/217/350/247/210/345/231/250/350/207/252/345/212/250/345/214/226/SKILL.md +241 -0
- package/src/agent-protocol.js +3 -0
- package/src/browser-tools.js +413 -0
- package/src/cli.js +19 -0
package/README.md
CHANGED
|
@@ -41,6 +41,8 @@ yuanflow-cli douyin user-posts <sec_user_id> --count 20 --cursor 0 --format agen
|
|
|
41
41
|
yuanflow-cli knowledge docs --format agent-json
|
|
42
42
|
yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
|
|
43
43
|
yuanflow-cli oss signed-url --key path/to/file.png --ttl-seconds 1800 --format agent-json
|
|
44
|
+
yuanflow-cli browser profile-path --platform douyin --account main --format agent-json
|
|
45
|
+
yuanflow-cli browser task-plan --platform xiaohongshu --task publish --account main --format agent-json
|
|
44
46
|
yuanflow-cli list douyin
|
|
45
47
|
```
|
|
46
48
|
|
|
@@ -153,6 +155,29 @@ yuanflow-cli oss copy --source-key temp/cover.png --target-key final/cover.png -
|
|
|
153
155
|
|
|
154
156
|
上传本地文件前必须确认用户授权,不能上传密钥、cookie、账号凭据或隐私文件。
|
|
155
157
|
|
|
158
|
+
### 自媒体浏览器自动化
|
|
159
|
+
|
|
160
|
+
`browser` 命令是自媒体平台专用浏览器自动化协议,用于账号登录态隔离、Cookie/profile 本地保存、账号页面采集、作品发布前检查和发布流程规划。它不用于普通网页搜索、通用浏览、截图或 CDP 连接。
|
|
161
|
+
|
|
162
|
+
当前命令只返回受控路径、任务计划和 dry-run 预检结果,不直接暴露裸 Playwright 或 Chrome 调试能力:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
yuanflow-cli browser profile-path --platform douyin --account main --format agent-json
|
|
166
|
+
yuanflow-cli browser session-init --platform xiaohongshu --account main --format agent-json
|
|
167
|
+
yuanflow-cli browser cookie-policy --platform douyin --account main --format agent-json
|
|
168
|
+
yuanflow-cli browser task-plan --platform douyin --task publish --account main --format agent-json
|
|
169
|
+
yuanflow-cli browser dry-run --platform xiaohongshu --task publish --account main --file ./post.mp4 --title "标题" --caption "正文" --cover ./cover.png --format agent-json
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
支持平台:`douyin`、`xiaohongshu`、`weibo`、`bilibili`、`tiktok`、`youtube`、`twitter`。
|
|
173
|
+
|
|
174
|
+
固定边界:
|
|
175
|
+
|
|
176
|
+
- Cookie、localStorage、sessionStorage 和 profile 只保存在本地受控目录,不输出、不上传。
|
|
177
|
+
- 扫码、验证码、人机验证、平台风控必须暂停并让用户手动处理。
|
|
178
|
+
- 最终发布、删除、修改账号资料等线上状态变更前必须获得用户确认。
|
|
179
|
+
- 普通网页浏览请使用 YuanFlow 通用浏览器能力,不使用本命令。
|
|
180
|
+
|
|
156
181
|
### 音视频在线转文字
|
|
157
182
|
|
|
158
183
|
`音视频在线转文字` 随 npm 包安装到 Skill bundle,用于通过 YuanFlow 在线 ASR 接口把音频或视频转成干净文本。视频输入需要先用 ffmpeg 分离音频,再上传 OSS。
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ YuanFlow Skill 是 `yuanflow-cli` 的 Agent Skill 仓库,用于把社媒平台
|
|
|
12
12
|
- `HTML报告生成/`:单页 HTML 报告生成 Skill,内置 9 种米色留白报告模板。
|
|
13
13
|
- `本地音视频转文字/`:本地 SenseVoice 音视频转文字 Skill,首次明确使用时按需下载模型。
|
|
14
14
|
- `音视频在线转文字/`:通过 YuanFlow 在线 ASR 接口把音频或视频转成干净文本,视频会先抽取音频并通过 OSS 中转。
|
|
15
|
+
- `自媒体浏览器自动化/`:自媒体平台专用浏览器自动化 Skill,用于登录态隔离、Cookie/profile 本地保存、页面采集和作品发布流程。
|
|
15
16
|
- `帐号监控/`:帐号搜索、主页资料、主页作品、历史快照、变化对比和 HTML 可视化分流 Skill。
|
|
16
17
|
- `个人创作库/`:长期保存、整理、复盘和复用用户认可的自媒体合格产出物。
|
|
17
18
|
- `IP运营/`:先查个人创作库历史记录,再结合自媒体知识库输出 IP 运营方案。
|
|
@@ -33,6 +34,7 @@ YuanFlow Skill 是 `yuanflow-cli` 的 Agent Skill 仓库,用于把社媒平台
|
|
|
33
34
|
- 把自媒体分析、数据复盘、文案方案、账号监控、知识梳理和执行计划生成可直接打开的单页 HTML 报告。
|
|
34
35
|
- 在用户明确要求本地转写时,把本地音频或视频转成文字;视频会先抽取音频,模型和缓存都保存在 Skill 自己目录下。
|
|
35
36
|
- 在用户要求在线转写、云端转写或 doubao-asr 时,把本地音频或视频经 OSS 中转后提交在线 ASR,默认只返回干净文本。
|
|
37
|
+
- 在用户要求自媒体平台浏览器自动化、账号登录态隔离、Cookie/profile 保存、平台页面采集或作品发布时,使用专用自媒体浏览器自动化 Skill;普通网页浏览和搜索仍使用通用浏览器能力。
|
|
36
38
|
- 监控抖音、小红书、微博、知乎、Bilibili、TikTok、YouTube、Twitter/X 帐号主页,保存快照并对比历史变化;抖音链路优先使用 `get-sec-user-id -> user-profile -> user-posts -> works detail`。
|
|
37
39
|
- 在用户要求保存、归档、复盘或复用已完成创作成果时,把选题、标题、文案、脚本、封面、剪辑思路、发布计划和数据复盘沉淀进个人创作库。
|
|
38
40
|
- 处理 IP 运营、直播话术、帐号定位、选题策划、文案创作、视频投流和直播投流等业务创作任务;这些 Skill 会先查个人创作库历史参考记录,再按自媒体知识库 `next_actions` 分层查询方法摘要。
|
|
@@ -108,6 +110,8 @@ yuanflow-skill list-skills
|
|
|
108
110
|
│ └─ scripts/
|
|
109
111
|
├─ 音视频在线转文字
|
|
110
112
|
│ └─ SKILL.md
|
|
113
|
+
├─ 自媒体浏览器自动化
|
|
114
|
+
│ └─ SKILL.md
|
|
111
115
|
├─ 个人创作库
|
|
112
116
|
│ ├─ SKILL.md
|
|
113
117
|
│ ├─ raw/
|
|
@@ -24,6 +24,7 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
24
24
|
- `HTML报告生成/`
|
|
25
25
|
- `本地音视频转文字/`
|
|
26
26
|
- `音视频在线转文字/`
|
|
27
|
+
- `自媒体浏览器自动化/`
|
|
27
28
|
- `帐号监控/`
|
|
28
29
|
- `个人创作库/`
|
|
29
30
|
- `IP运营/`
|
|
@@ -180,7 +181,21 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
180
181
|
|
|
181
182
|
- `OSS文件中转工具`
|
|
182
183
|
|
|
183
|
-
### 11. 走
|
|
184
|
+
### 11. 走 `自媒体浏览器自动化`
|
|
185
|
+
|
|
186
|
+
遇到下面这些需求,优先进入这个子 Skill:
|
|
187
|
+
|
|
188
|
+
- 自媒体平台浏览器自动化、账号登录态隔离、Cookie/profile 本地保存。
|
|
189
|
+
- 抖音、小红书、微博、Bilibili、TikTok、YouTube、Twitter/X 的页面浏览采集、账号资料快照、作品发布、草稿保存或发布前检查。
|
|
190
|
+
- 用户明确要求用浏览器完成自媒体平台登录、采集或发布。
|
|
191
|
+
|
|
192
|
+
不要把它用于普通网页搜索、通用浏览、截图或 CDP 连接;这些仍交给 YuanFlow 原有通用浏览器能力。
|
|
193
|
+
|
|
194
|
+
子 Skill 名称:
|
|
195
|
+
|
|
196
|
+
- `自媒体浏览器自动化`
|
|
197
|
+
|
|
198
|
+
### 12. 走 `生图技能`
|
|
184
199
|
|
|
185
200
|
遇到下面这些需求,优先进入这个子 Skill:
|
|
186
201
|
|
|
@@ -192,7 +207,7 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
192
207
|
|
|
193
208
|
- `生图技能`
|
|
194
209
|
|
|
195
|
-
###
|
|
210
|
+
### 13. 走 `HTML报告生成`
|
|
196
211
|
|
|
197
212
|
遇到下面这些需求,优先进入这个子 Skill:
|
|
198
213
|
|
|
@@ -204,7 +219,7 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
204
219
|
|
|
205
220
|
- `HTML报告生成`
|
|
206
221
|
|
|
207
|
-
###
|
|
222
|
+
### 14. 走 `个人创作库`
|
|
208
223
|
|
|
209
224
|
遇到下面这些需求,优先进入这个子 Skill:
|
|
210
225
|
|
|
@@ -218,7 +233,7 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
218
233
|
|
|
219
234
|
- `个人创作库`
|
|
220
235
|
|
|
221
|
-
###
|
|
236
|
+
### 15. 走 `本地音视频转文字`
|
|
222
237
|
|
|
223
238
|
只有当用户明确要求使用本地音视频转文字、本地转写、本地 ASR、离线转写或本地模型把音频/视频转成文字时,才进入这个子 Skill。
|
|
224
239
|
|
|
@@ -228,7 +243,7 @@ description: Use when the user asks about social-media API workflows, platform d
|
|
|
228
243
|
|
|
229
244
|
- `本地音视频转文字`
|
|
230
245
|
|
|
231
|
-
###
|
|
246
|
+
### 16. 走 `音视频在线转文字`
|
|
232
247
|
|
|
233
248
|
遇到下面这些需求,优先进入这个子 Skill:
|
|
234
249
|
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 自媒体浏览器自动化
|
|
3
|
+
description: Use when the user asks to automate self-media platform browser work such as Douyin, Xiaohongshu, Weibo, Bilibili, TikTok, YouTube, or Twitter/X account login-state management, Cookie/profile storage, profile/page collection, creator account data browsing, draft creation, or publishing works. Do not use for ordinary web browsing, general search, or screenshots.
|
|
4
|
+
tags:
|
|
5
|
+
- browser-automation
|
|
6
|
+
- self-media
|
|
7
|
+
- profile
|
|
8
|
+
- cookie
|
|
9
|
+
emoji: 🧭
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# 自媒体浏览器自动化
|
|
13
|
+
|
|
14
|
+
本 Skill 只用于自媒体平台的专用浏览器自动化:账号登录态隔离、Cookie/profile 本地保存、账号资料浏览采集、作品页面收集、草稿创建、作品发布前检查和发布执行。
|
|
15
|
+
|
|
16
|
+
不要把它和 YuanFlow 已有的通用浏览器能力混在一起:
|
|
17
|
+
|
|
18
|
+
- `浏览器启动模式`:通用网页浏览、打开页面、可见窗口、普通搜索。
|
|
19
|
+
- `浏览器CDP`:连接已有 Chrome、扫描 CDP 端口、共享浏览器实例。
|
|
20
|
+
- `自媒体浏览器自动化`:面向抖音、小红书、微博、Bilibili、TikTok、YouTube、Twitter/X 等平台的账号和发布流程。
|
|
21
|
+
|
|
22
|
+
普通网页搜索、资料查询、截图、打开页面,不使用本 Skill。只有任务涉及自媒体平台账号、登录态、平台页面采集、Cookie/profile、作品发布或草稿保存时,才使用本 Skill。
|
|
23
|
+
|
|
24
|
+
## 调用优先级
|
|
25
|
+
|
|
26
|
+
在 YuanFlow 主程序内,优先调用受控工具:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"action": "task-plan",
|
|
31
|
+
"platform": "douyin",
|
|
32
|
+
"task": "publish",
|
|
33
|
+
"account": "main"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
如果当前 Agent 没有 `yuanflow_browser_automation`,再使用 `yuanflow_cli_call` 调用托管 CLI:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"args": [
|
|
42
|
+
"browser",
|
|
43
|
+
"task-plan",
|
|
44
|
+
"--platform",
|
|
45
|
+
"douyin",
|
|
46
|
+
"--task",
|
|
47
|
+
"publish",
|
|
48
|
+
"--account",
|
|
49
|
+
"main",
|
|
50
|
+
"--format",
|
|
51
|
+
"agent-json"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
外部命令:
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
yuanflow-cli browser task-plan --platform douyin --task publish --account main --format agent-json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 固定边界
|
|
63
|
+
|
|
64
|
+
- Cookie、localStorage、sessionStorage、浏览器 profile 只保存在本地受控目录,不上传、不输出、不写入报告。
|
|
65
|
+
- 扫码、验证码、人机验证、二次验证、风控提醒,必须暂停并让用户手动处理。
|
|
66
|
+
- 发布、删除、修改账号资料、提交草稿、定时发布等会改变线上状态的动作,必须先向用户复述并等待确认。
|
|
67
|
+
- 不做绕过验证码、绕过风控、刷量、批量骚扰、未授权采集或平台禁止的自动化。
|
|
68
|
+
- 不直接调用 shell 执行裸 Playwright、Chrome 调试命令或读取浏览器敏感目录;优先走受控工具。
|
|
69
|
+
|
|
70
|
+
## 支持平台
|
|
71
|
+
|
|
72
|
+
当前受控协议支持:
|
|
73
|
+
|
|
74
|
+
| 平台参数 | 平台 |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `douyin` | 抖音 |
|
|
77
|
+
| `xiaohongshu` | 小红书 |
|
|
78
|
+
| `weibo` | 微博 |
|
|
79
|
+
| `bilibili` | Bilibili |
|
|
80
|
+
| `tiktok` | TikTok |
|
|
81
|
+
| `youtube` | YouTube |
|
|
82
|
+
| `twitter` | Twitter/X |
|
|
83
|
+
|
|
84
|
+
## 本地目录
|
|
85
|
+
|
|
86
|
+
默认本地根目录:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
%APPDATA%\YuanFlow\runtime_tools\browser-automation
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
单账号目录:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
%APPDATA%\YuanFlow\runtime_tools\browser-automation\accounts\<platform>\<account>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
关键子目录:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
profile 浏览器 profile
|
|
102
|
+
cookies Cookie 状态文件目录,仅保存本地,不输出值
|
|
103
|
+
tasks 任务预检、草稿、发布记录
|
|
104
|
+
snapshots 页面采集和帐号快照
|
|
105
|
+
downloads 浏览器下载文件
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 常用命令
|
|
109
|
+
|
|
110
|
+
查看 profile 和 Cookie 路径:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"args": [
|
|
115
|
+
"browser",
|
|
116
|
+
"profile-path",
|
|
117
|
+
"--platform",
|
|
118
|
+
"douyin",
|
|
119
|
+
"--account",
|
|
120
|
+
"main",
|
|
121
|
+
"--format",
|
|
122
|
+
"agent-json"
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
初始化账号目录:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"args": [
|
|
132
|
+
"browser",
|
|
133
|
+
"session-init",
|
|
134
|
+
"--platform",
|
|
135
|
+
"xiaohongshu",
|
|
136
|
+
"--account",
|
|
137
|
+
"main",
|
|
138
|
+
"--format",
|
|
139
|
+
"agent-json"
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
查看 Cookie/profile 规则:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"args": [
|
|
149
|
+
"browser",
|
|
150
|
+
"cookie-policy",
|
|
151
|
+
"--platform",
|
|
152
|
+
"douyin",
|
|
153
|
+
"--account",
|
|
154
|
+
"main",
|
|
155
|
+
"--format",
|
|
156
|
+
"agent-json"
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
生成发布任务计划:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"args": [
|
|
166
|
+
"browser",
|
|
167
|
+
"task-plan",
|
|
168
|
+
"--platform",
|
|
169
|
+
"douyin",
|
|
170
|
+
"--task",
|
|
171
|
+
"publish",
|
|
172
|
+
"--account",
|
|
173
|
+
"main",
|
|
174
|
+
"--format",
|
|
175
|
+
"agent-json"
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
预检发布参数:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"args": [
|
|
185
|
+
"browser",
|
|
186
|
+
"dry-run",
|
|
187
|
+
"--platform",
|
|
188
|
+
"xiaohongshu",
|
|
189
|
+
"--task",
|
|
190
|
+
"publish",
|
|
191
|
+
"--account",
|
|
192
|
+
"main",
|
|
193
|
+
"--file",
|
|
194
|
+
"D:\\media\\post.mp4",
|
|
195
|
+
"--title",
|
|
196
|
+
"标题",
|
|
197
|
+
"--caption",
|
|
198
|
+
"正文",
|
|
199
|
+
"--cover",
|
|
200
|
+
"D:\\media\\cover.png",
|
|
201
|
+
"--format",
|
|
202
|
+
"agent-json"
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## 任务流程
|
|
208
|
+
|
|
209
|
+
### 登录或保存登录态
|
|
210
|
+
|
|
211
|
+
1. 先执行 `profile-path` 或 `session-init` 获取账号目录。
|
|
212
|
+
2. 使用该账号专属 profile 打开平台登录页。
|
|
213
|
+
3. 遇到扫码、验证码或风控,暂停并让用户手动处理。
|
|
214
|
+
4. 登录完成后只报告“已保存本地登录态”和 profile 路径,不输出 Cookie 内容。
|
|
215
|
+
|
|
216
|
+
### 账号资料或页面采集
|
|
217
|
+
|
|
218
|
+
1. 确认平台、帐号别名、采集目标 URL 或账号标识。
|
|
219
|
+
2. 使用 `task-plan --task collect` 或 `task-plan --task account_snapshot` 获取计划。
|
|
220
|
+
3. 只采集公开可见页面信息、页面文本、作品列表和必要截图。
|
|
221
|
+
4. 保存到 `snapshots` 目录,后续可与历史快照对比。
|
|
222
|
+
|
|
223
|
+
### 作品发布
|
|
224
|
+
|
|
225
|
+
1. 发布前必须确认平台、帐号、素材、标题、正文、封面、话题、发布时间。
|
|
226
|
+
2. 先执行 `dry-run --task publish` 检查缺失项。
|
|
227
|
+
3. 打开发布入口并填写内容。
|
|
228
|
+
4. 到最终发布按钮前暂停,复述即将发布的内容。
|
|
229
|
+
5. 用户确认后再点击最终发布或保存草稿。
|
|
230
|
+
6. 记录发布结果、作品链接或失败原因到 `tasks` 目录。
|
|
231
|
+
|
|
232
|
+
## 输出要求
|
|
233
|
+
|
|
234
|
+
最终回复要简短说明:
|
|
235
|
+
|
|
236
|
+
- 使用的平台和帐号别名。
|
|
237
|
+
- 本次任务类型。
|
|
238
|
+
- profile、任务记录或快照保存路径。
|
|
239
|
+
- 是否需要用户手动处理登录、验证码、风控或最终发布确认。
|
|
240
|
+
|
|
241
|
+
失败时说明卡在哪一步、调用了什么受控命令、返回的 `error.code` 和 `error.message`。不要泄露 token、Authorization header、Cookie、localStorage 或 sessionStorage。
|
package/src/agent-protocol.js
CHANGED
|
@@ -3,6 +3,7 @@ import { listShortcuts } from './shortcuts.js';
|
|
|
3
3
|
import { listCommentCommands } from './comment-collector.js';
|
|
4
4
|
import { listKnowledgeCommands } from './knowledge-tools.js';
|
|
5
5
|
import { listOssCommands } from './oss-tools.js';
|
|
6
|
+
import { listBrowserCommands } from './browser-tools.js';
|
|
6
7
|
import { listSearchCommands, listWorkCommands } from './work-tools.js';
|
|
7
8
|
|
|
8
9
|
const ERROR_MAP = [
|
|
@@ -105,6 +106,7 @@ export function buildCommandRegistry() {
|
|
|
105
106
|
const searchCommands = listSearchCommands();
|
|
106
107
|
const knowledgeCommands = listKnowledgeCommands();
|
|
107
108
|
const ossCommands = listOssCommands();
|
|
109
|
+
const browserCommands = listBrowserCommands();
|
|
108
110
|
return [
|
|
109
111
|
...shortcuts,
|
|
110
112
|
...endpoints,
|
|
@@ -113,6 +115,7 @@ export function buildCommandRegistry() {
|
|
|
113
115
|
...searchCommands,
|
|
114
116
|
...knowledgeCommands,
|
|
115
117
|
...ossCommands,
|
|
118
|
+
...browserCommands,
|
|
116
119
|
].sort((left, right) => left.key.localeCompare(right.key));
|
|
117
120
|
}
|
|
118
121
|
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const SUPPORTED_PLATFORMS = {
|
|
6
|
+
douyin: {
|
|
7
|
+
label: '抖音',
|
|
8
|
+
domains: ['douyin.com'],
|
|
9
|
+
publishChecklist: ['视频文件', '标题/文案', '封面', '话题', '发布时间', '目标帐号'],
|
|
10
|
+
},
|
|
11
|
+
xiaohongshu: {
|
|
12
|
+
label: '小红书',
|
|
13
|
+
domains: ['xiaohongshu.com'],
|
|
14
|
+
publishChecklist: ['图片或视频', '标题', '正文', '话题', '发布时间', '目标帐号'],
|
|
15
|
+
},
|
|
16
|
+
weibo: {
|
|
17
|
+
label: '微博',
|
|
18
|
+
domains: ['weibo.com'],
|
|
19
|
+
publishChecklist: ['正文', '图片或视频', '话题', '发布时间', '目标帐号'],
|
|
20
|
+
},
|
|
21
|
+
bilibili: {
|
|
22
|
+
label: 'Bilibili',
|
|
23
|
+
domains: ['bilibili.com'],
|
|
24
|
+
publishChecklist: ['视频文件', '标题', '简介', '分区', '封面', '标签', '发布时间', '目标帐号'],
|
|
25
|
+
},
|
|
26
|
+
tiktok: {
|
|
27
|
+
label: 'TikTok',
|
|
28
|
+
domains: ['tiktok.com'],
|
|
29
|
+
publishChecklist: ['视频文件', 'caption', 'hashtags', 'schedule', 'target account'],
|
|
30
|
+
},
|
|
31
|
+
youtube: {
|
|
32
|
+
label: 'YouTube',
|
|
33
|
+
domains: ['youtube.com'],
|
|
34
|
+
publishChecklist: ['视频文件', '标题', '简介', '缩略图', '标签', '可见性', '发布时间', '目标频道'],
|
|
35
|
+
},
|
|
36
|
+
twitter: {
|
|
37
|
+
label: 'Twitter/X',
|
|
38
|
+
domains: ['x.com', 'twitter.com'],
|
|
39
|
+
publishChecklist: ['正文', '图片或视频', '话题', '发布时间', '目标帐号'],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const PLATFORM_ALIASES = {
|
|
44
|
+
抖音: 'douyin',
|
|
45
|
+
小红书: 'xiaohongshu',
|
|
46
|
+
xhs: 'xiaohongshu',
|
|
47
|
+
微博: 'weibo',
|
|
48
|
+
b站: 'bilibili',
|
|
49
|
+
哔哩哔哩: 'bilibili',
|
|
50
|
+
youtube: 'youtube',
|
|
51
|
+
油管: 'youtube',
|
|
52
|
+
x: 'twitter',
|
|
53
|
+
twitter: 'twitter',
|
|
54
|
+
推特: 'twitter',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const TASK_ALIASES = {
|
|
58
|
+
publish: 'publish',
|
|
59
|
+
发布: 'publish',
|
|
60
|
+
collect: 'collect',
|
|
61
|
+
采集: 'collect',
|
|
62
|
+
login: 'login',
|
|
63
|
+
登录: 'login',
|
|
64
|
+
account_snapshot: 'account_snapshot',
|
|
65
|
+
snapshot: 'account_snapshot',
|
|
66
|
+
帐号快照: 'account_snapshot',
|
|
67
|
+
账号快照: 'account_snapshot',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function listBrowserCommands() {
|
|
71
|
+
const commonOptions = [
|
|
72
|
+
option('--platform', 'platform', true, '平台:douyin、xiaohongshu、weibo、bilibili、tiktok、youtube、twitter。'),
|
|
73
|
+
option('--account', 'account', false, '帐号别名,默认 main。只用于本地 profile/cookie 分区。'),
|
|
74
|
+
option('--format', 'format', false, 'Agent 调用建议传 agent-json。'),
|
|
75
|
+
];
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
key: 'browser.profile-path',
|
|
79
|
+
command: 'browser profile-path',
|
|
80
|
+
kind: 'browser-automation',
|
|
81
|
+
description: '返回自媒体平台专用浏览器 profile、cookie、任务缓存路径。',
|
|
82
|
+
method: 'LOCAL',
|
|
83
|
+
positionals: [],
|
|
84
|
+
options: commonOptions,
|
|
85
|
+
returns: '返回受控本地目录,不读取或输出 cookie 内容。',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
key: 'browser.session-init',
|
|
89
|
+
command: 'browser session-init',
|
|
90
|
+
kind: 'browser-automation',
|
|
91
|
+
description: '初始化自媒体平台专用浏览器帐号目录和本地元数据。',
|
|
92
|
+
method: 'LOCAL',
|
|
93
|
+
positionals: [],
|
|
94
|
+
options: commonOptions,
|
|
95
|
+
returns: '创建 profile、cookies、tasks、snapshots 目录,并返回路径。',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: 'browser.cookie-policy',
|
|
99
|
+
command: 'browser cookie-policy',
|
|
100
|
+
kind: 'browser-automation',
|
|
101
|
+
description: '返回自媒体平台 Cookie/profile 保存规则和敏感信息处理边界。',
|
|
102
|
+
method: 'LOCAL',
|
|
103
|
+
positionals: [],
|
|
104
|
+
options: commonOptions,
|
|
105
|
+
returns: '返回 Cookie 保存路径和隐私边界说明,不输出 Cookie 值。',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: 'browser.task-plan',
|
|
109
|
+
command: 'browser task-plan',
|
|
110
|
+
kind: 'browser-automation',
|
|
111
|
+
description: '生成自媒体浏览器采集、帐号快照、登录或作品发布的受控执行计划。',
|
|
112
|
+
method: 'LOCAL',
|
|
113
|
+
positionals: [],
|
|
114
|
+
options: [
|
|
115
|
+
...commonOptions,
|
|
116
|
+
option('--task', 'task', true, '任务:publish、collect、login、account_snapshot。'),
|
|
117
|
+
option('--target', 'target', false, '主页、作品或发布入口 URL。'),
|
|
118
|
+
],
|
|
119
|
+
returns: '返回步骤、用户确认项、风控暂停点和可保存数据清单。',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: 'browser.dry-run',
|
|
123
|
+
command: 'browser dry-run',
|
|
124
|
+
kind: 'browser-automation',
|
|
125
|
+
description: '预检一次自媒体浏览器自动化任务,不启动浏览器、不提交发布。',
|
|
126
|
+
method: 'LOCAL',
|
|
127
|
+
positionals: [],
|
|
128
|
+
options: [
|
|
129
|
+
...commonOptions,
|
|
130
|
+
option('--task', 'task', true, '任务:publish、collect、login、account_snapshot。'),
|
|
131
|
+
option('--target', 'target', false, '主页、作品或发布入口 URL。'),
|
|
132
|
+
option('--file', 'file', false, '待发布素材路径,仅用于预检记录,不读取文件内容。'),
|
|
133
|
+
option('--title', 'title', false, '待发布标题。'),
|
|
134
|
+
option('--caption', 'caption', false, '待发布正文或 caption。'),
|
|
135
|
+
option('--cover', 'cover', false, '封面路径。'),
|
|
136
|
+
],
|
|
137
|
+
returns: '返回预检结果和缺失确认项。',
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function runBrowserCommand({ action, options }) {
|
|
143
|
+
const normalizedAction = normalizeAction(action);
|
|
144
|
+
if (normalizedAction === 'profile-path') {
|
|
145
|
+
return buildProfilePayload(options, { create: false });
|
|
146
|
+
}
|
|
147
|
+
if (normalizedAction === 'session-init') {
|
|
148
|
+
return buildSessionInitPayload(options);
|
|
149
|
+
}
|
|
150
|
+
if (normalizedAction === 'cookie-policy') {
|
|
151
|
+
return buildCookiePolicyPayload(options);
|
|
152
|
+
}
|
|
153
|
+
if (normalizedAction === 'task-plan') {
|
|
154
|
+
return buildTaskPlanPayload(options);
|
|
155
|
+
}
|
|
156
|
+
if (normalizedAction === 'dry-run') {
|
|
157
|
+
return buildDryRunPayload(options);
|
|
158
|
+
}
|
|
159
|
+
throw new Error('未知 browser 命令。用法:yuanflow-cli browser profile-path|session-init|cookie-policy|task-plan|dry-run');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function option(flag, name, required, label) {
|
|
163
|
+
return { flag, name, required, label };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeAction(action) {
|
|
167
|
+
return String(action || 'profile-path').trim().toLowerCase();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function normalizePlatform(value) {
|
|
171
|
+
const raw = String(value || '').trim();
|
|
172
|
+
const lowered = raw.toLowerCase();
|
|
173
|
+
const platform = PLATFORM_ALIASES[raw] || PLATFORM_ALIASES[lowered] || lowered;
|
|
174
|
+
if (!platform) {
|
|
175
|
+
throw new Error('缺少 --platform,例如 douyin、xiaohongshu、bilibili。');
|
|
176
|
+
}
|
|
177
|
+
if (!SUPPORTED_PLATFORMS[platform]) {
|
|
178
|
+
throw new Error(`不支持的平台:${raw}。当前支持:${Object.keys(SUPPORTED_PLATFORMS).join(', ')}`);
|
|
179
|
+
}
|
|
180
|
+
return platform;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeTask(value) {
|
|
184
|
+
const raw = String(value || '').trim();
|
|
185
|
+
const lowered = raw.toLowerCase();
|
|
186
|
+
const task = TASK_ALIASES[raw] || TASK_ALIASES[lowered] || lowered;
|
|
187
|
+
if (!task) {
|
|
188
|
+
throw new Error('缺少 --task,例如 publish、collect、login、account_snapshot。');
|
|
189
|
+
}
|
|
190
|
+
if (!['publish', 'collect', 'login', 'account_snapshot'].includes(task)) {
|
|
191
|
+
throw new Error(`不支持的 browser task:${raw}。`);
|
|
192
|
+
}
|
|
193
|
+
return task;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function safeSegment(value, fallback) {
|
|
197
|
+
const text = String(value || fallback || 'default').trim();
|
|
198
|
+
return text
|
|
199
|
+
.replace(/[<>:"/\\|?*\x00-\x1f]/g, '-')
|
|
200
|
+
.replace(/\s+/g, '-')
|
|
201
|
+
.slice(0, 80) || fallback;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function automationRoot() {
|
|
205
|
+
const configured = process.env.YUANFLOW_BROWSER_AUTOMATION_HOME;
|
|
206
|
+
if (configured) {
|
|
207
|
+
return path.resolve(configured);
|
|
208
|
+
}
|
|
209
|
+
const appData = process.env.APPDATA;
|
|
210
|
+
if (appData) {
|
|
211
|
+
return path.join(appData, 'YuanFlow', 'runtime_tools', 'browser-automation');
|
|
212
|
+
}
|
|
213
|
+
return path.join(os.homedir(), '.yuanflow', 'runtime_tools', 'browser-automation');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function buildPaths(options) {
|
|
217
|
+
const platform = normalizePlatform(options.named?.platform);
|
|
218
|
+
const account = safeSegment(options.named?.account || 'main', 'main');
|
|
219
|
+
const root = automationRoot();
|
|
220
|
+
const accountRoot = path.join(root, 'accounts', platform, account);
|
|
221
|
+
return {
|
|
222
|
+
platform,
|
|
223
|
+
platformLabel: SUPPORTED_PLATFORMS[platform].label,
|
|
224
|
+
account,
|
|
225
|
+
root,
|
|
226
|
+
accountRoot,
|
|
227
|
+
profileDir: path.join(accountRoot, 'profile'),
|
|
228
|
+
cookieFile: path.join(accountRoot, 'cookies', 'cookies.json'),
|
|
229
|
+
accountMetaFile: path.join(accountRoot, 'account.json'),
|
|
230
|
+
taskDir: path.join(accountRoot, 'tasks'),
|
|
231
|
+
snapshotDir: path.join(accountRoot, 'snapshots'),
|
|
232
|
+
downloadDir: path.join(accountRoot, 'downloads'),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function ensurePathSet(paths) {
|
|
237
|
+
for (const item of [
|
|
238
|
+
paths.profileDir,
|
|
239
|
+
path.dirname(paths.cookieFile),
|
|
240
|
+
paths.taskDir,
|
|
241
|
+
paths.snapshotDir,
|
|
242
|
+
paths.downloadDir,
|
|
243
|
+
]) {
|
|
244
|
+
mkdirSync(item, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function buildProfilePayload(options, { create }) {
|
|
249
|
+
const paths = buildPaths(options);
|
|
250
|
+
if (create && !options.dryRun) {
|
|
251
|
+
ensurePathSet(paths);
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
type: 'self-media-browser-profile',
|
|
255
|
+
created: Boolean(create && !options.dryRun),
|
|
256
|
+
dryRun: Boolean(options.dryRun),
|
|
257
|
+
platform: paths.platform,
|
|
258
|
+
platform_label: paths.platformLabel,
|
|
259
|
+
account: paths.account,
|
|
260
|
+
paths,
|
|
261
|
+
boundaries: browserBoundaries(),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildSessionInitPayload(options) {
|
|
266
|
+
const payload = buildProfilePayload(options, { create: true });
|
|
267
|
+
if (!options.dryRun) {
|
|
268
|
+
writeFileSync(
|
|
269
|
+
payload.paths.accountMetaFile,
|
|
270
|
+
`${JSON.stringify(
|
|
271
|
+
{
|
|
272
|
+
platform: payload.platform,
|
|
273
|
+
platform_label: payload.platform_label,
|
|
274
|
+
account: payload.account,
|
|
275
|
+
updated_at: new Date().toISOString(),
|
|
276
|
+
note: 'YuanFlow self-media browser automation account metadata. Cookie values are not written here.',
|
|
277
|
+
},
|
|
278
|
+
null,
|
|
279
|
+
2,
|
|
280
|
+
)}\n`,
|
|
281
|
+
'utf8',
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return payload;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function buildCookiePolicyPayload(options) {
|
|
288
|
+
const paths = buildPaths(options);
|
|
289
|
+
return {
|
|
290
|
+
type: 'self-media-cookie-policy',
|
|
291
|
+
platform: paths.platform,
|
|
292
|
+
platform_label: paths.platformLabel,
|
|
293
|
+
account: paths.account,
|
|
294
|
+
paths,
|
|
295
|
+
policy: [
|
|
296
|
+
'Cookie 和登录态只保存在本地受控 profile/cookies 目录。',
|
|
297
|
+
'不要在最终回复、日志、HTML 报告、截图说明或任务文件里输出 Cookie 值。',
|
|
298
|
+
'扫码、验证码、人机验证、风控二次确认必须暂停并让用户手动处理。',
|
|
299
|
+
'除非用户明确要求,不要上传 Cookie、localStorage、sessionStorage 或浏览器 profile。',
|
|
300
|
+
'普通网页浏览、搜索和截图不要使用本能力,应使用通用浏览器能力。',
|
|
301
|
+
],
|
|
302
|
+
boundaries: browserBoundaries(),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildTaskPlanPayload(options) {
|
|
307
|
+
const paths = buildPaths(options);
|
|
308
|
+
const task = normalizeTask(options.named?.task);
|
|
309
|
+
const target = String(options.named?.target || '').trim();
|
|
310
|
+
return {
|
|
311
|
+
type: 'self-media-browser-task-plan',
|
|
312
|
+
platform: paths.platform,
|
|
313
|
+
platform_label: paths.platformLabel,
|
|
314
|
+
account: paths.account,
|
|
315
|
+
task,
|
|
316
|
+
target,
|
|
317
|
+
paths,
|
|
318
|
+
steps: buildTaskSteps({ platform: paths.platform, task, target }),
|
|
319
|
+
user_confirmation_required: confirmationItems(paths.platform, task),
|
|
320
|
+
pause_points: [
|
|
321
|
+
'登录二维码',
|
|
322
|
+
'短信/邮箱/设备二次验证',
|
|
323
|
+
'验证码或滑块',
|
|
324
|
+
'平台风控提醒',
|
|
325
|
+
'发布前最终确认按钮',
|
|
326
|
+
'任何可能修改线上帐号状态的动作',
|
|
327
|
+
],
|
|
328
|
+
boundaries: browserBoundaries(),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildDryRunPayload(options) {
|
|
333
|
+
const plan = buildTaskPlanPayload(options);
|
|
334
|
+
const provided = {
|
|
335
|
+
file: options.named?.file || '',
|
|
336
|
+
title: options.named?.title || '',
|
|
337
|
+
caption: options.named?.caption || '',
|
|
338
|
+
cover: options.named?.cover || '',
|
|
339
|
+
target: options.named?.target || '',
|
|
340
|
+
};
|
|
341
|
+
const missing = plan.user_confirmation_required.filter((item) => {
|
|
342
|
+
if (item.includes('素材')) return !provided.file;
|
|
343
|
+
if (item.includes('标题')) return !provided.title;
|
|
344
|
+
if (item.includes('正文') || item.includes('文案') || item.includes('caption')) return !provided.caption;
|
|
345
|
+
if (item.includes('封面')) return !provided.cover;
|
|
346
|
+
return false;
|
|
347
|
+
});
|
|
348
|
+
return {
|
|
349
|
+
...plan,
|
|
350
|
+
dryRun: true,
|
|
351
|
+
provided,
|
|
352
|
+
missing_confirmation_items: missing,
|
|
353
|
+
ready_for_execution: missing.length === 0,
|
|
354
|
+
note: 'dry-run 只做参数和流程预检,不启动浏览器、不读取素材、不提交发布。',
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function buildTaskSteps({ platform, task, target }) {
|
|
359
|
+
const platformInfo = SUPPORTED_PLATFORMS[platform];
|
|
360
|
+
if (task === 'login') {
|
|
361
|
+
return [
|
|
362
|
+
`使用 ${platformInfo.label} 专用 profile 打开平台登录页。`,
|
|
363
|
+
'如出现扫码、验证码或二次验证,暂停并等待用户手动完成。',
|
|
364
|
+
'登录成功后只保存本地 profile/cookie 状态,不输出敏感字段。',
|
|
365
|
+
'记录帐号别名、平台和最后验证时间。',
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
if (task === 'collect' || task === 'account_snapshot') {
|
|
369
|
+
return [
|
|
370
|
+
`使用 ${platformInfo.label} 专用 profile 打开目标页面${target ? `:${target}` : ''}。`,
|
|
371
|
+
'采集公开可见的帐号资料、页面文本、作品列表和页面状态。',
|
|
372
|
+
'需要滚动或翻页时按平台页面状态逐步执行,避免高频请求。',
|
|
373
|
+
'保存本地快照,后续用于变化对比。',
|
|
374
|
+
];
|
|
375
|
+
}
|
|
376
|
+
return [
|
|
377
|
+
`使用 ${platformInfo.label} 专用 profile 打开创作/发布入口。`,
|
|
378
|
+
`发布前核对:${platformInfo.publishChecklist.join('、')}。`,
|
|
379
|
+
'填入素材、标题、正文、封面、话题和定时发布设置。',
|
|
380
|
+
'到达最终发布按钮前暂停,向用户复述发布内容并等待确认。',
|
|
381
|
+
'用户确认后再点击最终发布或保存草稿。',
|
|
382
|
+
'记录发布结果、作品链接或失败原因。',
|
|
383
|
+
];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function confirmationItems(platform, task) {
|
|
387
|
+
if (task === 'publish') {
|
|
388
|
+
return SUPPORTED_PLATFORMS[platform].publishChecklist;
|
|
389
|
+
}
|
|
390
|
+
if (task === 'login') {
|
|
391
|
+
return ['平台', '帐号别名', '是否允许本地保存登录态'];
|
|
392
|
+
}
|
|
393
|
+
return ['平台', '帐号别名', '采集目标 URL 或帐号标识', '采集范围'];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function browserBoundaries() {
|
|
397
|
+
return {
|
|
398
|
+
use_for: [
|
|
399
|
+
'自媒体平台登录态隔离',
|
|
400
|
+
'账号资料和作品页面采集',
|
|
401
|
+
'账号信息快照保存',
|
|
402
|
+
'作品发布、草稿保存和发布前检查',
|
|
403
|
+
],
|
|
404
|
+
not_for: [
|
|
405
|
+
'普通网页搜索',
|
|
406
|
+
'通用网页浏览或截图',
|
|
407
|
+
'绕过验证码、人机验证、平台风控',
|
|
408
|
+
'未授权抓取、批量骚扰、刷量或违规自动化',
|
|
409
|
+
],
|
|
410
|
+
preferred_runtime_tool: 'yuanflow_browser_automation',
|
|
411
|
+
fallback_tool: 'yuanflow_cli_call browser ...',
|
|
412
|
+
};
|
|
413
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { findShortcut, listShortcuts } from './shortcuts.js';
|
|
|
19
19
|
import { collectComments } from './comment-collector.js';
|
|
20
20
|
import { getKnowledgeDocs, navigateKnowledge } from './knowledge-tools.js';
|
|
21
21
|
import { copyObject, signedUrl, tempUpload } from './oss-tools.js';
|
|
22
|
+
import { runBrowserCommand } from './browser-tools.js';
|
|
22
23
|
import {
|
|
23
24
|
getWorkDetail,
|
|
24
25
|
getWorkDownload,
|
|
@@ -80,6 +81,11 @@ export async function main(argv) {
|
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
if (command === 'browser') {
|
|
85
|
+
await handleBrowser(rest);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
if (command === 'schema') {
|
|
84
90
|
await handleSchema(rest);
|
|
85
91
|
return;
|
|
@@ -380,6 +386,16 @@ async function handleOss(args) {
|
|
|
380
386
|
});
|
|
381
387
|
}
|
|
382
388
|
|
|
389
|
+
async function handleBrowser(args) {
|
|
390
|
+
const { positionals, options } = parseOptions(args);
|
|
391
|
+
const [action = 'profile-path'] = positionals;
|
|
392
|
+
const result = runBrowserCommand({ action, options });
|
|
393
|
+
await outputResult(result, options, {
|
|
394
|
+
command: `browser ${action}`,
|
|
395
|
+
meta: { kind: 'self-media-browser-automation' },
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
383
399
|
async function handleGeneratedCommand(platform, args) {
|
|
384
400
|
if (!getPlatforms().includes(platform)) {
|
|
385
401
|
throw new Error(`未知平台:${platform}。可用平台:${getPlatforms().join(', ')}`);
|
|
@@ -549,10 +565,13 @@ function printHelp() {
|
|
|
549
565
|
yuanflow-cli knowledge docs --dry-run
|
|
550
566
|
yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
|
|
551
567
|
yuanflow-cli oss signed-url --key path/to/file.png --ttl-seconds 1800 --format agent-json
|
|
568
|
+
yuanflow-cli browser profile-path --platform douyin --account main --format agent-json
|
|
569
|
+
yuanflow-cli browser task-plan --platform xiaohongshu --task publish --account main --format agent-json
|
|
552
570
|
yuanflow-cli list douyin
|
|
553
571
|
|
|
554
572
|
说明:
|
|
555
573
|
社媒请求调用 Yuan API 的 /social/*path;知识库和 OSS 原子能力调用 /api/* 或 /atomic/*。
|
|
574
|
+
browser 命令是自媒体平台专用浏览器自动化协议,只返回受控 profile/cookie/任务路径与执行计划,不用于普通网页搜索。
|
|
556
575
|
需要鉴权的请求都会使用 Authorization: Bearer <token>。
|
|
557
576
|
token 优先级:--token > YUANCHUANG_API_TOKEN > 本地 config.token。
|
|
558
577
|
YuanFlow 主程序内使用时,token 由主程序认证系统注入,不需要手动配置。
|