@web-auto/camo 0.1.13 → 0.1.15

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 CHANGED
@@ -302,7 +302,6 @@ camo container list [profileId]
302
302
  ### Autoscript
303
303
 
304
304
  ```bash
305
- camo autoscript scaffold xhs-unified [--output <file>] [options]
306
305
  camo autoscript validate <file>
307
306
  camo autoscript explain <file>
308
307
  camo autoscript snapshot <jsonl-file> [--out <snapshot-file>]
@@ -375,55 +374,38 @@ camo container watch xiaohongshu-batch-1 --throttle 500
375
374
  ### Autoscript Mode (Subscription + Operations)
376
375
 
377
376
  ```bash
378
- # Generate xiaohongshu unified-harvest migration script from webauto phase-unified-harvest
379
- camo autoscript scaffold xhs-unified \
380
- --output ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
381
- --profile xiaohongshu-batch-1 \
382
- --keyword "手机膜" \
383
- --tab-count 4 \
384
- --note-interval 900 \
385
- --do-comments \
386
- --do-likes \
387
- --max-notes 30
388
-
389
377
  # Validate + explain + run
390
- camo autoscript validate ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
391
- camo autoscript explain ./autoscripts/xiaohongshu/unified-harvest.autoscript.json
392
- camo autoscript run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
393
- --profile xiaohongshu-batch-1 \
394
- --jsonl-file ./runs/xhs-unified/run.jsonl \
395
- --summary-file ./runs/xhs-unified/run.summary.json
378
+ camo autoscript validate ./autoscripts/my-flow.autoscript.json
379
+ camo autoscript explain ./autoscripts/my-flow.autoscript.json
380
+ camo autoscript run ./autoscripts/my-flow.autoscript.json \
381
+ --profile my-profile \
382
+ --jsonl-file ./runs/my-flow/run.jsonl \
383
+ --summary-file ./runs/my-flow/run.summary.json
396
384
 
397
385
  # Build snapshot + replay summary from existing JSONL
398
- camo autoscript snapshot ./runs/xhs-unified/run.jsonl \
399
- --out ./runs/xhs-unified/run.snapshot.json
400
- camo autoscript replay ./runs/xhs-unified/run.jsonl \
401
- --summary-file ./runs/xhs-unified/replay.summary.json
386
+ camo autoscript snapshot ./runs/my-flow/run.jsonl \
387
+ --out ./runs/my-flow/run.snapshot.json
388
+ camo autoscript replay ./runs/my-flow/run.jsonl \
389
+ --summary-file ./runs/my-flow/replay.summary.json
402
390
 
403
391
  # Resume from a snapshot (optionally force rerun from a node)
404
- camo autoscript resume ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
405
- --snapshot ./runs/xhs-unified/run.snapshot.json \
406
- --from-node comments_harvest \
407
- --profile xiaohongshu-batch-1
392
+ camo autoscript resume ./autoscripts/my-flow.autoscript.json \
393
+ --snapshot ./runs/my-flow/run.snapshot.json \
394
+ --from-node some_operation \
395
+ --profile my-profile
408
396
 
409
397
  # Mock replay mode for deterministic local debugging
410
- camo autoscript mock-run ./autoscripts/xiaohongshu/unified-harvest.autoscript.json \
411
- --fixture ./autoscripts/xiaohongshu/fixtures/mock-run.json \
412
- --summary-file ./runs/xhs-unified/mock.summary.json
398
+ camo autoscript mock-run ./autoscripts/my-flow.autoscript.json \
399
+ --fixture ./autoscripts/fixtures/mock-run.json \
400
+ --summary-file ./runs/my-flow/mock.summary.json
413
401
  ```
414
402
 
415
- The xhs-unified scaffold includes anti-risk defaults:
416
- - operation pacing (`operationMinIntervalMs`, `eventCooldownMs`, `jitterMs`)
417
- - navigation/tab switch cooldown (`navigationMinIntervalMs`)
418
- - per-operation timeout budget (`timeoutMs`)
419
- - multi-tab rotation (`ensure_tab_pool`, `tab_pool_switch_next`)
420
-
421
403
  Example script:
422
404
 
423
405
  ```json
424
406
  {
425
- "name": "xhs-login-flow",
426
- "profileId": "xiaohongshu-batch-1",
407
+ "name": "generic-login-flow",
408
+ "profileId": "my-profile",
427
409
  "throttle": 500,
428
410
  "subscriptions": [
429
411
  { "id": "login_input", "selector": "#login-input" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/camo",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Camoufox Browser CLI - Cross-platform browser automation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,9 +1,6 @@
1
- import { executeXhsAutoscriptOperation, isXhsAutoscriptAction } from './xhs.mjs';
2
-
3
1
  export async function executeAutoscriptAction({ profileId, action, params = {} }) {
4
- if (isXhsAutoscriptAction(action)) {
5
- return executeXhsAutoscriptOperation({ profileId, action, params });
6
- }
2
+ void profileId;
3
+ void action;
4
+ void params;
7
5
  return null;
8
6
  }
9
-
@@ -339,15 +339,6 @@ export class AutoscriptRunner {
339
339
  'tab_pool_switch_slot',
340
340
  'sync_window_viewport',
341
341
  'verify_subscriptions',
342
- 'xhs_submit_search',
343
- 'xhs_open_detail',
344
- 'xhs_detail_harvest',
345
- 'xhs_expand_replies',
346
- 'xhs_comments_harvest',
347
- 'xhs_comment_match',
348
- 'xhs_comment_like',
349
- 'xhs_comment_reply',
350
- 'xhs_close_detail',
351
342
  ].includes(action)) {
352
343
  return 45_000;
353
344
  }
@@ -363,6 +354,14 @@ export class AutoscriptRunner {
363
354
  return this.getDefaultTimeoutMs(operation);
364
355
  }
365
356
 
357
+ resolvePlatform(operation) {
358
+ const candidate = operation?.params?.platform
359
+ ?? operation?.platform
360
+ ?? this.script?.defaults?.platform
361
+ ?? 'generic';
362
+ return String(candidate || 'generic').trim().toLowerCase() || 'generic';
363
+ }
364
+
366
365
  buildTriggerKey(operation, event) {
367
366
  const trigger = operation?.trigger || { type: 'startup' };
368
367
  if (trigger.type === 'startup') return 'startup';
@@ -477,13 +476,14 @@ export class AutoscriptRunner {
477
476
  data: { phase },
478
477
  };
479
478
  }
479
+ const platform = this.resolvePlatform(operation);
480
480
  const validation = operation.validation || {};
481
481
  return validateOperation({
482
482
  profileId: this.profileId,
483
483
  validationSpec: validation,
484
484
  phase,
485
485
  context,
486
- platform: 'xiaohongshu',
486
+ platform,
487
487
  });
488
488
  }
489
489
 
@@ -567,11 +567,12 @@ export class AutoscriptRunner {
567
567
  return { ok: false, code: 'RECOVERY_NOT_CONFIGURED', message: 'recovery not configured' };
568
568
  }
569
569
 
570
+ const platform = this.resolvePlatform(operation);
570
571
  const checkpointDoc = await captureCheckpoint({
571
572
  profileId: this.profileId,
572
573
  containerId: checkpoint.containerId || null,
573
574
  selector: operation.params?.selector || null,
574
- platform: 'xiaohongshu',
575
+ platform,
575
576
  });
576
577
  const baseCheckpoint = checkpointDoc?.data || {};
577
578
 
@@ -585,7 +586,7 @@ export class AutoscriptRunner {
585
586
  containerId: checkpoint.containerId || null,
586
587
  selector: operation.params?.selector || null,
587
588
  targetCheckpoint: checkpoint.targetCheckpoint || null,
588
- platform: 'xiaohongshu',
589
+ platform,
589
590
  });
590
591
  this.log('autoscript:recovery_action', {
591
592
  operationId: operation.id,
@@ -3,7 +3,6 @@ import path from 'node:path';
3
3
  import { getDefaultProfile } from '../utils/config.mjs';
4
4
  import { explainAutoscript, loadAndValidateAutoscript } from '../autoscript/schema.mjs';
5
5
  import { AutoscriptRunner } from '../autoscript/runtime.mjs';
6
- import { buildXhsUnifiedAutoscript } from '../autoscript/xhs-unified-template.mjs';
7
6
  import { safeAppendProgressEvent } from '../events/progress-log.mjs';
8
7
 
9
8
  function readFlagValue(args, names) {
@@ -31,22 +30,6 @@ function collectPositionals(args, startIndex = 2, valueFlags = new Set(['--profi
31
30
  return out;
32
31
  }
33
32
 
34
- function readToggleFlag(args, name, fallback) {
35
- const on = `--${name}`;
36
- const off = `--no-${name}`;
37
- if (args.includes(off)) return false;
38
- if (args.includes(on)) return true;
39
- return fallback;
40
- }
41
-
42
- function readIntegerFlag(args, names, fallback, min = 1) {
43
- const raw = readFlagValue(args, names);
44
- if (raw === null || raw === undefined || raw === '') return fallback;
45
- const num = Number(raw);
46
- if (!Number.isFinite(num)) return fallback;
47
- return Math.max(min, Math.floor(num));
48
- }
49
-
50
33
  function appendJsonLine(filePath, payload) {
51
34
  if (!filePath) return;
52
35
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
@@ -593,89 +576,6 @@ function createMockOperationExecutor(fixture) {
593
576
  };
594
577
  }
595
578
 
596
- async function handleScaffold(args) {
597
- const valueFlags = new Set([
598
- '--profile', '-p',
599
- '--keyword', '-k',
600
- '--env',
601
- '--output', '-o',
602
- '--output-root',
603
- '--throttle',
604
- '--tab-count',
605
- '--note-interval',
606
- '--max-notes',
607
- '--max-likes',
608
- '--match-mode',
609
- '--match-min-hits',
610
- '--match-keywords',
611
- '--like-keywords',
612
- '--reply-text',
613
- '--jsonl-file',
614
- '--jsonl',
615
- ]);
616
- const positionals = collectPositionals(args, 2, valueFlags);
617
- const template = positionals[0];
618
- if (template !== 'xhs-unified') {
619
- throw new Error('Usage: camo autoscript scaffold xhs-unified [--output <file>] [options]');
620
- }
621
-
622
- const outputPath = readFlagValue(args, ['--output', '-o'])
623
- || path.resolve('autoscripts/xiaohongshu/unified-harvest.autoscript.json');
624
- const profileId = readFlagValue(args, ['--profile', '-p']) || getDefaultProfile() || 'xiaohongshu-batch-1';
625
- const keyword = readFlagValue(args, ['--keyword', '-k']) || '手机膜';
626
- const env = readFlagValue(args, ['--env']) || 'debug';
627
- const outputRoot = readFlagValue(args, ['--output-root']) || '';
628
- const throttle = readIntegerFlag(args, ['--throttle'], 500, 100);
629
- const tabCount = readIntegerFlag(args, ['--tab-count'], 4, 1);
630
- const noteIntervalMs = readIntegerFlag(args, ['--note-interval'], 900, 200);
631
- const maxNotes = readIntegerFlag(args, ['--max-notes'], 30, 1);
632
- const maxLikesPerRound = readIntegerFlag(args, ['--max-likes'], 2, 1);
633
- const matchMinHits = readIntegerFlag(args, ['--match-min-hits'], 1, 1);
634
- const matchMode = readFlagValue(args, ['--match-mode']) || 'any';
635
- const matchKeywords = readFlagValue(args, ['--match-keywords']) || keyword;
636
- const likeKeywords = readFlagValue(args, ['--like-keywords']) || '';
637
- const replyText = readFlagValue(args, ['--reply-text']) || '感谢分享,已关注';
638
-
639
- const script = buildXhsUnifiedAutoscript({
640
- profileId,
641
- keyword,
642
- env,
643
- outputRoot,
644
- throttle,
645
- tabCount,
646
- noteIntervalMs,
647
- maxNotes,
648
- maxLikesPerRound,
649
- matchMode,
650
- matchMinHits,
651
- matchKeywords,
652
- likeKeywords,
653
- replyText,
654
- doHomepage: readToggleFlag(args, 'do-homepage', true),
655
- doImages: readToggleFlag(args, 'do-images', false),
656
- doComments: readToggleFlag(args, 'do-comments', true),
657
- doLikes: readToggleFlag(args, 'do-likes', false),
658
- doReply: readToggleFlag(args, 'do-reply', false),
659
- doOcr: readToggleFlag(args, 'do-ocr', false),
660
- persistComments: readToggleFlag(args, 'persist-comments', true),
661
- });
662
-
663
- const resolvedPath = path.resolve(outputPath);
664
- fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
665
- fs.writeFileSync(resolvedPath, `${JSON.stringify(script, null, 2)}\n`, 'utf8');
666
-
667
- console.log(JSON.stringify({
668
- ok: true,
669
- command: 'autoscript.scaffold',
670
- template,
671
- file: resolvedPath,
672
- profileId: script.profileId,
673
- operationCount: script.operations.length,
674
- subscriptionCount: script.subscriptions.length,
675
- hint: `Run: camo autoscript validate ${resolvedPath}`,
676
- }, null, 2));
677
- }
678
-
679
579
  async function handleValidate(args) {
680
580
  const filePath = collectPositionals(args)[0];
681
581
  if (!filePath) {
@@ -1067,8 +967,6 @@ async function handleMockRun(args) {
1067
967
  export async function handleAutoscriptCommand(args) {
1068
968
  const sub = args[1];
1069
969
  switch (sub) {
1070
- case 'scaffold':
1071
- return handleScaffold(args);
1072
970
  case 'validate':
1073
971
  return handleValidate(args);
1074
972
  case 'explain':
@@ -1084,10 +982,9 @@ export async function handleAutoscriptCommand(args) {
1084
982
  case 'mock-run':
1085
983
  return handleMockRun(args);
1086
984
  default:
1087
- console.log(`Usage: camo autoscript <scaffold|validate|explain|snapshot|replay|run|resume|mock-run> [args]
985
+ console.log(`Usage: camo autoscript <validate|explain|snapshot|replay|run|resume|mock-run> [args]
1088
986
 
1089
987
  Commands:
1090
- scaffold xhs-unified [--output <file>] [options] Generate xiaohongshu unified-harvest autoscript
1091
988
  validate <file> Validate autoscript schema and references
1092
989
  explain <file> Print normalized graph and defaults
1093
990
  snapshot <jsonl-file> [--out <snapshot-file>] Build resumable snapshot from run JSONL
@@ -40,14 +40,28 @@ export const XHS_CHECKPOINTS = {
40
40
  ],
41
41
  };
42
42
 
43
- export async function detectCheckpoint({ profileId, platform = 'xiaohongshu' }) {
44
- if (platform !== 'xiaohongshu') {
45
- return asErrorPayload('UNSUPPORTED_PLATFORM', `Unsupported platform: ${platform}`);
46
- }
47
-
43
+ export async function detectCheckpoint({ profileId, platform = 'generic' }) {
48
44
  try {
49
45
  const session = await ensureActiveSession(profileId);
50
46
  const resolvedProfile = session.profileId || profileId;
47
+
48
+ if (platform !== 'xiaohongshu') {
49
+ const url = await getCurrentUrl(resolvedProfile);
50
+ return {
51
+ ok: true,
52
+ code: 'CHECKPOINT_DETECTED',
53
+ message: 'Checkpoint detected',
54
+ data: {
55
+ profileId: resolvedProfile,
56
+ platform,
57
+ checkpoint: 'unknown',
58
+ url,
59
+ signals: [],
60
+ selectorHits: {},
61
+ },
62
+ };
63
+ }
64
+
51
65
  const [url, snapshot] = await Promise.all([
52
66
  getCurrentUrl(resolvedProfile),
53
67
  getDomSnapshotByProfile(resolvedProfile),
@@ -103,7 +117,7 @@ export async function captureCheckpoint({
103
117
  profileId,
104
118
  containerId = null,
105
119
  selector = null,
106
- platform = 'xiaohongshu',
120
+ platform = 'generic',
107
121
  }) {
108
122
  try {
109
123
  const session = await ensureActiveSession(profileId);
@@ -139,7 +153,7 @@ export async function restoreCheckpoint({
139
153
  containerId = null,
140
154
  selector = null,
141
155
  targetCheckpoint = null,
142
- platform = 'xiaohongshu',
156
+ platform = 'generic',
143
157
  }) {
144
158
  try {
145
159
  const session = await ensureActiveSession(profileId);
@@ -9,7 +9,7 @@ import {
9
9
  normalizeArray,
10
10
  } from './utils.mjs';
11
11
 
12
- async function validatePage(profileId, spec = {}, platform = 'xiaohongshu') {
12
+ async function validatePage(profileId, spec = {}, platform = 'generic') {
13
13
  const url = await getCurrentUrl(profileId);
14
14
  const includes = normalizeArray(spec.urlIncludes || []);
15
15
  const excludes = normalizeArray(spec.urlExcludes || []);
@@ -74,7 +74,7 @@ export async function validateOperation({
74
74
  validationSpec = {},
75
75
  phase = 'pre',
76
76
  context = {},
77
- platform = 'xiaohongshu',
77
+ platform = 'generic',
78
78
  }) {
79
79
  try {
80
80
  const mode = String(validationSpec.mode || 'none').toLowerCase();
@@ -155,7 +155,6 @@ CONTAINER FILTER & SUBSCRIPTION:
155
155
  container list [profileId] List visible elements in viewport
156
156
 
157
157
  AUTOSCRIPT (STRATEGY LAYER):
158
- autoscript scaffold xhs-unified [--output <file>] Generate xiaohongshu unified-harvest autoscript template
159
158
  autoscript validate <file> Validate autoscript schema and references
160
159
  autoscript explain <file> Print normalized graph and defaults
161
160
  autoscript snapshot <jsonl-file> [--out <snapshot-file>] Build resumable snapshot from run JSONL