@web-auto/webauto 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.
@@ -25,6 +25,39 @@ import { buildOpenDetailScript, buildSubmitSearchScript } from './xhs/search.mjs
25
25
 
26
26
  const XHS_OPERATION_LOCKS = new Map();
27
27
 
28
+ function emitOperationProgress(context, payload = {}) {
29
+ const emit = context?.emitProgress;
30
+ if (typeof emit !== 'function') return;
31
+ emit(payload);
32
+ }
33
+
34
+ function emitActionTrace(context, actionTrace = [], extra = {}) {
35
+ if (!Array.isArray(actionTrace) || actionTrace.length === 0) return;
36
+ for (let i = 0; i < actionTrace.length; i += 1) {
37
+ const row = actionTrace[i];
38
+ if (!row || typeof row !== 'object') continue;
39
+ const kind = String(row.kind || row.action || '').trim().toLowerCase() || 'trace';
40
+ emitOperationProgress(context, {
41
+ kind,
42
+ step: i + 1,
43
+ ...extra,
44
+ ...row,
45
+ });
46
+ }
47
+ }
48
+
49
+ function replaceEvaluateResultData(rawData, payload) {
50
+ if (rawData && typeof rawData === 'object') {
51
+ if (Object.prototype.hasOwnProperty.call(rawData, 'result')) {
52
+ return { ...rawData, result: payload };
53
+ }
54
+ if (rawData.data && typeof rawData.data === 'object' && Object.prototype.hasOwnProperty.call(rawData.data, 'result')) {
55
+ return { ...rawData, data: { ...rawData.data, result: payload } };
56
+ }
57
+ }
58
+ return payload;
59
+ }
60
+
28
61
  function toLockKey(text, fallback = '') {
29
62
  const value = String(text || '').trim();
30
63
  return value || fallback;
@@ -104,19 +137,40 @@ async function saveSharedClaimDoc(filePath, doc) {
104
137
  await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
105
138
  }
106
139
 
107
- async function executeSubmitSearchOperation({ profileId, params = {} }) {
140
+ async function executeSubmitSearchOperation({
141
+ profileId,
142
+ params = {},
143
+ context = {},
144
+ }) {
108
145
  const script = buildSubmitSearchScript(params);
109
146
  const highlight = params.highlight !== false;
110
147
  const lockKey = resolveSearchLockKey(params);
111
- return withSerializedLock(lockKey ? `xhs_submit_search:${lockKey}` : '', () => evaluateWithScript({
112
- profileId,
113
- script,
114
- message: 'xhs_submit_search done',
115
- highlight,
116
- }));
148
+ return withSerializedLock(lockKey ? `xhs_submit_search:${lockKey}` : '', async () => {
149
+ const operationResult = await evaluateWithScript({
150
+ profileId,
151
+ script,
152
+ message: 'xhs_submit_search done',
153
+ highlight,
154
+ });
155
+ const payload = extractEvaluateResultData(operationResult.data) || {};
156
+ const actionTrace = Array.isArray(payload.actionTrace) ? payload.actionTrace : [];
157
+ if (actionTrace.length > 0) {
158
+ emitActionTrace(context, actionTrace, { stage: 'xhs_submit_search' });
159
+ delete payload.actionTrace;
160
+ return {
161
+ ...operationResult,
162
+ data: replaceEvaluateResultData(operationResult.data, payload),
163
+ };
164
+ }
165
+ return operationResult;
166
+ });
117
167
  }
118
168
 
119
- async function executeOpenDetailOperation({ profileId, params = {} }) {
169
+ async function executeOpenDetailOperation({
170
+ profileId,
171
+ params = {},
172
+ context = {},
173
+ }) {
120
174
  const highlight = params.highlight !== false;
121
175
  const claimPath = resolveSharedClaimPath(params);
122
176
  const lockKey = claimPath ? `xhs_open_detail:${claimPath}` : '';
@@ -156,8 +210,15 @@ async function executeOpenDetailOperation({ profileId, params = {} }) {
156
210
  highlight,
157
211
  });
158
212
  const payload = extractEvaluateResultData(operationResult.data) || {};
213
+ const actionTrace = Array.isArray(payload.actionTrace) ? payload.actionTrace : [];
214
+ if (actionTrace.length > 0) {
215
+ emitActionTrace(context, actionTrace, { stage: 'xhs_open_detail' });
216
+ delete payload.actionTrace;
217
+ }
159
218
  return {
160
- operationResult,
219
+ operationResult: actionTrace.length > 0
220
+ ? { ...operationResult, data: replaceEvaluateResultData(operationResult.data, payload) }
221
+ : operationResult,
161
222
  payload: payload && typeof payload === 'object' ? payload : {},
162
223
  };
163
224
  };
@@ -361,7 +422,11 @@ async function handleRaiseError({ params }) {
361
422
  return asErrorPayload('OPERATION_FAILED', code || 'AUTOSCRIPT_ABORT');
362
423
  }
363
424
 
364
- async function executeCommentsHarvestOperation({ profileId, params = {} }) {
425
+ async function executeCommentsHarvestOperation({
426
+ profileId,
427
+ params = {},
428
+ context = {},
429
+ }) {
365
430
  const script = buildCommentsHarvestScript(params);
366
431
  const highlight = params.highlight !== false;
367
432
  const operationResult = await evaluateWithScript({
@@ -372,6 +437,11 @@ async function executeCommentsHarvestOperation({ profileId, params = {} }) {
372
437
  });
373
438
 
374
439
  const payload = extractEvaluateResultData(operationResult.data) || {};
440
+ const actionTrace = Array.isArray(payload.actionTrace) ? payload.actionTrace : [];
441
+ if (actionTrace.length > 0) {
442
+ emitActionTrace(context, actionTrace, { stage: 'xhs_comments_harvest' });
443
+ delete payload.actionTrace;
444
+ }
375
445
  const shouldPersistComments = params.persistComments === true || params.persistCollectedComments === true;
376
446
  const includeComments = params.includeComments !== false;
377
447
  const comments = Array.isArray(payload.comments) ? payload.comments : [];
@@ -379,12 +449,12 @@ async function executeCommentsHarvestOperation({ profileId, params = {} }) {
379
449
  if (!shouldPersistComments || !includeComments || comments.length === 0) {
380
450
  return {
381
451
  ...operationResult,
382
- data: {
452
+ data: replaceEvaluateResultData(operationResult.data, {
383
453
  ...payload,
384
454
  commentsPath: null,
385
455
  commentsAdded: 0,
386
456
  commentsTotal: Number(payload.collected || comments.length || 0),
387
- },
457
+ }),
388
458
  };
389
459
  }
390
460
 
@@ -434,10 +504,16 @@ export function isXhsAutoscriptAction(action) {
434
504
  return normalized === 'raise_error' || normalized.startsWith('xhs_');
435
505
  }
436
506
 
437
- export async function executeXhsAutoscriptOperation({ profileId, action, params = {} }) {
507
+ export async function executeXhsAutoscriptOperation({
508
+ profileId,
509
+ action,
510
+ params = {},
511
+ operation = null,
512
+ context = {},
513
+ }) {
438
514
  const handler = XHS_ACTION_HANDLERS[action];
439
515
  if (!handler) {
440
516
  return asErrorPayload('UNSUPPORTED_OPERATION', `Unsupported xhs operation: ${action}`);
441
517
  }
442
- return handler({ profileId, params });
518
+ return handler({ profileId, params, operation, context });
443
519
  }
@@ -640,6 +640,20 @@ export class AutoscriptRunner {
640
640
  attempt,
641
641
  maxAttempts,
642
642
  runtime: this.runtimeContext,
643
+ emitProgress: (payload = {}) => {
644
+ const detail = payload && typeof payload === 'object'
645
+ ? { ...payload }
646
+ : { value: payload };
647
+ const kind = String(detail.kind || '').trim() || 'trace';
648
+ if (Object.prototype.hasOwnProperty.call(detail, 'kind')) delete detail.kind;
649
+ this.log('autoscript:operation_progress', {
650
+ operationId: operation.id,
651
+ action: operation.action,
652
+ attempt,
653
+ kind,
654
+ ...detail,
655
+ });
656
+ },
643
657
  };
644
658
 
645
659
  await this.applyPacingBeforeAttempt(operation, attempt);
@@ -472,8 +472,28 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
472
472
  const env = toTrimmedString(rawOptions.env, 'prod');
473
473
  const outputRoot = toTrimmedString(rawOptions.outputRoot, '');
474
474
  const throttle = toPositiveInt(rawOptions.throttle, 900, 100);
475
- const tabCount = toPositiveInt(rawOptions.tabCount, 4, 1);
475
+ const tabCount = toPositiveInt(rawOptions.tabCount, 1, 1);
476
+ const tabOpenDelayMs = toNonNegativeInt(rawOptions.tabOpenDelayMs, 1400);
476
477
  const noteIntervalMs = toPositiveInt(rawOptions.noteIntervalMs, 1200, 200);
478
+ const submitMethod = toTrimmedString(rawOptions.submitMethod, 'click').toLowerCase();
479
+ const submitActionDelayMinMs = toPositiveInt(rawOptions.submitActionDelayMinMs, 180, 20);
480
+ const submitActionDelayMaxMs = toPositiveInt(rawOptions.submitActionDelayMaxMs, 620, submitActionDelayMinMs);
481
+ const submitSettleMinMs = toPositiveInt(rawOptions.submitSettleMinMs, 1200, 60);
482
+ const submitSettleMaxMs = toPositiveInt(rawOptions.submitSettleMaxMs, 2600, submitSettleMinMs);
483
+ const openDetailPreClickMinMs = toPositiveInt(rawOptions.openDetailPreClickMinMs, 220, 60);
484
+ const openDetailPreClickMaxMs = toPositiveInt(rawOptions.openDetailPreClickMaxMs, 700, openDetailPreClickMinMs);
485
+ const openDetailPollDelayMinMs = toPositiveInt(rawOptions.openDetailPollDelayMinMs, 130, 80);
486
+ const openDetailPollDelayMaxMs = toPositiveInt(rawOptions.openDetailPollDelayMaxMs, 320, openDetailPollDelayMinMs);
487
+ const openDetailPostOpenMinMs = toPositiveInt(rawOptions.openDetailPostOpenMinMs, 420, 120);
488
+ const openDetailPostOpenMaxMs = toPositiveInt(rawOptions.openDetailPostOpenMaxMs, 1100, openDetailPostOpenMinMs);
489
+ const commentsScrollStepMin = toPositiveInt(rawOptions.commentsScrollStepMin, 280, 120);
490
+ const commentsScrollStepMax = toPositiveInt(rawOptions.commentsScrollStepMax, 420, commentsScrollStepMin);
491
+ const commentsSettleMinMs = toPositiveInt(rawOptions.commentsSettleMinMs, 280, 80);
492
+ const commentsSettleMaxMs = toPositiveInt(rawOptions.commentsSettleMaxMs, 820, commentsSettleMinMs);
493
+ const defaultOperationMinIntervalMs = toNonNegativeInt(rawOptions.defaultOperationMinIntervalMs, 1200);
494
+ const defaultEventCooldownMs = toNonNegativeInt(rawOptions.defaultEventCooldownMs, 700);
495
+ const defaultPacingJitterMs = toNonNegativeInt(rawOptions.defaultPacingJitterMs, 900);
496
+ const navigationMinIntervalMs = toNonNegativeInt(rawOptions.navigationMinIntervalMs, 2200);
477
497
  const maxNotes = toPositiveInt(rawOptions.maxNotes, 30, 1);
478
498
  const maxComments = toNonNegativeInt(rawOptions.maxComments, 0);
479
499
  const resume = toBoolean(rawOptions.resume, false);
@@ -529,10 +549,10 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
529
549
  validationMode: 'none',
530
550
  recovery,
531
551
  pacing: {
532
- operationMinIntervalMs: 700,
533
- eventCooldownMs: 300,
534
- jitterMs: 220,
535
- navigationMinIntervalMs: 1800,
552
+ operationMinIntervalMs: defaultOperationMinIntervalMs,
553
+ eventCooldownMs: defaultEventCooldownMs,
554
+ jitterMs: defaultPacingJitterMs,
555
+ navigationMinIntervalMs,
536
556
  timeoutMs: 0,
537
557
  },
538
558
  timeoutMs: 0,
@@ -542,7 +562,27 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
542
562
  env,
543
563
  outputRoot,
544
564
  tabCount,
565
+ tabOpenDelayMs,
545
566
  noteIntervalMs,
567
+ submitMethod,
568
+ submitActionDelayMinMs,
569
+ submitActionDelayMaxMs,
570
+ submitSettleMinMs,
571
+ submitSettleMaxMs,
572
+ openDetailPreClickMinMs,
573
+ openDetailPreClickMaxMs,
574
+ openDetailPollDelayMinMs,
575
+ openDetailPollDelayMaxMs,
576
+ openDetailPostOpenMinMs,
577
+ openDetailPostOpenMaxMs,
578
+ commentsScrollStepMin,
579
+ commentsScrollStepMax,
580
+ commentsSettleMinMs,
581
+ commentsSettleMaxMs,
582
+ defaultOperationMinIntervalMs,
583
+ defaultEventCooldownMs,
584
+ defaultPacingJitterMs,
585
+ navigationMinIntervalMs,
546
586
  maxNotes,
547
587
  maxComments,
548
588
  maxLikesPerRound,
@@ -645,6 +685,11 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
645
685
  keyword,
646
686
  searchSerialKey,
647
687
  sharedHarvestPath,
688
+ method: submitMethod,
689
+ actionDelayMinMs: submitActionDelayMinMs,
690
+ actionDelayMaxMs: submitActionDelayMaxMs,
691
+ settleMinMs: submitSettleMinMs,
692
+ settleMaxMs: submitSettleMaxMs,
648
693
  },
649
694
  trigger: 'home_search_input.exist',
650
695
  dependsOn: ['fill_keyword'],
@@ -672,6 +717,12 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
672
717
  sharedHarvestPath,
673
718
  seedCollectCount,
674
719
  seedCollectMaxRounds,
720
+ preClickDelayMinMs: openDetailPreClickMinMs,
721
+ preClickDelayMaxMs: openDetailPreClickMaxMs,
722
+ pollDelayMinMs: openDetailPollDelayMinMs,
723
+ pollDelayMaxMs: openDetailPollDelayMaxMs,
724
+ postOpenDelayMinMs: openDetailPostOpenMinMs,
725
+ postOpenDelayMaxMs: openDetailPostOpenMaxMs,
675
726
  },
676
727
  trigger: 'search_result_item.exist',
677
728
  dependsOn: ['submit_search'],
@@ -732,8 +783,12 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
732
783
  persistComments,
733
784
  commentsLimit: maxComments,
734
785
  maxRounds: 48,
735
- scrollStep: 360,
736
- settleMs: 260,
786
+ scrollStep: commentsScrollStepMin,
787
+ scrollStepMin: commentsScrollStepMin,
788
+ scrollStepMax: commentsScrollStepMax,
789
+ settleMs: commentsSettleMinMs,
790
+ settleMinMs: commentsSettleMinMs,
791
+ settleMaxMs: commentsSettleMaxMs,
737
792
  stallRounds: 8,
738
793
  recoveryNoProgressRounds: 3,
739
794
  recoveryStuckRounds: 2,
@@ -874,6 +929,12 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
874
929
  resume,
875
930
  incrementalMax,
876
931
  sharedHarvestPath,
932
+ preClickDelayMinMs: openDetailPreClickMinMs,
933
+ preClickDelayMaxMs: openDetailPreClickMaxMs,
934
+ pollDelayMinMs: openDetailPollDelayMinMs,
935
+ pollDelayMaxMs: openDetailPollDelayMaxMs,
936
+ postOpenDelayMinMs: openDetailPostOpenMinMs,
937
+ postOpenDelayMaxMs: openDetailPostOpenMaxMs,
877
938
  },
878
939
  trigger: 'search_result_item.exist',
879
940
  dependsOn: ['switch_tab_round_robin'],
@@ -924,7 +985,7 @@ export function buildXhsUnifiedAutoscript(rawOptions = {}) {
924
985
  action: 'ensure_tab_pool',
925
986
  params: {
926
987
  tabCount,
927
- openDelayMs: 1200,
988
+ openDelayMs: tabOpenDelayMs,
928
989
  normalizeTabs: false,
929
990
  },
930
991
  trigger: 'search_result_item.exist',
@@ -1,10 +1,59 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'node:child_process';
2
+ import { execSync, spawnSync } from 'node:child_process';
3
3
  import fs from 'node:fs';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
+ import { createRequire } from 'node:module';
7
+ import { fileURLToPath } from 'node:url';
6
8
  import { BROWSER_SERVICE_URL, loadConfig, setRepoRoot } from './config.mjs';
7
9
 
10
+ const requireFromHere = createRequire(import.meta.url);
11
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
12
+
13
+ function resolveNodeBin() {
14
+ const explicit = String(process.env.WEBAUTO_NODE_BIN || '').trim();
15
+ if (explicit) return explicit;
16
+ const npmNode = String(process.env.npm_node_execpath || '').trim();
17
+ if (npmNode) return npmNode;
18
+ return process.execPath;
19
+ }
20
+
21
+ function resolveCamoCliEntry() {
22
+ try {
23
+ const resolved = requireFromHere.resolve('@web-auto/camo/bin/camo.mjs');
24
+ if (resolved && fs.existsSync(resolved)) return resolved;
25
+ } catch {
26
+ return null;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ function runCamoCli(args = [], options = {}) {
32
+ const entry = resolveCamoCliEntry();
33
+ if (!entry) {
34
+ return {
35
+ ok: false,
36
+ code: null,
37
+ stdout: '',
38
+ stderr: '@web-auto/camo/bin/camo.mjs not found',
39
+ entry: null,
40
+ };
41
+ }
42
+ const ret = spawnSync(resolveNodeBin(), [entry, ...args], {
43
+ encoding: 'utf8',
44
+ windowsHide: true,
45
+ stdio: options.stdio || 'pipe',
46
+ env: { ...process.env, ...(options.env || {}) },
47
+ });
48
+ return {
49
+ ok: ret.status === 0,
50
+ code: ret.status,
51
+ stdout: String(ret.stdout || ''),
52
+ stderr: String(ret.stderr || ''),
53
+ entry,
54
+ };
55
+ }
56
+
8
57
  export async function callAPI(action, payload = {}) {
9
58
  const r = await fetch(`${BROWSER_SERVICE_URL}/command`, {
10
59
  method: 'POST',
@@ -320,20 +369,13 @@ function scanCommonRepoRoots() {
320
369
 
321
370
  export function findRepoRootCandidate() {
322
371
  const cfg = loadConfig();
372
+ const cwdRoot = walkUpForRepoRoot(process.cwd());
373
+ const moduleRoot = walkUpForRepoRoot(MODULE_DIR);
323
374
  const candidates = [
324
375
  process.env.WEBAUTO_REPO_ROOT,
325
- process.cwd(),
326
376
  cfg.repoRoot,
327
- path.join('/Volumes', 'extension', 'code', 'webauto'),
328
- path.join('/Volumes', 'extension', 'code', 'WebAuto'),
329
- path.join(os.homedir(), 'Documents', 'github', 'webauto'),
330
- path.join(os.homedir(), 'Documents', 'github', 'WebAuto'),
331
- path.join(os.homedir(), 'github', 'webauto'),
332
- path.join(os.homedir(), 'github', 'WebAuto'),
333
- path.join('C:', 'code', 'webauto'),
334
- path.join('C:', 'code', 'WebAuto'),
335
- path.join('C:', 'Users', os.userInfo().username, 'code', 'webauto'),
336
- path.join('C:', 'Users', os.userInfo().username, 'code', 'WebAuto'),
377
+ moduleRoot,
378
+ cwdRoot,
337
379
  ].filter(Boolean);
338
380
 
339
381
  for (const root of candidates) {
@@ -345,22 +387,6 @@ export function findRepoRootCandidate() {
345
387
  return resolved;
346
388
  }
347
389
 
348
- const walked = walkUpForRepoRoot(process.cwd());
349
- if (walked) {
350
- if (cfg.repoRoot !== walked) {
351
- setRepoRoot(walked);
352
- }
353
- return walked;
354
- }
355
-
356
- const scanned = scanCommonRepoRoots();
357
- if (scanned) {
358
- if (cfg.repoRoot !== scanned) {
359
- setRepoRoot(scanned);
360
- }
361
- return scanned;
362
- }
363
-
364
390
  return null;
365
391
  }
366
392
 
@@ -384,12 +410,7 @@ export function detectCamoufoxPath() {
384
410
 
385
411
  export function ensureCamoufox() {
386
412
  if (detectCamoufoxPath()) return;
387
- console.log('Camoufox is not found. Installing...');
388
- execSync('npx --yes --package=camoufox camoufox fetch', { stdio: 'inherit' });
389
- if (!detectCamoufoxPath()) {
390
- throw new Error('Camoufox install finished but executable was not detected');
391
- }
392
- console.log('Camoufox installed.');
413
+ throw new Error('Camoufox is not installed. Run: webauto xhs install --download-browser');
393
414
  }
394
415
 
395
416
  export async function ensureBrowserService() {
@@ -405,19 +426,20 @@ export async function ensureBrowserService() {
405
426
 
406
427
  if (provider === 'camo') {
407
428
  const repoRoot = findRepoRootCandidate();
408
- if (repoRoot) {
409
- try {
410
- execSync(`npx --yes @web-auto/camo config repo-root ${JSON.stringify(repoRoot)}`, { stdio: 'ignore' });
411
- } catch {
412
- // best-effort only; init will still try using current config
413
- }
429
+ if (!repoRoot) {
430
+ throw new Error('WEBAUTO_REPO_ROOT is not set and no valid repo root was found');
431
+ }
432
+ const configRet = runCamoCli(['config', 'repo-root', repoRoot], { stdio: 'pipe' });
433
+ if (!configRet.ok) {
434
+ throw new Error(
435
+ `camo config repo-root failed: ${configRet.stderr.trim() || configRet.stdout.trim() || `exit ${configRet.code ?? 'null'}`}`,
436
+ );
414
437
  }
415
438
 
416
- try {
417
- console.log('Starting browser backend via camo init...');
418
- execSync('npx --yes @web-auto/camo init', { stdio: 'inherit' });
419
- } catch (error) {
420
- throw new Error(`camo init failed: ${error?.message || String(error)}`);
439
+ console.log('Starting browser backend via camo init...');
440
+ const initRet = runCamoCli(['init'], { stdio: 'inherit' });
441
+ if (!initRet.ok) {
442
+ throw new Error(`camo init failed: ${initRet.stderr.trim() || initRet.stdout.trim() || `exit ${initRet.code ?? 'null'}`}`);
421
443
  }
422
444
 
423
445
  for (let i = 0; i < 20; i += 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/webauto",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "webauto": "bin/webauto.mjs"
@@ -14,6 +14,7 @@
14
14
  "services/",
15
15
  "apps/desktop-console/dist/",
16
16
  "apps/desktop-console/default-settings.json",
17
+ "apps/desktop-console/package.json",
17
18
  "apps/desktop-console/entry/ui-console.mjs",
18
19
  "apps/desktop-console/entry/ui-cli.mjs",
19
20
  "apps/webauto/",
@@ -60,9 +61,9 @@
60
61
  "check:modules": "echo \"Module checks skipped\"",
61
62
  "check:ts": "tsc --noEmit -p tsconfig.services.json",
62
63
  "test:modules:unit": "npx tsx --test $(find modules -name \"*.test.ts\" -o -name \"*.test.mts\" 2>/dev/null | tr \"\\n\" \" \")",
63
- "test:desktop-console:unit": "tsx --test apps/desktop-console/src/main/profile-store.test.mts apps/desktop-console/src/main/index-streaming.test.mts apps/desktop-console/src/main/ui-cli-bridge.test.mts apps/desktop-console/src/main/task-gateway.test.mts apps/desktop-console/src/main/heartbeat-watchdog.test.mts apps/desktop-console/src/main/core-daemon-manager.test.mts apps/desktop-console/src/main/desktop-settings.test.mts apps/desktop-console/src/main/env-check.test.mts",
64
+ "test:desktop-console:unit": "tsx --test apps/desktop-console/src/main/profile-store.test.mts apps/desktop-console/src/main/index-streaming.test.mts apps/desktop-console/src/main/ui-cli-bridge.test.mts apps/desktop-console/src/main/task-gateway.test.mts apps/desktop-console/src/main/heartbeat-watchdog.test.mts apps/desktop-console/src/main/core-daemon-manager.test.mts apps/desktop-console/src/main/desktop-settings.test.mts apps/desktop-console/src/main/env-check.test.mts apps/desktop-console/src/main/state-bridge.test.mts",
64
65
  "test:desktop-console:renderer": "npm --prefix apps/desktop-console run test:renderer",
65
- "test:desktop-console:coverage": "c8 --reporter=text --reporter=lcov --all --src apps/desktop-console/src/main --extension .mts --extension .mjs --extension .ts --exclude \"**/*.test.*\" --check-coverage --lines 75 --functions 75 --branches 55 --statements 75 --include \"apps/desktop-console/src/main/profile-store.mts\" --include \"apps/desktop-console/src/main/ui-cli-bridge.mts\" --include \"apps/desktop-console/src/main/task-gateway.mts\" --include \"apps/desktop-console/src/main/heartbeat-watchdog.mts\" --include \"apps/desktop-console/src/main/core-daemon-manager.mts\" --include \"apps/desktop-console/src/main/desktop-settings.mts\" --include \"apps/desktop-console/src/main/env-check.mts\" tsx --test apps/desktop-console/src/main/profile-store.test.mts apps/desktop-console/src/main/index-streaming.test.mts apps/desktop-console/src/main/ui-cli-bridge.test.mts apps/desktop-console/src/main/task-gateway.test.mts apps/desktop-console/src/main/heartbeat-watchdog.test.mts apps/desktop-console/src/main/core-daemon-manager.test.mts apps/desktop-console/src/main/desktop-settings.test.mts apps/desktop-console/src/main/env-check.test.mts",
66
+ "test:desktop-console:coverage": "c8 --reporter=text --reporter=lcov --all --src apps/desktop-console/src/main --extension .mts --extension .mjs --extension .ts --exclude \"**/*.test.*\" --check-coverage --lines 75 --functions 75 --branches 55 --statements 75 --include \"apps/desktop-console/src/main/profile-store.mts\" --include \"apps/desktop-console/src/main/ui-cli-bridge.mts\" --include \"apps/desktop-console/src/main/task-gateway.mts\" --include \"apps/desktop-console/src/main/heartbeat-watchdog.mts\" --include \"apps/desktop-console/src/main/core-daemon-manager.mts\" --include \"apps/desktop-console/src/main/desktop-settings.mts\" --include \"apps/desktop-console/src/main/env-check.mts\" --include \"apps/desktop-console/src/main/state-bridge.mts\" tsx --test apps/desktop-console/src/main/profile-store.test.mts apps/desktop-console/src/main/index-streaming.test.mts apps/desktop-console/src/main/ui-cli-bridge.test.mts apps/desktop-console/src/main/task-gateway.test.mts apps/desktop-console/src/main/heartbeat-watchdog.test.mts apps/desktop-console/src/main/core-daemon-manager.test.mts apps/desktop-console/src/main/desktop-settings.test.mts apps/desktop-console/src/main/env-check.test.mts",
66
67
  "test:webauto:schedule:unit": "node --test tests/unit/webauto/schedule-store.test.mjs tests/unit/webauto/schedule-cli.test.mjs",
67
68
  "test:webauto:ui-cli:unit": "node --test tests/unit/webauto/ui-cli-command.test.mjs",
68
69
  "test:webauto:install:unit": "node --test tests/unit/webauto/xhs-install.test.mjs",