@web-auto/webauto 0.1.18 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -53
- package/apps/desktop-console/dist/main/index.mjs +227 -12
- package/apps/desktop-console/dist/renderer/index.js +237 -8
- package/apps/desktop-console/entry/ui-cli.mjs +282 -16
- package/apps/desktop-console/entry/ui-console.mjs +46 -15
- package/apps/webauto/entry/account.mjs +126 -27
- package/apps/webauto/entry/lib/account-detect.mjs +399 -9
- package/apps/webauto/entry/lib/account-store.mjs +201 -109
- package/apps/webauto/entry/lib/iflow-reply.mjs +194 -0
- package/apps/webauto/entry/lib/profile-policy.mjs +48 -0
- package/apps/webauto/entry/lib/profilepool.mjs +12 -0
- package/apps/webauto/entry/lib/schedule-store.mjs +29 -2
- package/apps/webauto/entry/lib/session-init.mjs +227 -0
- package/apps/webauto/entry/lib/upgrade-check.mjs +269 -0
- package/apps/webauto/entry/lib/xhs-unified-blocks.mjs +160 -0
- package/apps/webauto/entry/lib/xhs-unified-output-blocks.mjs +83 -0
- package/apps/webauto/entry/lib/xhs-unified-plan-blocks.mjs +55 -0
- package/apps/webauto/entry/lib/xhs-unified-profile-blocks.mjs +542 -0
- package/apps/webauto/entry/lib/xhs-unified-runtime-blocks.mjs +436 -0
- package/apps/webauto/entry/profilepool.mjs +56 -9
- package/apps/webauto/entry/smart-reply-cli.mjs +267 -0
- package/apps/webauto/entry/weibo-unified.mjs +84 -11
- package/apps/webauto/entry/xhs-orchestrate.mjs +43 -1
- package/apps/webauto/entry/xhs-unified.mjs +92 -997
- package/bin/webauto.mjs +22 -4
- package/dist/modules/camo-backend/src/index.js +33 -0
- package/dist/modules/camo-backend/src/internal/BrowserSession.js +232 -49
- package/dist/modules/camo-backend/src/internal/engine-manager.js +14 -13
- package/dist/modules/camo-backend/src/internal/ws-server.js +16 -19
- package/dist/modules/camo-runtime/src/utils/browser-service.mjs +38 -6
- package/dist/modules/workflow/blocks/EnsureSession.js +0 -8
- package/dist/modules/workflow/blocks/WeiboCollectFromLinksBlock.js +78 -6
- package/dist/modules/workflow/blocks/WeiboCollectSearchLinksBlock.js +266 -192
- package/dist/modules/workflow/definitions/weibo-search-workflow-v1.js +2 -0
- package/dist/modules/workflow/src/runner.js +2 -0
- package/dist/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +150 -37
- package/dist/modules/xiaohongshu/app/src/blocks/SmartReplyBlock.js +491 -0
- package/modules/camo-backend/src/index.ts +31 -0
- package/modules/camo-backend/src/internal/BrowserSession.ts +224 -53
- package/modules/camo-backend/src/internal/engine-manager.ts +14 -15
- package/modules/camo-backend/src/internal/ws-server.ts +17 -17
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/common.mjs +12 -2
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/persistence.mjs +57 -0
- package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +2475 -243
- package/modules/camo-runtime/src/autoscript/runtime.mjs +35 -30
- package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +80 -443
- package/modules/camo-runtime/src/container/runtime-core/checkpoint.mjs +39 -6
- package/modules/camo-runtime/src/container/runtime-core/operations/index.mjs +206 -39
- package/modules/camo-runtime/src/container/runtime-core/operations/tab-pool.mjs +0 -79
- package/modules/camo-runtime/src/container/runtime-core/operations/viewport.mjs +46 -0
- package/modules/camo-runtime/src/utils/browser-service.mjs +41 -6
- package/modules/camo-runtime/src/utils/js-policy.mjs +28 -0
- package/modules/workflow/blocks/EnsureSession.ts +0 -4
- package/modules/workflow/blocks/WeiboCollectFromLinksBlock.ts +81 -6
- package/modules/workflow/blocks/WeiboCollectSearchLinksBlock.ts +316 -0
- package/modules/workflow/definitions/weibo-search-workflow-v1.ts +2 -0
- package/modules/workflow/src/runner.ts +2 -0
- package/modules/xiaohongshu/app/src/blocks/ReplyInteractBlock.ts +198 -53
- package/modules/xiaohongshu/app/src/blocks/SmartReplyBlock.ts +706 -0
- package/package.json +2 -2
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/comments.mjs +0 -498
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/detail.mjs +0 -181
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +0 -691
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +0 -388
- package/modules/camo-runtime/src/container/runtime-core/operations/selector-scripts.mjs +0 -135
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
import { BROWSER_SERVICE_URL, loadConfig, setRepoRoot } from './config.mjs';
|
|
9
9
|
const requireFromHere = createRequire(import.meta.url);
|
|
10
10
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const DEFAULT_API_TIMEOUT_MS = 30000;
|
|
11
12
|
function resolveNodeBin() {
|
|
12
13
|
const explicit = String(process.env.WEBAUTO_NODE_BIN || '').trim();
|
|
13
14
|
if (explicit)
|
|
@@ -53,12 +54,43 @@ function runCamoCli(args = [], options = {}) {
|
|
|
53
54
|
entry,
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
function resolveApiTimeoutMs(options = {}) {
|
|
58
|
+
const optionValue = Number(options?.timeoutMs);
|
|
59
|
+
if (Number.isFinite(optionValue) && optionValue > 0) {
|
|
60
|
+
return Math.max(1000, Math.floor(optionValue));
|
|
61
|
+
}
|
|
62
|
+
const envValue = Number(process.env.CAMO_API_TIMEOUT_MS);
|
|
63
|
+
if (Number.isFinite(envValue) && envValue > 0) {
|
|
64
|
+
return Math.max(1000, Math.floor(envValue));
|
|
65
|
+
}
|
|
66
|
+
return DEFAULT_API_TIMEOUT_MS;
|
|
67
|
+
}
|
|
68
|
+
function isTimeoutError(error) {
|
|
69
|
+
const name = String(error?.name || '').toLowerCase();
|
|
70
|
+
const message = String(error?.message || '').toLowerCase();
|
|
71
|
+
return (name.includes('timeout')
|
|
72
|
+
|| name.includes('abort')
|
|
73
|
+
|| message.includes('timeout')
|
|
74
|
+
|| message.includes('timed out')
|
|
75
|
+
|| message.includes('aborted'));
|
|
76
|
+
}
|
|
77
|
+
export async function callAPI(action, payload = {}, options = {}) {
|
|
78
|
+
const timeoutMs = resolveApiTimeoutMs(options);
|
|
79
|
+
let r;
|
|
80
|
+
try {
|
|
81
|
+
r = await fetch(`${BROWSER_SERVICE_URL}/command`, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
body: JSON.stringify({ action, args: payload }),
|
|
85
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (isTimeoutError(error)) {
|
|
90
|
+
throw new Error(`browser-service timeout after ${timeoutMs}ms: ${action}`);
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
62
94
|
let body;
|
|
63
95
|
try {
|
|
64
96
|
body = await r.json();
|
|
@@ -160,8 +160,6 @@ export async function execute(input) {
|
|
|
160
160
|
return 1;
|
|
161
161
|
}
|
|
162
162
|
async function clearCssZoom() {
|
|
163
|
-
if (!profileId.startsWith('xiaohongshu_'))
|
|
164
|
-
return;
|
|
165
163
|
try {
|
|
166
164
|
await fetch(statusUrl, {
|
|
167
165
|
method: 'POST',
|
|
@@ -184,8 +182,6 @@ export async function execute(input) {
|
|
|
184
182
|
}
|
|
185
183
|
}
|
|
186
184
|
async function applyZoom() {
|
|
187
|
-
if (!profileId.startsWith('xiaohongshu_'))
|
|
188
|
-
return;
|
|
189
185
|
const metrics = await loadMetrics();
|
|
190
186
|
const zoom = resolveZoom(metrics);
|
|
191
187
|
if (zoom === null) {
|
|
@@ -239,8 +235,6 @@ export async function execute(input) {
|
|
|
239
235
|
return null;
|
|
240
236
|
}
|
|
241
237
|
async function applyBrowserZoom() {
|
|
242
|
-
if (!profileId.startsWith('xiaohongshu_'))
|
|
243
|
-
return;
|
|
244
238
|
const target = resolveBrowserZoomTarget();
|
|
245
239
|
if (!target || Math.abs(target - 1) < 0.01)
|
|
246
240
|
return;
|
|
@@ -278,8 +272,6 @@ export async function execute(input) {
|
|
|
278
272
|
console.log(`[EnsureSession] browser zoom applied target~${target} steps=${steps} key=${key} profile=${profileId}`);
|
|
279
273
|
}
|
|
280
274
|
async function resetBrowserZoom() {
|
|
281
|
-
if (!profileId.startsWith('xiaohongshu_'))
|
|
282
|
-
return;
|
|
283
275
|
if (process.env.WEBAUTO_RESET_BROWSER_ZOOM === '0')
|
|
284
276
|
return;
|
|
285
277
|
const key = os.platform() === 'darwin' ? 'Meta+0' : 'Control+0';
|
|
@@ -96,9 +96,11 @@ async function saveMarkdown(keywordDir, entry, content, comments, commentStats,
|
|
|
96
96
|
}
|
|
97
97
|
export async function execute(input) {
|
|
98
98
|
const { sessionId, keyword, env = 'debug', targetCount, maxComments = 0, collectComments: enableComments = false, // 默认不采集评论,加快速度
|
|
99
|
-
serviceUrl = 'http://127.0.0.1:7704', } = input;
|
|
99
|
+
tabCount: requestedTabCount = 1, tabOpenDelayMs: requestedTabOpenDelayMs = 800, serviceUrl = 'http://127.0.0.1:7704', } = input;
|
|
100
100
|
const profile = sessionId;
|
|
101
101
|
const controllerUrl = `${serviceUrl}/command`;
|
|
102
|
+
const tabCount = Math.max(1, Math.min(8, Number(requestedTabCount || 1) || 1));
|
|
103
|
+
const tabOpenDelayMs = Math.max(0, Number(requestedTabOpenDelayMs || 0) || 0);
|
|
102
104
|
const keywordDir = path.join(resolveDownloadRoot(), 'weibo', env, sanitizeFilenamePart(keyword));
|
|
103
105
|
const linksPath = path.join(keywordDir, 'phase2-links.jsonl');
|
|
104
106
|
const links = await readJsonl(linksPath);
|
|
@@ -109,7 +111,7 @@ export async function execute(input) {
|
|
|
109
111
|
linksPath,
|
|
110
112
|
processedCount: 0,
|
|
111
113
|
persistedCount: 0,
|
|
112
|
-
stats: { postsProcessed: 0, totalComments: 0, errors: 0 },
|
|
114
|
+
stats: { postsProcessed: 0, totalComments: 0, errors: 0, tabsUsed: 0 },
|
|
113
115
|
error: 'No links found in phase2-links.jsonl',
|
|
114
116
|
};
|
|
115
117
|
}
|
|
@@ -153,6 +155,67 @@ export async function execute(input) {
|
|
|
153
155
|
await controllerAction('goto', { url });
|
|
154
156
|
await new Promise(r => setTimeout(r, 500)); // 减少间隔 // 减少等待时间
|
|
155
157
|
}
|
|
158
|
+
async function listPagesDetailed() {
|
|
159
|
+
const res = await controllerAction('browser:page:list', { profileId: profile });
|
|
160
|
+
const value = unwrapResult(res);
|
|
161
|
+
const pages = Array.isArray(value?.pages) ? value.pages : (Array.isArray(value) ? value : []);
|
|
162
|
+
return pages
|
|
163
|
+
.map((item) => ({
|
|
164
|
+
index: Number(item?.index),
|
|
165
|
+
url: String(item?.url || ''),
|
|
166
|
+
active: item?.active === true,
|
|
167
|
+
}))
|
|
168
|
+
.filter((item) => Number.isFinite(item.index));
|
|
169
|
+
}
|
|
170
|
+
async function switchToPage(index) {
|
|
171
|
+
await controllerAction('browser:page:switch', { profileId: profile, index });
|
|
172
|
+
await new Promise((r) => setTimeout(r, 260));
|
|
173
|
+
}
|
|
174
|
+
async function openNewTabAndResolveIndex(existingIndexes) {
|
|
175
|
+
await controllerAction('system:shortcut', { app: 'camoufox', shortcut: 'new-tab' });
|
|
176
|
+
if (tabOpenDelayMs > 0) {
|
|
177
|
+
await new Promise((r) => setTimeout(r, tabOpenDelayMs));
|
|
178
|
+
}
|
|
179
|
+
const after = await listPagesDetailed();
|
|
180
|
+
const active = after.find((item) => item.active);
|
|
181
|
+
if (active && !existingIndexes.has(active.index))
|
|
182
|
+
return active.index;
|
|
183
|
+
const added = after.find((item) => !existingIndexes.has(item.index));
|
|
184
|
+
if (added)
|
|
185
|
+
return added.index;
|
|
186
|
+
const fallback = after
|
|
187
|
+
.map((item) => item.index)
|
|
188
|
+
.filter((idx) => !existingIndexes.has(idx))
|
|
189
|
+
.sort((a, b) => a - b);
|
|
190
|
+
return fallback.length > 0 ? fallback[fallback.length - 1] : null;
|
|
191
|
+
}
|
|
192
|
+
async function ensureTabPool(count) {
|
|
193
|
+
const pages = await listPagesDetailed().catch(() => []);
|
|
194
|
+
const active = pages.find((item) => item.active);
|
|
195
|
+
const pool = [];
|
|
196
|
+
if (active && Number.isFinite(active.index)) {
|
|
197
|
+
pool.push(active.index);
|
|
198
|
+
}
|
|
199
|
+
else if (pages.length > 0) {
|
|
200
|
+
pool.push(pages[0].index);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
pool.push(0);
|
|
204
|
+
}
|
|
205
|
+
while (pool.length < count) {
|
|
206
|
+
const idx = await openNewTabAndResolveIndex(new Set(pool));
|
|
207
|
+
if (!Number.isFinite(Number(idx)))
|
|
208
|
+
break;
|
|
209
|
+
const next = Number(idx);
|
|
210
|
+
if (pool.includes(next))
|
|
211
|
+
break;
|
|
212
|
+
pool.push(next);
|
|
213
|
+
}
|
|
214
|
+
if (pool.length > 0) {
|
|
215
|
+
await switchToPage(pool[0]).catch(() => null);
|
|
216
|
+
}
|
|
217
|
+
return pool;
|
|
218
|
+
}
|
|
156
219
|
async function extractPostContent() {
|
|
157
220
|
const script = `
|
|
158
221
|
(() => {
|
|
@@ -223,12 +286,20 @@ export async function execute(input) {
|
|
|
223
286
|
let persistedCount = 0;
|
|
224
287
|
let totalComments = 0;
|
|
225
288
|
let errors = 0;
|
|
289
|
+
let tabsUsed = 1;
|
|
226
290
|
try {
|
|
227
|
-
const targetLinks = links.slice(0, targetCount);
|
|
228
|
-
|
|
291
|
+
const targetLinks = links.slice(0, Math.max(1, Number(targetCount || 0) || 1));
|
|
292
|
+
const tabPool = await ensureTabPool(tabCount).catch(() => [0]);
|
|
293
|
+
const roundRobinTabs = tabPool.length > 0 ? tabPool : [0];
|
|
294
|
+
tabsUsed = roundRobinTabs.length;
|
|
295
|
+
console.log(`[WeiboCollectFromLinks] tab pool ready: [${roundRobinTabs.join(', ')}]`);
|
|
296
|
+
for (let idx = 0; idx < targetLinks.length; idx += 1) {
|
|
297
|
+
const link = targetLinks[idx];
|
|
229
298
|
processedCount++;
|
|
230
|
-
|
|
299
|
+
const tabIndex = roundRobinTabs[idx % roundRobinTabs.length];
|
|
300
|
+
console.log(`[WeiboCollectFromLinks] Processing: ${link.statusId} (tab=${tabIndex})`);
|
|
231
301
|
try {
|
|
302
|
+
await switchToPage(tabIndex);
|
|
232
303
|
await gotoUrl(link.safeUrl);
|
|
233
304
|
let currentUrl = await getCurrentUrl();
|
|
234
305
|
if (!currentUrl) {
|
|
@@ -265,6 +336,7 @@ export async function execute(input) {
|
|
|
265
336
|
postsProcessed: processedCount,
|
|
266
337
|
totalComments,
|
|
267
338
|
errors,
|
|
339
|
+
tabsUsed,
|
|
268
340
|
},
|
|
269
341
|
};
|
|
270
342
|
}
|
|
@@ -275,7 +347,7 @@ export async function execute(input) {
|
|
|
275
347
|
linksPath,
|
|
276
348
|
processedCount,
|
|
277
349
|
persistedCount,
|
|
278
|
-
stats: { postsProcessed: processedCount, totalComments, errors },
|
|
350
|
+
stats: { postsProcessed: processedCount, totalComments, errors, tabsUsed },
|
|
279
351
|
error: error.message,
|
|
280
352
|
};
|
|
281
353
|
}
|