@web-auto/webauto 0.1.18 → 0.1.19

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 (65) hide show
  1. package/README.md +122 -53
  2. package/apps/desktop-console/dist/main/index.mjs +227 -12
  3. package/apps/desktop-console/dist/renderer/index.js +237 -8
  4. package/apps/desktop-console/entry/ui-cli.mjs +282 -16
  5. package/apps/desktop-console/entry/ui-console.mjs +46 -15
  6. package/apps/webauto/entry/account.mjs +126 -27
  7. package/apps/webauto/entry/lib/account-detect.mjs +399 -9
  8. package/apps/webauto/entry/lib/account-store.mjs +201 -109
  9. package/apps/webauto/entry/lib/iflow-reply.mjs +194 -0
  10. package/apps/webauto/entry/lib/profile-policy.mjs +48 -0
  11. package/apps/webauto/entry/lib/profilepool.mjs +12 -0
  12. package/apps/webauto/entry/lib/schedule-store.mjs +29 -2
  13. package/apps/webauto/entry/lib/session-init.mjs +227 -0
  14. package/apps/webauto/entry/lib/upgrade-check.mjs +269 -0
  15. package/apps/webauto/entry/lib/xhs-unified-blocks.mjs +160 -0
  16. package/apps/webauto/entry/lib/xhs-unified-output-blocks.mjs +83 -0
  17. package/apps/webauto/entry/lib/xhs-unified-plan-blocks.mjs +55 -0
  18. package/apps/webauto/entry/lib/xhs-unified-profile-blocks.mjs +542 -0
  19. package/apps/webauto/entry/lib/xhs-unified-runtime-blocks.mjs +436 -0
  20. package/apps/webauto/entry/profilepool.mjs +56 -9
  21. package/apps/webauto/entry/smart-reply-cli.mjs +267 -0
  22. package/apps/webauto/entry/weibo-unified.mjs +84 -11
  23. package/apps/webauto/entry/xhs-orchestrate.mjs +43 -1
  24. package/apps/webauto/entry/xhs-unified.mjs +92 -997
  25. package/bin/webauto.mjs +22 -4
  26. package/dist/modules/camo-backend/src/index.js +33 -0
  27. package/dist/modules/camo-backend/src/internal/BrowserSession.js +232 -49
  28. package/dist/modules/camo-backend/src/internal/engine-manager.js +14 -13
  29. package/dist/modules/camo-backend/src/internal/ws-server.js +16 -19
  30. package/dist/modules/camo-runtime/src/utils/browser-service.mjs +38 -6
  31. package/dist/modules/workflow/blocks/EnsureSession.js +0 -8
  32. package/dist/modules/workflow/blocks/WeiboCollectFromLinksBlock.js +78 -6
  33. package/dist/modules/workflow/blocks/WeiboCollectSearchLinksBlock.js +266 -192
  34. package/dist/modules/workflow/definitions/weibo-search-workflow-v1.js +2 -0
  35. package/dist/modules/workflow/src/runner.js +2 -0
  36. package/dist/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +150 -37
  37. package/dist/modules/xiaohongshu/app/src/blocks/SmartReplyBlock.js +491 -0
  38. package/modules/camo-backend/src/index.ts +31 -0
  39. package/modules/camo-backend/src/internal/BrowserSession.ts +224 -53
  40. package/modules/camo-backend/src/internal/engine-manager.ts +14 -15
  41. package/modules/camo-backend/src/internal/ws-server.ts +17 -17
  42. package/modules/camo-runtime/src/autoscript/action-providers/xhs/common.mjs +12 -2
  43. package/modules/camo-runtime/src/autoscript/action-providers/xhs/persistence.mjs +57 -0
  44. package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +2475 -243
  45. package/modules/camo-runtime/src/autoscript/runtime.mjs +35 -30
  46. package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +80 -443
  47. package/modules/camo-runtime/src/container/runtime-core/checkpoint.mjs +39 -6
  48. package/modules/camo-runtime/src/container/runtime-core/operations/index.mjs +206 -39
  49. package/modules/camo-runtime/src/container/runtime-core/operations/tab-pool.mjs +0 -79
  50. package/modules/camo-runtime/src/container/runtime-core/operations/viewport.mjs +46 -0
  51. package/modules/camo-runtime/src/utils/browser-service.mjs +41 -6
  52. package/modules/camo-runtime/src/utils/js-policy.mjs +28 -0
  53. package/modules/workflow/blocks/EnsureSession.ts +0 -4
  54. package/modules/workflow/blocks/WeiboCollectFromLinksBlock.ts +81 -6
  55. package/modules/workflow/blocks/WeiboCollectSearchLinksBlock.ts +316 -0
  56. package/modules/workflow/definitions/weibo-search-workflow-v1.ts +2 -0
  57. package/modules/workflow/src/runner.ts +2 -0
  58. package/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.ts +198 -53
  59. package/modules/xiaohongshu/app/src/blocks/SmartReplyBlock.ts +706 -0
  60. package/package.json +2 -2
  61. package/modules/camo-runtime/src/autoscript/action-providers/xhs/comments.mjs +0 -498
  62. package/modules/camo-runtime/src/autoscript/action-providers/xhs/detail.mjs +0 -181
  63. package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +0 -691
  64. package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +0 -388
  65. package/modules/camo-runtime/src/container/runtime-core/operations/selector-scripts.mjs +0 -135
@@ -10,13 +10,19 @@ import {
10
10
  removeAccount,
11
11
  updateAccount,
12
12
  } from './lib/account-store.mjs';
13
- import { ensureProfile } from './lib/profilepool.mjs';
14
- import { syncXhsAccountByProfile, syncXhsAccountsByProfiles } from './lib/account-detect.mjs';
13
+ import { assertProfileExists } from './lib/profilepool.mjs';
14
+ import {
15
+ syncWeiboAccountByProfile,
16
+ syncWeiboAccountsByProfiles,
17
+ syncXhsAccountByProfile,
18
+ syncXhsAccountsByProfiles,
19
+ } from './lib/account-detect.mjs';
15
20
  import { publishBusEvent } from './lib/bus-publish.mjs';
16
21
  import { runCamo } from './lib/camo-cli.mjs';
17
22
 
18
23
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
19
24
  const XHS_HOME_URL = 'https://www.xiaohongshu.com';
25
+ const WEIBO_HOME_URL = 'https://weibo.com';
20
26
 
21
27
  function output(payload, jsonMode) {
22
28
  if (jsonMode) {
@@ -35,9 +41,44 @@ function parseBoolean(value, fallback = false) {
35
41
  return fallback;
36
42
  }
37
43
 
44
+ function parseIntWithFallback(value, fallback) {
45
+ const parsed = Math.floor(Number(value));
46
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
47
+ return parsed;
48
+ }
49
+
50
+ function resolveLoginViewport() {
51
+ const width = Math.max(900, parseIntWithFallback(process.env.WEBAUTO_VIEWPORT_WIDTH, 1440));
52
+ const height = Math.max(700, parseIntWithFallback(process.env.WEBAUTO_VIEWPORT_HEIGHT, 1100));
53
+ return { width, height };
54
+ }
55
+
56
+ async function applyLoginViewport(profileId) {
57
+ const id = String(profileId || '').trim();
58
+ if (!id) return { ok: false, error: 'missing_profile_id' };
59
+ const viewport = resolveLoginViewport();
60
+ try {
61
+ const { callAPI } = await import('../../../modules/camo-runtime/src/utils/browser-service.mjs');
62
+ const payload = await callAPI('page:setViewport', {
63
+ profileId: id,
64
+ width: viewport.width,
65
+ height: viewport.height,
66
+ });
67
+ const result = payload?.result || payload?.body || payload || {};
68
+ return {
69
+ ok: true,
70
+ width: Number(result.width) || viewport.width,
71
+ height: Number(result.height) || viewport.height,
72
+ };
73
+ } catch (error) {
74
+ return { ok: false, error: error?.message || String(error) };
75
+ }
76
+ }
77
+
38
78
  function inferLoginUrl(platform) {
39
79
  const value = String(platform || '').trim().toLowerCase();
40
80
  if (!value || value === 'xiaohongshu' || value === 'xhs') return XHS_HOME_URL;
81
+ if (value === 'weibo' || value === 'wb') return WEIBO_HOME_URL;
41
82
  return 'https://example.com';
42
83
  }
43
84
 
@@ -49,9 +90,47 @@ function normalizeAlias(input) {
49
90
  function normalizePlatform(input, fallback = 'xiaohongshu') {
50
91
  const raw = String(input || fallback).trim().toLowerCase();
51
92
  if (!raw || raw === 'xhs') return 'xiaohongshu';
93
+ if (raw === 'wb') return 'weibo';
52
94
  return raw;
53
95
  }
54
96
 
97
+ function isSupportedSyncPlatform(platform) {
98
+ return platform === 'xiaohongshu' || platform === 'weibo';
99
+ }
100
+
101
+ function resolveSyncPlatformByProfile(profileId, fallback = 'xiaohongshu') {
102
+ const id = String(profileId || '').trim();
103
+ if (!id) return normalizePlatform(fallback);
104
+ const candidates = ['xiaohongshu', 'weibo'];
105
+ for (const platform of candidates) {
106
+ const rows = listAccountProfiles({ platform }).profiles || [];
107
+ if (rows.some((row) => String(row?.profileId || '').trim() === id)) return platform;
108
+ }
109
+ return normalizePlatform(fallback);
110
+ }
111
+
112
+ async function syncByProfileAndPlatform(profileId, platform, options = {}) {
113
+ const normalizedPlatform = normalizePlatform(platform || 'xiaohongshu');
114
+ if (normalizedPlatform === 'xiaohongshu') {
115
+ return syncXhsAccountByProfile(profileId, options);
116
+ }
117
+ if (normalizedPlatform === 'weibo') {
118
+ return syncWeiboAccountByProfile(profileId, options);
119
+ }
120
+ throw new Error(`account sync unsupported platform: ${normalizedPlatform}`);
121
+ }
122
+
123
+ async function syncProfilesByPlatform(profileIds, platform, options = {}) {
124
+ const normalizedPlatform = normalizePlatform(platform || 'xiaohongshu');
125
+ if (normalizedPlatform === 'xiaohongshu') {
126
+ return syncXhsAccountsByProfiles(profileIds, options);
127
+ }
128
+ if (normalizedPlatform === 'weibo') {
129
+ return syncWeiboAccountsByProfiles(profileIds, options);
130
+ }
131
+ throw new Error(`account sync unsupported platform: ${normalizedPlatform}`);
132
+ }
133
+
55
134
  async function publishAccountEvent(type, payload) {
56
135
  try {
57
136
  await publishBusEvent({
@@ -161,17 +240,17 @@ Usage:
161
240
  webauto account list [--platform <name>] [--json]
162
241
  webauto account list --records [--json]
163
242
  webauto account add [--platform <name>] [--alias <alias>] [--name <name>] [--username <username>] [--profile <id>] [--fingerprint <id>] [--status pending|active|disabled|archived] [--json]
164
- webauto account get <id|alias> [--json]
165
- webauto account update <id|alias> [--alias <alias>|--clear-alias] [--name <name>] [--username <name>] [--profile <id>] [--fingerprint <id>] [--status pending|active|disabled|archived] [--json]
166
- webauto account delete <id|alias> [--delete-profile] [--delete-fingerprint] [--json]
167
- webauto account login <id|alias> [--url <url>] [--idle-timeout <duration>] [--sync-alias] [--json]
168
- webauto account sync-alias <id|alias> [--selector <css>] [--alias <value>] [--json]
169
- webauto account sync <profileId|all> [--pending-while-login] [--resolve-alias] [--json]
243
+ webauto account get <id|alias|profileId|accountId> [--platform <name>] [--json]
244
+ webauto account update <id|alias|profileId|accountId> [--alias <alias>|--clear-alias] [--name <name>] [--username <name>] [--profile <id>] [--fingerprint <id>] [--status pending|active|disabled|archived] [--json]
245
+ webauto account delete <id|alias|profileId|accountId> [--delete-profile] [--delete-fingerprint] [--json]
246
+ webauto account login <id|alias|profileId|accountId> [--platform <name>] [--url <url>] [--idle-timeout <duration>] [--sync-alias] [--json]
247
+ webauto account sync-alias <id|alias|profileId|accountId> [--platform <name>] [--selector <css>] [--alias <value>] [--json]
248
+ webauto account sync <profileId|all> [--platform <xiaohongshu|weibo>] [--pending-while-login] [--resolve-alias] [--json]
170
249
 
171
250
  Notes:
172
251
  - 账号数据默认保存到 WEBAUTO 根目录下的 accounts(Windows 优先 D:/webauto,缺失时回落 ~/.webauto,可用 WEBAUTO_HOME 覆盖)
173
252
  - list 默认按 profile 展示账号有效态(valid/invalid)
174
- - add 会自动创建并关联 profile/fingerprint(未指定时自动编号)
253
+ - add 不会自动创建 profile;必须传入已存在的 --profile
175
254
  - login 会通过 @web-auto/camo 拉起浏览器并绑定账号 profile
176
255
  - 只有识别到账号 id 的 profile 才会进入 valid 状态
177
256
  - sync --pending-while-login 会在登录过程中保持待登录状态,避免过早标记失效
@@ -180,6 +259,7 @@ Examples:
180
259
  webauto account add --platform xiaohongshu --alias 主号
181
260
  webauto account list
182
261
  webauto account sync all
262
+ webauto account sync all --platform weibo
183
263
  webauto account login xhs-0001 --url https://www.xiaohongshu.com --idle-timeout 30m
184
264
  webauto account sync-alias xhs-0001
185
265
  webauto account update xhs-0001 --alias 运营1号
@@ -199,13 +279,17 @@ async function cmdListRecords(jsonMode) {
199
279
  }
200
280
 
201
281
  async function cmdAdd(argv, jsonMode) {
282
+ const profileId = String(argv.profile || argv['profile-id'] || '').trim();
283
+ if (!profileId) {
284
+ throw new Error('missing --profile, automatic profile creation is disabled');
285
+ }
202
286
  const result = await addAccount({
203
287
  id: argv.id,
204
288
  platform: argv.platform,
205
289
  alias: argv.alias,
206
290
  name: argv.name,
207
291
  username: argv.username,
208
- profileId: argv.profile || argv['profile-id'],
292
+ profileId,
209
293
  fingerprintId: argv.fingerprint || argv['fingerprint-id'],
210
294
  status: argv.status,
211
295
  });
@@ -219,12 +303,13 @@ async function cmdAdd(argv, jsonMode) {
219
303
  });
220
304
  }
221
305
 
222
- async function cmdGet(idOrAlias, jsonMode) {
223
- const account = getAccount(idOrAlias);
306
+ async function cmdGet(idOrAlias, argv, jsonMode) {
307
+ const account = getAccount(idOrAlias, { platform: argv.platform });
224
308
  output({ ok: true, account }, jsonMode);
225
309
  }
226
310
 
227
311
  async function cmdUpdate(idOrAlias, argv, jsonMode) {
312
+ const target = getAccount(idOrAlias);
228
313
  const patch = {};
229
314
  if (argv.platform !== undefined) patch.platform = argv.platform;
230
315
  if (argv.name !== undefined) patch.name = argv.name;
@@ -246,7 +331,7 @@ async function cmdUpdate(idOrAlias, argv, jsonMode) {
246
331
  if (!Object.keys(patch).length) {
247
332
  throw new Error('no update fields provided');
248
333
  }
249
- const account = await updateAccount(idOrAlias, patch);
334
+ const account = await updateAccount(target.id, patch);
250
335
  output({ ok: true, account }, jsonMode);
251
336
  await publishAccountEvent('account:update', {
252
337
  profileId: account?.profileId || null,
@@ -258,7 +343,8 @@ async function cmdUpdate(idOrAlias, argv, jsonMode) {
258
343
  }
259
344
 
260
345
  async function cmdDelete(idOrAlias, argv, jsonMode) {
261
- const result = removeAccount(idOrAlias, {
346
+ const target = getAccount(idOrAlias);
347
+ const result = removeAccount(target.id, {
262
348
  deleteProfile: argv['delete-profile'] === true,
263
349
  deleteFingerprint: argv['delete-fingerprint'] === true,
264
350
  });
@@ -271,9 +357,10 @@ async function cmdDelete(idOrAlias, argv, jsonMode) {
271
357
  }
272
358
 
273
359
  async function cmdLogin(idOrAlias, argv, jsonMode) {
274
- const account = getAccount(idOrAlias);
275
- await ensureProfile(account.profileId);
276
- const url = String(argv.url || inferLoginUrl(account.platform)).trim();
360
+ const account = getAccount(idOrAlias, { platform: argv.platform });
361
+ const accountPlatform = normalizePlatform(account.platform || 'xiaohongshu');
362
+ assertProfileExists(account.profileId);
363
+ const url = String(argv.url || inferLoginUrl(accountPlatform)).trim();
277
364
  // Default idle timeout: 30 minutes, configurable via env or CLI.
278
365
  // Keep validation semantics aligned with camo parseDurationMs.
279
366
  const idleTimeout = String(argv['idle-timeout'] || process.env.WEBAUTO_LOGIN_IDLE_TIMEOUT || '30m').trim() || '30m';
@@ -288,8 +375,9 @@ async function cmdLogin(idOrAlias, argv, jsonMode) {
288
375
  process.exit(1);
289
376
  }
290
377
 
291
- const pendingProfile = await syncXhsAccountByProfile(account.profileId, { pendingWhileLogin: true }).catch((error) => ({
378
+ const pendingProfile = await syncByProfileAndPlatform(account.profileId, accountPlatform, { pendingWhileLogin: true }).catch((error) => ({
292
379
  profileId: account.profileId,
380
+ platform: accountPlatform,
293
381
  valid: false,
294
382
  status: 'pending',
295
383
  reason: `waiting_login_sync:${error?.message || String(error)}`,
@@ -317,6 +405,7 @@ async function cmdLogin(idOrAlias, argv, jsonMode) {
317
405
  }, jsonMode);
318
406
  process.exit(1);
319
407
  }
408
+ const viewport = await applyLoginViewport(account.profileId);
320
409
 
321
410
  const cookieAuto = runCamo(['cookies', 'auto', 'start', account.profileId, '--interval', '5000'], {
322
411
  rootDir: ROOT,
@@ -337,8 +426,9 @@ async function cmdLogin(idOrAlias, argv, jsonMode) {
337
426
  }
338
427
  }
339
428
 
340
- const accountSync = await syncXhsAccountByProfile(account.profileId, { pendingWhileLogin: true }).catch((error) => ({
429
+ const accountSync = await syncByProfileAndPlatform(account.profileId, accountPlatform, { pendingWhileLogin: true }).catch((error) => ({
341
430
  profileId: account.profileId,
431
+ platform: accountPlatform,
342
432
  valid: false,
343
433
  status: 'pending',
344
434
  reason: `waiting_login_sync:${error?.message || String(error)}`,
@@ -351,6 +441,7 @@ async function cmdLogin(idOrAlias, argv, jsonMode) {
351
441
  url,
352
442
  idleTimeout,
353
443
  camo: startResult.json || startResult.stdout || null,
444
+ viewport,
354
445
  pendingProfile,
355
446
  cookieAuto: cookieAuto.ok
356
447
  ? { ok: true }
@@ -368,7 +459,7 @@ async function cmdLogin(idOrAlias, argv, jsonMode) {
368
459
  }
369
460
 
370
461
  async function cmdSyncAlias(idOrAlias, argv, jsonMode) {
371
- const account = getAccount(idOrAlias);
462
+ const account = getAccount(idOrAlias, { platform: argv.platform });
372
463
  let alias = normalizeAlias(argv.alias);
373
464
  let source = 'manual';
374
465
  let candidates = [];
@@ -397,25 +488,33 @@ async function cmdSyncAlias(idOrAlias, argv, jsonMode) {
397
488
  async function cmdSync(target, argv, jsonMode) {
398
489
  const pendingWhileLogin = parseBoolean(argv['pending-while-login'], false);
399
490
  const resolveAlias = parseBoolean(argv['resolve-alias'], false);
400
- const platform = normalizePlatform(argv.platform || 'xiaohongshu');
491
+ const explicitPlatform = normalizeAlias(argv.platform) ? normalizePlatform(argv.platform) : '';
401
492
  const value = String(target || '').trim().toLowerCase();
402
493
  if (!value || value === 'all') {
403
- if (platform !== 'xiaohongshu') {
404
- throw new Error(`account sync currently supports platform=xiaohongshu only, got: ${platform}`);
494
+ const platform = explicitPlatform || 'xiaohongshu';
495
+ if (!isSupportedSyncPlatform(platform)) {
496
+ throw new Error(`account sync unsupported platform: ${platform}`);
405
497
  }
406
- const rows = listAccountProfiles({ platform: 'xiaohongshu' }).profiles;
498
+ const rows = listAccountProfiles({ platform }).profiles;
407
499
  const profileIds = rows.map((item) => item.profileId);
408
- const synced = await syncXhsAccountsByProfiles(profileIds, { pendingWhileLogin, resolveAlias });
500
+ const synced = await syncProfilesByPlatform(profileIds, platform, { pendingWhileLogin, resolveAlias });
409
501
  output({ ok: true, count: synced.length, profiles: synced }, jsonMode);
410
502
  await publishAccountEvent('account:sync', {
503
+ platform,
411
504
  count: synced.length,
412
505
  profiles: synced,
413
506
  });
414
507
  return;
415
508
  }
416
- const synced = await syncXhsAccountByProfile(target, { pendingWhileLogin, resolveAlias });
509
+ const profileId = String(target || '').trim();
510
+ const platform = explicitPlatform || resolveSyncPlatformByProfile(profileId, 'xiaohongshu');
511
+ if (!isSupportedSyncPlatform(platform)) {
512
+ throw new Error(`account sync unsupported platform: ${platform}`);
513
+ }
514
+ const synced = await syncByProfileAndPlatform(profileId, platform, { pendingWhileLogin, resolveAlias });
417
515
  output({ ok: true, profile: synced }, jsonMode);
418
516
  await publishAccountEvent('account:sync', {
517
+ platform,
419
518
  profile: synced,
420
519
  });
421
520
  }
@@ -436,7 +535,7 @@ async function main() {
436
535
 
437
536
  if (cmd === 'list') return argv.records ? cmdListRecords(jsonMode) : cmdList(jsonMode, argv.platform);
438
537
  if (cmd === 'add') return cmdAdd(argv, jsonMode);
439
- if (cmd === 'get') return cmdGet(arg1, jsonMode);
538
+ if (cmd === 'get') return cmdGet(arg1, argv, jsonMode);
440
539
  if (cmd === 'update') return cmdUpdate(arg1, argv, jsonMode);
441
540
  if (cmd === 'delete' || cmd === 'remove' || cmd === 'rm') return cmdDelete(arg1, argv, jsonMode);
442
541
  if (cmd === 'login') return cmdLogin(arg1, argv, jsonMode);