@web-auto/webauto 0.1.3 → 0.1.6

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 (174) hide show
  1. package/apps/desktop-console/default-settings.json +2 -2
  2. package/apps/desktop-console/dist/main/index.mjs +915 -85
  3. package/apps/desktop-console/dist/main/preload.mjs +7 -0
  4. package/apps/desktop-console/dist/renderer/index.html +622 -50
  5. package/apps/desktop-console/dist/renderer/index.js +2415 -470
  6. package/apps/desktop-console/dist/renderer/run.mts +6 -5
  7. package/apps/desktop-console/entry/ui-cli.mjs +672 -0
  8. package/apps/desktop-console/entry/ui-console.mjs +416 -29
  9. package/apps/webauto/entry/account.mjs +89 -53
  10. package/apps/webauto/entry/browser-status.mjs +7 -10
  11. package/apps/webauto/entry/lib/account-detect.mjs +254 -28
  12. package/apps/webauto/entry/lib/account-store.mjs +219 -30
  13. package/apps/webauto/entry/lib/bus-publish.mjs +63 -0
  14. package/apps/webauto/entry/lib/camo-cli.mjs +93 -0
  15. package/apps/webauto/entry/lib/profilepool.mjs +14 -5
  16. package/apps/webauto/entry/lib/quota-status.mjs +23 -0
  17. package/apps/webauto/entry/lib/schedule-store.mjs +1068 -0
  18. package/apps/webauto/entry/profilepool.mjs +106 -17
  19. package/apps/webauto/entry/schedule.mjs +612 -0
  20. package/apps/webauto/entry/weibo-unified.mjs +134 -0
  21. package/apps/webauto/entry/xhs-install.mjs +236 -29
  22. package/apps/webauto/entry/xhs-status.mjs +5 -2
  23. package/apps/webauto/entry/xhs-unified.mjs +631 -98
  24. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/comment_item/container.json +40 -0
  25. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_expand_button/container.json +38 -0
  26. package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_list/container.json +37 -0
  27. package/apps/webauto/resources/container-library/weibo/weibo_search_page/container.json +8 -3
  28. package/apps/webauto/resources/container-library/weibo/weibo_search_page/login_anchor/container.json +30 -0
  29. package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_bar/container.json +47 -0
  30. package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_button/container.json +39 -0
  31. package/bin/camoufox-cli.mjs +61 -0
  32. package/bin/webauto.mjs +301 -54
  33. package/dist/modules/camo-backend/src/index.js +49 -1
  34. package/dist/modules/camo-backend/src/internal/BrowserSession.js +572 -3
  35. package/dist/modules/camo-backend/src/internal/SessionManager.js +13 -1
  36. package/dist/modules/camo-backend/src/internal/storage-paths.js +6 -0
  37. package/dist/modules/collection-manager/bloom-filter.js +91 -0
  38. package/dist/modules/collection-manager/date-utils.js +275 -0
  39. package/dist/modules/collection-manager/index.js +258 -0
  40. package/dist/modules/collection-manager/storage.js +195 -0
  41. package/dist/modules/collection-manager/types.js +47 -0
  42. package/dist/modules/logging/src/index.js +1 -1
  43. package/dist/modules/process-registry/index.js +230 -0
  44. package/dist/modules/rate-limiter/index.js +242 -0
  45. package/dist/modules/workflow/blocks/ExecuteWeiboSearchBlock.js +128 -0
  46. package/dist/modules/workflow/blocks/PersistXhsNoteBlock.js +7 -3
  47. package/dist/modules/workflow/blocks/RenderMarkdown.js +4 -1
  48. package/dist/modules/workflow/blocks/WeiboCollectCommentsBlock.js +282 -0
  49. package/dist/modules/workflow/blocks/WeiboCollectFromLinksBlock.js +283 -0
  50. package/dist/modules/workflow/blocks/WeiboCollectSearchLinksBlock.js +208 -0
  51. package/dist/modules/workflow/blocks/WeiboCollectTimelineListBlock.js +128 -0
  52. package/dist/modules/workflow/blocks/WeiboCollectUserPostsListBlock.js +127 -0
  53. package/dist/modules/workflow/blocks/helpers/downloadPaths.js +21 -0
  54. package/dist/modules/workflow/config/workflowRegistry.js +2 -0
  55. package/dist/modules/workflow/definitions/weibo-search-workflow-v1.js +47 -0
  56. package/dist/modules/workflow/src/runner.js +6 -0
  57. package/dist/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +4 -0
  58. package/dist/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +2 -2
  59. package/dist/modules/xiaohongshu/app/src/blocks/helpers/sharding.js +123 -0
  60. package/dist/modules/xiaohongshu/app/src/container-registry/src/index.d.ts +37 -0
  61. package/dist/modules/xiaohongshu/app/src/container-registry/src/index.js +184 -0
  62. package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.d.ts +31 -0
  63. package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.js +71 -0
  64. package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.d.ts +48 -0
  65. package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.js +259 -0
  66. package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.d.ts +28 -0
  67. package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.js +319 -0
  68. package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.d.ts +36 -0
  69. package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.js +162 -0
  70. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.d.ts +36 -0
  71. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.js +301 -0
  72. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.d.ts +29 -0
  73. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.js +195 -0
  74. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.d.ts +25 -0
  75. package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.js +164 -0
  76. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.d.ts +66 -0
  77. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.js +139 -0
  78. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.d.ts +16 -0
  79. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.js +36 -0
  80. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.d.ts +27 -0
  81. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.js +213 -0
  82. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.d.ts +18 -0
  83. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.js +121 -0
  84. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.d.ts +34 -0
  85. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.js +1249 -0
  86. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.d.ts +17 -0
  87. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.js +703 -0
  88. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.d.ts +15 -0
  89. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.js +41 -0
  90. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.d.ts +26 -0
  91. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.js +44 -0
  92. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.d.ts +29 -0
  93. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.js +150 -0
  94. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.d.ts +38 -0
  95. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.js +117 -0
  96. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.d.ts +30 -0
  97. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.js +102 -0
  98. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.d.ts +23 -0
  99. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.js +109 -0
  100. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.d.ts +32 -0
  101. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +117 -0
  102. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.d.ts +35 -0
  103. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.js +114 -0
  104. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.d.ts +34 -0
  105. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.js +90 -0
  106. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.d.ts +111 -0
  107. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +1009 -0
  108. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.d.ts +20 -0
  109. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.js +233 -0
  110. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.d.ts +48 -0
  111. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +291 -0
  112. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.d.ts +23 -0
  113. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.js +240 -0
  114. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.d.ts +55 -0
  115. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.js +126 -0
  116. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.d.ts +21 -0
  117. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.js +99 -0
  118. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.d.ts +5 -0
  119. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.js +27 -0
  120. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.d.ts +37 -0
  121. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.js +165 -0
  122. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.d.ts +33 -0
  123. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.js +270 -0
  124. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.d.ts +9 -0
  125. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.js +9 -0
  126. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.d.ts +50 -0
  127. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.js +222 -0
  128. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.d.ts +10 -0
  129. package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.js +43 -0
  130. package/dist/services/shared/serviceProcessLogger.js +1 -1
  131. package/dist/services/unified-api/server.js +105 -11
  132. package/modules/camo-backend/src/index.ts +46 -1
  133. package/modules/camo-backend/src/internal/BrowserSession.ts +619 -3
  134. package/modules/camo-backend/src/internal/SessionManager.ts +12 -1
  135. package/modules/camo-backend/src/internal/storage-paths.ts +5 -0
  136. package/modules/camo-runtime/src/autoscript/action-providers/xhs/comments.mjs +38 -2
  137. package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +47 -2
  138. package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +94 -11
  139. package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +208 -2
  140. package/modules/camo-runtime/src/autoscript/runtime.mjs +7 -1
  141. package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +76 -43
  142. package/modules/camo-runtime/src/container/runtime-core/operations/index.mjs +75 -1
  143. package/modules/camo-runtime/src/container/runtime-core/operations/selector-scripts.mjs +71 -4
  144. package/modules/camo-runtime/src/container/runtime-core/operations/tab-pool.mjs +183 -27
  145. package/modules/collection-manager/bloom-filter.ts +112 -0
  146. package/modules/collection-manager/date-utils.ts +316 -0
  147. package/modules/collection-manager/index.ts +309 -0
  148. package/modules/collection-manager/package.json +10 -0
  149. package/modules/collection-manager/storage.ts +174 -0
  150. package/modules/collection-manager/types.ts +156 -0
  151. package/modules/logging/src/index.ts +1 -1
  152. package/modules/process-registry/index.ts +284 -0
  153. package/modules/rate-limiter/index.ts +322 -0
  154. package/modules/state/src/paths.ts +9 -1
  155. package/modules/task-scheduler/index.ts +293 -0
  156. package/modules/workflow/blocks/ExecuteWeiboSearchBlock.ts +167 -0
  157. package/modules/workflow/blocks/PersistXhsNoteBlock.ts +7 -3
  158. package/modules/workflow/blocks/RenderMarkdown.ts +4 -1
  159. package/modules/workflow/blocks/WeiboCollectCommentsBlock.ts +339 -0
  160. package/modules/workflow/blocks/WeiboCollectFromLinksBlock.ts +338 -0
  161. package/modules/workflow/blocks/helpers/downloadPaths.ts +16 -0
  162. package/modules/workflow/config/workflowRegistry.ts +2 -0
  163. package/modules/workflow/definitions/weibo-search-workflow-v1.ts +47 -0
  164. package/modules/workflow/src/runner.ts +6 -0
  165. package/modules/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.ts +1 -1
  166. package/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.ts +4 -0
  167. package/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.ts +2 -3
  168. package/modules/xiaohongshu/app/src/blocks/helpers/sharding.ts +152 -0
  169. package/package.json +14 -5
  170. package/scripts/postinstall-resources.mjs +62 -0
  171. package/scripts/test/run-coverage.mjs +76 -0
  172. package/scripts/weibo/search.ts +49 -0
  173. package/services/shared/serviceProcessLogger.ts +1 -1
  174. package/services/unified-api/server.ts +98 -12
@@ -1,3 +1,85 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import fs from 'node:fs/promises';
4
+ function sanitizeForPath(name, fallback = 'unknown') {
5
+ const text = String(name || '').trim();
6
+ if (!text)
7
+ return fallback;
8
+ const cleaned = text.replace(/[\\/:"*?<>|]+/g, '_').trim();
9
+ return cleaned || fallback;
10
+ }
11
+ function asStringArray(input) {
12
+ if (!Array.isArray(input))
13
+ return [];
14
+ return input
15
+ .map((item) => String(item || '').trim())
16
+ .filter(Boolean);
17
+ }
18
+ function normalizeCollectedUrls(input, completedNoteIds) {
19
+ if (!Array.isArray(input))
20
+ return [];
21
+ const out = [];
22
+ const seen = new Set();
23
+ for (const row of input) {
24
+ if (!row || typeof row !== 'object')
25
+ continue;
26
+ const noteId = String(row.noteId || '').trim();
27
+ if (!noteId || completedNoteIds.has(noteId) || seen.has(noteId))
28
+ continue;
29
+ seen.add(noteId);
30
+ const safeUrl = String(row.safeUrl || '').trim();
31
+ const searchUrl = String(row.searchUrl || '').trim();
32
+ const timestampRaw = Number(row.timestamp);
33
+ const item = {
34
+ noteId,
35
+ safeUrl,
36
+ ...(searchUrl ? { searchUrl } : {}),
37
+ ...(Number.isFinite(timestampRaw) ? { timestamp: timestampRaw } : {}),
38
+ };
39
+ out.push(item);
40
+ }
41
+ return out;
42
+ }
43
+ async function resolveStatePath(input) {
44
+ const root = resolveDownloadRoot(input.downloadRoot);
45
+ const sanitizedEnv = sanitizeForPath(input.env, 'debug');
46
+ const sanitizedKeyword = sanitizeForPath(input.keyword, 'unknown');
47
+ const rawEnv = String(input.env || '').trim();
48
+ const rawKeyword = String(input.keyword || '').trim();
49
+ const candidates = [
50
+ path.join(root, 'xiaohongshu', sanitizedEnv, sanitizedKeyword, '.collect-state.json'),
51
+ ];
52
+ const legacyPath = path.join(root, 'xiaohongshu', rawEnv, rawKeyword, '.collect-state.json');
53
+ if (legacyPath !== candidates[0])
54
+ candidates.push(legacyPath);
55
+ for (const filePath of candidates) {
56
+ try {
57
+ await fs.access(filePath);
58
+ return filePath;
59
+ }
60
+ catch {
61
+ // continue
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ async function getXhsPendingItems(input) {
67
+ const statePath = await resolveStatePath(input);
68
+ if (!statePath)
69
+ return [];
70
+ try {
71
+ const content = await fs.readFile(statePath, 'utf8');
72
+ const state = JSON.parse(content);
73
+ const completed = new Set(asStringArray(state?.detailCollection?.completedNoteIds));
74
+ return normalizeCollectedUrls(state?.listCollection?.collectedUrls, completed);
75
+ }
76
+ catch (error) {
77
+ const code = String(error?.code || '');
78
+ const kind = code === 'ENOENT' ? 'missing_state' : code === 'EACCES' ? 'access_denied' : 'invalid_state';
79
+ console.warn(`[xhs.sharding] failed to load pending items (${kind}) from ${statePath}: ${error?.message || String(error)}`);
80
+ return [];
81
+ }
82
+ }
1
83
  export function fnv1a32(input) {
2
84
  // FNV-1a 32-bit
3
85
  let hash = 0x811c9dc5;
@@ -39,4 +121,45 @@ export function shardFilterByIndexMod(items, shard) {
39
121
  return list;
40
122
  return list.filter((_, idx) => idx % shard.count === shard.index);
41
123
  }
124
+ export function resolveDownloadRoot(customRoot) {
125
+ const fromArg = String(customRoot || '').trim();
126
+ if (fromArg)
127
+ return path.resolve(fromArg);
128
+ const fromEnv = String(process.env.WEBAUTO_DOWNLOAD_ROOT || process.env.WEBAUTO_DOWNLOAD_DIR || '').trim();
129
+ if (fromEnv)
130
+ return path.resolve(fromEnv);
131
+ const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
132
+ return path.join(home, '.webauto', 'download');
133
+ }
134
+ export async function buildDynamicShardPlan(input) {
135
+ const { keyword, env, downloadRoot, validProfiles } = input;
136
+ if (!validProfiles.length)
137
+ return [];
138
+ // Load pending items (not completed yet)
139
+ const pendingItems = await getXhsPendingItems({ keyword, env, downloadRoot });
140
+ if (pendingItems.length === 0) {
141
+ return validProfiles.map((profileId) => ({ profileId, assignedNoteIds: [], totalPending: 0 }));
142
+ }
143
+ // Calculate how many notes each profile should handle
144
+ // Using ceil to ensure we cover all pending items
145
+ const perProfileCount = Math.ceil(pendingItems.length / validProfiles.length);
146
+ const plans = [];
147
+ for (let i = 0; i < validProfiles.length; i++) {
148
+ const profileId = validProfiles[i];
149
+ const startIdx = i * perProfileCount;
150
+ const endIdx = Math.min(startIdx + perProfileCount, pendingItems.length);
151
+ const assignedNoteIds = pendingItems.slice(startIdx, endIdx).map((item) => item.noteId);
152
+ plans.push({
153
+ profileId,
154
+ assignedNoteIds,
155
+ totalPending: pendingItems.length,
156
+ });
157
+ }
158
+ return plans;
159
+ }
160
+ export async function getPendingItemsByNoteIds(input) {
161
+ const pendingItems = await getXhsPendingItems({ keyword: input.keyword, env: input.env, downloadRoot: input.downloadRoot });
162
+ const noteIdSet = new Set(input.noteIds);
163
+ return pendingItems.filter((item) => noteIdSet.has(item.noteId));
164
+ }
42
165
  //# sourceMappingURL=sharding.js.map
@@ -0,0 +1,37 @@
1
+ export interface SelectorDefinition {
2
+ css?: string;
3
+ id?: string;
4
+ classes?: string[];
5
+ variant?: string;
6
+ score?: number;
7
+ }
8
+ export interface ContainerDefinition {
9
+ id: string;
10
+ name?: string;
11
+ type?: string;
12
+ selectors?: SelectorDefinition[];
13
+ children?: string[];
14
+ page_patterns?: string[];
15
+ pagePatterns?: string[];
16
+ metadata?: Record<string, any>;
17
+ [key: string]: any;
18
+ }
19
+ export declare class ContainerRegistry {
20
+ private indexCache;
21
+ listSites(): {
22
+ key: string;
23
+ website: string;
24
+ path: string;
25
+ }[];
26
+ getContainersForSite(siteKey: string): Record<string, ContainerDefinition>;
27
+ resolveSiteKey(url: string): string | null;
28
+ load(): Promise<void>;
29
+ getContainersForUrl(url: string): Record<string, ContainerDefinition>;
30
+ private fetchContainersForSite;
31
+ private ensureIndex;
32
+ private loadSiteContainers;
33
+ private walkSite;
34
+ private loadLegacyFile;
35
+ private findSiteKey;
36
+ }
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,184 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { fileURLToPath } from 'url';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const PROJECT_ROOT = resolveProjectRoot(__dirname);
8
+ // 新的外置容器树根目录(开发用):~/.webauto/container-lib
9
+ const PRIMARY_USER_CONTAINER_ROOT = process.env.WEBAUTO_CONTAINER_ROOT || path.join(os.homedir(), '.webauto', 'container-lib');
10
+ const INDEX_PATH = path.join(PROJECT_ROOT, 'apps/webauto/resources/container-library.index.json');
11
+ function isLegacyContainer(definition) {
12
+ try {
13
+ return Boolean(definition?.metadata?.legacy_data);
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ export class ContainerRegistry {
20
+ indexCache = null;
21
+ listSites() {
22
+ const registry = this.ensureIndex();
23
+ return Object.entries(registry).map(([key, meta]) => ({
24
+ key,
25
+ website: meta.website || '',
26
+ path: meta.path || '',
27
+ }));
28
+ }
29
+ getContainersForSite(siteKey) {
30
+ if (!siteKey)
31
+ return {};
32
+ const registry = this.ensureIndex();
33
+ const site = registry[siteKey] || { path: path.join('apps/webauto/resources/container-library', siteKey) };
34
+ return this.fetchContainersForSite(siteKey, site);
35
+ }
36
+ resolveSiteKey(url) {
37
+ const registry = this.ensureIndex();
38
+ return this.findSiteKey(url, registry);
39
+ }
40
+ async load() {
41
+ // 兼容异步加载,但当前是同步实现
42
+ this.ensureIndex();
43
+ return;
44
+ }
45
+ getContainersForUrl(url) {
46
+ const registry = this.ensureIndex();
47
+ const siteKey = this.findSiteKey(url, registry);
48
+ if (!siteKey) {
49
+ return {};
50
+ }
51
+ const site = registry[siteKey] || { path: `apps/webauto/resources/container-library/${siteKey}` };
52
+ return this.fetchContainersForSite(siteKey, site);
53
+ }
54
+ fetchContainersForSite(siteKey, site) {
55
+ // 不做内存级别缓存,确保用户容器定义变更后,每次调用都能读取到最新文件。
56
+ // 内部会同时加载内置容器与用户容器目录并合并。
57
+ return this.loadSiteContainers(siteKey, site?.path);
58
+ }
59
+ ensureIndex() {
60
+ if (this.indexCache) {
61
+ return this.indexCache;
62
+ }
63
+ if (fs.existsSync(INDEX_PATH)) {
64
+ try {
65
+ this.indexCache = (JSON.parse(fs.readFileSync(INDEX_PATH, 'utf-8')) || {});
66
+ return this.indexCache;
67
+ }
68
+ catch {
69
+ // fall through
70
+ }
71
+ }
72
+ this.indexCache = {};
73
+ return this.indexCache;
74
+ }
75
+ loadSiteContainers(siteKey, relativePath) {
76
+ const containers = {};
77
+ const builtinPath = path.join(PROJECT_ROOT, relativePath || path.join('apps/webauto/resources/container-library', siteKey));
78
+ if (fs.existsSync(builtinPath)) {
79
+ this.walkSite(builtinPath, containers);
80
+ this.loadLegacyFile(builtinPath, containers);
81
+ }
82
+ const userPath = path.join(PRIMARY_USER_CONTAINER_ROOT, siteKey);
83
+ if (fs.existsSync(userPath)) {
84
+ this.walkSite(userPath, containers);
85
+ this.loadLegacyFile(userPath, containers);
86
+ }
87
+ return containers;
88
+ }
89
+ walkSite(sitePath, output) {
90
+ const stack = [{ dir: sitePath, parts: [] }];
91
+ while (stack.length) {
92
+ const { dir, parts } = stack.pop();
93
+ let hasContainerFile = false;
94
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
95
+ if (entry.isFile() && entry.name === 'container.json') {
96
+ const relParts = parts.length ? parts : [path.basename(dir)];
97
+ const containerId = relParts.join('.');
98
+ try {
99
+ const raw = JSON.parse(fs.readFileSync(path.join(dir, entry.name), 'utf-8'));
100
+ if (raw && typeof raw === 'object') {
101
+ if (isLegacyContainer(raw)) {
102
+ continue;
103
+ }
104
+ const id = raw.id || containerId;
105
+ output[id] = { id, ...raw };
106
+ }
107
+ }
108
+ catch {
109
+ // ignore malformed container
110
+ }
111
+ hasContainerFile = true;
112
+ }
113
+ else if (entry.isDirectory()) {
114
+ stack.push({ dir: path.join(dir, entry.name), parts: [...parts, entry.name] });
115
+ }
116
+ }
117
+ if (!hasContainerFile && parts.length === 0) {
118
+ // root dir may not contain direct containers, continue
119
+ continue;
120
+ }
121
+ }
122
+ }
123
+ loadLegacyFile(sitePath, output) {
124
+ const legacyFile = path.join(sitePath, 'containers.json');
125
+ if (!fs.existsSync(legacyFile)) {
126
+ return;
127
+ }
128
+ try {
129
+ const raw = JSON.parse(fs.readFileSync(legacyFile, 'utf-8'));
130
+ const containers = raw?.containers;
131
+ if (containers && typeof containers === 'object') {
132
+ for (const [key, value] of Object.entries(containers)) {
133
+ if (!output[key] && value && typeof value === 'object') {
134
+ if (isLegacyContainer(value)) {
135
+ continue;
136
+ }
137
+ output[key] = { id: key, ...value };
138
+ }
139
+ }
140
+ }
141
+ }
142
+ catch {
143
+ // ignore legacy parse error
144
+ }
145
+ }
146
+ findSiteKey(url, registry) {
147
+ let host = '';
148
+ try {
149
+ const parsed = new URL(url);
150
+ host = (parsed.hostname || '').toLowerCase();
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ let bestKey = null;
156
+ let bestLen = -1;
157
+ for (const [key, value] of Object.entries(registry)) {
158
+ const domain = (value.website || '').toLowerCase();
159
+ if (!domain)
160
+ continue;
161
+ if (host === domain || host.endsWith(`.${domain}`)) {
162
+ if (domain.length > bestLen) {
163
+ bestKey = key;
164
+ bestLen = domain.length;
165
+ }
166
+ }
167
+ }
168
+ return bestKey;
169
+ }
170
+ }
171
+ function resolveProjectRoot(startDir) {
172
+ let current = startDir;
173
+ const { root } = path.parse(startDir);
174
+ while (true) {
175
+ if (fs.existsSync(path.join(current, 'package.json'))) {
176
+ return current;
177
+ }
178
+ if (current === root) {
179
+ return startDir;
180
+ }
181
+ current = path.resolve(current, '..');
182
+ }
183
+ }
184
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Workflow Block: AnchorVerificationBlock
3
+ *
4
+ * 职责:
5
+ * - 基于容器定义(container-library)验证指定容器锚点是否在视口内且可见
6
+ * - 支持进入锚点 / 离开锚点两种语义(enter/exit)
7
+ * - 避免在这里直接调用 containers:match,降低超时对主流程的影响
8
+ */
9
+ export interface AnchorVerificationInput {
10
+ sessionId: string;
11
+ containerId: string;
12
+ operation: 'enter' | 'exit';
13
+ expectedVisible?: boolean;
14
+ timeoutMs?: number;
15
+ serviceUrl?: string;
16
+ }
17
+ export interface AnchorVerificationOutput {
18
+ success: boolean;
19
+ verified: boolean;
20
+ containerFound: boolean;
21
+ visible: boolean;
22
+ rect?: {
23
+ x: number;
24
+ y: number;
25
+ width: number;
26
+ height: number;
27
+ };
28
+ error?: string;
29
+ }
30
+ export declare function execute(input: AnchorVerificationInput): Promise<AnchorVerificationOutput>;
31
+ //# sourceMappingURL=AnchorVerificationBlock.d.ts.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Workflow Block: AnchorVerificationBlock
3
+ *
4
+ * 职责:
5
+ * - 基于容器定义(container-library)验证指定容器锚点是否在视口内且可见
6
+ * - 支持进入锚点 / 离开锚点两种语义(enter/exit)
7
+ * - 避免在这里直接调用 containers:match,降低超时对主流程的影响
8
+ */
9
+ export async function execute(input) {
10
+ const { sessionId, containerId, operation, expectedVisible = operation === 'enter',
11
+ // timeoutMs 目前只用于 browser:execute 的防御性超时
12
+ timeoutMs = 10000, serviceUrl = 'http://127.0.0.1:7701' } = input;
13
+ try {
14
+ console.log(`[AnchorVerification] ${operation} anchor: ${containerId}`);
15
+ // 通过 container-library 定义获取 selector,并在页面内高亮 + 读取 Rect。
16
+ // 注意:这里不再调用 containers:match,避免复用阶段性 P0 问题。
17
+ const { verifyAnchorByContainerId } = await import('./helpers/containerAnchors.js');
18
+ const anchor = await verifyAnchorByContainerId(containerId, sessionId, serviceUrl, '3px solid #ff4444', 2000);
19
+ if (!anchor.found) {
20
+ return {
21
+ success: false,
22
+ verified: false,
23
+ containerFound: false,
24
+ visible: false,
25
+ error: anchor.error || `容器未找到: ${containerId}`
26
+ };
27
+ }
28
+ const rect = anchor.rect;
29
+ if (!rect) {
30
+ return {
31
+ success: false,
32
+ verified: false,
33
+ containerFound: true,
34
+ visible: false,
35
+ error: '未获取到锚点 Rect'
36
+ };
37
+ }
38
+ // 视口内判定:在这里不直接依赖 window.innerHeight,
39
+ // 仅要求宽高 > 0,由调用方结合 Rect 再做更严格判断。
40
+ const visible = rect.width > 0 && rect.height > 0;
41
+ // enter: 期望可见;exit: 期望不可见/不可命中
42
+ const verified = visible === !!expectedVisible;
43
+ if (!verified) {
44
+ return {
45
+ success: false,
46
+ verified: false,
47
+ containerFound: true,
48
+ visible,
49
+ rect,
50
+ error: `锚点验证失败: 期望${expectedVisible ? '可见' : '不可见'}, 实际${visible ? '可见' : '不可见'}`
51
+ };
52
+ }
53
+ return {
54
+ success: true,
55
+ verified: true,
56
+ containerFound: true,
57
+ visible,
58
+ rect
59
+ };
60
+ }
61
+ catch (err) {
62
+ return {
63
+ success: false,
64
+ verified: false,
65
+ containerFound: false,
66
+ visible: false,
67
+ error: `锚点验证异常: ${err.message}`
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=AnchorVerificationBlock.js.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Workflow Block: DetectPageStateBlock
3
+ *
4
+ * 基于 URL + 容器匹配检测当前页面阶段:
5
+ * - xiaohongshu: login / detail / search / home / unknown
6
+ * - weibo: login / detail / search / home / unknown
7
+ *
8
+ * 用于在进入各 Phase 前做“入口锚点”判定。
9
+ */
10
+ export interface DetectPageStateOutput {
11
+ url: string;
12
+ stage: PageStage;
13
+ rootId?: string | null;
14
+ matchIds?: string[];
15
+ dom?: {
16
+ hasDetailMask?: boolean;
17
+ hasSearchInput?: boolean;
18
+ readyState?: string;
19
+ title?: string;
20
+ };
21
+ error?: string;
22
+ }
23
+ export type PageStage = 'login' | 'detail' | 'search' | 'home' | 'unknown';
24
+ export interface DetectPageStateInput {
25
+ sessionId: string;
26
+ platform?: 'xiaohongshu' | 'weibo' | 'auto';
27
+ serviceUrl?: string;
28
+ }
29
+ export interface DetectPageStateOutput {
30
+ success: boolean;
31
+ sessionId: string;
32
+ platform: 'xiaohongshu' | 'weibo' | 'unknown';
33
+ url: string;
34
+ stage: PageStage;
35
+ pageName?: string;
36
+ rootId?: string | null;
37
+ matchIds?: string[];
38
+ /** DOM side signals (optional, only when available) */
39
+ dom?: {
40
+ hasDetailMask?: boolean;
41
+ hasSearchInput?: boolean;
42
+ readyState?: string;
43
+ title?: string;
44
+ };
45
+ error?: string;
46
+ }
47
+ export declare function execute(input: DetectPageStateInput): Promise<DetectPageStateOutput>;
48
+ //# sourceMappingURL=DetectPageStateBlock.d.ts.map