autokap 1.0.6 → 1.0.8

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 (347) hide show
  1. package/assets/chrome/ios-statusbar-comparison-reference.jpg +0 -0
  2. package/assets/chrome/ios-statusbar-dark-reference.jpg +0 -0
  3. package/assets/chrome/ios-statusbar-light-reference.jpg +0 -0
  4. package/assets/cursors/macos.svg +4 -0
  5. package/assets/cursors/windows.svg +15 -0
  6. package/assets/devices/ipad-pro-11-m4.json +52 -0
  7. package/assets/devices/iphone-16-pro.json +53 -0
  8. package/assets/devices/macbook-air-13.json +45 -0
  9. package/assets/frames/MacBook Air 13.svg +242 -0
  10. package/assets/frames/Status bar - iPhone.png +0 -0
  11. Menu bar- iPad.png +0 -0
  12. package/assets/frames/iPad Pro M4 11_.png +0 -0
  13. package/assets/frames/iPhone 16 Pro.png +0 -0
  14. package/assets/icons/Cellular Connection.svg +3 -0
  15. package/assets/icons/Union.svg +6 -0
  16. package/assets/icons/Wifi.svg +3 -0
  17. package/assets/icons/battery.svg +5 -0
  18. package/assets/icons/battery_charging.svg +8 -0
  19. package/assets/skill/OPCODE-REFERENCE.md +607 -0
  20. package/assets/skill/README.md +39 -0
  21. package/assets/skill/SKILL.md +453 -468
  22. package/assets/skill/STUDIO-SKILL.md +476 -0
  23. package/assets/skill/references/examples.md +104 -0
  24. package/assets/skill/references/interactive-demo.md +225 -0
  25. package/assets/skill/references/mock-data.md +178 -0
  26. package/dist/abort.d.ts +5 -0
  27. package/dist/abort.js +44 -0
  28. package/dist/action-verifier.d.ts +29 -0
  29. package/dist/action-verifier.js +133 -0
  30. package/dist/agent-action-recovery.d.ts +45 -0
  31. package/dist/agent-action-recovery.js +370 -0
  32. package/dist/agent-message-utils.d.ts +21 -0
  33. package/dist/agent-message-utils.js +77 -0
  34. package/dist/agent-url-utils.d.ts +30 -0
  35. package/dist/agent-url-utils.js +138 -0
  36. package/dist/agent.d.ts +226 -0
  37. package/dist/agent.js +6666 -0
  38. package/dist/ak-tree.d.ts +39 -0
  39. package/dist/ak-tree.js +368 -0
  40. package/dist/alt-text.d.ts +26 -0
  41. package/dist/alt-text.js +55 -0
  42. package/dist/auth-capture.d.ts +17 -0
  43. package/dist/auth-capture.js +164 -0
  44. package/dist/benchmark.d.ts +59 -0
  45. package/dist/benchmark.js +135 -0
  46. package/dist/billing-operation-logging.d.ts +38 -0
  47. package/dist/billing-operation-logging.js +248 -0
  48. package/dist/browser-bar.d.ts +48 -0
  49. package/dist/browser-bar.js +284 -0
  50. package/dist/browser-pool.d.ts +7 -0
  51. package/dist/browser-pool.js +15 -5
  52. package/dist/browser-utils.d.ts +31 -0
  53. package/dist/browser-utils.js +97 -0
  54. package/dist/browser.d.ts +76 -1
  55. package/dist/browser.js +1657 -39
  56. package/dist/capture-alt-text.d.ts +12 -0
  57. package/dist/capture-alt-text.js +52 -0
  58. package/dist/capture-encryption.d.ts +10 -0
  59. package/dist/capture-encryption.js +41 -0
  60. package/dist/capture-language-preflight.d.ts +41 -0
  61. package/dist/capture-language-preflight.js +300 -0
  62. package/dist/capture-llm-page-identity.d.ts +15 -0
  63. package/dist/capture-llm-page-identity.js +128 -0
  64. package/dist/capture-model-resolution.d.ts +9 -0
  65. package/dist/capture-model-resolution.js +21 -0
  66. package/dist/capture-page-identity.d.ts +7 -0
  67. package/dist/capture-page-identity.js +352 -0
  68. package/dist/capture-preset-credentials.d.ts +62 -0
  69. package/dist/capture-preset-credentials.js +184 -0
  70. package/dist/capture-request-plan.d.ts +58 -0
  71. package/dist/capture-request-plan.js +264 -0
  72. package/dist/capture-run-optimizer.d.ts +139 -0
  73. package/dist/capture-run-optimizer.js +863 -0
  74. package/dist/capture-selector-memory.d.ts +31 -0
  75. package/dist/capture-selector-memory.js +345 -0
  76. package/dist/capture-session-profile-encryption.d.ts +2 -0
  77. package/dist/capture-session-profile-encryption.js +22 -0
  78. package/dist/capture-step-timeout.d.ts +10 -0
  79. package/dist/capture-step-timeout.js +30 -0
  80. package/dist/capture-strategy.d.ts +36 -0
  81. package/dist/capture-strategy.js +95 -0
  82. package/dist/capture-studio-sync.d.ts +23 -0
  83. package/dist/capture-studio-sync.js +172 -0
  84. package/dist/capture-surface-contract.d.ts +36 -0
  85. package/dist/capture-surface-contract.js +299 -0
  86. package/dist/capture-transition-engine.d.ts +28 -0
  87. package/dist/capture-transition-engine.js +292 -0
  88. package/dist/capture-variant-state.d.ts +56 -0
  89. package/dist/capture-variant-state.js +182 -0
  90. package/dist/capture-verification.d.ts +35 -0
  91. package/dist/capture-verification.js +95 -0
  92. package/dist/capture-viewport-lock.d.ts +48 -0
  93. package/dist/capture-viewport-lock.js +74 -0
  94. package/dist/circuit-breaker.d.ts +42 -0
  95. package/dist/circuit-breaker.js +119 -0
  96. package/dist/cli-config.d.ts +8 -1
  97. package/dist/cli-config.js +62 -6
  98. package/dist/cli-contract.d.ts +15 -0
  99. package/dist/cli-contract.js +167 -0
  100. package/dist/cli-runner-local.d.ts +12 -0
  101. package/dist/cli-runner-local.js +102 -0
  102. package/dist/cli-runner.d.ts +34 -0
  103. package/dist/cli-runner.js +433 -0
  104. package/dist/cli-utils.d.ts +0 -1
  105. package/dist/cli-utils.js +2 -5
  106. package/dist/cli.js +1005 -252
  107. package/dist/clip-orchestrator.d.ts +148 -0
  108. package/dist/clip-orchestrator.js +957 -0
  109. package/dist/clip-postprocess.d.ts +42 -0
  110. package/dist/clip-postprocess.js +201 -0
  111. package/dist/cookie-dismiss.d.ts +2 -0
  112. package/dist/cookie-dismiss.js +48 -13
  113. package/dist/cost-logging.d.ts +35 -0
  114. package/dist/cost-logging.js +242 -0
  115. package/dist/cost-resolution-monitor.d.ts +16 -0
  116. package/dist/cost-resolution-monitor.js +34 -0
  117. package/dist/credential-templates.d.ts +5 -0
  118. package/dist/credential-templates.js +60 -0
  119. package/dist/cursor-overlay-script.d.ts +6 -0
  120. package/dist/cursor-overlay-script.js +169 -0
  121. package/dist/dom-css-purger.d.ts +65 -0
  122. package/dist/dom-css-purger.js +333 -0
  123. package/dist/dom-font-inliner.d.ts +45 -0
  124. package/dist/dom-font-inliner.js +148 -0
  125. package/dist/dom-patch-resolver.d.ts +52 -0
  126. package/dist/dom-patch-resolver.js +242 -0
  127. package/dist/dom-serializer.d.ts +82 -0
  128. package/dist/dom-serializer.js +378 -0
  129. package/dist/element-capture.d.ts +13 -0
  130. package/dist/element-capture.js +522 -0
  131. package/dist/env-validation.d.ts +5 -0
  132. package/dist/env-validation.js +29 -0
  133. package/dist/execution-schema.d.ts +4423 -0
  134. package/dist/execution-schema.js +507 -0
  135. package/dist/execution-types.d.ts +886 -0
  136. package/dist/execution-types.js +65 -0
  137. package/dist/fonts-loader.d.ts +14 -0
  138. package/dist/fonts-loader.js +55 -0
  139. package/dist/hybrid-navigator.d.ts +138 -0
  140. package/dist/hybrid-navigator.js +468 -0
  141. package/dist/index.d.ts +18 -0
  142. package/dist/index.js +17 -0
  143. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  144. package/dist/legacy/agent-action-recovery.js +370 -0
  145. package/dist/legacy/agent-message-utils.d.ts +21 -0
  146. package/dist/legacy/agent-message-utils.js +77 -0
  147. package/dist/legacy/agent-url-utils.d.ts +30 -0
  148. package/dist/legacy/agent-url-utils.js +138 -0
  149. package/dist/legacy/agent.d.ts +226 -0
  150. package/dist/legacy/agent.js +6666 -0
  151. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  152. package/dist/legacy/clip-orchestrator.js +957 -0
  153. package/dist/legacy/credential-templates.d.ts +5 -0
  154. package/dist/legacy/credential-templates.js +60 -0
  155. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  156. package/dist/legacy/hybrid-navigator.js +468 -0
  157. package/dist/legacy/llm-usage.d.ts +17 -0
  158. package/dist/legacy/llm-usage.js +45 -0
  159. package/dist/legacy/prompt-cache.d.ts +10 -0
  160. package/dist/legacy/prompt-cache.js +24 -0
  161. package/dist/legacy/prompts.d.ts +175 -0
  162. package/dist/legacy/prompts.js +1038 -0
  163. package/dist/legacy/tools.d.ts +4 -0
  164. package/dist/legacy/tools.js +216 -0
  165. package/dist/legacy/video-agent.d.ts +143 -0
  166. package/dist/legacy/video-agent.js +4788 -0
  167. package/dist/legacy/video-observation.d.ts +36 -0
  168. package/dist/legacy/video-observation.js +192 -0
  169. package/dist/legacy/video-planner.d.ts +12 -0
  170. package/dist/legacy/video-planner.js +501 -0
  171. package/dist/legacy/video-prompts.d.ts +37 -0
  172. package/dist/legacy/video-prompts.js +569 -0
  173. package/dist/legacy/video-tools.d.ts +3 -0
  174. package/dist/legacy/video-tools.js +59 -0
  175. package/dist/legacy/video-variant-state.d.ts +29 -0
  176. package/dist/legacy/video-variant-state.js +80 -0
  177. package/dist/legacy/vision-model.d.ts +17 -0
  178. package/dist/legacy/vision-model.js +74 -0
  179. package/dist/llm-healer.d.ts +63 -0
  180. package/dist/llm-healer.js +166 -0
  181. package/dist/llm-provider.d.ts +29 -0
  182. package/dist/llm-provider.js +80 -0
  183. package/dist/llm-usage.d.ts +17 -0
  184. package/dist/llm-usage.js +45 -0
  185. package/dist/logger.d.ts +6 -2
  186. package/dist/logger.js +15 -1
  187. package/dist/mockup-html.d.ts +119 -0
  188. package/dist/mockup-html.js +263 -0
  189. package/dist/mockup.d.ts +187 -0
  190. package/dist/mockup.js +869 -0
  191. package/dist/mouse-animation.d.ts +46 -0
  192. package/dist/mouse-animation.js +114 -0
  193. package/dist/opcode-actions.d.ts +42 -0
  194. package/dist/opcode-actions.js +511 -0
  195. package/dist/opcode-runner.d.ts +51 -0
  196. package/dist/opcode-runner.js +770 -0
  197. package/dist/openrouter-client.d.ts +40 -0
  198. package/dist/openrouter-client.js +16 -0
  199. package/dist/overlay-engine.d.ts +24 -0
  200. package/dist/overlay-engine.js +176 -0
  201. package/dist/overlay-utils.d.ts +14 -0
  202. package/dist/overlay-utils.js +13 -0
  203. package/dist/postcondition.d.ts +16 -0
  204. package/dist/postcondition.js +269 -0
  205. package/dist/posthog.d.ts +4 -0
  206. package/dist/posthog.js +26 -0
  207. package/dist/program-patcher.d.ts +25 -0
  208. package/dist/program-patcher.js +44 -0
  209. package/dist/prompt-cache.d.ts +10 -0
  210. package/dist/prompt-cache.js +24 -0
  211. package/dist/prompts.d.ts +175 -0
  212. package/dist/prompts.js +1038 -0
  213. package/dist/provider-config.d.ts +12 -0
  214. package/dist/provider-config.js +15 -0
  215. package/dist/recovery-chain.d.ts +37 -0
  216. package/dist/recovery-chain.js +350 -0
  217. package/dist/remote-browser.d.ts +215 -0
  218. package/dist/remote-browser.js +360 -0
  219. package/dist/safari-browser-bar.d.ts +15 -0
  220. package/dist/safari-browser-bar.js +95 -0
  221. package/dist/safari-toolbar-asset.d.ts +15 -0
  222. package/dist/safari-toolbar-asset.js +12 -0
  223. package/dist/security.d.ts +21 -0
  224. package/dist/security.js +608 -0
  225. package/dist/selector-resolver.d.ts +34 -0
  226. package/dist/selector-resolver.js +181 -0
  227. package/dist/semantic-resolver.d.ts +35 -0
  228. package/dist/semantic-resolver.js +161 -0
  229. package/dist/server-capture-runtime.d.ts +125 -0
  230. package/dist/server-capture-runtime.js +585 -0
  231. package/dist/server-credit-usage.d.ts +12 -0
  232. package/dist/server-credit-usage.js +41 -0
  233. package/dist/server-posthog.d.ts +2 -0
  234. package/dist/server-posthog.js +16 -0
  235. package/dist/server-project-webhooks.d.ts +59 -0
  236. package/dist/server-project-webhooks.js +123 -0
  237. package/dist/server-screenshot-watermark.d.ts +7 -0
  238. package/dist/server-screenshot-watermark.js +60 -0
  239. package/dist/session-profile.d.ts +86 -0
  240. package/dist/session-profile.js +1536 -0
  241. package/dist/sf-pro-fonts.d.ts +4 -0
  242. package/dist/sf-pro-fonts.js +7 -0
  243. package/dist/sf-pro-symbols.d.ts +1 -0
  244. package/dist/sf-pro-symbols.js +55 -0
  245. package/dist/skill-packaging.d.ts +28 -0
  246. package/dist/skill-packaging.js +169 -0
  247. package/dist/smart-wait.d.ts +27 -0
  248. package/dist/smart-wait.js +81 -0
  249. package/dist/status-bar-l10n.d.ts +14 -0
  250. package/dist/status-bar-l10n.js +177 -0
  251. package/dist/status-bar-render.d.ts +20 -0
  252. package/dist/status-bar-render.js +410 -0
  253. package/dist/status-bar.d.ts +53 -0
  254. package/dist/status-bar.js +620 -0
  255. package/dist/svg-browser-bar.d.ts +33 -0
  256. package/dist/svg-browser-bar.js +206 -0
  257. package/dist/svg-status-bar.d.ts +36 -0
  258. package/dist/svg-status-bar.js +597 -0
  259. package/dist/svg-text.d.ts +61 -0
  260. package/dist/svg-text.js +118 -0
  261. package/dist/tools.d.ts +4 -0
  262. package/dist/tools.js +216 -0
  263. package/dist/types.d.ts +240 -5
  264. package/dist/types.js +23 -1
  265. package/dist/v2/action-verifier.d.ts +29 -0
  266. package/dist/v2/action-verifier.js +133 -0
  267. package/dist/v2/alt-text.d.ts +26 -0
  268. package/dist/v2/alt-text.js +55 -0
  269. package/dist/v2/benchmark.d.ts +59 -0
  270. package/dist/v2/benchmark.js +135 -0
  271. package/dist/v2/capture-strategy.d.ts +30 -0
  272. package/dist/v2/capture-strategy.js +67 -0
  273. package/dist/v2/capture-verification.d.ts +35 -0
  274. package/dist/v2/capture-verification.js +95 -0
  275. package/dist/v2/circuit-breaker.d.ts +42 -0
  276. package/dist/v2/circuit-breaker.js +119 -0
  277. package/dist/v2/cli-runner-local.d.ts +11 -0
  278. package/dist/v2/cli-runner-local.js +91 -0
  279. package/dist/v2/cli-runner.d.ts +34 -0
  280. package/dist/v2/cli-runner.js +300 -0
  281. package/dist/v2/compiler-prompts.d.ts +27 -0
  282. package/dist/v2/compiler-prompts.js +123 -0
  283. package/dist/v2/compiler.d.ts +37 -0
  284. package/dist/v2/compiler.js +147 -0
  285. package/dist/v2/explorer.d.ts +41 -0
  286. package/dist/v2/explorer.js +56 -0
  287. package/dist/v2/index.d.ts +37 -0
  288. package/dist/v2/index.js +31 -0
  289. package/dist/v2/llm-healer.d.ts +62 -0
  290. package/dist/v2/llm-healer.js +166 -0
  291. package/dist/v2/llm-provider.d.ts +29 -0
  292. package/dist/v2/llm-provider.js +80 -0
  293. package/dist/v2/opcode-runner.d.ts +47 -0
  294. package/dist/v2/opcode-runner.js +634 -0
  295. package/dist/v2/overlay-engine.d.ts +24 -0
  296. package/dist/v2/overlay-engine.js +150 -0
  297. package/dist/v2/postcondition.d.ts +16 -0
  298. package/dist/v2/postcondition.js +249 -0
  299. package/dist/v2/program-patcher.d.ts +25 -0
  300. package/dist/v2/program-patcher.js +44 -0
  301. package/dist/v2/recovery-chain.d.ts +30 -0
  302. package/dist/v2/recovery-chain.js +368 -0
  303. package/dist/v2/schema.d.ts +2580 -0
  304. package/dist/v2/schema.js +295 -0
  305. package/dist/v2/selector-resolver.d.ts +34 -0
  306. package/dist/v2/selector-resolver.js +181 -0
  307. package/dist/v2/semantic-resolver.d.ts +35 -0
  308. package/dist/v2/semantic-resolver.js +161 -0
  309. package/dist/v2/smart-wait.d.ts +27 -0
  310. package/dist/v2/smart-wait.js +81 -0
  311. package/dist/v2/types.d.ts +444 -0
  312. package/dist/v2/types.js +19 -0
  313. package/dist/v2/web-playwright-local.d.ts +69 -0
  314. package/dist/v2/web-playwright-local.js +392 -0
  315. package/dist/version.d.ts +1 -0
  316. package/dist/version.js +5 -0
  317. package/dist/video-agent.d.ts +143 -0
  318. package/dist/video-agent.js +4788 -0
  319. package/dist/video-observation.d.ts +36 -0
  320. package/dist/video-observation.js +192 -0
  321. package/dist/video-planner.d.ts +12 -0
  322. package/dist/video-planner.js +501 -0
  323. package/dist/video-prompts.d.ts +37 -0
  324. package/dist/video-prompts.js +554 -0
  325. package/dist/video-tools.d.ts +3 -0
  326. package/dist/video-tools.js +59 -0
  327. package/dist/video-variant-state.d.ts +29 -0
  328. package/dist/video-variant-state.js +80 -0
  329. package/dist/vision-model.d.ts +17 -0
  330. package/dist/vision-model.js +74 -0
  331. package/dist/web-playwright-local.d.ts +126 -0
  332. package/dist/web-playwright-local.js +819 -0
  333. package/dist/ws-auth.d.ts +20 -0
  334. package/dist/ws-auth.js +70 -0
  335. package/dist/ws-broadcast.d.ts +34 -0
  336. package/dist/ws-broadcast.js +85 -0
  337. package/dist/ws-connection-limits.d.ts +12 -0
  338. package/dist/ws-connection-limits.js +44 -0
  339. package/dist/ws-handler-utils.d.ts +32 -0
  340. package/dist/ws-handler-utils.js +139 -0
  341. package/dist/ws-handler.d.ts +10 -0
  342. package/dist/ws-handler.js +1793 -0
  343. package/dist/ws-metrics-server.d.ts +9 -0
  344. package/dist/ws-metrics-server.js +31 -0
  345. package/dist/ws-server.d.ts +9 -0
  346. package/dist/ws-server.js +92 -0
  347. package/package.json +142 -71
@@ -0,0 +1,12 @@
1
+ interface GenerateAltTextParams {
2
+ url: string;
3
+ prompt: string;
4
+ lang: string;
5
+ theme: 'light' | 'dark';
6
+ targetLabel?: string;
7
+ elementName?: string;
8
+ model: string;
9
+ apiKey: string;
10
+ }
11
+ export declare function generateAltText(params: GenerateAltTextParams): Promise<string | null>;
12
+ export {};
@@ -0,0 +1,52 @@
1
+ import { zdrParam } from './provider-config.js';
2
+ export async function generateAltText(params) {
3
+ const { url, prompt, lang, theme, targetLabel, elementName, model, apiKey } = params;
4
+ const contextParts = [
5
+ `URL: ${url}`,
6
+ `Capture intent: "${prompt}"`,
7
+ `Theme: ${theme}`,
8
+ ];
9
+ if (targetLabel)
10
+ contextParts.push(`Device: ${targetLabel}`);
11
+ if (elementName)
12
+ contextParts.push(`Isolated element: ${elementName}`);
13
+ const systemPrompt = `You generate concise alt text for web page screenshots, for accessibility purposes.
14
+ Write in ${lang}. Output ONLY the alt text, nothing else. Keep it under 150 characters.
15
+ Describe what the screenshot represents based on the context provided.`;
16
+ try {
17
+ const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
18
+ method: 'POST',
19
+ headers: {
20
+ Authorization: `Bearer ${apiKey}`,
21
+ 'Content-Type': 'application/json',
22
+ 'HTTP-Referer': 'https://autokap.app',
23
+ 'X-Title': 'Screenshot Agent',
24
+ },
25
+ body: JSON.stringify({
26
+ model,
27
+ max_tokens: 100,
28
+ stream: false,
29
+ messages: [
30
+ { role: 'system', content: systemPrompt },
31
+ { role: 'user', content: contextParts.join('\n') },
32
+ ],
33
+ provider: { ...zdrParam() },
34
+ }),
35
+ signal: AbortSignal.timeout(5_000),
36
+ });
37
+ if (!res.ok) {
38
+ console.warn(`[generate-alt-text] LLM call failed: HTTP ${res.status}`);
39
+ return null;
40
+ }
41
+ const json = (await res.json());
42
+ const content = json.choices?.[0]?.message?.content?.trim();
43
+ if (!content)
44
+ return null;
45
+ return content.replace(/^["']|["']$/g, '');
46
+ }
47
+ catch {
48
+ console.warn('[generate-alt-text] ALT text generation failed');
49
+ return null;
50
+ }
51
+ }
52
+ //# sourceMappingURL=capture-alt-text.js.map
@@ -0,0 +1,10 @@
1
+ export interface EncryptedEnvelope {
2
+ __encrypted: true;
3
+ version: 1;
4
+ iv: string;
5
+ tag: string;
6
+ ciphertext: string;
7
+ }
8
+ export declare function encrypt(plaintext: string, secret: string): EncryptedEnvelope;
9
+ export declare function decrypt(envelope: EncryptedEnvelope, secret: string): string;
10
+ export declare function isEncryptedEnvelope(value: unknown): value is EncryptedEnvelope;
@@ -0,0 +1,41 @@
1
+ import crypto from 'node:crypto';
2
+ function deriveKey(secret) {
3
+ return crypto.createHash('sha256').update(secret).digest();
4
+ }
5
+ export function encrypt(plaintext, secret) {
6
+ const key = deriveKey(secret);
7
+ const iv = crypto.randomBytes(12);
8
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
9
+ const ciphertext = Buffer.concat([
10
+ cipher.update(plaintext, 'utf8'),
11
+ cipher.final(),
12
+ ]);
13
+ const tag = cipher.getAuthTag();
14
+ return {
15
+ __encrypted: true,
16
+ version: 1,
17
+ iv: iv.toString('base64'),
18
+ tag: tag.toString('base64'),
19
+ ciphertext: ciphertext.toString('base64'),
20
+ };
21
+ }
22
+ export function decrypt(envelope, secret) {
23
+ const key = deriveKey(secret);
24
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, Buffer.from(envelope.iv, 'base64'));
25
+ decipher.setAuthTag(Buffer.from(envelope.tag, 'base64'));
26
+ return Buffer.concat([
27
+ decipher.update(Buffer.from(envelope.ciphertext, 'base64')),
28
+ decipher.final(),
29
+ ]).toString('utf8');
30
+ }
31
+ export function isEncryptedEnvelope(value) {
32
+ if (!value || typeof value !== 'object')
33
+ return false;
34
+ const candidate = value;
35
+ return (candidate.__encrypted === true
36
+ && candidate.version === 1
37
+ && typeof candidate.iv === 'string'
38
+ && typeof candidate.tag === 'string'
39
+ && typeof candidate.ciphertext === 'string');
40
+ }
41
+ //# sourceMappingURL=capture-encryption.js.map
@@ -0,0 +1,41 @@
1
+ import type { Browser } from "./browser.js";
2
+ import { type RequestedLanguageState, type RequestedThemeState } from "./session-profile.js";
3
+ import type { AgentResult, LoginCredentials, StepUsage, ValidatedSessionProfile, VideoPageSignals } from "./types.js";
4
+ import { type ScreenshotSelectorMemoryUpdate } from "./capture-selector-memory.js";
5
+ interface LanguageObservation {
6
+ currentUrl: string;
7
+ signals: VideoPageSignals | null;
8
+ languageState: RequestedLanguageState | null;
9
+ themeState: RequestedThemeState | null;
10
+ }
11
+ export interface ScreenshotLanguagePreflightResult {
12
+ ok: boolean;
13
+ resolvedBy: "already_active" | "deterministic_repair" | "language_preflight_agent" | "blocked";
14
+ observation: LanguageObservation;
15
+ usage: StepUsage[];
16
+ selectorUpdates: ScreenshotSelectorMemoryUpdate[];
17
+ rebasedToStartUrl: boolean;
18
+ reason?: string;
19
+ }
20
+ export declare function ensureScreenshotVariantLanguage(params: {
21
+ browser: Browser;
22
+ requestedLang: string;
23
+ requestedTheme: "light" | "dark";
24
+ startUrl?: string;
25
+ profile?: ValidatedSessionProfile;
26
+ credentials?: LoginCredentials;
27
+ selectorMemory?: Record<string, string[]>;
28
+ langInstructions?: string;
29
+ themeInstructions?: string;
30
+ onLog?: (message: string) => void;
31
+ performRepair?: () => Promise<{
32
+ repaired: boolean;
33
+ updates: ScreenshotSelectorMemoryUpdate[];
34
+ } | null>;
35
+ rebaseToStartUrl?: (currentUrl: string, startUrl: string) => Promise<void>;
36
+ runLanguageSwitchAgent?: (state: {
37
+ languageState: RequestedLanguageState | null;
38
+ themeState: RequestedThemeState | null;
39
+ }) => Promise<AgentResult | null>;
40
+ }): Promise<ScreenshotLanguagePreflightResult>;
41
+ export {};
@@ -0,0 +1,300 @@
1
+ import { evaluateRequestedLanguageState, evaluateRequestedThemeState, performDeterministicSessionRepair, } from "./session-profile.js";
2
+ import { extractSelectorUpdates, } from "./capture-selector-memory.js";
3
+ import { isCaptureStepTimeoutError, withCaptureStepTimeout, } from "./capture-step-timeout.js";
4
+ import { urlMatchesCaptureTarget } from "./capture-run-optimizer.js";
5
+ const LANGUAGE_OBSERVATION_TIMEOUT_MS = 12000;
6
+ const LANGUAGE_REPAIR_TIMEOUT_MS = 15000;
7
+ const LANGUAGE_STABILITY_DELAY_MS = 900;
8
+ function urlsRoughlyMatch(expectedUrl, currentUrl) {
9
+ if (!expectedUrl || !currentUrl)
10
+ return false;
11
+ return urlMatchesCaptureTarget(currentUrl, expectedUrl) || urlMatchesCaptureTarget(expectedUrl, currentUrl);
12
+ }
13
+ async function waitForVariantStability(browser, delayMs) {
14
+ if (typeof browser.wait === "function") {
15
+ await browser.wait(delayMs);
16
+ return;
17
+ }
18
+ const pageWait = browser.currentPage.waitForTimeout;
19
+ if (typeof pageWait === "function") {
20
+ await pageWait.call(browser.currentPage, delayMs);
21
+ return;
22
+ }
23
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
24
+ }
25
+ function summarizeRequestedLanguageState(state) {
26
+ if (!state)
27
+ return "language_state=unavailable";
28
+ return [
29
+ state.requested ? `requested=${state.requested}` : null,
30
+ state.detected ? `detected=${state.detected}` : "detected=unknown",
31
+ `ambiguous=${state.ambiguous}`,
32
+ ...state.reasons.slice(0, 3),
33
+ ]
34
+ .filter(Boolean)
35
+ .join("; ");
36
+ }
37
+ function summarizeRequestedThemeState(state) {
38
+ if (!state)
39
+ return "theme_state=unavailable";
40
+ return [
41
+ state.requested ? `requested=${state.requested}` : null,
42
+ state.detected ? `detected=${state.detected}` : "detected=unknown",
43
+ `ambiguous=${state.ambiguous}`,
44
+ ...state.reasons.slice(0, 3),
45
+ ]
46
+ .filter(Boolean)
47
+ .join("; ");
48
+ }
49
+ function summarizeVariantObservation(observation) {
50
+ return [
51
+ summarizeRequestedLanguageState(observation.languageState),
52
+ summarizeRequestedThemeState(observation.themeState),
53
+ ].join(" | ");
54
+ }
55
+ function isObservationReady(observation) {
56
+ const languageReady = !!(observation.languageState?.active
57
+ && !observation.languageState.ambiguous);
58
+ const themeReady = !!(observation.themeState?.active
59
+ && !observation.themeState.ambiguous);
60
+ return languageReady && themeReady;
61
+ }
62
+ function buildFailureReason(requestedLang, requestedTheme, observation) {
63
+ const failures = [];
64
+ if (!observation.languageState) {
65
+ failures.push(`unable to inspect the current UI language for "${requestedLang}"`);
66
+ }
67
+ else if (!observation.languageState.active || observation.languageState.ambiguous) {
68
+ failures.push(`requested "${requestedLang}", detected "${observation.languageState.detected ?? "unknown"}" (${observation.languageState.reasons.join("; ")})`);
69
+ }
70
+ if (!observation.themeState) {
71
+ failures.push(`unable to inspect the current UI theme for "${requestedTheme}"`);
72
+ }
73
+ else if (!observation.themeState.active || observation.themeState.ambiguous) {
74
+ failures.push(`theme requested "${requestedTheme}", detected "${observation.themeState.detected ?? "unknown"}" (${observation.themeState.reasons.join("; ")})`);
75
+ }
76
+ if (failures.length === 0) {
77
+ return "";
78
+ }
79
+ return `Language preflight failed: ${failures.join(" | ")}.`;
80
+ }
81
+ async function observeLanguageState(browser, requestedLang, params) {
82
+ const currentUrl = browser.currentPage.url();
83
+ const phase = params?.phase ?? "inspection";
84
+ params?.onLog?.(`Language guard: inspecting fixed UI chrome during ${phase}.`);
85
+ const signals = await withCaptureStepTimeout(() => browser.capturePageSignals(), {
86
+ stepLabel: `inspecting fixed UI chrome during ${phase}`,
87
+ timeoutMs: LANGUAGE_OBSERVATION_TIMEOUT_MS,
88
+ }).catch((error) => {
89
+ if (isCaptureStepTimeoutError(error)) {
90
+ params?.onLog?.(`Language guard: fixed UI inspection timed out after ${error.timeoutMs}ms during ${phase}.`);
91
+ return null;
92
+ }
93
+ params?.onLog?.(`Language guard: fixed UI inspection failed during ${phase} (${error instanceof Error ? error.message : String(error)}).`);
94
+ return null;
95
+ });
96
+ return {
97
+ currentUrl,
98
+ signals,
99
+ languageState: signals
100
+ ? evaluateRequestedLanguageState({
101
+ currentUrl,
102
+ requestedLang,
103
+ signals,
104
+ })
105
+ : null,
106
+ themeState: signals
107
+ ? evaluateRequestedThemeState({
108
+ requestedTheme: params?.requestedTheme,
109
+ signals,
110
+ })
111
+ : null,
112
+ };
113
+ }
114
+ async function confirmStableLanguageState(params) {
115
+ if (!isObservationReady(params.observation)) {
116
+ return params.observation;
117
+ }
118
+ params.onLog?.(`Language guard: waiting ${LANGUAGE_STABILITY_DELAY_MS}ms to confirm the variant remains stable during ${params.phase}.`);
119
+ await waitForVariantStability(params.browser, LANGUAGE_STABILITY_DELAY_MS);
120
+ return observeLanguageState(params.browser, params.requestedLang, {
121
+ onLog: params.onLog,
122
+ phase: `${params.phase} stability verification`,
123
+ requestedTheme: params.requestedTheme,
124
+ });
125
+ }
126
+ export async function ensureScreenshotVariantLanguage(params) {
127
+ const usage = [];
128
+ const selectorUpdates = [];
129
+ const requestedLang = params.requestedLang.trim().toLowerCase();
130
+ let rebasedToStartUrl = false;
131
+ const rebaseIfNeeded = async (observation) => {
132
+ if (!params.startUrl
133
+ || !params.rebaseToStartUrl
134
+ || urlsRoughlyMatch(params.startUrl, observation.currentUrl)) {
135
+ return observation;
136
+ }
137
+ params.onLog?.(`Language guard: rebasing back to ${params.startUrl} after language switch from ${observation.currentUrl}.`);
138
+ await params.rebaseToStartUrl(observation.currentUrl, params.startUrl);
139
+ rebasedToStartUrl = true;
140
+ return observeLanguageState(params.browser, requestedLang, {
141
+ onLog: params.onLog,
142
+ phase: "post-rebase verification",
143
+ requestedTheme: params.requestedTheme,
144
+ });
145
+ };
146
+ let observation = await observeLanguageState(params.browser, requestedLang, {
147
+ onLog: params.onLog,
148
+ phase: "initial preflight",
149
+ requestedTheme: params.requestedTheme,
150
+ });
151
+ if (isObservationReady(observation)) {
152
+ return {
153
+ ok: true,
154
+ resolvedBy: "already_active",
155
+ observation,
156
+ usage,
157
+ selectorUpdates,
158
+ rebasedToStartUrl,
159
+ };
160
+ }
161
+ params.onLog?.(`Language guard: requested variant "${requestedLang}/${params.requestedTheme}" is not active yet; ${summarizeVariantObservation(observation)}.`);
162
+ params.onLog?.("Language guard: running deterministic variant repair before the main workflow.");
163
+ const repair = await withCaptureStepTimeout(async () => (params.performRepair
164
+ ?? (async () => {
165
+ const result = await performDeterministicSessionRepair(params.browser, {
166
+ startUrl: params.startUrl,
167
+ requestedLang,
168
+ requestedTheme: params.requestedTheme,
169
+ credentials: params.credentials,
170
+ profile: params.profile,
171
+ selectorMemory: params.selectorMemory,
172
+ }).catch(() => null);
173
+ return result
174
+ ? {
175
+ repaired: result.repaired,
176
+ updates: result.updates.map((update) => ({
177
+ ...update,
178
+ source: "deterministic",
179
+ })),
180
+ }
181
+ : null;
182
+ }))(), {
183
+ stepLabel: "running deterministic variant repair",
184
+ timeoutMs: LANGUAGE_REPAIR_TIMEOUT_MS,
185
+ }).catch((error) => {
186
+ if (isCaptureStepTimeoutError(error)) {
187
+ params.onLog?.(`Language guard: deterministic repair timed out after ${error.timeoutMs}ms.`);
188
+ return null;
189
+ }
190
+ params.onLog?.(`Language guard: deterministic repair failed (${error instanceof Error ? error.message : String(error)}).`);
191
+ return null;
192
+ });
193
+ if (repair?.updates.length) {
194
+ selectorUpdates.push(...repair.updates);
195
+ }
196
+ observation = await observeLanguageState(params.browser, requestedLang, {
197
+ onLog: params.onLog,
198
+ phase: "post-repair verification",
199
+ requestedTheme: params.requestedTheme,
200
+ });
201
+ if (isObservationReady(observation)) {
202
+ observation = await rebaseIfNeeded(observation);
203
+ observation = await confirmStableLanguageState({
204
+ browser: params.browser,
205
+ requestedLang,
206
+ requestedTheme: params.requestedTheme,
207
+ observation,
208
+ onLog: params.onLog,
209
+ phase: "post-deterministic-repair",
210
+ });
211
+ }
212
+ if (isObservationReady(observation)) {
213
+ params.onLog?.(`Language guard: fixed UI variant switched successfully after deterministic repair; ${summarizeVariantObservation(observation)}.`);
214
+ return {
215
+ ok: true,
216
+ resolvedBy: "deterministic_repair",
217
+ observation,
218
+ usage,
219
+ selectorUpdates,
220
+ rebasedToStartUrl,
221
+ };
222
+ }
223
+ if (!params.runLanguageSwitchAgent) {
224
+ const reason = buildFailureReason(requestedLang, params.requestedTheme, observation);
225
+ params.onLog?.(`Language guard: blocking variant before main workflow. ${reason}`);
226
+ return {
227
+ ok: false,
228
+ resolvedBy: "blocked",
229
+ observation,
230
+ usage,
231
+ selectorUpdates,
232
+ rebasedToStartUrl,
233
+ reason,
234
+ };
235
+ }
236
+ params.onLog?.(`Language guard: deterministic repair was insufficient; launching dedicated variant preflight. ${summarizeVariantObservation(observation)}.`);
237
+ const preflightAgentResult = await params.runLanguageSwitchAgent({
238
+ languageState: observation.languageState,
239
+ themeState: observation.themeState,
240
+ }).catch(() => null);
241
+ if (preflightAgentResult?.usage?.length) {
242
+ usage.push(...preflightAgentResult.usage);
243
+ }
244
+ if (preflightAgentResult?.actions?.length) {
245
+ selectorUpdates.push(...extractSelectorUpdates(preflightAgentResult.actions));
246
+ }
247
+ observation = await observeLanguageState(params.browser, requestedLang, {
248
+ onLog: params.onLog,
249
+ phase: "post-variant-switch verification",
250
+ requestedTheme: params.requestedTheme,
251
+ });
252
+ if (isObservationReady(observation)) {
253
+ observation = await rebaseIfNeeded(observation);
254
+ observation = await confirmStableLanguageState({
255
+ browser: params.browser,
256
+ requestedLang,
257
+ requestedTheme: params.requestedTheme,
258
+ observation,
259
+ onLog: params.onLog,
260
+ phase: "post-variant-switch",
261
+ });
262
+ }
263
+ if (isObservationReady(observation)) {
264
+ params.onLog?.(`Language guard: fixed UI variant switched successfully after dedicated variant preflight. ${summarizeVariantObservation(observation)}.`);
265
+ return {
266
+ ok: true,
267
+ resolvedBy: "language_preflight_agent",
268
+ observation,
269
+ usage,
270
+ selectorUpdates,
271
+ rebasedToStartUrl,
272
+ };
273
+ }
274
+ // Trust the agent when it succeeded (verification passed) but metadata signals
275
+ // still disagree. This handles SPAs and sites that don't update <html lang> or
276
+ // locale controls after client-side navigation (e.g., /en/ with lang="fr-FR").
277
+ if (preflightAgentResult?.success) {
278
+ params.onLog?.(`Language guard: metadata signals still disagree but variant preflight agent succeeded — trusting agent assessment. ${summarizeVariantObservation(observation)}.`);
279
+ return {
280
+ ok: true,
281
+ resolvedBy: "language_preflight_agent",
282
+ observation,
283
+ usage,
284
+ selectorUpdates,
285
+ rebasedToStartUrl,
286
+ };
287
+ }
288
+ const reason = buildFailureReason(requestedLang, params.requestedTheme, observation);
289
+ params.onLog?.(`Language guard: blocking variant before main workflow. ${reason}`);
290
+ return {
291
+ ok: false,
292
+ resolvedBy: "blocked",
293
+ observation,
294
+ usage,
295
+ selectorUpdates,
296
+ rebasedToStartUrl,
297
+ reason,
298
+ };
299
+ }
300
+ //# sourceMappingURL=capture-language-preflight.js.map
@@ -0,0 +1,15 @@
1
+ import type { CapturePageIdentity, StepUsage } from "./types.js";
2
+ import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
3
+ export interface PageIdentityInferenceResult {
4
+ identities: Record<string, CapturePageIdentity>;
5
+ usage: StepUsage | null;
6
+ }
7
+ /**
8
+ * Use a cheap LLM call to classify all pages in a capture run.
9
+ * Falls back to regex heuristics if the LLM call fails.
10
+ */
11
+ export declare function inferPageIdentitiesWithLLM(pageRuns: ScreenshotPageRunInput[], model: string, apiKey: string, providerPrefs?: Record<string, {
12
+ order?: string[];
13
+ require?: string[];
14
+ disallow?: string[];
15
+ }>): Promise<PageIdentityInferenceResult>;
@@ -0,0 +1,128 @@
1
+ import { buildCapturePageIdentitySummary, inferCapturePageIdentity, normalizeCaptureIdentityTokens, } from "./capture-page-identity.js";
2
+ import { zdrParam } from "./provider-config.js";
3
+ /**
4
+ * Use a cheap LLM call to classify all pages in a capture run.
5
+ * Falls back to regex heuristics if the LLM call fails.
6
+ */
7
+ export async function inferPageIdentitiesWithLLM(pageRuns, model, apiKey, providerPrefs) {
8
+ // Regex fallback for all pages
9
+ const regexFallback = Object.fromEntries(pageRuns.map((pr) => [pr.pageId ?? "main", inferCapturePageIdentity(pr)]));
10
+ if (pageRuns.length === 0) {
11
+ return { identities: regexFallback, usage: null };
12
+ }
13
+ const pagesDescription = pageRuns.map((pr, i) => {
14
+ const pageId = pr.pageId ?? "main";
15
+ return `${i + 1}. pageId="${pageId}" | url="${pr.url}" | prompt="${pr.prompt}"`;
16
+ }).join("\n");
17
+ const systemPrompt = `You classify web page capture targets for a screenshot automation tool.
18
+
19
+ IMPORTANT: The "pageId" is a user-defined identifier — it is NOT a technical description. Do NOT infer the page type from the pageId name alone. A page named "preset_creation_modal" might actually be a sidebar panel, not a DOM dialog. Base your classification on the PROMPT text and URL, not the pageId.
20
+
21
+ For each page, determine:
22
+ - "kind": one of "gallery", "modal_selection", "modal_configuration", "editor_route", "detail_route", "unknown"
23
+ - "gallery": a non-modal overview/list/grid/collection page
24
+ - "modal_selection": the prompt explicitly describes opening a dialog/modal/popup to pick or select something
25
+ - "modal_configuration": the prompt explicitly describes opening a dialog/modal/popup to configure or edit something
26
+ - "editor_route": a dedicated full-page destination with its own route
27
+ - "detail_route": a non-modal detail/content state that is not just a generic overview
28
+ - "unknown": none of the above
29
+ - "dialogTarget": true ONLY if the prompt explicitly says to open an actual DOM dialog, modal, or popup overlay. Set to FALSE for: sidebar panels, drawers, slide-over panels, full pages, dropdown menus, popovers, or any UI that is part of the main page layout. When in doubt, set to false.
30
+ - "dedicatedRoute": true if the capture target has its own URL route (not just a modal on top of another page)
31
+ - "subjectTokens": 2-6 short semantic tokens that describe the FINAL visible capture target. Exclude navigation verbs and chrome words such as: click, open, wait, sidebar, header, page, route, menu, button, first, current.
32
+
33
+ Respond with a JSON array of objects: [{ "pageId": "...", "kind": "...", "dialogTarget": true/false, "dedicatedRoute": true/false, "subjectTokens": ["...", "..."] }]
34
+ Output ONLY valid JSON, nothing else.`;
35
+ try {
36
+ const providerBody = { provider: { ...providerPrefs?.[model], ...zdrParam() } };
37
+ const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
38
+ method: "POST",
39
+ headers: {
40
+ Authorization: `Bearer ${apiKey}`,
41
+ "Content-Type": "application/json",
42
+ "HTTP-Referer": "https://github.com/screenshot-agent",
43
+ "X-Title": "Screenshot Agent",
44
+ },
45
+ body: JSON.stringify({
46
+ model,
47
+ max_tokens: 512,
48
+ stream: false,
49
+ response_format: { type: "json_object" },
50
+ messages: [
51
+ { role: "system", content: systemPrompt },
52
+ { role: "user", content: `Classify these ${pageRuns.length} page capture targets:\n\n${pagesDescription}` },
53
+ ],
54
+ ...providerBody,
55
+ }),
56
+ signal: AbortSignal.timeout(30_000),
57
+ });
58
+ if (!res.ok) {
59
+ console.warn(`[llm-page-identity] LLM call failed: HTTP ${res.status}`);
60
+ return { identities: regexFallback, usage: null };
61
+ }
62
+ const json = await res.json();
63
+ const content = json.choices?.[0]?.message?.content?.trim();
64
+ const usage = {
65
+ stepNumber: 0,
66
+ stepType: "page_identity_classification",
67
+ generationId: json.id ?? null,
68
+ modelRequested: model,
69
+ modelUsed: json.model ?? null,
70
+ promptTokens: json.usage?.prompt_tokens ?? null,
71
+ completionTokens: json.usage?.completion_tokens ?? null,
72
+ totalTokens: json.usage?.total_tokens ?? null,
73
+ imagesInPrompt: 0,
74
+ };
75
+ if (!content)
76
+ return { identities: regexFallback, usage };
77
+ // Parse — handle both array and { pages: [...] } wrapper
78
+ let classifications;
79
+ const parsed = JSON.parse(content);
80
+ if (Array.isArray(parsed)) {
81
+ classifications = parsed;
82
+ }
83
+ else if (Array.isArray(parsed.pages)) {
84
+ classifications = parsed.pages;
85
+ }
86
+ else if (Array.isArray(parsed.classifications)) {
87
+ classifications = parsed.classifications;
88
+ }
89
+ else {
90
+ console.warn("[llm-page-identity] Unexpected LLM response shape:", content.slice(0, 200));
91
+ return { identities: regexFallback, usage };
92
+ }
93
+ // Merge LLM results with regex fallback (LLM wins, regex fills gaps)
94
+ const result = { ...regexFallback };
95
+ for (const cls of classifications) {
96
+ const pageId = cls.pageId;
97
+ if (!pageId || !result[pageId])
98
+ continue;
99
+ const validKinds = [
100
+ "gallery", "modal_selection", "modal_configuration", "editor_route", "detail_route", "unknown",
101
+ ];
102
+ const kind = validKinds.includes(cls.kind) ? cls.kind : result[pageId].kind;
103
+ const subjectTokens = normalizeCaptureIdentityTokens(cls.subjectTokens ?? []);
104
+ result[pageId] = {
105
+ ...result[pageId],
106
+ kind,
107
+ subjectTokens: subjectTokens.length > 0 ? subjectTokens : result[pageId].subjectTokens,
108
+ dialogTarget: typeof cls.dialogTarget === "boolean" ? cls.dialogTarget : result[pageId].dialogTarget,
109
+ dedicatedRoute: typeof cls.dedicatedRoute === "boolean" ? cls.dedicatedRoute : result[pageId].dedicatedRoute,
110
+ summary: buildCapturePageIdentitySummary(kind, subjectTokens.length > 0 ? subjectTokens : result[pageId].subjectTokens, (() => {
111
+ try {
112
+ const pageRun = pageRuns.find((pr) => (pr.pageId ?? "main") === pageId);
113
+ return new URL(pageRun?.url ?? "").pathname.toLowerCase();
114
+ }
115
+ catch {
116
+ return "/";
117
+ }
118
+ })()),
119
+ };
120
+ }
121
+ return { identities: result, usage };
122
+ }
123
+ catch (err) {
124
+ console.warn(`[llm-page-identity] LLM classification failed, using regex fallback:`, err instanceof Error ? err.message : err);
125
+ return { identities: regexFallback, usage: null };
126
+ }
127
+ }
128
+ //# sourceMappingURL=capture-llm-page-identity.js.map
@@ -0,0 +1,9 @@
1
+ export declare function resolveRunModels(params: {
2
+ requestModel?: string | null;
3
+ defaultModel?: string | null;
4
+ fallbackModel?: string | null;
5
+ presetId?: string | null;
6
+ }): {
7
+ model: string;
8
+ fallbackModel?: string;
9
+ };
@@ -0,0 +1,21 @@
1
+ function normalizeModelId(value) {
2
+ if (typeof value !== 'string')
3
+ return null;
4
+ const trimmed = value.trim();
5
+ return trimmed.length > 0 ? trimmed : null;
6
+ }
7
+ export function resolveRunModels(params) {
8
+ const requestModel = normalizeModelId(params.requestModel);
9
+ const defaultModel = normalizeModelId(params.defaultModel);
10
+ const fallbackModel = normalizeModelId(params.fallbackModel);
11
+ const preferAdminModel = Boolean(params.presetId && (defaultModel || fallbackModel));
12
+ const model = (preferAdminModel
13
+ ? defaultModel ?? fallbackModel ?? requestModel
14
+ : requestModel ?? defaultModel ?? fallbackModel)
15
+ ?? '';
16
+ return {
17
+ model,
18
+ fallbackModel: fallbackModel && fallbackModel !== model ? fallbackModel : undefined,
19
+ };
20
+ }
21
+ //# sourceMappingURL=capture-model-resolution.js.map
@@ -0,0 +1,7 @@
1
+ import type { CapturePageIdentity, CapturePageIdentityKind } from "./types.js";
2
+ import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
3
+ export declare function normalizeCaptureIdentityTokens(values: Array<string | null | undefined>, options?: {
4
+ stopWords?: ReadonlySet<string>;
5
+ }): string[];
6
+ export declare function buildCapturePageIdentitySummary(kind: CapturePageIdentityKind, tokens: string[], urlPath: string): string;
7
+ export declare function inferCapturePageIdentity(pageRun: ScreenshotPageRunInput): CapturePageIdentity;