@web-auto/camo 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +142 -1
  2. package/package.json +2 -1
  3. package/scripts/check-file-size.mjs +80 -0
  4. package/scripts/file-size-policy.json +8 -0
  5. package/src/autoscript/action-providers/index.mjs +9 -0
  6. package/src/autoscript/action-providers/xhs/comments.mjs +412 -0
  7. package/src/autoscript/action-providers/xhs/common.mjs +77 -0
  8. package/src/autoscript/action-providers/xhs/detail.mjs +181 -0
  9. package/src/autoscript/action-providers/xhs/interaction.mjs +466 -0
  10. package/src/autoscript/action-providers/xhs/like-rules.mjs +57 -0
  11. package/src/autoscript/action-providers/xhs/persistence.mjs +167 -0
  12. package/src/autoscript/action-providers/xhs/search.mjs +174 -0
  13. package/src/autoscript/action-providers/xhs.mjs +133 -0
  14. package/src/autoscript/impact-engine.mjs +78 -0
  15. package/src/autoscript/runtime.mjs +1015 -0
  16. package/src/autoscript/schema.mjs +370 -0
  17. package/src/autoscript/xhs-unified-template.mjs +931 -0
  18. package/src/cli.mjs +185 -79
  19. package/src/commands/autoscript.mjs +1100 -0
  20. package/src/commands/browser.mjs +215 -5
  21. package/src/commands/container.mjs +298 -75
  22. package/src/commands/events.mjs +152 -0
  23. package/src/commands/lifecycle.mjs +17 -3
  24. package/src/commands/window.mjs +94 -2
  25. package/src/container/change-notifier.mjs +165 -24
  26. package/src/container/element-filter.mjs +51 -5
  27. package/src/container/runtime-core/checkpoint.mjs +195 -0
  28. package/src/container/runtime-core/index.mjs +21 -0
  29. package/src/container/runtime-core/operations/index.mjs +351 -0
  30. package/src/container/runtime-core/operations/selector-scripts.mjs +68 -0
  31. package/src/container/runtime-core/operations/tab-pool.mjs +544 -0
  32. package/src/container/runtime-core/operations/viewport.mjs +143 -0
  33. package/src/container/runtime-core/subscription.mjs +87 -0
  34. package/src/container/runtime-core/utils.mjs +94 -0
  35. package/src/container/runtime-core/validation.mjs +127 -0
  36. package/src/container/runtime-core.mjs +1 -0
  37. package/src/container/subscription-registry.mjs +459 -0
  38. package/src/core/actions.mjs +573 -0
  39. package/src/core/browser.mjs +270 -0
  40. package/src/core/index.mjs +53 -0
  41. package/src/core/utils.mjs +87 -0
  42. package/src/events/daemon-entry.mjs +33 -0
  43. package/src/events/daemon.mjs +80 -0
  44. package/src/events/progress-log.mjs +109 -0
  45. package/src/events/ws-server.mjs +239 -0
  46. package/src/lib/client.mjs +8 -5
  47. package/src/lifecycle/session-registry.mjs +8 -4
  48. package/src/lifecycle/session-watchdog.mjs +267 -0
  49. package/src/utils/browser-service.mjs +232 -9
  50. package/src/utils/config.mjs +58 -2
  51. package/src/utils/help.mjs +28 -4
package/README.md CHANGED
@@ -68,12 +68,16 @@ camo create fingerprint --os <os> --region <region>
68
68
  ### Browser Control
69
69
 
70
70
  ```bash
71
- camo start [profileId] [--url <url>] [--headless]
71
+ camo start [profileId] [--url <url>] [--headless] [--width <w> --height <h>]
72
72
  camo stop [profileId]
73
73
  camo status [profileId]
74
74
  camo shutdown # Shutdown browser-service (all sessions)
75
75
  ```
76
76
 
77
+ `camo start` in headful mode now persists window size per profile and reuses that size on next start.
78
+ If no saved size exists, it defaults to near-fullscreen (full width, slight vertical reserve).
79
+ Use `--width/--height` to override and update the saved profile size.
80
+
77
81
  ### Lifecycle & Cleanup
78
82
 
79
83
  ```bash
@@ -146,6 +150,41 @@ camo mouse wheel [profileId] [--deltax <px>] [--deltay <px>]
146
150
  camo system display # Show display metrics
147
151
  ```
148
152
 
153
+ ### Container Subscription
154
+
155
+ ```bash
156
+ camo container init [--source <container-library-dir>] [--force]
157
+ camo container sets [--site <siteKey>]
158
+ camo container register [profileId] <setId...> [--append]
159
+ camo container targets [profileId]
160
+ camo container filter [profileId] <selector...>
161
+ camo container watch [profileId] [--selector <css>] [--throttle <ms>]
162
+ camo container list [profileId]
163
+ ```
164
+
165
+ ### Autoscript
166
+
167
+ ```bash
168
+ camo autoscript scaffold xhs-unified [--output <file>] [options]
169
+ camo autoscript validate <file>
170
+ camo autoscript explain <file>
171
+ camo autoscript snapshot <jsonl-file> [--out <snapshot-file>]
172
+ camo autoscript replay <jsonl-file> [--summary-file <path>]
173
+ camo autoscript run <file> [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
174
+ camo autoscript resume <file> --snapshot <snapshot-file> [--from-node <nodeId>] [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
175
+ camo autoscript mock-run <file> --fixture <fixture.json> [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
176
+ ```
177
+
178
+ ### Progress Events (WS)
179
+
180
+ ```bash
181
+ camo events serve [--host 127.0.0.1] [--port 7788]
182
+ camo events tail [--profile <id>] [--run-id <id>] [--events e1,e2] [--replay 50]
183
+ camo events recent [--limit 50]
184
+ ```
185
+
186
+ By default, non-`events` commands auto-start the progress daemon (`/events`) in background.
187
+
149
188
  ## Fingerprint Options
150
189
 
151
190
  ### OS Options
@@ -180,11 +219,113 @@ camo system display # Show display metrics
180
219
  - Session registry: `~/.webauto/sessions/`
181
220
  - Lock files: `~/.webauto/locks/`
182
221
  - GeoIP database: `~/.webauto/geoip/GeoLite2-City.mmdb`
222
+ - User container root: `~/.webauto/container-lib/`
223
+ - Subscription root: `~/.webauto/container-subscriptions/`
224
+
225
+ ### Subscription-driven Watch
226
+
227
+ ```bash
228
+ # 1) Migrate container-library into subscription sets
229
+ camo container init --source /Users/fanzhang/Documents/github/webauto/container-library
230
+
231
+ # 2) Register sets to a profile
232
+ camo container register xiaohongshu-batch-1 xiaohongshu_home xiaohongshu_home.search_input
233
+
234
+ # 3) Start watch using registered selectors (no --selector needed)
235
+ camo container watch xiaohongshu-batch-1 --throttle 500
236
+ ```
237
+
238
+ ### Autoscript Mode (Subscription + Operations)
239
+
240
+ ```bash
241
+ # Generate xiaohongshu unified-harvest migration script from webauto phase-unified-harvest
242
+ camo autoscript scaffold xhs-unified \
243
+ --output ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
244
+ --profile xiaohongshu-batch-1 \
245
+ --keyword "手机膜" \
246
+ --tab-count 4 \
247
+ --note-interval 900 \
248
+ --do-comments \
249
+ --do-likes \
250
+ --max-notes 30
251
+
252
+ # Validate + explain + run
253
+ camo autoscript validate ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
254
+ camo autoscript explain ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
255
+ camo autoscript run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
256
+ --profile xiaohongshu-batch-1 \
257
+ --jsonl-file ./runs/xhs-unified/run.jsonl \
258
+ --summary-file ./runs/xhs-unified/run.summary.json
259
+
260
+ # Build snapshot + replay summary from existing JSONL
261
+ camo autoscript snapshot ./runs/xhs-unified/run.jsonl \
262
+ --out ./runs/xhs-unified/run.snapshot.json
263
+ camo autoscript replay ./runs/xhs-unified/run.jsonl \
264
+ --summary-file ./runs/xhs-unified/replay.summary.json
265
+
266
+ # Resume from a snapshot (optionally force rerun from a node)
267
+ camo autoscript resume ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
268
+ --snapshot ./runs/xhs-unified/run.snapshot.json \
269
+ --from-node comments_harvest \
270
+ --profile xiaohongshu-batch-1
271
+
272
+ # Mock replay mode for deterministic local debugging
273
+ camo autoscript mock-run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
274
+ --fixture ./autoscripts/xiaohongshu/fixtures/mock-run.json \
275
+ --summary-file ./runs/xhs-unified/mock.summary.json
276
+ ```
277
+
278
+ The xhs-unified scaffold includes anti-risk defaults:
279
+ - operation pacing (`operationMinIntervalMs`, `eventCooldownMs`, `jitterMs`)
280
+ - navigation/tab switch cooldown (`navigationMinIntervalMs`)
281
+ - per-operation timeout budget (`timeoutMs`)
282
+ - multi-tab rotation (`ensure_tab_pool`, `tab_pool_switch_next`)
283
+
284
+ Example script:
285
+
286
+ ```json
287
+ {
288
+ "name": "xhs-login-flow",
289
+ "profileId": "xiaohongshu-batch-1",
290
+ "throttle": 500,
291
+ "subscriptions": [
292
+ { "id": "login_input", "selector": "#login-input" },
293
+ { "id": "submit_btn", "selector": "button.submit" }
294
+ ],
295
+ "operations": [
296
+ {
297
+ "id": "fill_login",
298
+ "action": "type",
299
+ "selector": "#login-input",
300
+ "text": "demo@example.com",
301
+ "trigger": "login_input.appear"
302
+ },
303
+ {
304
+ "id": "click_submit",
305
+ "action": "click",
306
+ "selector": "button.submit",
307
+ "trigger": { "subscription": "submit_btn", "event": "exist" },
308
+ "conditions": [
309
+ { "type": "operation_done", "operationId": "fill_login" },
310
+ { "type": "subscription_exist", "subscriptionId": "submit_btn" }
311
+ ]
312
+ }
313
+ ]
314
+ }
315
+ ```
316
+
317
+ Condition types:
318
+ - `operation_done`: previous operation completed
319
+ - `subscription_exist`: subscribed element currently exists
320
+ - `subscription_appear`: subscribed element has appeared at least once
183
321
 
184
322
  ### Environment Variables
185
323
 
186
324
  - `WEBAUTO_BROWSER_URL` - Browser service URL (default: `http://127.0.0.1:7704`)
187
325
  - `WEBAUTO_REPO_ROOT` - WebAuto repository root (optional)
326
+ - `WEBAUTO_CONTAINER_ROOT` - User container root override (default: `~/.webauto/container-lib`)
327
+ - `CAMO_PROGRESS_EVENTS_FILE` - Optional progress event JSONL path override
328
+ - `CAMO_PROGRESS_WS_HOST` / `CAMO_PROGRESS_WS_PORT` - Progress websocket daemon bind address (default: `127.0.0.1:7788`)
188
329
 
189
330
  ## Session Persistence
190
331
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/camo",
3
- "version": "0.1.0003",
3
+ "version": "0.1.5",
4
4
  "description": "Camoufox Browser CLI - Cross-platform browser automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "node scripts/build.mjs",
18
+ "check:file-size": "node scripts/check-file-size.mjs",
18
19
  "test": "node --test 'tests/**/*.test.mjs'",
19
20
  "test:coverage": "c8 --reporter=text --reporter=lcov node --test 'tests/**/*.test.mjs'",
20
21
  "version:bump": "node scripts/bump-version.mjs",
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ const ROOT = process.cwd();
6
+ const TARGET_DIR = path.join(ROOT, 'src');
7
+ const MAX_LINES = Number(process.env.FILE_MAX_LINES || 500);
8
+ const POLICY_FILE = path.join(ROOT, 'scripts', 'file-size-policy.json');
9
+ const EXTS = new Set(['.mjs', '.js', '.ts', '.tsx']);
10
+
11
+ async function walk(dir) {
12
+ const out = [];
13
+ const entries = await fs.readdir(dir, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ const fullPath = path.join(dir, entry.name);
16
+ if (entry.isDirectory()) {
17
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
18
+ out.push(...await walk(fullPath));
19
+ continue;
20
+ }
21
+ if (!entry.isFile()) continue;
22
+ const ext = path.extname(entry.name).toLowerCase();
23
+ if (!EXTS.has(ext)) continue;
24
+ out.push(fullPath);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function countLines(content) {
30
+ if (!content) return 0;
31
+ return content.split(/\r?\n/).length;
32
+ }
33
+
34
+ function relative(p) {
35
+ return path.relative(ROOT, p).replaceAll(path.sep, '/');
36
+ }
37
+
38
+ async function main() {
39
+ let policy = { defaultMaxLines: MAX_LINES, overrides: {} };
40
+ try {
41
+ const raw = await fs.readFile(POLICY_FILE, 'utf8');
42
+ const parsed = JSON.parse(raw);
43
+ policy = {
44
+ defaultMaxLines: Number(parsed?.defaultMaxLines || MAX_LINES),
45
+ overrides: parsed?.overrides && typeof parsed.overrides === 'object' ? parsed.overrides : {},
46
+ };
47
+ } catch {}
48
+
49
+ const files = await walk(TARGET_DIR);
50
+ const violations = [];
51
+
52
+ for (const file of files) {
53
+ const text = await fs.readFile(file, 'utf8');
54
+ const lines = countLines(text);
55
+ const rel = relative(file);
56
+ const overrideLimit = Number(policy.overrides?.[rel]);
57
+ const limit = Number.isFinite(overrideLimit) && overrideLimit > 0
58
+ ? overrideLimit
59
+ : policy.defaultMaxLines;
60
+ if (lines > limit) {
61
+ violations.push({ file: rel, lines, limit });
62
+ }
63
+ }
64
+
65
+ if (violations.length === 0) {
66
+ console.log(`file-size-check: OK (${files.length} files, default max ${policy.defaultMaxLines} lines)`);
67
+ return;
68
+ }
69
+
70
+ console.error(`file-size-check: ${violations.length} file(s) exceed configured limits`);
71
+ for (const item of violations) {
72
+ console.error(` - ${item.file}: ${item.lines} > ${item.limit}`);
73
+ }
74
+ process.exit(1);
75
+ }
76
+
77
+ main().catch((err) => {
78
+ console.error(`file-size-check: failed - ${err?.message || err}`);
79
+ process.exit(1);
80
+ });
@@ -0,0 +1,8 @@
1
+ {
2
+ "defaultMaxLines": 500,
3
+ "overrides": {
4
+ "src/autoscript/runtime.mjs": 700,
5
+ "src/autoscript/xhs-unified-template.mjs": 950,
6
+ "src/core/actions.mjs": 620
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ import { executeXhsAutoscriptOperation, isXhsAutoscriptAction } from './xhs.mjs';
2
+
3
+ export async function executeAutoscriptAction({ profileId, action, params = {} }) {
4
+ if (isXhsAutoscriptAction(action)) {
5
+ return executeXhsAutoscriptOperation({ profileId, action, params });
6
+ }
7
+ return null;
8
+ }
9
+