@web-auto/webauto 0.1.4 → 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 +13 -4
  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
@@ -0,0 +1,213 @@
1
+ import path from 'node:path';
2
+ async function controllerAction(action, payload, apiUrl) {
3
+ const res = await fetch(`${apiUrl}/v1/controller/action`, {
4
+ method: 'POST',
5
+ headers: { 'Content-Type': 'application/json' },
6
+ body: JSON.stringify({ action, payload }),
7
+ signal: AbortSignal.timeout(20000),
8
+ });
9
+ const data = await res.json().catch(() => ({}));
10
+ return data.data || data;
11
+ }
12
+ async function browserServiceCommand(action, args, serviceUrl) {
13
+ const res = await fetch(`${serviceUrl}/command`, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify({ action, args }),
17
+ signal: AbortSignal.timeout(20000),
18
+ });
19
+ const data = await res.json().catch(() => ({}));
20
+ if (!res.ok || data?.ok === false)
21
+ throw new Error(data?.error || 'browser-service error');
22
+ return data;
23
+ }
24
+ function delay(ms) {
25
+ return new Promise((r) => setTimeout(r, ms));
26
+ }
27
+ function resolveDefaultCookiePath(profile) {
28
+ const envRoot = String(process.env.WEBAUTO_PATHS_COOKIES || '').trim();
29
+ if (envRoot)
30
+ return path.join(envRoot, `${profile}.json`);
31
+ const portableRoot = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
32
+ if (portableRoot)
33
+ return path.join(portableRoot, '.webauto', 'cookies', `${profile}.json`);
34
+ const homeDir = process.platform === 'win32'
35
+ ? String(process.env.USERPROFILE || '').trim()
36
+ : String(process.env.HOME || '').trim();
37
+ if (homeDir)
38
+ return path.join(homeDir, '.webauto', 'cookies', `${profile}.json`);
39
+ return path.join('.webauto', 'cookies', `${profile}.json`);
40
+ }
41
+ function hashCookiePairs(pairs) {
42
+ const normalized = pairs
43
+ .map((c) => `${c.name}=${c.value}`)
44
+ .sort()
45
+ .join('|');
46
+ // cheap hash; only used for change detection
47
+ let h = 0;
48
+ for (let i = 0; i < normalized.length; i++) {
49
+ h = (h * 31 + normalized.charCodeAt(i)) >>> 0;
50
+ }
51
+ return h.toString(16);
52
+ }
53
+ function extractSessions(payload) {
54
+ if (!payload)
55
+ return [];
56
+ if (Array.isArray(payload.sessions))
57
+ return payload.sessions;
58
+ if (Array.isArray(payload.data?.sessions))
59
+ return payload.data.sessions;
60
+ if (Array.isArray(payload.result?.sessions))
61
+ return payload.result.sessions;
62
+ if (payload.data)
63
+ return extractSessions(payload.data);
64
+ return [];
65
+ }
66
+ async function listSessions(apiUrl) {
67
+ const raw = await controllerAction('session:list', {}, apiUrl);
68
+ return extractSessions(raw);
69
+ }
70
+ function hasSession(sessions, profile) {
71
+ return sessions.some((s) => s?.profileId === profile || s?.session_id === profile);
72
+ }
73
+ async function getDocumentCookiePairs(apiUrl, profile) {
74
+ // 只读扫描:使用 document.cookie 的可见部分做“变化检测”即可。
75
+ const raw = await controllerAction('browser:execute', {
76
+ profile,
77
+ script: `(() => document.cookie.split(';').map(s => s.trim()).filter(Boolean))()`,
78
+ }, apiUrl);
79
+ const arr = raw?.result || raw?.data?.result;
80
+ const list = Array.isArray(arr) ? arr : [];
81
+ const pairs = [];
82
+ for (const item of list) {
83
+ const idx = String(item).indexOf('=');
84
+ if (idx <= 0)
85
+ continue;
86
+ pairs.push({ name: String(item).slice(0, idx), value: String(item).slice(idx + 1) });
87
+ }
88
+ return pairs;
89
+ }
90
+ async function fetchContainerTree(apiUrl, profile) {
91
+ let currentUrl = '';
92
+ try {
93
+ const urlRes = await controllerAction('browser:execute', { profile, script: 'location.href' }, apiUrl);
94
+ currentUrl = urlRes?.result || urlRes?.data?.result || '';
95
+ }
96
+ catch {
97
+ currentUrl = '';
98
+ }
99
+ try {
100
+ const res = await controllerAction('containers:match', {
101
+ profile,
102
+ ...(currentUrl ? { url: currentUrl } : {}),
103
+ maxDepth: 2,
104
+ maxChildren: 5,
105
+ }, apiUrl);
106
+ return res?.snapshot?.container_tree || res?.container_tree || res?.data?.snapshot?.container_tree || null;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ function findContainer(node, pattern) {
113
+ if (!node)
114
+ return null;
115
+ if (pattern.test(node.id || node.defId || ''))
116
+ return node;
117
+ if (Array.isArray(node.children)) {
118
+ for (const child of node.children) {
119
+ const found = findContainer(child, pattern);
120
+ if (found)
121
+ return found;
122
+ }
123
+ }
124
+ return null;
125
+ }
126
+ async function isLoggedIn(apiUrl, profile) {
127
+ const tree = await fetchContainerTree(apiUrl, profile);
128
+ if (!tree)
129
+ return false;
130
+ const loginAnchor = findContainer(tree, /\.login_anchor$/);
131
+ if (loginAnchor)
132
+ return true;
133
+ const loginGuard = findContainer(tree, /xiaohongshu_login\.login_guard$/);
134
+ if (loginGuard)
135
+ return false;
136
+ return false;
137
+ }
138
+ export async function execute(input) {
139
+ const { profile, unifiedApiUrl = 'http://127.0.0.1:7701', browserServiceUrl = 'http://127.0.0.1:7704', scanIntervalMs = 15000, stableCount = 3, cookiePath, } = input;
140
+ const resolvedCookiePath = cookiePath || resolveDefaultCookiePath(profile);
141
+ let lastHash = '';
142
+ let stableRounds = 0;
143
+ let scanRounds = 0;
144
+ let lastLoggedIn = false;
145
+ let autoCookiesStarted = false;
146
+ while (true) {
147
+ scanRounds += 1;
148
+ const sessions = await listSessions(unifiedApiUrl).catch(() => []);
149
+ if (!hasSession(sessions, profile)) {
150
+ throw new Error(`[Phase1MonitorCookie] session closed: ${profile}`);
151
+ }
152
+ const loggedIn = await isLoggedIn(unifiedApiUrl, profile);
153
+ lastLoggedIn = loggedIn;
154
+ const pairs = await getDocumentCookiePairs(unifiedApiUrl, profile);
155
+ const currentHash = hashCookiePairs(pairs);
156
+ console.log(`[Phase1MonitorCookie] round=${scanRounds} loggedIn=${loggedIn} cookieCount=${pairs.length} hash=${currentHash}`);
157
+ if (currentHash === lastHash) {
158
+ stableRounds += 1;
159
+ }
160
+ else {
161
+ stableRounds = 0;
162
+ lastHash = currentHash;
163
+ }
164
+ // 只有登录成功时才保存,并且必须“变化后稳定”
165
+ if (loggedIn && stableRounds >= stableCount) {
166
+ console.log('[Phase1MonitorCookie] Cookie stable, saving...');
167
+ let saveResult = null;
168
+ try {
169
+ saveResult = await browserServiceCommand('saveCookiesIfStable', { profileId: profile, path: resolvedCookiePath, minDelayMs: 2000 }, browserServiceUrl);
170
+ }
171
+ catch (err) {
172
+ console.warn(`[Phase1MonitorCookie] saveCookiesIfStable failed: ${err?.message || String(err)}`);
173
+ }
174
+ if (!Boolean(saveResult?.saved)) {
175
+ // Fallback: force save once so downstream checks can find explicit cookie file.
176
+ let forced = null;
177
+ try {
178
+ forced = await browserServiceCommand('saveCookies', { profileId: profile, path: resolvedCookiePath }, browserServiceUrl);
179
+ }
180
+ catch {
181
+ forced = null;
182
+ }
183
+ const forcedCount = Number(forced?.count ?? forced?.body?.count ?? -1);
184
+ if (forcedCount >= 0) {
185
+ saveResult = { ...(saveResult || {}), saved: true, count: forcedCount };
186
+ console.log(`[Phase1MonitorCookie] saveCookies fallback count=${forcedCount}`);
187
+ }
188
+ }
189
+ if (!Boolean(saveResult?.saved)) {
190
+ console.warn('[Phase1MonitorCookie] cookie not saved yet, continue scanning');
191
+ await delay(scanIntervalMs);
192
+ continue;
193
+ }
194
+ // 进入常驻模式:开启 Browser Service 自动保存 cookie,不阻塞后续 Phase2
195
+ if (!autoCookiesStarted) {
196
+ await browserServiceCommand('autoCookies:start', { profileId: profile, intervalMs: scanIntervalMs }, browserServiceUrl);
197
+ autoCookiesStarted = true;
198
+ console.log(`[Phase1MonitorCookie] autoCookies:start enabled intervalMs=${scanIntervalMs}`);
199
+ }
200
+ return {
201
+ success: true,
202
+ profile,
203
+ loggedIn: true,
204
+ saved: true,
205
+ cookiePath: resolvedCookiePath,
206
+ scanRounds,
207
+ autoCookiesStarted,
208
+ };
209
+ }
210
+ await delay(scanIntervalMs);
211
+ }
212
+ }
213
+ //# sourceMappingURL=Phase1MonitorCookieBlock.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Phase 1 Block: 启动/复用 Profile 会话
3
+ */
4
+ export interface StartProfileInput {
5
+ profile: string;
6
+ url?: string;
7
+ headless?: boolean;
8
+ browserServiceUrl?: string;
9
+ ownerPid?: number;
10
+ }
11
+ export interface StartProfileOutput {
12
+ started: boolean;
13
+ profile: string;
14
+ url: string;
15
+ headless: boolean;
16
+ }
17
+ export declare function execute(input: StartProfileInput): Promise<StartProfileOutput>;
18
+ //# sourceMappingURL=Phase1StartProfileBlock.d.ts.map
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Phase 1 Block: 启动/复用 Profile 会话
3
+ */
4
+ const DEFAULT_TIMEOUT_MS = process.platform === 'win32' ? 60000 : 20000;
5
+ async function browserServiceCommand(action, args, serviceUrl, timeoutMs = DEFAULT_TIMEOUT_MS) {
6
+ const res = await fetch(`${serviceUrl}/command`, {
7
+ method: 'POST',
8
+ headers: { 'Content-Type': 'application/json' },
9
+ body: JSON.stringify({ action, args }),
10
+ signal: AbortSignal.timeout(timeoutMs),
11
+ });
12
+ const data = await res.json().catch(() => ({}));
13
+ if (!res.ok || data?.error)
14
+ throw new Error(data?.error || 'browser-service error');
15
+ return data;
16
+ }
17
+ export async function execute(input) {
18
+ const { profile, url = 'https://www.xiaohongshu.com', headless = false, browserServiceUrl = 'http://127.0.0.1:7704', ownerPid = process.pid, } = input;
19
+ console.log(`[Phase1StartProfile] 启动 profile=${profile} headless=${headless}`);
20
+ function clamp(n, min, max) {
21
+ if (!Number.isFinite(n))
22
+ return min;
23
+ return Math.min(Math.max(n, min), max);
24
+ }
25
+ function pickFirstPositive(...values) {
26
+ for (const value of values) {
27
+ const parsed = Number(value ?? 0);
28
+ if (Number.isFinite(parsed) && parsed > 0)
29
+ return parsed;
30
+ }
31
+ return 0;
32
+ }
33
+ function resolveEffectiveMax(displayValue, browserValue) {
34
+ if (displayValue > 0 && browserValue > 0) {
35
+ return Math.min(displayValue, browserValue);
36
+ }
37
+ return displayValue > 0 ? displayValue : browserValue;
38
+ }
39
+ async function resolveViewportSize() {
40
+ try {
41
+ const displayRes = await browserServiceCommand('system:display', {}, browserServiceUrl);
42
+ const res = await browserServiceCommand('evaluate', {
43
+ profileId: profile,
44
+ script: `(() => ({
45
+ innerWidth: window.innerWidth,
46
+ innerHeight: window.innerHeight,
47
+ screenWidth: window.screen?.width,
48
+ screenHeight: window.screen?.height,
49
+ availWidth: window.screen?.availWidth,
50
+ availHeight: window.screen?.availHeight
51
+ }))()`,
52
+ }, browserServiceUrl);
53
+ const payload = res?.body || res?.data || res;
54
+ const metrics = payload?.result || payload?.data?.result || null;
55
+ const displayPayload = displayRes?.body || displayRes?.data || displayRes;
56
+ const display = displayPayload?.metrics || displayPayload?.data?.metrics || null;
57
+ // These reflect *browser* idea of available size (can be scaled / not equal to physical pixels).
58
+ const maxW = Number(metrics?.availWidth || metrics?.screenWidth || 0);
59
+ const maxH = Number(metrics?.availHeight || metrics?.screenHeight || 0);
60
+ const innerW = Number(metrics?.innerWidth || 0);
61
+ const innerH = Number(metrics?.innerHeight || 0);
62
+ // Prefer OS work area (excludes taskbar/dock), fall back to total screen size.
63
+ // IMPORTANT: do not let browser metrics override OS metrics when present.
64
+ const displayW = pickFirstPositive(display?.workWidth, display?.width, display?.nativeWidth);
65
+ const displayH = pickFirstPositive(display?.workHeight, display?.height, display?.nativeHeight);
66
+ // Use inner dimensions as the floor - browser cannot render smaller than its inner window
67
+ const innerFloorW = Math.max(innerW, 900);
68
+ const innerFloorH = Math.max(innerH, 720);
69
+ const effectiveMaxW = resolveEffectiveMax(displayW, maxW);
70
+ const effectiveMaxH = resolveEffectiveMax(displayH, maxH);
71
+ // Hard requirement: do not exceed browser-reported max size to avoid oversized window
72
+ // that ends up partially off-screen (causing non-centered viewport).
73
+ const targetW = maxW > 0 ? maxW : (effectiveMaxW > 0 ? effectiveMaxW : (displayW > 0 ? displayW : 1920));
74
+ const targetH = maxH > 0 ? maxH : (effectiveMaxH > 0 ? effectiveMaxH : (displayH > 0 ? displayH : 1400));
75
+ // Keep a reasonable minimum, but do NOT clamp max height/width; use real screen size.
76
+ const width = Math.max(innerFloorW, Math.floor(targetW));
77
+ const height = Math.max(innerFloorH, Math.floor(targetH));
78
+ console.log(`[Phase1StartProfile] display metrics: browser=${maxW}x${maxH} display=${displayW}x${displayH} ` +
79
+ `effective=${effectiveMaxW}x${effectiveMaxH} inner=${innerW}x${innerH} ` +
80
+ `target=${targetW}x${targetH} finalViewport=${width}x${height}`);
81
+ return { width, height };
82
+ }
83
+ catch {
84
+ return { width: 1440, height: 1080 };
85
+ }
86
+ }
87
+ await browserServiceCommand('start', {
88
+ profileId: profile,
89
+ headless,
90
+ url,
91
+ ownerPid,
92
+ }, browserServiceUrl);
93
+ const viewport = await resolveViewportSize();
94
+ await browserServiceCommand('page:setViewport', {
95
+ profileId: profile,
96
+ width: viewport.width,
97
+ height: viewport.height,
98
+ }, browserServiceUrl);
99
+ // Ensure window centered on screen to keep viewport in expected position
100
+ try {
101
+ await browserServiceCommand('window:move', {
102
+ profileId: profile,
103
+ x: 0,
104
+ y: 0,
105
+ width: viewport.width,
106
+ height: viewport.height,
107
+ }, browserServiceUrl);
108
+ console.log('[Phase1StartProfile] window positioned at (0,0)');
109
+ }
110
+ catch (err) {
111
+ console.warn('[Phase1StartProfile] window move failed:', err?.message || String(err));
112
+ }
113
+ console.log(`[Phase1StartProfile] viewport set: ${viewport.width}x${viewport.height}`);
114
+ return {
115
+ started: true,
116
+ profile,
117
+ url,
118
+ headless,
119
+ };
120
+ }
121
+ //# sourceMappingURL=Phase1StartProfileBlock.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Phase 2 Block: 采集安全链接
3
+ *
4
+ * 职责:通过容器点击进入详情,获取带 xsec_token 的安全 URL
5
+ */
6
+ export interface CollectLinksInput {
7
+ keyword: string;
8
+ targetCount: number;
9
+ profile?: string;
10
+ unifiedApiUrl?: string;
11
+ env?: string;
12
+ alreadyCollectedNoteIds?: string[];
13
+ onLink?: (link: {
14
+ noteId: string;
15
+ safeUrl: string;
16
+ searchUrl: string;
17
+ ts: string;
18
+ }, meta: {
19
+ collected: number;
20
+ targetCount: number;
21
+ }) => Promise<void> | void;
22
+ }
23
+ export interface CollectLinksOutput {
24
+ links: Array<{
25
+ noteId: string;
26
+ safeUrl: string;
27
+ searchUrl: string;
28
+ ts: string;
29
+ }>;
30
+ totalCollected: number;
31
+ termination?: 'reached_target' | 'no_progress_after_3_retries';
32
+ }
33
+ export declare function execute(input: CollectLinksInput): Promise<CollectLinksOutput>;
34
+ //# sourceMappingURL=Phase2CollectLinksBlock.d.ts.map