yuanflow-cli 0.1.21 → 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 +37 -0
- package/package.json +1 -1
- package/skills/yuanflow-skill/IP/350/277/220/350/220/245/SKILL.md +52 -0
- package/skills/yuanflow-skill/README.md +26 -0
- package/skills/yuanflow-skill/SKILL.md +45 -7
- package/skills/yuanflow-skill//345/270/220/345/217/267/345/256/232/344/275/215/SKILL.md +53 -0
- package/skills/yuanflow-skill//346/226/207/346/241/210/345/210/233/344/275/234/SKILL.md +50 -0
- package/skills/yuanflow-skill//347/233/264/346/222/255/346/212/225/346/265/201/347/255/226/347/225/245/SKILL.md +53 -0
- package/skills/yuanflow-skill//347/233/264/346/222/255/350/257/235/346/234/257/347/224/237/346/210/220/SKILL.md +54 -0
- 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/skills/yuanflow-skill//350/207/252/345/252/222/344/275/223/347/237/245/350/257/206/345/272/223/SKILL.md +10 -0
- package/skills/yuanflow-skill//350/247/206/351/242/221/346/212/225/346/265/201/347/255/226/347/225/245/SKILL.md +53 -0
- package/skills/yuanflow-skill//351/200/211/351/242/230/347/255/226/345/210/222/SKILL.md +54 -0
- package/src/agent-protocol.js +3 -0
- package/src/browser-tools.js +413 -0
- package/src/cli.js +19 -0
|
@@ -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 由主程序认证系统注入,不需要手动配置。
|