@web-auto/camo 0.1.2 → 0.1.4

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 +137 -0
  2. package/package.json +7 -3
  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 +190 -78
  19. package/src/commands/autoscript.mjs +1100 -0
  20. package/src/commands/browser.mjs +20 -4
  21. package/src/commands/container.mjs +401 -0
  22. package/src/commands/events.mjs +152 -0
  23. package/src/commands/lifecycle.mjs +17 -3
  24. package/src/commands/window.mjs +32 -1
  25. package/src/container/change-notifier.mjs +311 -0
  26. package/src/container/element-filter.mjs +143 -0
  27. package/src/container/index.mjs +3 -0
  28. package/src/container/runtime-core/checkpoint.mjs +195 -0
  29. package/src/container/runtime-core/index.mjs +21 -0
  30. package/src/container/runtime-core/operations/index.mjs +351 -0
  31. package/src/container/runtime-core/operations/selector-scripts.mjs +68 -0
  32. package/src/container/runtime-core/operations/tab-pool.mjs +544 -0
  33. package/src/container/runtime-core/operations/viewport.mjs +143 -0
  34. package/src/container/runtime-core/subscription.mjs +87 -0
  35. package/src/container/runtime-core/utils.mjs +94 -0
  36. package/src/container/runtime-core/validation.mjs +127 -0
  37. package/src/container/runtime-core.mjs +1 -0
  38. package/src/container/subscription-registry.mjs +459 -0
  39. package/src/core/actions.mjs +573 -0
  40. package/src/core/browser.mjs +270 -0
  41. package/src/core/index.mjs +53 -0
  42. package/src/core/utils.mjs +87 -0
  43. package/src/events/daemon-entry.mjs +33 -0
  44. package/src/events/daemon.mjs +80 -0
  45. package/src/events/progress-log.mjs +109 -0
  46. package/src/events/ws-server.mjs +239 -0
  47. package/src/lib/client.mjs +200 -0
  48. package/src/lifecycle/session-registry.mjs +8 -4
  49. package/src/lifecycle/session-watchdog.mjs +220 -0
  50. package/src/utils/browser-service.mjs +232 -9
  51. package/src/utils/help.mjs +28 -0
package/README.md CHANGED
@@ -146,6 +146,41 @@ camo mouse wheel [profileId] [--deltax <px>] [--deltay <px>]
146
146
  camo system display # Show display metrics
147
147
  ```
148
148
 
149
+ ### Container Subscription
150
+
151
+ ```bash
152
+ camo container init [--source <container-library-dir>] [--force]
153
+ camo container sets [--site <siteKey>]
154
+ camo container register [profileId] <setId...> [--append]
155
+ camo container targets [profileId]
156
+ camo container filter [profileId] <selector...>
157
+ camo container watch [profileId] [--selector <css>] [--throttle <ms>]
158
+ camo container list [profileId]
159
+ ```
160
+
161
+ ### Autoscript
162
+
163
+ ```bash
164
+ camo autoscript scaffold xhs-unified [--output <file>] [options]
165
+ camo autoscript validate <file>
166
+ camo autoscript explain <file>
167
+ camo autoscript snapshot <jsonl-file> [--out <snapshot-file>]
168
+ camo autoscript replay <jsonl-file> [--summary-file <path>]
169
+ camo autoscript run <file> [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
170
+ camo autoscript resume <file> --snapshot <snapshot-file> [--from-node <nodeId>] [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
171
+ camo autoscript mock-run <file> --fixture <fixture.json> [--profile <id>] [--jsonl-file <path>] [--summary-file <path>]
172
+ ```
173
+
174
+ ### Progress Events (WS)
175
+
176
+ ```bash
177
+ camo events serve [--host 127.0.0.1] [--port 7788]
178
+ camo events tail [--profile <id>] [--run-id <id>] [--events e1,e2] [--replay 50]
179
+ camo events recent [--limit 50]
180
+ ```
181
+
182
+ By default, non-`events` commands auto-start the progress daemon (`/events`) in background.
183
+
149
184
  ## Fingerprint Options
150
185
 
151
186
  ### OS Options
@@ -180,11 +215,113 @@ camo system display # Show display metrics
180
215
  - Session registry: `~/.webauto/sessions/`
181
216
  - Lock files: `~/.webauto/locks/`
182
217
  - GeoIP database: `~/.webauto/geoip/GeoLite2-City.mmdb`
218
+ - User container root: `~/.webauto/container-lib/`
219
+ - Subscription root: `~/.webauto/container-subscriptions/`
220
+
221
+ ### Subscription-driven Watch
222
+
223
+ ```bash
224
+ # 1) Migrate container-library into subscription sets
225
+ camo container init --source /Users/fanzhang/Documents/github/webauto/container-library
226
+
227
+ # 2) Register sets to a profile
228
+ camo container register xiaohongshu-batch-1 xiaohongshu_home xiaohongshu_home.search_input
229
+
230
+ # 3) Start watch using registered selectors (no --selector needed)
231
+ camo container watch xiaohongshu-batch-1 --throttle 500
232
+ ```
233
+
234
+ ### Autoscript Mode (Subscription + Operations)
235
+
236
+ ```bash
237
+ # Generate xiaohongshu unified-harvest migration script from webauto phase-unified-harvest
238
+ camo autoscript scaffold xhs-unified \
239
+ --output ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
240
+ --profile xiaohongshu-batch-1 \
241
+ --keyword "手机膜" \
242
+ --tab-count 4 \
243
+ --note-interval 900 \
244
+ --do-comments \
245
+ --do-likes \
246
+ --max-notes 30
247
+
248
+ # Validate + explain + run
249
+ camo autoscript validate ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
250
+ camo autoscript explain ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
251
+ camo autoscript run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
252
+ --profile xiaohongshu-batch-1 \
253
+ --jsonl-file ./runs/xhs-unified/run.jsonl \
254
+ --summary-file ./runs/xhs-unified/run.summary.json
255
+
256
+ # Build snapshot + replay summary from existing JSONL
257
+ camo autoscript snapshot ./runs/xhs-unified/run.jsonl \
258
+ --out ./runs/xhs-unified/run.snapshot.json
259
+ camo autoscript replay ./runs/xhs-unified/run.jsonl \
260
+ --summary-file ./runs/xhs-unified/replay.summary.json
261
+
262
+ # Resume from a snapshot (optionally force rerun from a node)
263
+ camo autoscript resume ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
264
+ --snapshot ./runs/xhs-unified/run.snapshot.json \
265
+ --from-node comments_harvest \
266
+ --profile xiaohongshu-batch-1
267
+
268
+ # Mock replay mode for deterministic local debugging
269
+ camo autoscript mock-run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
270
+ --fixture ./autoscripts/xiaohongshu/fixtures/mock-run.json \
271
+ --summary-file ./runs/xhs-unified/mock.summary.json
272
+ ```
273
+
274
+ The xhs-unified scaffold includes anti-risk defaults:
275
+ - operation pacing (`operationMinIntervalMs`, `eventCooldownMs`, `jitterMs`)
276
+ - navigation/tab switch cooldown (`navigationMinIntervalMs`)
277
+ - per-operation timeout budget (`timeoutMs`)
278
+ - multi-tab rotation (`ensure_tab_pool`, `tab_pool_switch_next`)
279
+
280
+ Example script:
281
+
282
+ ```json
283
+ {
284
+ "name": "xhs-login-flow",
285
+ "profileId": "xiaohongshu-batch-1",
286
+ "throttle": 500,
287
+ "subscriptions": [
288
+ { "id": "login_input", "selector": "#login-input" },
289
+ { "id": "submit_btn", "selector": "button.submit" }
290
+ ],
291
+ "operations": [
292
+ {
293
+ "id": "fill_login",
294
+ "action": "type",
295
+ "selector": "#login-input",
296
+ "text": "demo@example.com",
297
+ "trigger": "login_input.appear"
298
+ },
299
+ {
300
+ "id": "click_submit",
301
+ "action": "click",
302
+ "selector": "button.submit",
303
+ "trigger": { "subscription": "submit_btn", "event": "exist" },
304
+ "conditions": [
305
+ { "type": "operation_done", "operationId": "fill_login" },
306
+ { "type": "subscription_exist", "subscriptionId": "submit_btn" }
307
+ ]
308
+ }
309
+ ]
310
+ }
311
+ ```
312
+
313
+ Condition types:
314
+ - `operation_done`: previous operation completed
315
+ - `subscription_exist`: subscribed element currently exists
316
+ - `subscription_appear`: subscribed element has appeared at least once
183
317
 
184
318
  ### Environment Variables
185
319
 
186
320
  - `WEBAUTO_BROWSER_URL` - Browser service URL (default: `http://127.0.0.1:7704`)
187
321
  - `WEBAUTO_REPO_ROOT` - WebAuto repository root (optional)
322
+ - `WEBAUTO_CONTAINER_ROOT` - User container root override (default: `~/.webauto/container-lib`)
323
+ - `CAMO_PROGRESS_EVENTS_FILE` - Optional progress event JSONL path override
324
+ - `CAMO_PROGRESS_WS_HOST` / `CAMO_PROGRESS_WS_PORT` - Progress websocket daemon bind address (default: `127.0.0.1:7788`)
188
325
 
189
326
  ## Session Persistence
190
327
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/camo",
3
- "version": "0.1.0002",
3
+ "version": "0.1.4",
4
4
  "description": "Camoufox Browser CLI - Cross-platform browser automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,9 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "node scripts/build.mjs",
18
- "test": "node --test tests/*.test.mjs",
18
+ "check:file-size": "node scripts/check-file-size.mjs",
19
+ "test": "node --test 'tests/**/*.test.mjs'",
20
+ "test:coverage": "c8 --reporter=text --reporter=lcov node --test 'tests/**/*.test.mjs'",
19
21
  "version:bump": "node scripts/bump-version.mjs",
20
22
  "install:global": "npm run build && npm install -g .",
21
23
  "uninstall:global": "npm uninstall -g @web-auto/camo",
@@ -32,8 +34,10 @@
32
34
  "engines": {
33
35
  "node": ">=20.0.0"
34
36
  },
35
- "dependencies": {},
36
37
  "peerDependencies": {
37
38
  "camoufox": "^0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "c8": "^10.1.3"
38
42
  }
39
43
  }
@@ -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
+