@web-auto/webauto 0.1.8 → 0.1.11

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 (43) hide show
  1. package/apps/desktop-console/dist/main/index.mjs +909 -105
  2. package/apps/desktop-console/dist/main/preload.mjs +3 -0
  3. package/apps/desktop-console/dist/renderer/index.html +9 -1
  4. package/apps/desktop-console/dist/renderer/index.js +796 -331
  5. package/apps/desktop-console/entry/ui-cli.mjs +59 -9
  6. package/apps/desktop-console/entry/ui-console.mjs +8 -3
  7. package/apps/webauto/entry/account.mjs +70 -9
  8. package/apps/webauto/entry/lib/account-detect.mjs +106 -25
  9. package/apps/webauto/entry/lib/account-store.mjs +122 -35
  10. package/apps/webauto/entry/lib/profilepool.mjs +45 -13
  11. package/apps/webauto/entry/lib/schedule-store.mjs +1 -25
  12. package/apps/webauto/entry/profilepool.mjs +45 -3
  13. package/apps/webauto/entry/schedule.mjs +44 -2
  14. package/apps/webauto/entry/weibo-unified.mjs +2 -2
  15. package/apps/webauto/entry/xhs-install.mjs +248 -52
  16. package/apps/webauto/entry/xhs-unified.mjs +33 -6
  17. package/bin/webauto.mjs +137 -5
  18. package/dist/modules/camo-runtime/src/utils/browser-service.mjs +4 -0
  19. package/dist/services/unified-api/server.js +5 -0
  20. package/dist/services/unified-api/task-state.js +2 -0
  21. package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +142 -14
  22. package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +16 -1
  23. package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +104 -0
  24. package/modules/camo-runtime/src/autoscript/runtime.mjs +14 -4
  25. package/modules/camo-runtime/src/autoscript/schema.mjs +9 -0
  26. package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +9 -2
  27. package/modules/camo-runtime/src/container/runtime-core/checkpoint.mjs +107 -1
  28. package/modules/camo-runtime/src/container/runtime-core/subscription.mjs +24 -2
  29. package/modules/camo-runtime/src/utils/browser-service.mjs +4 -0
  30. package/package.json +7 -3
  31. package/runtime/infra/utils/README.md +13 -0
  32. package/runtime/infra/utils/scripts/README.md +0 -0
  33. package/runtime/infra/utils/scripts/development/eval-in-session.mjs +40 -0
  34. package/runtime/infra/utils/scripts/development/highlight-search-containers.mjs +35 -0
  35. package/runtime/infra/utils/scripts/service/kill-port.mjs +24 -0
  36. package/runtime/infra/utils/scripts/service/start-api.mjs +103 -0
  37. package/runtime/infra/utils/scripts/service/start-browser-service.mjs +173 -0
  38. package/runtime/infra/utils/scripts/service/stop-api.mjs +30 -0
  39. package/runtime/infra/utils/scripts/service/stop-browser-service.mjs +104 -0
  40. package/runtime/infra/utils/scripts/test-services.mjs +94 -0
  41. package/scripts/bump-version.mjs +120 -0
  42. package/services/unified-api/server.ts +4 -0
  43. package/services/unified-api/task-state.ts +5 -0
@@ -3,34 +3,213 @@ import minimist from 'minimist';
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
- import { existsSync, rmSync } from 'node:fs';
6
+ import { existsSync, rmSync, mkdirSync, writeFileSync, renameSync, statSync } from 'node:fs';
7
7
  import { fileURLToPath } from 'node:url';
8
8
 
9
- function run(cmd, args) {
10
- const lower = String(cmd || '').toLowerCase();
9
+ const DEFAULT_COMMAND_TIMEOUT_MS = (() => {
10
+ const raw = Number(process.env.WEBAUTO_INSTALL_TIMEOUT_MS || 600000);
11
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 600000;
12
+ })();
13
+
14
+ function run(cmd, args, options = {}) {
15
+ const command = normalizeCommand(cmd);
16
+ const timeoutMs = Number.isFinite(Number(options?.timeoutMs))
17
+ ? Math.max(1000, Number(options.timeoutMs))
18
+ : DEFAULT_COMMAND_TIMEOUT_MS;
19
+ const lower = String(command || '').toLowerCase();
11
20
  const spawnOptions = {
12
21
  encoding: 'utf8',
13
22
  windowsHide: true,
14
- timeout: 120000,
23
+ timeout: timeoutMs,
15
24
  };
16
25
  if (process.platform === 'win32' && (lower.endsWith('.cmd') || lower.endsWith('.bat'))) {
17
- const cmdLine = [quoteCmdArg(cmd), ...args.map(quoteCmdArg)].join(' ');
26
+ const cmdLine = [quoteCmdArg(command), ...args.map(quoteCmdArg)].join(' ');
18
27
  return spawnSync('cmd.exe', ['/d', '/s', '/c', cmdLine], spawnOptions);
19
28
  }
20
29
  if (process.platform === 'win32' && lower.endsWith('.ps1')) {
21
30
  return spawnSync(
22
31
  'powershell.exe',
23
- ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', cmd, ...args],
32
+ ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', command, ...args],
24
33
  spawnOptions,
25
34
  );
26
35
  }
27
- return spawnSync(cmd, args, spawnOptions);
36
+ return spawnSync(command, args, spawnOptions);
28
37
  }
29
38
 
30
39
  function quoteCmdArg(value) {
31
- if (!value) return '""';
32
- if (!/[\s"]/u.test(value)) return value;
33
- return `"${String(value).replace(/"/g, '""')}"`;
40
+ const normalized = normalizeCommand(value);
41
+ if (!normalized) return '""';
42
+ if (!/[\s"]/u.test(normalized)) return normalized;
43
+ return `"${String(normalized).replace(/"/g, '""')}"`;
44
+ }
45
+
46
+ function normalizeCommand(value) {
47
+ const text = String(value || '').trim();
48
+ if (!text) return '';
49
+ if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
50
+ return text.slice(1, -1).trim();
51
+ }
52
+ return text;
53
+ }
54
+
55
+ function hasValidGeoIPFile(filePath) {
56
+ try {
57
+ if (!existsSync(filePath)) return false;
58
+ const stat = statSync(filePath);
59
+ return Number(stat?.size || 0) > 1024;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function stripAnsi(input) {
66
+ return String(input || '').replace(/\x1b\[[0-9;]*m/g, '');
67
+ }
68
+
69
+ function resolvePathFromOutput(stdout, stderr) {
70
+ const merged = `${stripAnsi(stdout)}\n${stripAnsi(stderr)}`;
71
+ const lines = merged
72
+ .split(/\r?\n/)
73
+ .map((x) => x.trim())
74
+ .filter(Boolean);
75
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
76
+ const line = lines[i];
77
+ if (line.startsWith('/') || /^[A-Z]:\\/i.test(line)) return line;
78
+ }
79
+ return '';
80
+ }
81
+
82
+ function commandReportsExistingPath(ret) {
83
+ if (!ret || ret.status !== 0) return false;
84
+ const resolvedPath = resolvePathFromOutput(ret.stdout, ret.stderr);
85
+ if (!resolvedPath) return false;
86
+ if (!existsSync(resolvedPath)) return false;
87
+ const executable =
88
+ process.platform === 'win32'
89
+ ? path.join(resolvedPath, 'camoufox.exe')
90
+ : path.join(resolvedPath, 'camoufox');
91
+ return existsSync(executable);
92
+ }
93
+
94
+ function summarizeCommand(ret) {
95
+ if (!ret) return '';
96
+ const bits = [
97
+ ret?.error?.message || '',
98
+ stripAnsi(ret?.stderr || '').trim(),
99
+ stripAnsi(ret?.stdout || '').trim(),
100
+ ].filter(Boolean);
101
+ return bits.length ? bits.join(' | ').slice(0, 900) : '';
102
+ }
103
+
104
+ function runCamoufoxCommand(args) {
105
+ const pyArgs = ['-m', 'camoufox', ...args];
106
+ const candidates =
107
+ process.platform === 'win32'
108
+ ? [
109
+ { cmd: 'camoufox', args },
110
+ { cmd: 'python', args: pyArgs },
111
+ { cmd: 'py', args: ['-3', ...pyArgs] },
112
+ ]
113
+ : [
114
+ { cmd: 'camoufox', args },
115
+ { cmd: 'python3', args: pyArgs },
116
+ ];
117
+ const attempts = [];
118
+ for (const candidate of candidates) {
119
+ const ret = run(candidate.cmd, candidate.args);
120
+ attempts.push({
121
+ command: `${candidate.cmd} ${candidate.args.join(' ')}`.trim(),
122
+ status: ret?.status ?? null,
123
+ error: ret?.error?.message || null,
124
+ detail: summarizeCommand(ret),
125
+ });
126
+ if (ret.status === 0) return { ok: true, ret, attempts };
127
+ }
128
+ const viaPackage = runPackageCommand('camoufox', ['camoufox', ...args]);
129
+ attempts.push({
130
+ command: `npx/npm exec camoufox ${args.join(' ')}`.trim(),
131
+ status: viaPackage?.status ?? null,
132
+ error: viaPackage?.error?.message || null,
133
+ detail: summarizeCommand(viaPackage),
134
+ });
135
+ return { ok: viaPackage.status === 0, ret: viaPackage, attempts };
136
+ }
137
+
138
+ function runCamoCommand(args) {
139
+ const candidates = [{ cmd: 'camo', args }];
140
+ const attempts = [];
141
+ for (const candidate of candidates) {
142
+ const ret = run(candidate.cmd, candidate.args);
143
+ attempts.push({
144
+ command: `${candidate.cmd} ${candidate.args.join(' ')}`.trim(),
145
+ status: ret?.status ?? null,
146
+ error: ret?.error?.message || null,
147
+ detail: summarizeCommand(ret),
148
+ });
149
+ if (ret.status === 0) return { ok: true, ret, attempts };
150
+ }
151
+ const viaPackage = runPackageCommand('@web-auto/camo', ['camo', ...args]);
152
+ attempts.push({
153
+ command: `npx/npm exec camo ${args.join(' ')}`.trim(),
154
+ status: viaPackage?.status ?? null,
155
+ error: viaPackage?.error?.message || null,
156
+ detail: summarizeCommand(viaPackage),
157
+ });
158
+ return { ok: viaPackage.status === 0, ret: viaPackage, attempts };
159
+ }
160
+
161
+ function checkCamoufoxInstalled() {
162
+ const pathCheck = runCamoufoxCommand(['path']);
163
+ if (!pathCheck.ok) return false;
164
+ return commandReportsExistingPath(pathCheck.ret);
165
+ }
166
+
167
+ function installCamoufox() {
168
+ return runCamoufoxCommand(['fetch']);
169
+ }
170
+
171
+ function checkGeoIPInstalled() {
172
+ return hasValidGeoIPFile(resolveGeoIPPath());
173
+ }
174
+
175
+ function installGeoIP() {
176
+ return runCamoCommand(['init', 'geoip']);
177
+ }
178
+
179
+ async function installGeoIPDirect() {
180
+ const target = resolveGeoIPPath();
181
+ const tmp = `${target}.tmp`;
182
+ const timeoutMs = (() => {
183
+ const raw = Number(process.env.WEBAUTO_GEOIP_TIMEOUT_MS || 300000);
184
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 300000;
185
+ })();
186
+ const url = String(process.env.WEBAUTO_GEOIP_URL || 'https://git.io/GeoLite2-City.mmdb').trim();
187
+ mkdirSync(path.dirname(target), { recursive: true });
188
+ const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
189
+ if (!res.ok) throw new Error(`geoip_download_http_${res.status}`);
190
+ const bytes = Buffer.from(await res.arrayBuffer());
191
+ if (!bytes || bytes.length < 1024) throw new Error('geoip_download_too_small');
192
+ writeFileSync(tmp, bytes);
193
+ renameSync(tmp, target);
194
+ return target;
195
+ }
196
+
197
+ async function ensureGeoIPInstalled() {
198
+ const commandResult = installGeoIP();
199
+ if (checkGeoIPInstalled()) {
200
+ return { ok: true, source: 'camo', ret: commandResult.ret };
201
+ }
202
+ try {
203
+ await installGeoIPDirect();
204
+ return { ok: checkGeoIPInstalled(), source: 'direct', ret: commandResult.ret };
205
+ } catch (error) {
206
+ return {
207
+ ok: false,
208
+ source: 'none',
209
+ ret: commandResult.ret,
210
+ detail: error?.message || String(error),
211
+ };
212
+ }
34
213
  }
35
214
 
36
215
  function resolveOnPath(candidates, pathEnv = process.env.PATH || process.env.Path || '', delimiter = path.delimiter) {
@@ -73,50 +252,46 @@ function runPackageCommand(packageName, commandArgs) {
73
252
  return run(resolveNpmBin(), ['exec', '--yes', `--package=${packageName}`, '--', ...commandArgs]);
74
253
  }
75
254
 
76
- function resolveWebautoRoot() {
77
- const portableRoot = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
78
- if (portableRoot) return path.join(portableRoot, '.webauto');
79
- return path.join(os.homedir(), '.webauto');
255
+ function normalizePathForPlatform(raw, platform = process.platform) {
256
+ const input = String(raw || '').trim();
257
+ const isWinPath = platform === 'win32' || /^[A-Za-z]:[\\/]/.test(input);
258
+ const pathApi = isWinPath ? path.win32 : path;
259
+ return isWinPath ? pathApi.normalize(input) : path.resolve(input);
80
260
  }
81
261
 
82
- function resolveGeoIPPath() {
83
- return path.join(resolveWebautoRoot(), 'geoip', 'GeoLite2-City.mmdb');
262
+ function normalizeLegacyWebautoRoot(raw, platform = process.platform) {
263
+ const pathApi = platform === 'win32' ? path.win32 : path;
264
+ const resolved = normalizePathForPlatform(raw, platform);
265
+ const base = pathApi.basename(resolved).toLowerCase();
266
+ if (base === '.webauto' || base === 'webauto') return resolved;
267
+ return pathApi.join(resolved, '.webauto');
84
268
  }
85
269
 
86
- function checkCamoufoxInstalled() {
87
- const candidates =
88
- process.platform === 'win32'
89
- ? [
90
- { cmd: 'python', args: ['-m', 'camoufox', 'path'] },
91
- { cmd: 'py', args: ['-3', '-m', 'camoufox', 'path'] },
92
- ]
93
- : [{ cmd: 'python3', args: ['-m', 'camoufox', 'path'] }];
94
- for (const candidate of candidates) {
95
- const ret = run(candidate.cmd, candidate.args);
96
- if (ret.status === 0) return true;
97
- }
98
- const npxRet = runPackageCommand('camoufox', ['camoufox', 'path']);
99
- if (npxRet.status === 0) return true;
100
- return false;
101
- }
270
+ function resolveWebautoRoot(options = {}) {
271
+ const env = options.env || process.env;
272
+ const platform = String(options.platform || process.platform);
273
+ const pathApi = platform === 'win32' ? path.win32 : path;
274
+ const homeDir = String(options.homeDir || os.homedir());
275
+ const explicitHome = String(env.WEBAUTO_HOME || '').trim();
276
+ if (explicitHome) return normalizePathForPlatform(explicitHome, platform);
102
277
 
103
- function installCamoufox() {
104
- const ret = runPackageCommand('camoufox', ['camoufox', 'fetch']);
105
- return ret.status === 0;
106
- }
278
+ const legacyRoot = String(env.WEBAUTO_ROOT || env.WEBAUTO_PORTABLE_ROOT || '').trim();
279
+ if (legacyRoot) return normalizeLegacyWebautoRoot(legacyRoot, platform);
107
280
 
108
- function checkGeoIPInstalled() {
109
- return existsSync(resolveGeoIPPath());
281
+ const hasDDrive = typeof options.hasDDrive === 'boolean'
282
+ ? options.hasDDrive
283
+ : (platform === 'win32' && existsSync('D:\\'));
284
+ if (platform === 'win32') return hasDDrive ? 'D:\\webauto' : pathApi.join(homeDir, '.webauto');
285
+ return pathApi.join(homeDir, '.webauto');
110
286
  }
111
287
 
112
- function installGeoIP() {
113
- const ret = runPackageCommand('@web-auto/camo', ['camo', 'init', 'geoip']);
114
- return ret.status === 0;
288
+ function resolveGeoIPPath() {
289
+ return path.join(resolveWebautoRoot(), 'geoip', 'GeoLite2-City.mmdb');
115
290
  }
116
291
 
292
+
117
293
  function uninstallCamoufox() {
118
- const ret = runPackageCommand('camoufox', ['camoufox', 'remove']);
119
- return ret.status === 0;
294
+ return runCamoufoxCommand(['remove']);
120
295
  }
121
296
 
122
297
  function uninstallGeoIP() {
@@ -203,6 +378,7 @@ async function main() {
203
378
  let camoufoxInstalled = before.camoufoxInstalled;
204
379
  let geoipInstalled = before.geoipInstalled;
205
380
  let operationError = null;
381
+ let operationDetail = '';
206
382
 
207
383
  if (mode === 'auto' || mode === 'install') {
208
384
  if (browser && !camoufoxInstalled) {
@@ -211,17 +387,25 @@ async function main() {
211
387
  if (!camoufoxInstalled) operationError = operationError || 'camoufox_install_failed';
212
388
  }
213
389
  if (geoip && !geoipInstalled) {
214
- actions.geoipInstalled = installGeoIP();
390
+ const geoResult = await ensureGeoIPInstalled();
391
+ actions.geoipInstalled = geoResult.ok;
215
392
  geoipInstalled = checkGeoIPInstalled();
216
- if (!geoipInstalled) operationError = operationError || 'geoip_install_failed';
393
+ if (!geoipInstalled) {
394
+ operationError = operationError || 'geoip_install_failed';
395
+ operationDetail = operationDetail || geoResult.detail || summarizeCommand(geoResult.ret);
396
+ }
217
397
  }
218
398
  }
219
399
 
220
400
  if (mode === 'uninstall' || mode === 'reinstall') {
221
401
  if (browser) {
222
- actions.browserUninstalled = uninstallCamoufox();
402
+ const removeResult = uninstallCamoufox();
403
+ actions.browserUninstalled = removeResult.ok;
223
404
  camoufoxInstalled = checkCamoufoxInstalled();
224
- if (camoufoxInstalled) operationError = operationError || 'camoufox_uninstall_failed';
405
+ if (camoufoxInstalled) {
406
+ operationError = operationError || 'camoufox_uninstall_failed';
407
+ operationDetail = operationDetail || summarizeCommand(removeResult.ret);
408
+ }
225
409
  }
226
410
  if (geoip) {
227
411
  actions.geoipUninstalled = uninstallGeoIP();
@@ -232,14 +416,22 @@ async function main() {
232
416
 
233
417
  if (mode === 'reinstall') {
234
418
  if (browser) {
235
- actions.browserInstalled = installCamoufox();
419
+ const installResult = installCamoufox();
420
+ actions.browserInstalled = installResult.ok;
236
421
  camoufoxInstalled = checkCamoufoxInstalled();
237
- if (!camoufoxInstalled) operationError = operationError || 'camoufox_install_failed';
422
+ if (!camoufoxInstalled) {
423
+ operationError = operationError || 'camoufox_install_failed';
424
+ operationDetail = operationDetail || summarizeCommand(installResult.ret);
425
+ }
238
426
  }
239
427
  if (geoip) {
240
- actions.geoipInstalled = installGeoIP();
428
+ const geoResult = await ensureGeoIPInstalled();
429
+ actions.geoipInstalled = geoResult.ok;
241
430
  geoipInstalled = checkGeoIPInstalled();
242
- if (!geoipInstalled) operationError = operationError || 'geoip_install_failed';
431
+ if (!geoipInstalled) {
432
+ operationError = operationError || 'geoip_install_failed';
433
+ operationDetail = operationDetail || geoResult.detail || summarizeCommand(geoResult.ret);
434
+ }
243
435
  }
244
436
  }
245
437
 
@@ -289,10 +481,11 @@ async function main() {
289
481
  backendHealthy: after.backendHealthy,
290
482
  geoipInstalled: after.geoipInstalled,
291
483
  operationError,
484
+ operationDetail: operationDetail || null,
292
485
  message: ok
293
486
  ? '资源状态就绪'
294
487
  : operationError
295
- ? `资源操作失败: ${operationError}`
488
+ ? `资源操作失败: ${operationError}${operationDetail ? ` (${operationDetail})` : ''}`
296
489
  : browser && !after.camoufoxInstalled
297
490
  ? 'Camoufox 未安装'
298
491
  : geoip && !after.geoipInstalled
@@ -318,4 +511,7 @@ export const __internals = {
318
511
  resolveWebautoRoot,
319
512
  resolveGeoIPPath,
320
513
  resolveModeAndSelection,
514
+ resolvePathFromOutput,
515
+ commandReportsExistingPath,
516
+ runCamoufoxCommand,
321
517
  };
@@ -9,7 +9,7 @@ import { buildXhsUnifiedAutoscript } from '../../../modules/camo-runtime/src/aut
9
9
  import { normalizeAutoscript, validateAutoscript } from '../../../modules/camo-runtime/src/autoscript/schema.mjs';
10
10
  import { AutoscriptRunner } from '../../../modules/camo-runtime/src/autoscript/runtime.mjs';
11
11
  import { syncXhsAccountsByProfiles } from './lib/account-detect.mjs';
12
- import { markProfileInvalid } from './lib/account-store.mjs';
12
+ import { listAccountProfiles, markProfileInvalid } from './lib/account-store.mjs';
13
13
  import { listProfilesForPool } from './lib/profilepool.mjs';
14
14
  import { runCamo } from './lib/camo-cli.mjs';
15
15
  import { publishBusEvent } from './lib/bus-publish.mjs';
@@ -60,6 +60,19 @@ function parseProfiles(argv) {
60
60
  return [];
61
61
  }
62
62
 
63
+ function resolveDefaultXhsProfiles() {
64
+ const rows = listAccountProfiles({ platform: 'xiaohongshu' }).profiles || [];
65
+ const valid = rows
66
+ .filter((row) => row?.valid === true && String(row?.accountId || '').trim())
67
+ .sort((a, b) => {
68
+ const ta = Date.parse(String(a?.updatedAt || '')) || 0;
69
+ const tb = Date.parse(String(b?.updatedAt || '')) || 0;
70
+ if (tb !== ta) return tb - ta;
71
+ return String(a?.profileId || '').localeCompare(String(b?.profileId || ''));
72
+ });
73
+ return Array.from(new Set(valid.map((row) => String(row.profileId || '').trim()).filter(Boolean)));
74
+ }
75
+
63
76
  function sanitizeForPath(name, fallback = 'unknown') {
64
77
  const text = String(name || '').trim();
65
78
  if (!text) return fallback;
@@ -230,6 +243,7 @@ function createTaskReporter(seed = {}) {
230
243
  profileId: String(seed.profileId || 'unknown').trim() || 'unknown',
231
244
  keyword: String(seed.keyword || '').trim(),
232
245
  phase: 'unified',
246
+ uiTriggerId: String(seed.uiTriggerId || '').trim(),
233
247
  };
234
248
  const createdRunIds = new Set();
235
249
 
@@ -305,6 +319,7 @@ function buildTemplateOptions(argv, profileId, overrides = {}) {
305
319
  const likeKeywords = String(argv['like-keywords'] || '').trim();
306
320
  const replyText = String(argv['reply-text'] || '感谢分享,已关注').trim() || '感谢分享,已关注';
307
321
  const outputRoot = String(argv['output-root'] || '').trim();
322
+ const uiTriggerId = String(argv['ui-trigger-id'] || process.env.WEBAUTO_UI_TRIGGER_ID || '').trim();
308
323
  const resume = parseBool(argv.resume, false);
309
324
  const incrementalMax = parseBool(argv['incremental-max'], true);
310
325
  const sharedHarvestPath = String(overrides.sharedHarvestPath ?? argv['shared-harvest-path'] ?? '').trim();
@@ -329,6 +344,7 @@ function buildTemplateOptions(argv, profileId, overrides = {}) {
329
344
  inputMode,
330
345
  headless,
331
346
  ocrCommand,
347
+ uiTriggerId,
332
348
  outputRoot,
333
349
  throttle,
334
350
  tabCount,
@@ -537,6 +553,7 @@ async function runProfile(spec, argv, baseOverrides = {}) {
537
553
  const reporter = createTaskReporter({
538
554
  profileId,
539
555
  keyword: options.keyword,
556
+ uiTriggerId: options.uiTriggerId,
540
557
  });
541
558
  let activeRunId = '';
542
559
  const pushTaskSnapshot = (status = 'running') => {
@@ -600,9 +617,9 @@ async function runProfile(spec, argv, baseOverrides = {}) {
600
617
  pushTaskSnapshot('running');
601
618
  }
602
619
  if (
603
- merged.event === 'autoscript:event'
604
- && merged.subscriptionId === 'login_guard'
605
- && (merged.type === 'appear' || merged.type === 'exist')
620
+ merged.event === 'autoscript:operation_error'
621
+ && String(merged.operationId || '').trim() === 'abort_on_login_guard'
622
+ && String(merged.message || '').includes('LOGIN_GUARD_DETECTED')
606
623
  ) {
607
624
  try {
608
625
  markProfileInvalid(profileId, 'login_guard_runtime');
@@ -919,8 +936,18 @@ export async function runUnified(argv, overrides = {}) {
919
936
 
920
937
  const env = String(argv.env || 'prod').trim() || 'prod';
921
938
  const busEnabled = parseBool(argv['bus-events'], false) || process.env.WEBAUTO_BUS_EVENTS === '1';
922
- const profiles = parseProfiles(argv);
923
- if (profiles.length === 0) throw new Error('missing --profile or --profiles or --profilepool');
939
+ let profiles = parseProfiles(argv);
940
+ if (profiles.length === 0) {
941
+ profiles = resolveDefaultXhsProfiles();
942
+ if (profiles.length > 0) {
943
+ console.log(JSON.stringify({
944
+ event: 'xhs.unified.auto_profiles_selected',
945
+ platform: 'xiaohongshu',
946
+ profiles,
947
+ }));
948
+ }
949
+ }
950
+ if (profiles.length === 0) throw new Error('missing --profile/--profiles/--profilepool and no valid xiaohongshu account profile found');
924
951
  await Promise.all(profiles.map((profileId) => ensureProfileSession(profileId)));
925
952
  const defaultMaxNotes = parseIntFlag(argv['max-notes'] ?? argv.target, 30, 1);
926
953
  const totalNotes = parseNonNegativeInt(argv['total-notes'] ?? argv['total-target'], 0);