@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.
- package/apps/desktop-console/dist/main/index.mjs +909 -105
- 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 +796 -331
- package/apps/desktop-console/entry/ui-cli.mjs +59 -9
- package/apps/desktop-console/entry/ui-console.mjs +8 -3
- package/apps/webauto/entry/account.mjs +70 -9
- package/apps/webauto/entry/lib/account-detect.mjs +106 -25
- package/apps/webauto/entry/lib/account-store.mjs +122 -35
- package/apps/webauto/entry/lib/profilepool.mjs +45 -13
- package/apps/webauto/entry/lib/schedule-store.mjs +1 -25
- 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 +248 -52
- package/apps/webauto/entry/xhs-unified.mjs +33 -6
- package/bin/webauto.mjs +137 -5
- 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 +7 -3
- package/runtime/infra/utils/README.md +13 -0
- package/runtime/infra/utils/scripts/README.md +0 -0
- package/runtime/infra/utils/scripts/development/eval-in-session.mjs +40 -0
- package/runtime/infra/utils/scripts/development/highlight-search-containers.mjs +35 -0
- package/runtime/infra/utils/scripts/service/kill-port.mjs +24 -0
- package/runtime/infra/utils/scripts/service/start-api.mjs +103 -0
- package/runtime/infra/utils/scripts/service/start-browser-service.mjs +173 -0
- package/runtime/infra/utils/scripts/service/stop-api.mjs +30 -0
- package/runtime/infra/utils/scripts/service/stop-browser-service.mjs +104 -0
- package/runtime/infra/utils/scripts/test-services.mjs +94 -0
- 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) {
|
|
@@ -73,50 +252,46 @@ function runPackageCommand(packageName, commandArgs) {
|
|
|
73
252
|
return run(resolveNpmBin(), ['exec', '--yes', `--package=${packageName}`, '--', ...commandArgs]);
|
|
74
253
|
}
|
|
75
254
|
|
|
76
|
-
function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
83
|
-
|
|
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
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
390
|
+
const geoResult = await ensureGeoIPInstalled();
|
|
391
|
+
actions.geoipInstalled = geoResult.ok;
|
|
215
392
|
geoipInstalled = checkGeoIPInstalled();
|
|
216
|
-
if (!geoipInstalled)
|
|
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
|
-
|
|
402
|
+
const removeResult = uninstallCamoufox();
|
|
403
|
+
actions.browserUninstalled = removeResult.ok;
|
|
223
404
|
camoufoxInstalled = checkCamoufoxInstalled();
|
|
224
|
-
if (camoufoxInstalled)
|
|
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
|
-
|
|
419
|
+
const installResult = installCamoufox();
|
|
420
|
+
actions.browserInstalled = installResult.ok;
|
|
236
421
|
camoufoxInstalled = checkCamoufoxInstalled();
|
|
237
|
-
if (!camoufoxInstalled)
|
|
422
|
+
if (!camoufoxInstalled) {
|
|
423
|
+
operationError = operationError || 'camoufox_install_failed';
|
|
424
|
+
operationDetail = operationDetail || summarizeCommand(installResult.ret);
|
|
425
|
+
}
|
|
238
426
|
}
|
|
239
427
|
if (geoip) {
|
|
240
|
-
|
|
428
|
+
const geoResult = await ensureGeoIPInstalled();
|
|
429
|
+
actions.geoipInstalled = geoResult.ok;
|
|
241
430
|
geoipInstalled = checkGeoIPInstalled();
|
|
242
|
-
if (!geoipInstalled)
|
|
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:
|
|
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);
|