@web-auto/camo 0.1.14 → 0.1.16

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.14",
3
+ "version": "0.1.16",
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,
@@ -969,6 +970,7 @@ export class AutoscriptRunner {
969
970
  profileId: this.profileId,
970
971
  subscriptions: this.script.subscriptions,
971
972
  throttle: this.script.throttle,
973
+ filterMode: this.script?.defaults?.filterMode || 'strict',
972
974
  onEvent: async (event) => {
973
975
  this.log('autoscript:event', {
974
976
  type: event.type,
@@ -127,9 +127,14 @@ function normalizeSubscription(item, index, defaults) {
127
127
  const selector = toTrimmedString(item.selector);
128
128
  if (!selector) return null;
129
129
  const events = toArray(item.events).map((name) => toTrimmedString(name)).filter(Boolean);
130
+ const pageUrlIncludes = toArray(item.pageUrlIncludes).map((token) => toTrimmedString(token)).filter(Boolean);
131
+ const pageUrlExcludes = toArray(item.pageUrlExcludes).map((token) => toTrimmedString(token)).filter(Boolean);
130
132
  return {
131
133
  id,
132
134
  selector,
135
+ visible: item.visible !== false,
136
+ pageUrlIncludes,
137
+ pageUrlExcludes,
133
138
  events: events.length > 0 ? events : ['appear', 'exist', 'disappear', 'change'],
134
139
  dependsOn: toArray(item.dependsOn).map((x) => toTrimmedString(x)).filter(Boolean),
135
140
  retry: normalizeRetry(item.retry, defaults.retry),
@@ -243,6 +248,7 @@ export function normalizeAutoscript(raw, sourcePath = null) {
243
248
  retry: normalizeRetry(defaults.retry, {}),
244
249
  impact: toTrimmedString(defaults.impact || 'op') || 'op',
245
250
  onFailure: toTrimmedString(defaults.onFailure || 'chain_stop') || 'chain_stop',
251
+ filterMode: toTrimmedString(defaults.filterMode || 'strict') || 'strict',
246
252
  validationMode: toTrimmedString(defaults.validationMode || 'none') || 'none',
247
253
  recovery: normalizeRecovery(defaults.recovery, {}),
248
254
  pacing: normalizePacing(defaults.pacing, {}),
package/src/cli.mjs CHANGED
@@ -160,7 +160,11 @@ async function handleConfigCommand(args) {
160
160
  }
161
161
 
162
162
  async function main() {
163
- const args = process.argv.slice(2);
163
+ const rawArgs = process.argv.slice(2);
164
+ const jsEnabled = rawArgs.includes('--js');
165
+ if (jsEnabled) process.env.CAMO_ALLOW_JS = '1';
166
+ else delete process.env.CAMO_ALLOW_JS;
167
+ const args = rawArgs.filter((arg) => arg !== '--js');
164
168
  const cmd = args[0];
165
169
 
166
170
  if (!cmd) {
@@ -1,9 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { getDefaultProfile } from '../utils/config.mjs';
3
+ import { getDefaultProfile, listProfiles } 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,20 +30,16 @@ 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));
33
+ function assertExistingProfile(profileId) {
34
+ const id = String(profileId || '').trim();
35
+ if (!id) {
36
+ throw new Error('profileId is required');
37
+ }
38
+ const known = new Set(listProfiles());
39
+ if (!known.has(id)) {
40
+ throw new Error(`profile not found: ${id}. create it first with "camo profile create ${id}"`);
41
+ }
42
+ return id;
48
43
  }
49
44
 
50
45
  function appendJsonLine(filePath, payload) {
@@ -593,89 +588,6 @@ function createMockOperationExecutor(fixture) {
593
588
  };
594
589
  }
595
590
 
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
591
  async function handleValidate(args) {
680
592
  const filePath = collectPositionals(args)[0];
681
593
  if (!filePath) {
@@ -883,6 +795,7 @@ async function handleRun(args) {
883
795
  if (!profileId) {
884
796
  throw new Error('profileId is required. Set in script or pass --profile <id>');
885
797
  }
798
+ assertExistingProfile(profileId);
886
799
 
887
800
  await executeAutoscriptRuntime({
888
801
  commandName: 'autoscript.run',
@@ -996,6 +909,7 @@ async function handleResume(args) {
996
909
  if (!profileId) {
997
910
  throw new Error('profileId is required. Set in script or pass --profile <id>');
998
911
  }
912
+ assertExistingProfile(profileId);
999
913
  const resumeState = buildResumeStateFromSnapshot(script, snapshot, fromNode || null);
1000
914
  await executeAutoscriptRuntime({
1001
915
  commandName: 'autoscript.resume',
@@ -1067,8 +981,6 @@ async function handleMockRun(args) {
1067
981
  export async function handleAutoscriptCommand(args) {
1068
982
  const sub = args[1];
1069
983
  switch (sub) {
1070
- case 'scaffold':
1071
- return handleScaffold(args);
1072
984
  case 'validate':
1073
985
  return handleValidate(args);
1074
986
  case 'explain':
@@ -1084,10 +996,9 @@ export async function handleAutoscriptCommand(args) {
1084
996
  case 'mock-run':
1085
997
  return handleMockRun(args);
1086
998
  default:
1087
- console.log(`Usage: camo autoscript <scaffold|validate|explain|snapshot|replay|run|resume|mock-run> [args]
999
+ console.log(`Usage: camo autoscript <validate|explain|snapshot|replay|run|resume|mock-run> [args]
1088
1000
 
1089
1001
  Commands:
1090
- scaffold xhs-unified [--output <file>] [options] Generate xiaohongshu unified-harvest autoscript
1091
1002
  validate <file> Validate autoscript schema and references
1092
1003
  explain <file> Print normalized graph and defaults
1093
1004
  snapshot <jsonl-file> [--out <snapshot-file>] Build resumable snapshot from run JSONL