@web-auto/webauto 0.1.7 → 0.1.9
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/apps/desktop-console/dist/main/index.mjs +802 -90
- package/apps/desktop-console/dist/main/preload.mjs +3 -0
- package/apps/desktop-console/dist/renderer/index.html +9 -1
- package/apps/desktop-console/dist/renderer/index.js +784 -332
- package/apps/desktop-console/entry/ui-cli.mjs +23 -8
- package/apps/desktop-console/entry/ui-console.mjs +8 -3
- package/apps/webauto/entry/account.mjs +69 -8
- package/apps/webauto/entry/lib/account-detect.mjs +106 -25
- package/apps/webauto/entry/lib/account-store.mjs +121 -22
- package/apps/webauto/entry/lib/schedule-store.mjs +0 -12
- package/apps/webauto/entry/profilepool.mjs +45 -3
- package/apps/webauto/entry/schedule.mjs +44 -2
- package/apps/webauto/entry/weibo-unified.mjs +2 -2
- package/apps/webauto/entry/xhs-install.mjs +220 -51
- package/apps/webauto/entry/xhs-unified.mjs +33 -6
- package/bin/webauto.mjs +80 -4
- package/dist/modules/camo-runtime/src/utils/browser-service.mjs +4 -0
- package/dist/services/unified-api/server.js +5 -0
- package/dist/services/unified-api/task-state.js +2 -0
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +142 -14
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +16 -1
- package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +104 -0
- package/modules/camo-runtime/src/autoscript/runtime.mjs +14 -4
- package/modules/camo-runtime/src/autoscript/schema.mjs +9 -0
- package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +9 -2
- package/modules/camo-runtime/src/container/runtime-core/checkpoint.mjs +107 -1
- package/modules/camo-runtime/src/container/runtime-core/subscription.mjs +24 -2
- package/modules/camo-runtime/src/utils/browser-service.mjs +4 -0
- package/package.json +6 -3
- package/scripts/bump-version.mjs +120 -0
- package/services/unified-api/server.ts +4 -0
- 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
|
-
|
|
10
|
-
const
|
|
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:
|
|
23
|
+
timeout: timeoutMs,
|
|
15
24
|
};
|
|
16
25
|
if (process.platform === 'win32' && (lower.endsWith('.cmd') || lower.endsWith('.bat'))) {
|
|
17
|
-
const cmdLine = [quoteCmdArg(
|
|
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',
|
|
32
|
+
['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', command, ...args],
|
|
24
33
|
spawnOptions,
|
|
25
34
|
);
|
|
26
35
|
}
|
|
27
|
-
return spawnSync(
|
|
36
|
+
return spawnSync(command, args, spawnOptions);
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
function quoteCmdArg(value) {
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
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) {
|
|
@@ -83,40 +262,9 @@ function resolveGeoIPPath() {
|
|
|
83
262
|
return path.join(resolveWebautoRoot(), 'geoip', 'GeoLite2-City.mmdb');
|
|
84
263
|
}
|
|
85
264
|
|
|
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
|
-
}
|
|
102
|
-
|
|
103
|
-
function installCamoufox() {
|
|
104
|
-
const ret = runPackageCommand('camoufox', ['camoufox', 'fetch']);
|
|
105
|
-
return ret.status === 0;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function checkGeoIPInstalled() {
|
|
109
|
-
return existsSync(resolveGeoIPPath());
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function installGeoIP() {
|
|
113
|
-
const ret = runPackageCommand('@web-auto/camo', ['camo', 'init', 'geoip']);
|
|
114
|
-
return ret.status === 0;
|
|
115
|
-
}
|
|
116
265
|
|
|
117
266
|
function uninstallCamoufox() {
|
|
118
|
-
|
|
119
|
-
return ret.status === 0;
|
|
267
|
+
return runCamoufoxCommand(['remove']);
|
|
120
268
|
}
|
|
121
269
|
|
|
122
270
|
function uninstallGeoIP() {
|
|
@@ -203,6 +351,7 @@ async function main() {
|
|
|
203
351
|
let camoufoxInstalled = before.camoufoxInstalled;
|
|
204
352
|
let geoipInstalled = before.geoipInstalled;
|
|
205
353
|
let operationError = null;
|
|
354
|
+
let operationDetail = '';
|
|
206
355
|
|
|
207
356
|
if (mode === 'auto' || mode === 'install') {
|
|
208
357
|
if (browser && !camoufoxInstalled) {
|
|
@@ -211,17 +360,25 @@ async function main() {
|
|
|
211
360
|
if (!camoufoxInstalled) operationError = operationError || 'camoufox_install_failed';
|
|
212
361
|
}
|
|
213
362
|
if (geoip && !geoipInstalled) {
|
|
214
|
-
|
|
363
|
+
const geoResult = await ensureGeoIPInstalled();
|
|
364
|
+
actions.geoipInstalled = geoResult.ok;
|
|
215
365
|
geoipInstalled = checkGeoIPInstalled();
|
|
216
|
-
if (!geoipInstalled)
|
|
366
|
+
if (!geoipInstalled) {
|
|
367
|
+
operationError = operationError || 'geoip_install_failed';
|
|
368
|
+
operationDetail = operationDetail || geoResult.detail || summarizeCommand(geoResult.ret);
|
|
369
|
+
}
|
|
217
370
|
}
|
|
218
371
|
}
|
|
219
372
|
|
|
220
373
|
if (mode === 'uninstall' || mode === 'reinstall') {
|
|
221
374
|
if (browser) {
|
|
222
|
-
|
|
375
|
+
const removeResult = uninstallCamoufox();
|
|
376
|
+
actions.browserUninstalled = removeResult.ok;
|
|
223
377
|
camoufoxInstalled = checkCamoufoxInstalled();
|
|
224
|
-
if (camoufoxInstalled)
|
|
378
|
+
if (camoufoxInstalled) {
|
|
379
|
+
operationError = operationError || 'camoufox_uninstall_failed';
|
|
380
|
+
operationDetail = operationDetail || summarizeCommand(removeResult.ret);
|
|
381
|
+
}
|
|
225
382
|
}
|
|
226
383
|
if (geoip) {
|
|
227
384
|
actions.geoipUninstalled = uninstallGeoIP();
|
|
@@ -232,14 +389,22 @@ async function main() {
|
|
|
232
389
|
|
|
233
390
|
if (mode === 'reinstall') {
|
|
234
391
|
if (browser) {
|
|
235
|
-
|
|
392
|
+
const installResult = installCamoufox();
|
|
393
|
+
actions.browserInstalled = installResult.ok;
|
|
236
394
|
camoufoxInstalled = checkCamoufoxInstalled();
|
|
237
|
-
if (!camoufoxInstalled)
|
|
395
|
+
if (!camoufoxInstalled) {
|
|
396
|
+
operationError = operationError || 'camoufox_install_failed';
|
|
397
|
+
operationDetail = operationDetail || summarizeCommand(installResult.ret);
|
|
398
|
+
}
|
|
238
399
|
}
|
|
239
400
|
if (geoip) {
|
|
240
|
-
|
|
401
|
+
const geoResult = await ensureGeoIPInstalled();
|
|
402
|
+
actions.geoipInstalled = geoResult.ok;
|
|
241
403
|
geoipInstalled = checkGeoIPInstalled();
|
|
242
|
-
if (!geoipInstalled)
|
|
404
|
+
if (!geoipInstalled) {
|
|
405
|
+
operationError = operationError || 'geoip_install_failed';
|
|
406
|
+
operationDetail = operationDetail || geoResult.detail || summarizeCommand(geoResult.ret);
|
|
407
|
+
}
|
|
243
408
|
}
|
|
244
409
|
}
|
|
245
410
|
|
|
@@ -289,10 +454,11 @@ async function main() {
|
|
|
289
454
|
backendHealthy: after.backendHealthy,
|
|
290
455
|
geoipInstalled: after.geoipInstalled,
|
|
291
456
|
operationError,
|
|
457
|
+
operationDetail: operationDetail || null,
|
|
292
458
|
message: ok
|
|
293
459
|
? '资源状态就绪'
|
|
294
460
|
: operationError
|
|
295
|
-
? `资源操作失败: ${operationError}`
|
|
461
|
+
? `资源操作失败: ${operationError}${operationDetail ? ` (${operationDetail})` : ''}`
|
|
296
462
|
: browser && !after.camoufoxInstalled
|
|
297
463
|
? 'Camoufox 未安装'
|
|
298
464
|
: geoip && !after.geoipInstalled
|
|
@@ -318,4 +484,7 @@ export const __internals = {
|
|
|
318
484
|
resolveWebautoRoot,
|
|
319
485
|
resolveGeoIPPath,
|
|
320
486
|
resolveModeAndSelection,
|
|
487
|
+
resolvePathFromOutput,
|
|
488
|
+
commandReportsExistingPath,
|
|
489
|
+
runCamoufoxCommand,
|
|
321
490
|
};
|
|
@@ -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:
|
|
604
|
-
&& merged.
|
|
605
|
-
&& (merged.
|
|
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
|
-
|
|
923
|
-
if (profiles.length === 0)
|
|
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);
|
package/bin/webauto.mjs
CHANGED
|
@@ -78,6 +78,17 @@ function uiConsoleScriptPath() {
|
|
|
78
78
|
return path.join(ROOT, 'apps', 'desktop-console', 'entry', 'ui-console.mjs');
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
function readRootVersion() {
|
|
82
|
+
try {
|
|
83
|
+
const pkg = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
84
|
+
return String(pkg.version || '').trim() || '0.0.0';
|
|
85
|
+
} catch {
|
|
86
|
+
return '0.0.0';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const ROOT_VERSION = readRootVersion();
|
|
91
|
+
|
|
81
92
|
function printMainHelp() {
|
|
82
93
|
console.log(`webauto CLI
|
|
83
94
|
|
|
@@ -100,10 +111,14 @@ Core Commands:
|
|
|
100
111
|
webauto xhs unified [xhs options...]
|
|
101
112
|
webauto xhs status [--run-id <id>] [--json]
|
|
102
113
|
webauto xhs orchestrate [xhs options...]
|
|
114
|
+
webauto version [--json]
|
|
115
|
+
webauto version bump [patch|minor|major]
|
|
103
116
|
|
|
104
117
|
Build & Release:
|
|
105
118
|
webauto build:dev # Local link mode
|
|
106
|
-
webauto build:release # Full release gate (
|
|
119
|
+
webauto build:release # Full release gate (默认自动 bump patch 版本)
|
|
120
|
+
webauto build:release -- --bump minor
|
|
121
|
+
webauto build:release -- --no-bump
|
|
107
122
|
webauto build:release -- --skip-tests
|
|
108
123
|
webauto build:release -- --skip-pack
|
|
109
124
|
|
|
@@ -132,6 +147,7 @@ Tips:
|
|
|
132
147
|
- account 命令会转发到 apps/webauto/entry/account.mjs
|
|
133
148
|
- schedule 命令会转发到 apps/webauto/entry/schedule.mjs
|
|
134
149
|
- 全量参数请看: webauto xhs --help
|
|
150
|
+
- 当前 CLI 版本: ${ROOT_VERSION}
|
|
135
151
|
`);
|
|
136
152
|
}
|
|
137
153
|
|
|
@@ -342,6 +358,21 @@ Examples:
|
|
|
342
358
|
`);
|
|
343
359
|
}
|
|
344
360
|
|
|
361
|
+
function printVersionHelp() {
|
|
362
|
+
console.log(`webauto version
|
|
363
|
+
|
|
364
|
+
Usage:
|
|
365
|
+
webauto version [--json]
|
|
366
|
+
webauto version bump [patch|minor|major] [--json]
|
|
367
|
+
|
|
368
|
+
Examples:
|
|
369
|
+
webauto version
|
|
370
|
+
webauto version --json
|
|
371
|
+
webauto version bump
|
|
372
|
+
webauto version bump minor
|
|
373
|
+
`);
|
|
374
|
+
}
|
|
375
|
+
|
|
345
376
|
function exists(p) {
|
|
346
377
|
try {
|
|
347
378
|
return existsSync(p);
|
|
@@ -454,6 +485,7 @@ async function ensureDepsAndBuild() {
|
|
|
454
485
|
}
|
|
455
486
|
|
|
456
487
|
async function uiConsole({ build, install, checkOnly, noDaemon }) {
|
|
488
|
+
console.log(`[webauto] version ${ROOT_VERSION}`);
|
|
457
489
|
const okServices = checkServicesBuilt();
|
|
458
490
|
const okDeps = checkDesktopConsoleDeps();
|
|
459
491
|
const okUiBuilt = checkDesktopConsoleBuilt();
|
|
@@ -517,12 +549,14 @@ async function uiConsole({ build, install, checkOnly, noDaemon }) {
|
|
|
517
549
|
async function main() {
|
|
518
550
|
const rawArgv = process.argv.slice(2);
|
|
519
551
|
const args = minimist(process.argv.slice(2), {
|
|
520
|
-
boolean: ['help', 'build', 'install', 'check', 'full', 'link', 'skip-tests', 'skip-pack', 'no-daemon'],
|
|
552
|
+
boolean: ['help', 'build', 'install', 'check', 'full', 'link', 'skip-tests', 'skip-pack', 'no-daemon', 'no-bump', 'json'],
|
|
553
|
+
string: ['bump'],
|
|
521
554
|
alias: { h: 'help' },
|
|
522
555
|
});
|
|
523
556
|
|
|
524
557
|
const cmd = String(args._[0] || '').trim();
|
|
525
558
|
const sub = String(args._[1] || '').trim();
|
|
559
|
+
const noDaemon = rawArgv.includes('--no-daemon') || rawArgv.includes('--foreground') || args['no-daemon'] === true;
|
|
526
560
|
|
|
527
561
|
if (args.help) {
|
|
528
562
|
if (cmd === 'account') {
|
|
@@ -551,6 +585,10 @@ async function main() {
|
|
|
551
585
|
printXhsHelp();
|
|
552
586
|
return;
|
|
553
587
|
}
|
|
588
|
+
if (cmd === 'version') {
|
|
589
|
+
printVersionHelp();
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
554
592
|
printMainHelp();
|
|
555
593
|
return;
|
|
556
594
|
}
|
|
@@ -560,11 +598,37 @@ async function main() {
|
|
|
560
598
|
build: false,
|
|
561
599
|
install: false,
|
|
562
600
|
checkOnly: false,
|
|
563
|
-
noDaemon
|
|
601
|
+
noDaemon,
|
|
564
602
|
});
|
|
565
603
|
return;
|
|
566
604
|
}
|
|
567
605
|
|
|
606
|
+
if (cmd === 'version') {
|
|
607
|
+
const jsonMode = args.json === true;
|
|
608
|
+
const action = String(args._[1] || '').trim();
|
|
609
|
+
if (!action) {
|
|
610
|
+
const out = { name: '@web-auto/webauto', version: ROOT_VERSION };
|
|
611
|
+
if (jsonMode) console.log(JSON.stringify(out, null, 2));
|
|
612
|
+
else console.log(`@web-auto/webauto v${ROOT_VERSION}`);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (action !== 'bump') {
|
|
616
|
+
console.error(`Unknown version action: ${action}`);
|
|
617
|
+
printVersionHelp();
|
|
618
|
+
process.exit(2);
|
|
619
|
+
}
|
|
620
|
+
const bumpType = String(args._[2] || args.bump || 'patch').trim().toLowerCase();
|
|
621
|
+
if (!['patch', 'minor', 'major'].includes(bumpType)) {
|
|
622
|
+
console.error(`Unsupported bump type: ${bumpType}`);
|
|
623
|
+
process.exit(2);
|
|
624
|
+
}
|
|
625
|
+
const script = path.join(ROOT, 'scripts', 'bump-version.mjs');
|
|
626
|
+
const cmdArgs = [script, bumpType];
|
|
627
|
+
if (jsonMode) cmdArgs.push('--json');
|
|
628
|
+
await run(process.execPath, cmdArgs);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
568
632
|
// build:dev - local development mode
|
|
569
633
|
if (cmd === 'build:dev') {
|
|
570
634
|
console.log('[webauto] Running local dev setup...');
|
|
@@ -580,8 +644,20 @@ async function main() {
|
|
|
580
644
|
if (cmd === 'build:release') {
|
|
581
645
|
const skipTests = args['skip-tests'] === true;
|
|
582
646
|
const skipPack = args['skip-pack'] === true;
|
|
647
|
+
const noBump = args['no-bump'] === true;
|
|
648
|
+
const bumpType = String(args.bump || 'patch').trim().toLowerCase();
|
|
649
|
+
if (!['patch', 'minor', 'major'].includes(bumpType)) {
|
|
650
|
+
console.error(`Unsupported --bump value: ${bumpType}`);
|
|
651
|
+
process.exit(2);
|
|
652
|
+
}
|
|
583
653
|
console.log('[webauto] Running release gate...');
|
|
584
654
|
const npm = npmRunner();
|
|
655
|
+
if (!noBump) {
|
|
656
|
+
const bumpScript = path.join(ROOT, 'scripts', 'bump-version.mjs');
|
|
657
|
+
await run(process.execPath, [bumpScript, bumpType]);
|
|
658
|
+
} else {
|
|
659
|
+
console.log('[webauto] Skip version bump (--no-bump)');
|
|
660
|
+
}
|
|
585
661
|
await run(npm.cmd, [...npm.prefix, 'run', 'prebuild']);
|
|
586
662
|
if (!skipTests) {
|
|
587
663
|
await run(npm.cmd, [...npm.prefix, 'run', 'test:ci']);
|
|
@@ -608,7 +684,7 @@ async function main() {
|
|
|
608
684
|
build: args.build === true,
|
|
609
685
|
install: args.install === true,
|
|
610
686
|
checkOnly: args.check === true,
|
|
611
|
-
noDaemon
|
|
687
|
+
noDaemon,
|
|
612
688
|
});
|
|
613
689
|
return;
|
|
614
690
|
}
|
|
@@ -213,6 +213,7 @@ function buildDomSnapshotScript(maxDepth, maxChildren) {
|
|
|
213
213
|
const root = collect(document.body || document.documentElement, 0, 'root');
|
|
214
214
|
return {
|
|
215
215
|
dom_tree: root,
|
|
216
|
+
current_url: String(window.location.href || ''),
|
|
216
217
|
viewport: {
|
|
217
218
|
width: viewportWidth,
|
|
218
219
|
height: viewportHeight,
|
|
@@ -235,6 +236,9 @@ export async function getDomSnapshotByProfile(profileId, options = {}) {
|
|
|
235
236
|
height: Number(payload.viewport.height) || 0,
|
|
236
237
|
};
|
|
237
238
|
}
|
|
239
|
+
if (tree && payload.current_url) {
|
|
240
|
+
tree.__url = String(payload.current_url);
|
|
241
|
+
}
|
|
238
242
|
return tree;
|
|
239
243
|
}
|
|
240
244
|
export async function getViewportByProfile(profileId) {
|
|
@@ -274,11 +274,13 @@ class UnifiedApiServer {
|
|
|
274
274
|
return existing;
|
|
275
275
|
const profileId = String(seed?.profileId || 'unknown').trim() || 'unknown';
|
|
276
276
|
const keyword = String(seed?.keyword || '').trim();
|
|
277
|
+
const uiTriggerId = String(seed?.uiTriggerId || seed?.triggerId || '').trim();
|
|
277
278
|
const phase = normalizeTaskPhase(seed?.phase);
|
|
278
279
|
return this.taskRegistry.createTask({
|
|
279
280
|
runId: normalizedRunId,
|
|
280
281
|
profileId,
|
|
281
282
|
keyword,
|
|
283
|
+
uiTriggerId,
|
|
282
284
|
phase,
|
|
283
285
|
});
|
|
284
286
|
};
|
|
@@ -290,6 +292,7 @@ class UnifiedApiServer {
|
|
|
290
292
|
const phase = normalizeTaskPhase(payload?.phase);
|
|
291
293
|
const profileId = String(payload?.profileId || '').trim();
|
|
292
294
|
const keyword = String(payload?.keyword || '').trim();
|
|
295
|
+
const uiTriggerId = String(payload?.uiTriggerId || payload?.triggerId || '').trim();
|
|
293
296
|
const details = payload?.details && typeof payload.details === 'object' ? payload.details : undefined;
|
|
294
297
|
const patch = {};
|
|
295
298
|
if (phase !== 'unknown')
|
|
@@ -298,6 +301,8 @@ class UnifiedApiServer {
|
|
|
298
301
|
patch.profileId = profileId;
|
|
299
302
|
if (keyword)
|
|
300
303
|
patch.keyword = keyword;
|
|
304
|
+
if (uiTriggerId)
|
|
305
|
+
patch.uiTriggerId = uiTriggerId;
|
|
301
306
|
if (details)
|
|
302
307
|
patch.details = details;
|
|
303
308
|
if (Object.keys(patch).length > 0) {
|
|
@@ -11,6 +11,7 @@ class TaskStateRegistry {
|
|
|
11
11
|
runId: partial.runId,
|
|
12
12
|
profileId: partial.profileId,
|
|
13
13
|
keyword: partial.keyword,
|
|
14
|
+
uiTriggerId: partial.uiTriggerId ? String(partial.uiTriggerId).trim() : undefined,
|
|
14
15
|
phase: partial.phase || 'unknown',
|
|
15
16
|
status: 'starting',
|
|
16
17
|
progress: { total: 0, processed: 0, failed: 0 },
|
|
@@ -22,6 +23,7 @@ class TaskStateRegistry {
|
|
|
22
23
|
imagesDownloaded: 0,
|
|
23
24
|
ocrProcessed: 0,
|
|
24
25
|
},
|
|
26
|
+
createdAt: now,
|
|
25
27
|
startedAt: now,
|
|
26
28
|
updatedAt: now,
|
|
27
29
|
details: {
|