@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
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import {
|
|
5
4
|
ensureProfile,
|
|
6
5
|
resolveFingerprintsRoot,
|
|
7
6
|
resolveNextProfileId,
|
|
8
7
|
resolveProfilesRoot,
|
|
8
|
+
resolveWebautoRoot,
|
|
9
9
|
} from './profilepool.mjs';
|
|
10
10
|
|
|
11
11
|
const INDEX_FILE = 'index.json';
|
|
12
12
|
const META_FILE = 'meta.json';
|
|
13
13
|
const DEFAULT_PLATFORM = 'xiaohongshu';
|
|
14
|
+
const DEFAULT_PROFILE_PREFIX = 'profile';
|
|
14
15
|
const STATUS_ACTIVE = 'active';
|
|
15
16
|
const STATUS_VALID = 'valid';
|
|
16
17
|
const STATUS_INVALID = 'invalid';
|
|
@@ -118,18 +119,6 @@ function ensureSafeName(name, field) {
|
|
|
118
119
|
return value;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
function resolvePortableRoot() {
|
|
122
|
-
const root = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
|
|
123
|
-
return root ? path.join(root, '.webauto') : '';
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function resolveWebautoRoot() {
|
|
127
|
-
const portableRoot = resolvePortableRoot();
|
|
128
|
-
if (portableRoot) return portableRoot;
|
|
129
|
-
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
130
|
-
return path.join(home, '.webauto');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
122
|
export function resolveAccountsRoot() {
|
|
134
123
|
const envRoot = String(process.env.WEBAUTO_PATHS_ACCOUNTS || '').trim();
|
|
135
124
|
if (envRoot) return envRoot;
|
|
@@ -213,21 +202,31 @@ function saveIndex(index) {
|
|
|
213
202
|
return payload;
|
|
214
203
|
}
|
|
215
204
|
|
|
216
|
-
function
|
|
205
|
+
function resolveLegacyProfilePrefix(platform) {
|
|
217
206
|
if (platform === 'xiaohongshu') return 'xiaohongshu-batch';
|
|
218
207
|
const slug = toSlug(platform) || 'account';
|
|
219
208
|
return `${slug}-account`;
|
|
220
209
|
}
|
|
221
210
|
|
|
211
|
+
function resolveProfilePrefix() {
|
|
212
|
+
return DEFAULT_PROFILE_PREFIX;
|
|
213
|
+
}
|
|
214
|
+
|
|
222
215
|
function resolveProfileSeq(profileId, platform) {
|
|
223
216
|
const value = String(profileId || '').trim();
|
|
224
217
|
if (!value) return null;
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
218
|
+
const prefixes = [
|
|
219
|
+
resolveProfilePrefix(),
|
|
220
|
+
resolveLegacyProfilePrefix(platform),
|
|
221
|
+
].filter(Boolean);
|
|
222
|
+
for (const prefix of prefixes) {
|
|
223
|
+
const match = value.match(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}-([0-9]+)$`));
|
|
224
|
+
if (!match) continue;
|
|
225
|
+
const seq = Number(match[1]);
|
|
226
|
+
if (!Number.isFinite(seq) || seq < 0) return null;
|
|
227
|
+
return seq;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
231
230
|
}
|
|
232
231
|
|
|
233
232
|
function resolveUsedAutoSeq(index, platform) {
|
|
@@ -280,23 +279,34 @@ function resolveAccountOrThrow(index, key) {
|
|
|
280
279
|
throw new Error(`account not found: ${idOrAlias}`);
|
|
281
280
|
}
|
|
282
281
|
|
|
283
|
-
function
|
|
282
|
+
function resolveBindingKey(profileId, platform) {
|
|
283
|
+
const pid = String(profileId || '').trim();
|
|
284
|
+
const pf = normalizePlatform(platform);
|
|
285
|
+
if (!pid) return '';
|
|
286
|
+
return `${pid}::${pf}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function resolveAccountByProfile(index, profileId, platform = null) {
|
|
284
290
|
const value = String(profileId || '').trim();
|
|
285
291
|
if (!value) return null;
|
|
292
|
+
const wantedPlatform = platform ? normalizePlatform(platform) : null;
|
|
286
293
|
let matched = null;
|
|
287
294
|
for (const item of index.accounts) {
|
|
288
295
|
if (String(item?.profileId || '').trim() !== value) continue;
|
|
296
|
+
if (wantedPlatform && normalizePlatform(item?.platform) !== wantedPlatform) continue;
|
|
289
297
|
matched = pickNewerRecord(matched, item);
|
|
290
298
|
}
|
|
291
299
|
return matched;
|
|
292
300
|
}
|
|
293
301
|
|
|
294
|
-
function resolveAccountByAccountId(index, accountId) {
|
|
302
|
+
function resolveAccountByAccountId(index, accountId, platform = null) {
|
|
295
303
|
const value = normalizeText(accountId);
|
|
296
304
|
if (!value) return null;
|
|
305
|
+
const wantedPlatform = platform ? normalizePlatform(platform) : null;
|
|
297
306
|
let matched = null;
|
|
298
307
|
for (const item of index.accounts) {
|
|
299
308
|
if (normalizeText(item?.accountId) !== value) continue;
|
|
309
|
+
if (wantedPlatform && normalizePlatform(item?.platform) !== wantedPlatform) continue;
|
|
300
310
|
matched = pickNewerRecord(matched, item);
|
|
301
311
|
}
|
|
302
312
|
return matched;
|
|
@@ -342,39 +352,76 @@ function buildProfileAccountView(profileId, record = null) {
|
|
|
342
352
|
};
|
|
343
353
|
}
|
|
344
354
|
|
|
345
|
-
export function listAccountProfiles() {
|
|
355
|
+
export function listAccountProfiles(options = {}) {
|
|
346
356
|
const index = loadIndex();
|
|
357
|
+
const platformFilter = normalizeText(options?.platform) ? normalizePlatform(options.platform) : null;
|
|
347
358
|
const byAccountId = new Map();
|
|
348
359
|
for (const record of index.accounts) {
|
|
360
|
+
const recordPlatform = normalizePlatform(record?.platform);
|
|
361
|
+
if (platformFilter && recordPlatform !== platformFilter) continue;
|
|
349
362
|
const accountId = normalizeText(record?.accountId);
|
|
350
363
|
if (!accountId) continue;
|
|
351
|
-
|
|
364
|
+
const key = `${recordPlatform}::${accountId}`;
|
|
365
|
+
byAccountId.set(key, pickNewerRecord(byAccountId.get(key), record));
|
|
352
366
|
}
|
|
353
367
|
const deduped = [];
|
|
354
368
|
for (const record of index.accounts) {
|
|
369
|
+
const recordPlatform = normalizePlatform(record?.platform);
|
|
370
|
+
if (platformFilter && recordPlatform !== platformFilter) continue;
|
|
355
371
|
const accountId = normalizeText(record?.accountId);
|
|
356
372
|
if (accountId) {
|
|
357
|
-
|
|
373
|
+
const key = `${recordPlatform}::${accountId}`;
|
|
374
|
+
if (byAccountId.get(key) !== record) continue;
|
|
358
375
|
}
|
|
359
376
|
deduped.push(record);
|
|
360
377
|
}
|
|
361
|
-
const
|
|
378
|
+
const byBinding = new Map();
|
|
362
379
|
for (const record of deduped) {
|
|
363
380
|
const profileId = normalizeText(record?.profileId);
|
|
364
381
|
if (!profileId) continue;
|
|
365
|
-
|
|
382
|
+
const key = resolveBindingKey(profileId, record?.platform);
|
|
383
|
+
if (!key) continue;
|
|
384
|
+
byBinding.set(key, pickNewerRecord(byBinding.get(key), record));
|
|
385
|
+
}
|
|
386
|
+
const rows = Array.from(byBinding.values())
|
|
387
|
+
.sort((a, b) => {
|
|
388
|
+
const seqCmp = Number(a?.seq || 0) - Number(b?.seq || 0);
|
|
389
|
+
if (seqCmp !== 0) return seqCmp;
|
|
390
|
+
const pCmp = String(a?.profileId || '').localeCompare(String(b?.profileId || ''));
|
|
391
|
+
if (pCmp !== 0) return pCmp;
|
|
392
|
+
return normalizePlatform(a?.platform).localeCompare(normalizePlatform(b?.platform));
|
|
393
|
+
})
|
|
394
|
+
.map((record) => buildProfileAccountView(record?.profileId, record));
|
|
395
|
+
const validProfilesSet = new Set();
|
|
396
|
+
const invalidProfilesSet = new Set();
|
|
397
|
+
const validProfilesByPlatform = {};
|
|
398
|
+
const invalidProfilesByPlatform = {};
|
|
399
|
+
for (const row of rows) {
|
|
400
|
+
const platform = normalizePlatform(row.platform);
|
|
401
|
+
if (!validProfilesByPlatform[platform]) validProfilesByPlatform[platform] = [];
|
|
402
|
+
if (!invalidProfilesByPlatform[platform]) invalidProfilesByPlatform[platform] = [];
|
|
403
|
+
if (row.valid) {
|
|
404
|
+
validProfilesSet.add(row.profileId);
|
|
405
|
+
if (!validProfilesByPlatform[platform].includes(row.profileId)) {
|
|
406
|
+
validProfilesByPlatform[platform].push(row.profileId);
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
invalidProfilesSet.add(row.profileId);
|
|
410
|
+
if (!invalidProfilesByPlatform[platform].includes(row.profileId)) {
|
|
411
|
+
invalidProfilesByPlatform[platform].push(row.profileId);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
366
414
|
}
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
.map(([profileId, record]) => buildProfileAccountView(profileId, record));
|
|
370
|
-
const validProfiles = rows.filter((item) => item.valid).map((item) => item.profileId);
|
|
371
|
-
const invalidProfiles = rows.filter((item) => !item.valid).map((item) => item.profileId);
|
|
415
|
+
const validProfiles = Array.from(validProfilesSet);
|
|
416
|
+
const invalidProfiles = Array.from(invalidProfilesSet).filter((profileId) => !validProfilesSet.has(profileId));
|
|
372
417
|
return {
|
|
373
418
|
root: resolveAccountsRoot(),
|
|
374
419
|
count: rows.length,
|
|
375
420
|
profiles: rows,
|
|
376
421
|
validProfiles,
|
|
377
422
|
invalidProfiles,
|
|
423
|
+
validProfilesByPlatform,
|
|
424
|
+
invalidProfilesByPlatform,
|
|
378
425
|
};
|
|
379
426
|
}
|
|
380
427
|
|
|
@@ -426,6 +473,39 @@ export async function addAccount(input = {}) {
|
|
|
426
473
|
status === STATUS_VALID
|
|
427
474
|
? null
|
|
428
475
|
: (status === STATUS_PENDING ? 'waiting_login' : 'missing_account_id');
|
|
476
|
+
|
|
477
|
+
if (accountId) {
|
|
478
|
+
const existing = resolveAccountByAccountId(index, accountId, platform);
|
|
479
|
+
if (existing) {
|
|
480
|
+
const next = {
|
|
481
|
+
...existing,
|
|
482
|
+
platform,
|
|
483
|
+
status,
|
|
484
|
+
valid: status === STATUS_VALID,
|
|
485
|
+
reason,
|
|
486
|
+
accountId,
|
|
487
|
+
name: accountId,
|
|
488
|
+
alias: alias || existing.alias || null,
|
|
489
|
+
username: normalizeText(input.username) || existing.username || null,
|
|
490
|
+
profileId,
|
|
491
|
+
fingerprintId,
|
|
492
|
+
updatedAt: createdAt,
|
|
493
|
+
aliasSource: alias ? (normalizeAlias(input.alias) ? 'manual' : 'username') : existing.aliasSource || null,
|
|
494
|
+
};
|
|
495
|
+
if (alias) ensureAliasUnique(index.accounts, alias, existing.id);
|
|
496
|
+
const rowIndex = index.accounts.findIndex((item) => item?.id === existing.id);
|
|
497
|
+
if (rowIndex < 0) throw new Error(`account not found: ${existing.id}`);
|
|
498
|
+
index.accounts[rowIndex] = next;
|
|
499
|
+
saveIndex(index);
|
|
500
|
+
persistAccountMeta(next);
|
|
501
|
+
return {
|
|
502
|
+
root: resolveAccountsRoot(),
|
|
503
|
+
account: next,
|
|
504
|
+
deduped: true,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
429
509
|
const account = {
|
|
430
510
|
id,
|
|
431
511
|
seq,
|
|
@@ -553,8 +633,8 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
553
633
|
const status = accountId ? STATUS_VALID : (pendingMode ? STATUS_PENDING : STATUS_INVALID);
|
|
554
634
|
|
|
555
635
|
const index = loadIndex();
|
|
556
|
-
const existingByProfile = resolveAccountByProfile(index, profileId);
|
|
557
|
-
const existingByAccountId = accountId ? resolveAccountByAccountId(index, accountId) : null;
|
|
636
|
+
const existingByProfile = resolveAccountByProfile(index, profileId, platform);
|
|
637
|
+
const existingByAccountId = accountId ? resolveAccountByAccountId(index, accountId, platform) : null;
|
|
558
638
|
let target = existingByAccountId || existingByProfile || null;
|
|
559
639
|
const purgeIds = new Set();
|
|
560
640
|
|
|
@@ -611,7 +691,11 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
611
691
|
}
|
|
612
692
|
|
|
613
693
|
const staleIds = index.accounts
|
|
614
|
-
.filter((item) =>
|
|
694
|
+
.filter((item) => (
|
|
695
|
+
String(item?.profileId || '').trim() === profileId
|
|
696
|
+
&& normalizePlatform(item?.platform) === platform
|
|
697
|
+
&& !hasPersistentAccountId(item)
|
|
698
|
+
))
|
|
615
699
|
.map((item) => String(item?.id || '').trim())
|
|
616
700
|
.filter(Boolean);
|
|
617
701
|
if (staleIds.length > 0) {
|
|
@@ -677,6 +761,7 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
677
761
|
if (accountId) {
|
|
678
762
|
for (const row of index.accounts) {
|
|
679
763
|
if (!row || row.id === target.id) continue;
|
|
764
|
+
if (normalizePlatform(row?.platform) !== platform) continue;
|
|
680
765
|
if (normalizeText(row.accountId) === accountId) {
|
|
681
766
|
purgeIds.add(row.id);
|
|
682
767
|
}
|
|
@@ -727,6 +812,7 @@ export function markProfileInvalid(profileId, reason = 'login_guard') {
|
|
|
727
812
|
const id = ensureSafeName(normalizeText(profileId), 'profileId');
|
|
728
813
|
return upsertProfileAccountState({
|
|
729
814
|
profileId: id,
|
|
815
|
+
platform: DEFAULT_PLATFORM,
|
|
730
816
|
accountId: null,
|
|
731
817
|
reason,
|
|
732
818
|
});
|
|
@@ -736,6 +822,7 @@ export function markProfilePending(profileId, reason = 'waiting_login') {
|
|
|
736
822
|
const id = ensureSafeName(normalizeText(profileId), 'profileId');
|
|
737
823
|
return upsertProfileAccountState({
|
|
738
824
|
profileId: id,
|
|
825
|
+
platform: DEFAULT_PLATFORM,
|
|
739
826
|
accountId: null,
|
|
740
827
|
status: STATUS_PENDING,
|
|
741
828
|
reason,
|
|
@@ -2,32 +2,64 @@ import os from 'node:os';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function hasDrive(letter) {
|
|
6
|
+
if (process.platform !== 'win32') return false;
|
|
7
|
+
try {
|
|
8
|
+
return fs.existsSync(`${String(letter || '').replace(/[^A-Za-z]/g, '').toUpperCase()}:\\`);
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
8
12
|
}
|
|
9
13
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
function normalizePathForPlatform(input, platform = process.platform) {
|
|
15
|
+
const raw = String(input || '').trim();
|
|
16
|
+
const isWinPath = platform === 'win32' || /^[A-Za-z]:[\\/]/.test(raw);
|
|
17
|
+
const pathApi = isWinPath ? path.win32 : path;
|
|
18
|
+
return isWinPath ? pathApi.normalize(raw) : path.resolve(raw);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeLegacyWebautoRoot(input, platform = process.platform) {
|
|
22
|
+
const pathApi = platform === 'win32' ? path.win32 : path;
|
|
23
|
+
const resolved = normalizePathForPlatform(input, platform);
|
|
24
|
+
const base = pathApi.basename(resolved).toLowerCase();
|
|
25
|
+
if (base === '.webauto' || base === 'webauto') return resolved;
|
|
26
|
+
return pathApi.join(resolved, '.webauto');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveHomeDir(platform = process.platform) {
|
|
30
|
+
if (platform === 'win32') return process.env.USERPROFILE || os.homedir();
|
|
14
31
|
return process.env.HOME || os.homedir();
|
|
15
32
|
}
|
|
16
33
|
|
|
34
|
+
export function resolveWebautoRoot(options = {}) {
|
|
35
|
+
const env = options.env || process.env;
|
|
36
|
+
const platform = String(options.platform || process.platform);
|
|
37
|
+
const homeDir = String(options.homeDir || resolveHomeDir(platform));
|
|
38
|
+
const pathApi = platform === 'win32' ? path.win32 : path;
|
|
39
|
+
|
|
40
|
+
const explicitHome = String(env.WEBAUTO_HOME || '').trim();
|
|
41
|
+
if (explicitHome) return normalizePathForPlatform(explicitHome, platform);
|
|
42
|
+
|
|
43
|
+
const legacyRoot = String(env.WEBAUTO_ROOT || env.WEBAUTO_PORTABLE_ROOT || '').trim();
|
|
44
|
+
if (legacyRoot) return normalizeLegacyWebautoRoot(legacyRoot, platform);
|
|
45
|
+
|
|
46
|
+
if (platform === 'win32') {
|
|
47
|
+
const dDriveExists = typeof options.hasDDrive === 'boolean' ? options.hasDDrive : hasDrive('D');
|
|
48
|
+
return dDriveExists ? 'D:\\webauto' : pathApi.join(homeDir, '.webauto');
|
|
49
|
+
}
|
|
50
|
+
return pathApi.join(homeDir, '.webauto');
|
|
51
|
+
}
|
|
52
|
+
|
|
17
53
|
export function resolveProfilesRoot() {
|
|
18
54
|
const envProfiles = String(process.env.WEBAUTO_PATHS_PROFILES || '').trim();
|
|
19
55
|
if (envProfiles) return envProfiles;
|
|
20
|
-
|
|
21
|
-
if (portableRoot) return path.join(portableRoot, 'profiles');
|
|
22
|
-
return path.join(resolveHomeDir(), '.webauto', 'profiles');
|
|
56
|
+
return path.join(resolveWebautoRoot(), 'profiles');
|
|
23
57
|
}
|
|
24
58
|
|
|
25
59
|
export function resolveFingerprintsRoot() {
|
|
26
60
|
const envFps = String(process.env.WEBAUTO_PATHS_FINGERPRINTS || '').trim();
|
|
27
61
|
if (envFps) return envFps;
|
|
28
|
-
|
|
29
|
-
if (portableRoot) return path.join(portableRoot, 'fingerprints');
|
|
30
|
-
return path.join(resolveHomeDir(), '.webauto', 'fingerprints');
|
|
62
|
+
return path.join(resolveWebautoRoot(), 'fingerprints');
|
|
31
63
|
}
|
|
32
64
|
|
|
33
65
|
export function listProfiles() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
2
|
import path from 'node:path';
|
|
3
|
+
import { resolveWebautoRoot } from './profilepool.mjs';
|
|
4
4
|
|
|
5
5
|
const INDEX_FILE = 'index.json';
|
|
6
6
|
const DEFAULT_COMMAND_TYPE = 'xhs-unified';
|
|
@@ -78,18 +78,6 @@ function normalizeMaxRuns(value, fallback = null) {
|
|
|
78
78
|
return Math.max(1, Math.floor(n));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function resolvePortableRoot() {
|
|
82
|
-
const root = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
|
|
83
|
-
return root ? path.join(root, '.webauto') : '';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function resolveWebautoRoot() {
|
|
87
|
-
const portableRoot = resolvePortableRoot();
|
|
88
|
-
if (portableRoot) return portableRoot;
|
|
89
|
-
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
90
|
-
return path.join(home, '.webauto');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
81
|
export function resolveSchedulesRoot() {
|
|
94
82
|
const explicit = String(process.env.WEBAUTO_PATHS_SCHEDULES || '').trim();
|
|
95
83
|
if (explicit) return explicit;
|
|
@@ -464,20 +452,11 @@ function validateCommand(task) {
|
|
|
464
452
|
|
|
465
453
|
function validateXhsCommand(argv) {
|
|
466
454
|
const keyword = normalizeText(argv.keyword || argv.k);
|
|
467
|
-
const profile = normalizeText(argv.profile);
|
|
468
|
-
const profiles = normalizeText(argv.profiles);
|
|
469
|
-
const profilepool = normalizeText(argv.profilepool);
|
|
470
455
|
if (!keyword) throw new Error('task command argv missing keyword');
|
|
471
|
-
if (!profile && !profiles && !profilepool) {
|
|
472
|
-
throw new Error('task command argv missing profile/profiles/profilepool');
|
|
473
|
-
}
|
|
474
456
|
}
|
|
475
457
|
|
|
476
458
|
function validateGenericCommand(argv, platform, commandType = '') {
|
|
477
459
|
const keyword = normalizeText(argv.keyword || argv.k);
|
|
478
|
-
const profile = normalizeText(argv.profile);
|
|
479
|
-
const profiles = normalizeText(argv.profiles);
|
|
480
|
-
const profilepool = normalizeText(argv.profilepool);
|
|
481
460
|
let weiboTaskType = '';
|
|
482
461
|
if (platform === 'weibo') {
|
|
483
462
|
weiboTaskType = String(argv['task-type'] || argv.taskType || '').trim();
|
|
@@ -495,9 +474,6 @@ function validateGenericCommand(argv, platform, commandType = '') {
|
|
|
495
474
|
if (!keyword && (platform !== 'weibo' || weiboTaskType === 'search')) {
|
|
496
475
|
throw new Error('task command argv missing keyword');
|
|
497
476
|
}
|
|
498
|
-
if (!profile && !profiles && !profilepool) {
|
|
499
|
-
throw new Error('task command argv missing profile/profiles/profilepool');
|
|
500
|
-
}
|
|
501
477
|
}
|
|
502
478
|
|
|
503
479
|
function normalizeScheduleFields(input = {}, fallback = {}) {
|
|
@@ -173,6 +173,46 @@ async function cmdMigrateFingerprints(jsonMode) {
|
|
|
173
173
|
output({ ok: true, checked: profiles.length, ensured: created.length }, jsonMode);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
async function cmdGotoProfile(profileId, argv, jsonMode) {
|
|
177
|
+
const id = String(profileId || '').trim();
|
|
178
|
+
if (!id) throw new Error('profileId is required');
|
|
179
|
+
const url = String(argv.url || argv._?.[2] || '').trim();
|
|
180
|
+
if (!url) throw new Error('url is required');
|
|
181
|
+
await ensureProfile(id);
|
|
182
|
+
|
|
183
|
+
const gotoRet = runCamo(['goto', id, url], { rootDir: ROOT, timeoutMs: 30000 });
|
|
184
|
+
if (gotoRet.ok) {
|
|
185
|
+
output({
|
|
186
|
+
ok: true,
|
|
187
|
+
profileId: id,
|
|
188
|
+
url,
|
|
189
|
+
mode: 'goto',
|
|
190
|
+
result: gotoRet.json || null,
|
|
191
|
+
}, jsonMode);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const idleTimeout = String(argv['idle-timeout'] || process.env.WEBAUTO_LOGIN_IDLE_TIMEOUT || '30m').trim() || '30m';
|
|
196
|
+
const startRet = runCamo(['start', id, '--url', url, '--idle-timeout', idleTimeout], { rootDir: ROOT });
|
|
197
|
+
if (!startRet.ok) {
|
|
198
|
+
output({
|
|
199
|
+
ok: false,
|
|
200
|
+
profileId: id,
|
|
201
|
+
url,
|
|
202
|
+
mode: 'start',
|
|
203
|
+
error: startRet.stderr || startRet.stdout || gotoRet.stderr || gotoRet.stdout || 'goto/start failed',
|
|
204
|
+
}, jsonMode);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
output({
|
|
208
|
+
ok: true,
|
|
209
|
+
profileId: id,
|
|
210
|
+
url,
|
|
211
|
+
mode: 'start',
|
|
212
|
+
session: startRet.json || null,
|
|
213
|
+
}, jsonMode);
|
|
214
|
+
}
|
|
215
|
+
|
|
176
216
|
async function main() {
|
|
177
217
|
const argv = minimist(process.argv.slice(2));
|
|
178
218
|
const cmd = String(argv._[0] || '').trim();
|
|
@@ -180,14 +220,16 @@ async function main() {
|
|
|
180
220
|
const jsonMode = argv.json === true;
|
|
181
221
|
|
|
182
222
|
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
183
|
-
console.log('Usage: node apps/webauto/entry/profilepool.mjs <list|add|login|login-profile|migrate-fingerprints> ... [--json]');
|
|
223
|
+
console.log('Usage: node apps/webauto/entry/profilepool.mjs <list|add|login|login-profile|goto-profile|migrate-fingerprints> ... [--json]');
|
|
224
|
+
console.log('Default profile prefix: profile (e.g. profile-0, profile-1)');
|
|
184
225
|
return;
|
|
185
226
|
}
|
|
186
227
|
|
|
187
228
|
if (cmd === 'list') return cmdList(arg1, jsonMode);
|
|
188
|
-
if (cmd === 'add') return cmdAdd(arg1 || '
|
|
229
|
+
if (cmd === 'add') return cmdAdd(arg1 || 'profile', jsonMode);
|
|
189
230
|
if (cmd === 'login-profile') return cmdLoginProfile(arg1, argv, jsonMode);
|
|
190
|
-
if (cmd === '
|
|
231
|
+
if (cmd === 'goto-profile') return cmdGotoProfile(arg1, argv, jsonMode);
|
|
232
|
+
if (cmd === 'login') return cmdLogin(arg1 || 'profile', argv, jsonMode);
|
|
191
233
|
if (cmd === 'migrate-fingerprints') return cmdMigrateFingerprints(jsonMode);
|
|
192
234
|
|
|
193
235
|
throw new Error(`unknown command: ${cmd}`);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
resolveSchedulesRoot,
|
|
23
23
|
updateScheduleTask,
|
|
24
24
|
} from './lib/schedule-store.mjs';
|
|
25
|
+
import { listAccountProfiles } from './lib/account-store.mjs';
|
|
25
26
|
|
|
26
27
|
let xhsRunnerPromise = null;
|
|
27
28
|
let weiboRunnerPromise = null;
|
|
@@ -70,6 +71,46 @@ function parseJson(text, fallback = {}) {
|
|
|
70
71
|
return JSON.parse(raw.replace(/^\uFEFF/, ''));
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
function normalizePlatformByCommandType(commandType) {
|
|
75
|
+
const value = String(commandType || '').trim().toLowerCase();
|
|
76
|
+
if (value.startsWith('weibo')) return 'weibo';
|
|
77
|
+
if (value.startsWith('1688')) return '1688';
|
|
78
|
+
return 'xiaohongshu';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function hasProfileArg(argv = {}) {
|
|
82
|
+
return Boolean(
|
|
83
|
+
String(argv?.profile || '').trim()
|
|
84
|
+
|| String(argv?.profiles || '').trim()
|
|
85
|
+
|| String(argv?.profilepool || '').trim(),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function pickAutoProfile(platform) {
|
|
90
|
+
const rows = listAccountProfiles({ platform }).profiles || [];
|
|
91
|
+
const validRows = rows
|
|
92
|
+
.filter((row) => row?.valid === true && String(row?.accountId || '').trim())
|
|
93
|
+
.sort((a, b) => {
|
|
94
|
+
const ta = Date.parse(String(a?.updatedAt || '')) || 0;
|
|
95
|
+
const tb = Date.parse(String(b?.updatedAt || '')) || 0;
|
|
96
|
+
if (tb !== ta) return tb - ta;
|
|
97
|
+
return String(a?.profileId || '').localeCompare(String(b?.profileId || ''));
|
|
98
|
+
});
|
|
99
|
+
return String(validRows[0]?.profileId || '').trim();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function ensureProfileArgForTask(commandType, commandArgv = {}) {
|
|
103
|
+
const argv = commandArgv && typeof commandArgv === 'object' ? { ...commandArgv } : {};
|
|
104
|
+
if (hasProfileArg(argv)) return argv;
|
|
105
|
+
const platform = normalizePlatformByCommandType(commandType);
|
|
106
|
+
const profile = pickAutoProfile(platform);
|
|
107
|
+
if (!profile) {
|
|
108
|
+
throw new Error(`missing profile/profiles/profilepool and no valid account for platform=${platform}`);
|
|
109
|
+
}
|
|
110
|
+
argv.profile = profile;
|
|
111
|
+
return argv;
|
|
112
|
+
}
|
|
113
|
+
|
|
73
114
|
function safeReadJsonFile(filePath) {
|
|
74
115
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
75
116
|
return parseJson(raw, {});
|
|
@@ -265,14 +306,15 @@ async function executeTask(task, options = {}) {
|
|
|
265
306
|
const quietExecutors = options.quietExecutors === true;
|
|
266
307
|
try {
|
|
267
308
|
const commandType = String(task?.commandType || 'xhs-unified').trim();
|
|
309
|
+
const commandArgv = ensureProfileArgForTask(commandType, task?.commandArgv || {});
|
|
268
310
|
const result = await withConsoleSilenced(quietExecutors, async () => {
|
|
269
311
|
if (commandType === 'xhs-unified') {
|
|
270
312
|
const runUnified = await getXhsRunner();
|
|
271
|
-
return runUnified(
|
|
313
|
+
return runUnified(commandArgv);
|
|
272
314
|
}
|
|
273
315
|
if (commandType.startsWith('weibo-')) {
|
|
274
316
|
const runWeiboUnified = await getWeiboRunner();
|
|
275
|
-
return runWeiboUnified(
|
|
317
|
+
return runWeiboUnified(commandArgv);
|
|
276
318
|
}
|
|
277
319
|
if (commandType === '1688-search') {
|
|
278
320
|
throw new Error(`executor_not_implemented: ${commandType}`);
|
|
@@ -4,7 +4,7 @@ import { runWorkflowById } from '../../../dist/modules/workflow/src/runner.js';
|
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
|
|
6
6
|
const WEIBO_HOME_URL = 'https://www.weibo.com';
|
|
7
|
-
const DEFAULT_PROFILE = '
|
|
7
|
+
const DEFAULT_PROFILE = 'profile-0';
|
|
8
8
|
|
|
9
9
|
async function runCommand(argv) {
|
|
10
10
|
const profile = String(argv.profile || DEFAULT_PROFILE).trim();
|
|
@@ -106,7 +106,7 @@ if (isDirectExec) {
|
|
|
106
106
|
export async function runWeiboUnified(argv) {
|
|
107
107
|
const workflowId = String(argv.workflow || 'weibo-search-v1').trim();
|
|
108
108
|
const keyword = String(argv.keyword || argv.k || '').trim();
|
|
109
|
-
const profile = String(argv.profile ||
|
|
109
|
+
const profile = String(argv.profile || DEFAULT_PROFILE).trim();
|
|
110
110
|
const targetCount = Number(argv['max-notes'] || argv.target || argv['max-notes'] || 50);
|
|
111
111
|
const maxComments = Number(argv['max-comments'] || 0);
|
|
112
112
|
const env = String(argv.env || 'debug').trim();
|