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,42 @@
1
+ import type { ClipOptions } from './types.js';
2
+ /**
3
+ * Resolve the system ffmpeg binary path. Throws if not found.
4
+ */
5
+ export declare function ensureFfmpegAvailable(): Promise<string>;
6
+ /**
7
+ * Convert a WebM recording to an optimized GIF using a 2-pass palette approach.
8
+ * Pass 1: generate an optimal palette. Pass 2: encode with that palette.
9
+ */
10
+ export declare function convertToGif(webmPath: string, outputPath: string, opts?: {
11
+ fps?: number;
12
+ maxWidth?: number;
13
+ loop?: boolean;
14
+ }): Promise<void>;
15
+ /**
16
+ * Convert a WebM recording to an MP4 with web-optimized settings.
17
+ */
18
+ export declare function convertToMp4(webmPath: string, outputPath: string): Promise<void>;
19
+ /**
20
+ * Extract the first frame of a WebM as a PNG thumbnail.
21
+ */
22
+ export declare function extractThumbnail(webmPath: string, outputPath: string): Promise<void>;
23
+ /**
24
+ * Trim a recording: skip dead frames at the start and cap duration.
25
+ */
26
+ export declare function trimRecording(inputPath: string, outputPath: string, startSec?: number, maxDurationSec?: number): Promise<void>;
27
+ /**
28
+ * Get the duration of a media file in milliseconds.
29
+ */
30
+ export declare function getMediaDurationMs(filePath: string): Promise<number>;
31
+ export interface ClipPostProcessResult {
32
+ gifPath?: string;
33
+ mp4Path?: string;
34
+ thumbnailPath?: string;
35
+ durationMs: number;
36
+ fileSizeBytes?: number;
37
+ }
38
+ /**
39
+ * Full post-processing pipeline for a single clip recording:
40
+ * trim → GIF and/or MP4 → thumbnail → measure.
41
+ */
42
+ export declare function postProcessClipRecording(webmPath: string, outputDir: string, clipId: string, options?: ClipOptions): Promise<ClipPostProcessResult>;
@@ -0,0 +1,201 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { rm, stat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ const execFileAsync = promisify(execFile);
6
+ // ── Default values ──────────────────────────────────────────────────
7
+ const DEFAULT_GIF_FPS = 15;
8
+ const DEFAULT_GIF_MAX_WIDTH = 800;
9
+ const DEFAULT_MAX_DURATION_SEC = 8;
10
+ const DEFAULT_TRIM_START_SEC = 0.3;
11
+ // ── ffmpeg detection ────────────────────────────────────────────────
12
+ const cachedBinaryPaths = {};
13
+ async function ensureBinaryAvailable(binaryName) {
14
+ const cached = cachedBinaryPaths[binaryName];
15
+ if (cached)
16
+ return cached;
17
+ const resolver = process.platform === 'win32' ? 'where' : 'which';
18
+ const { stdout } = await execFileAsync(resolver, [binaryName]);
19
+ const resolvedPath = stdout
20
+ .split(/\r?\n/)
21
+ .map(line => line.trim())
22
+ .find(Boolean);
23
+ if (!resolvedPath) {
24
+ throw new Error(`${binaryName} not found`);
25
+ }
26
+ cachedBinaryPaths[binaryName] = resolvedPath;
27
+ return resolvedPath;
28
+ }
29
+ /**
30
+ * Resolve the system ffmpeg binary path. Throws if not found.
31
+ */
32
+ export async function ensureFfmpegAvailable() {
33
+ try {
34
+ return await ensureBinaryAvailable('ffmpeg');
35
+ }
36
+ catch {
37
+ throw new Error('ffmpeg is required for clip post-processing but was not found on your system. ' +
38
+ 'Install it via: brew install ffmpeg (macOS), apt install ffmpeg (Linux), or choco install ffmpeg (Windows).');
39
+ }
40
+ }
41
+ // ── Conversion functions ────────────────────────────────────────────
42
+ /**
43
+ * Convert a WebM recording to an optimized GIF using a 2-pass palette approach.
44
+ * Pass 1: generate an optimal palette. Pass 2: encode with that palette.
45
+ */
46
+ export async function convertToGif(webmPath, outputPath, opts = {}) {
47
+ const ffmpeg = await ensureFfmpegAvailable();
48
+ const fps = opts.fps ?? DEFAULT_GIF_FPS;
49
+ const maxWidth = opts.maxWidth ?? DEFAULT_GIF_MAX_WIDTH;
50
+ const loopFlag = (opts.loop ?? true) ? '0' : '-1';
51
+ const paletteDir = path.dirname(outputPath);
52
+ const palettePath = path.join(paletteDir, `_palette_${Date.now()}.png`);
53
+ const scaleFilter = `fps=${fps},scale=${maxWidth}:-1:flags=lanczos`;
54
+ // Pass 1: Generate palette
55
+ await execFileAsync(ffmpeg, [
56
+ '-i', webmPath,
57
+ '-vf', `${scaleFilter},palettegen=stats_mode=diff`,
58
+ '-y', palettePath,
59
+ ]);
60
+ // Pass 2: Encode GIF with palette
61
+ try {
62
+ await execFileAsync(ffmpeg, [
63
+ '-i', webmPath,
64
+ '-i', palettePath,
65
+ '-lavfi', `${scaleFilter}[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=3`,
66
+ '-loop', loopFlag,
67
+ '-y', outputPath,
68
+ ]);
69
+ }
70
+ finally {
71
+ // Clean up palette file
72
+ try {
73
+ await stat(palettePath);
74
+ await rm(palettePath, { force: true });
75
+ }
76
+ catch { /* ignore */ }
77
+ }
78
+ }
79
+ /**
80
+ * Convert a WebM recording to an MP4 with web-optimized settings.
81
+ */
82
+ export async function convertToMp4(webmPath, outputPath) {
83
+ const ffmpeg = await ensureFfmpegAvailable();
84
+ await execFileAsync(ffmpeg, [
85
+ '-i', webmPath,
86
+ '-c:v', 'libx264',
87
+ '-preset', 'slow',
88
+ '-crf', '22',
89
+ '-pix_fmt', 'yuv420p',
90
+ '-movflags', '+faststart',
91
+ '-an',
92
+ '-y', outputPath,
93
+ ]);
94
+ }
95
+ /**
96
+ * Extract the first frame of a WebM as a PNG thumbnail.
97
+ */
98
+ export async function extractThumbnail(webmPath, outputPath) {
99
+ const ffmpeg = await ensureFfmpegAvailable();
100
+ await execFileAsync(ffmpeg, [
101
+ '-i', webmPath,
102
+ '-frames:v', '1',
103
+ '-y', outputPath,
104
+ ]);
105
+ }
106
+ /**
107
+ * Trim a recording: skip dead frames at the start and cap duration.
108
+ */
109
+ export async function trimRecording(inputPath, outputPath, startSec = DEFAULT_TRIM_START_SEC, maxDurationSec = DEFAULT_MAX_DURATION_SEC) {
110
+ const ffmpeg = await ensureFfmpegAvailable();
111
+ await execFileAsync(ffmpeg, [
112
+ '-ss', String(startSec),
113
+ '-i', inputPath,
114
+ '-t', String(maxDurationSec),
115
+ '-c', 'copy',
116
+ '-y', outputPath,
117
+ ]);
118
+ }
119
+ /**
120
+ * Get the duration of a media file in milliseconds.
121
+ */
122
+ export async function getMediaDurationMs(filePath) {
123
+ const ffprobe = await ensureBinaryAvailable('ffprobe');
124
+ const { stdout } = await execFileAsync(ffprobe, [
125
+ '-v', 'error',
126
+ '-show_entries', 'format=duration',
127
+ '-of', 'default=noprint_wrappers=1:nokey=1',
128
+ filePath,
129
+ ]);
130
+ return Math.round(parseFloat(stdout.trim()) * 1000);
131
+ }
132
+ /**
133
+ * Freeze the last frame of a video for `durationSec` additional seconds.
134
+ * Uses ffmpeg's tpad filter to hold the final frame in place.
135
+ */
136
+ async function freezeLastFrame(inputPath, outputPath, durationSec) {
137
+ const ffmpeg = await ensureFfmpegAvailable();
138
+ const stopDurationMs = Math.round(durationSec * 1000);
139
+ await execFileAsync(ffmpeg, [
140
+ '-i', inputPath,
141
+ '-vf', `tpad=stop_mode=clone:stop_duration=${stopDurationMs}ms`,
142
+ '-c:v', 'libvpx-vp9',
143
+ '-crf', '30',
144
+ '-b:v', '0',
145
+ '-an',
146
+ '-y', outputPath,
147
+ ]);
148
+ }
149
+ /**
150
+ * Full post-processing pipeline for a single clip recording:
151
+ * trim → GIF and/or MP4 → thumbnail → measure.
152
+ */
153
+ export async function postProcessClipRecording(webmPath, outputDir, clipId, options = {}) {
154
+ const maxDuration = options.maxDurationSec ?? DEFAULT_MAX_DURATION_SEC;
155
+ // Step 1: Trim the recording — cut the setup phase (page load, overlay dismiss)
156
+ const trimStart = options.trimStartSec ?? DEFAULT_TRIM_START_SEC;
157
+ const trimmedPath = path.join(outputDir, `${clipId}_trimmed.webm`);
158
+ await trimRecording(webmPath, trimmedPath, trimStart, maxDuration);
159
+ // Step 1b: Optionally freeze the last frame for a pause before looping
160
+ const holdSec = Math.min(Math.max(options.holdLastFrameSec ?? 0, 0), 10);
161
+ let sourcePath = trimmedPath;
162
+ if (holdSec > 0) {
163
+ const paddedPath = path.join(outputDir, `${clipId}_padded.webm`);
164
+ await freezeLastFrame(trimmedPath, paddedPath, holdSec);
165
+ sourcePath = paddedPath;
166
+ }
167
+ const durationMs = await getMediaDurationMs(sourcePath);
168
+ const result = { durationMs };
169
+ // Step 2: Always persist both a GIF and an MP4.
170
+ // The GIF remains useful for delivery and embeds, while the MP4 is the
171
+ // high-fidelity source for Studio playback and downstream re-renders.
172
+ const gifPath = path.join(outputDir, `${clipId}.gif`);
173
+ await convertToGif(sourcePath, gifPath, {
174
+ fps: options.gifFps,
175
+ maxWidth: options.gifMaxWidth,
176
+ loop: options.loop,
177
+ });
178
+ result.gifPath = gifPath;
179
+ const gifStat = await stat(gifPath);
180
+ result.fileSizeBytes = gifStat.size;
181
+ const mp4Path = path.join(outputDir, `${clipId}.mp4`);
182
+ await convertToMp4(sourcePath, mp4Path);
183
+ result.mp4Path = mp4Path;
184
+ // Step 3: Extract thumbnail
185
+ const thumbnailPath = path.join(outputDir, `${clipId}_thumb.png`);
186
+ await extractThumbnail(sourcePath, thumbnailPath);
187
+ result.thumbnailPath = thumbnailPath;
188
+ // Clean up intermediate files
189
+ try {
190
+ await rm(trimmedPath, { force: true });
191
+ }
192
+ catch { /* ignore */ }
193
+ if (sourcePath !== trimmedPath) {
194
+ try {
195
+ await rm(sourcePath, { force: true });
196
+ }
197
+ catch { /* ignore */ }
198
+ }
199
+ return result;
200
+ }
201
+ //# sourceMappingURL=clip-postprocess.js.map
@@ -1,4 +1,6 @@
1
1
  import type { Page } from 'playwright';
2
+ export declare function getCaptureHideCSS(): string;
3
+ export declare function ensureCaptureHideStyles(page: Page): Promise<void>;
2
4
  export declare function dismissCookiesAndWidgets(page: Page): Promise<{
3
5
  dismissed: boolean;
4
6
  method: string | null;
@@ -82,6 +82,51 @@ const HIDE_SELECTORS = [
82
82
  '[id*="cookieBanner"]', '[id*="cookieConsent"]',
83
83
  '[class*="gdpr"]', '[id*="gdpr"]',
84
84
  ];
85
+ // Dev-only chrome that should never appear in captures
86
+ const DEV_TOOL_SELECTORS = [
87
+ // Next.js dev indicator / devtools
88
+ 'nextjs-portal',
89
+ '#__next-build-watcher',
90
+ '[data-next-badge-root]',
91
+ // Astro dev toolbar
92
+ 'astro-dev-toolbar',
93
+ // Vite overlays
94
+ 'vite-error-overlay',
95
+ 'vite-plugin-checker-error-overlay',
96
+ // Nuxt devtools
97
+ 'nuxt-devtools',
98
+ 'nuxt-devtools-frame',
99
+ '#nuxt-devtools-anchor',
100
+ '#nuxt-devtools-container',
101
+ ];
102
+ const CAPTURE_HIDE_STYLE_ID = 'autokap-capture-hide-style';
103
+ export function getCaptureHideCSS() {
104
+ return [...HIDE_SELECTORS, ...DEV_TOOL_SELECTORS].map(selector => `${selector} { display: none !important; visibility: hidden !important; pointer-events: none !important; }`).join('\n');
105
+ }
106
+ export async function ensureCaptureHideStyles(page) {
107
+ try {
108
+ await page.evaluate(({ styleId, css }) => {
109
+ const parent = document.head ?? document.documentElement;
110
+ if (!parent)
111
+ return;
112
+ let style = document.getElementById(styleId);
113
+ if (!style) {
114
+ style = document.createElement('style');
115
+ style.id = styleId;
116
+ parent.appendChild(style);
117
+ }
118
+ if (style.textContent !== css) {
119
+ style.textContent = css;
120
+ }
121
+ }, {
122
+ styleId: CAPTURE_HIDE_STYLE_ID,
123
+ css: getCaptureHideCSS(),
124
+ });
125
+ }
126
+ catch {
127
+ // Page might have navigated away
128
+ }
129
+ }
85
130
  export async function dismissCookiesAndWidgets(page) {
86
131
  // Strategy 1: Click known CMP buttons
87
132
  for (const selector of CMP_SELECTORS) {
@@ -91,7 +136,7 @@ export async function dismissCookiesAndWidgets(page) {
91
136
  await button.click({ timeout: 2000 });
92
137
  logger.info(`Cookie dismissed via CMP selector: ${selector}`);
93
138
  await page.waitForTimeout(500);
94
- await injectHideCSS(page);
139
+ await ensureCaptureHideStyles(page);
95
140
  return { dismissed: true, method: `cmp:${selector}` };
96
141
  }
97
142
  }
@@ -148,7 +193,7 @@ export async function dismissCookiesAndWidgets(page) {
148
193
  if (dismissed) {
149
194
  logger.info(`Cookie dismissed via text match: "${dismissed}"`);
150
195
  await page.waitForTimeout(500);
151
- await injectHideCSS(page);
196
+ await ensureCaptureHideStyles(page);
152
197
  return { dismissed: true, method: `text:${dismissed}` };
153
198
  }
154
199
  }
@@ -156,17 +201,7 @@ export async function dismissCookiesAndWidgets(page) {
156
201
  // Text-based search failed
157
202
  }
158
203
  // Strategy 3: Always inject CSS to hide known overlays and widgets
159
- await injectHideCSS(page);
204
+ await ensureCaptureHideStyles(page);
160
205
  return { dismissed: false, method: null };
161
206
  }
162
- async function injectHideCSS(page) {
163
- try {
164
- await page.addStyleTag({
165
- content: HIDE_SELECTORS.map(s => `${s} { display: none !important; visibility: hidden !important; pointer-events: none !important; }`).join('\n'),
166
- });
167
- }
168
- catch {
169
- // Page might have navigated away
170
- }
171
- }
172
207
  //# sourceMappingURL=cookie-dismiss.js.map
@@ -0,0 +1,35 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ import type { StepUsage } from './types.js';
3
+ export interface CostLogContext {
4
+ runId: string;
5
+ userId: string;
6
+ projectId?: string;
7
+ presetId?: string;
8
+ lang?: string;
9
+ theme?: string;
10
+ captureType?: 'fullpage' | 'element' | 'video';
11
+ elementName?: string;
12
+ targetId?: string;
13
+ captureId?: string | null;
14
+ viewportWidth?: number | null;
15
+ viewportHeight?: number | null;
16
+ }
17
+ export declare function insertCostLogs(supabase: SupabaseClient, ctx: CostLogContext, steps: StepUsage[]): Promise<string[]>;
18
+ export declare function updateCostLogCaptureContext(supabase: SupabaseClient, costLogIds: string[], params: {
19
+ captureId?: string | null;
20
+ targetId?: string | null;
21
+ viewportWidth?: number | null;
22
+ viewportHeight?: number | null;
23
+ }): Promise<void>;
24
+ /**
25
+ * Retry resolution for previously failed cost logs.
26
+ * Designed to be called by a reconciliation cron job.
27
+ */
28
+ export declare function reconcileUnresolvedCosts(supabase: SupabaseClient, apiKey: string): Promise<{
29
+ retried: number;
30
+ resolved: number;
31
+ }>;
32
+ export declare function resolveAllCosts(supabase: SupabaseClient, entries: Array<{
33
+ costLogId: string;
34
+ generationId: string | null;
35
+ }>, apiKey: string, serviceClient?: SupabaseClient): Promise<void>;
@@ -0,0 +1,242 @@
1
+ function isFiniteInteger(value) {
2
+ return typeof value === 'number' && Number.isInteger(value);
3
+ }
4
+ function normalizeUsageForCostLog(step, index) {
5
+ const normalizedStepNumber = isFiniteInteger(step.stepNumber) && step.stepNumber >= 0
6
+ ? step.stepNumber
7
+ : index + 1;
8
+ const normalizedStepType = typeof step.stepType === 'string' && step.stepType.trim().length > 0
9
+ ? step.stepType
10
+ : 'agent_iteration';
11
+ const normalizedModelRequested = typeof step.modelRequested === 'string' && step.modelRequested.trim().length > 0
12
+ ? step.modelRequested
13
+ : step.modelUsed ?? 'unknown';
14
+ const normalizedImagesInPrompt = typeof step.imagesInPrompt === 'number' && Number.isFinite(step.imagesInPrompt)
15
+ ? step.imagesInPrompt
16
+ : 0;
17
+ if (normalizedStepNumber !== step.stepNumber
18
+ || normalizedStepType !== step.stepType
19
+ || normalizedModelRequested !== step.modelRequested
20
+ || normalizedImagesInPrompt !== step.imagesInPrompt) {
21
+ console.warn('Normalizing invalid cost log usage entry:', JSON.stringify({
22
+ stepNumber: step.stepNumber ?? null,
23
+ stepType: step.stepType ?? null,
24
+ modelRequested: step.modelRequested ?? null,
25
+ modelUsed: step.modelUsed ?? null,
26
+ fallbackStepNumber: normalizedStepNumber,
27
+ }));
28
+ }
29
+ return {
30
+ ...step,
31
+ stepNumber: normalizedStepNumber,
32
+ stepType: normalizedStepType,
33
+ modelRequested: normalizedModelRequested,
34
+ imagesInPrompt: normalizedImagesInPrompt,
35
+ };
36
+ }
37
+ export async function insertCostLogs(supabase, ctx, steps) {
38
+ if (steps.length === 0)
39
+ return [];
40
+ const rows = steps.map((rawStep, index) => {
41
+ const step = normalizeUsageForCostLog(rawStep, index);
42
+ return {
43
+ id: crypto.randomUUID(),
44
+ run_id: ctx.runId,
45
+ user_id: ctx.userId,
46
+ project_id: ctx.projectId ?? null,
47
+ preset_id: ctx.presetId ?? null,
48
+ capture_id: ctx.captureId ?? null,
49
+ step_number: step.stepNumber,
50
+ step_type: step.stepType,
51
+ model_requested: step.modelRequested,
52
+ model_used: step.modelUsed,
53
+ generation_id: step.generationId,
54
+ prompt_tokens: step.promptTokens,
55
+ completion_tokens: step.completionTokens,
56
+ total_tokens: step.totalTokens,
57
+ cost_resolved: false,
58
+ images_in_prompt: step.imagesInPrompt,
59
+ lang: ctx.lang ?? null,
60
+ theme: ctx.theme ?? null,
61
+ capture_type: ctx.captureType ?? null,
62
+ element_name: ctx.elementName ?? null,
63
+ target_id: ctx.targetId ?? null,
64
+ viewport_width: ctx.viewportWidth ?? null,
65
+ viewport_height: ctx.viewportHeight ?? null,
66
+ system_prompt_chars: step.systemPromptChars ?? null,
67
+ tool_schema_chars: step.toolSchemaChars ?? null,
68
+ user_payload_chars: step.userPayloadChars ?? null,
69
+ accessibility_chars: step.accessibilityChars ?? null,
70
+ interactive_element_count: step.interactiveElementCount ?? null,
71
+ action_history_count: step.actionHistoryCount ?? null,
72
+ elements_chars: step.elementsChars ?? null,
73
+ session_summary_chars: step.sessionSummaryChars ?? null,
74
+ selector_memory_chars: step.selectorMemoryChars ?? null,
75
+ agent_context_chars: step.agentContextChars ?? null,
76
+ profile_validation_status: step.profileValidationStatus ?? null,
77
+ session_profile_source: step.sessionProfileSource ?? null,
78
+ repair_path_used: step.repairPathUsed ?? null,
79
+ evaluator_used: step.evaluatorUsed ?? false,
80
+ cache_read_tokens: step.cacheReadTokens ?? null,
81
+ cache_write_tokens: step.cacheWriteTokens ?? null,
82
+ session_profile_reused: step.sessionProfileReused ?? false,
83
+ action_replay_used: step.actionReplayUsed ?? false,
84
+ };
85
+ });
86
+ try {
87
+ const { error } = await supabase.from('cost_logs').insert(rows);
88
+ if (error) {
89
+ console.error('Failed to insert cost logs:', error.message);
90
+ return [];
91
+ }
92
+ return rows.map((row) => row.id);
93
+ }
94
+ catch (error) {
95
+ console.error('Failed to insert cost logs:', error.message);
96
+ return [];
97
+ }
98
+ }
99
+ export async function updateCostLogCaptureContext(supabase, costLogIds, params) {
100
+ if (costLogIds.length === 0)
101
+ return;
102
+ try {
103
+ const { error } = await supabase
104
+ .from('cost_logs')
105
+ .update({
106
+ capture_id: params.captureId ?? null,
107
+ target_id: params.targetId ?? null,
108
+ viewport_width: params.viewportWidth ?? null,
109
+ viewport_height: params.viewportHeight ?? null,
110
+ })
111
+ .in('id', costLogIds);
112
+ if (error) {
113
+ console.error('Failed to update cost log capture context:', error.message);
114
+ }
115
+ }
116
+ catch (error) {
117
+ console.error('Failed to update cost log capture context:', error.message);
118
+ }
119
+ }
120
+ async function resolveCost(updateClient, generationId, costLogId, apiKey) {
121
+ const delays = [10_000, 20_000, 40_000, 80_000, 160_000];
122
+ for (let attempt = 0; attempt < delays.length; attempt += 1) {
123
+ await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
124
+ // Track resolution attempt (best-effort)
125
+ try {
126
+ await updateClient
127
+ .from('cost_logs')
128
+ .update({
129
+ resolution_attempts: attempt + 1,
130
+ last_resolution_attempt_at: new Date().toISOString(),
131
+ cost_resolution_status: 'pending',
132
+ })
133
+ .eq('id', costLogId);
134
+ }
135
+ catch { /* best-effort tracking */ }
136
+ try {
137
+ const response = await fetch(`https://openrouter.ai/api/v1/generation?id=${generationId}`, { headers: { Authorization: `Bearer ${apiKey}` } });
138
+ if (response.status === 404 && attempt < delays.length - 1) {
139
+ continue;
140
+ }
141
+ if (!response.ok) {
142
+ console.error(`OpenRouter generation lookup failed (${generationId}): HTTP ${response.status}`);
143
+ if (attempt === delays.length - 1) {
144
+ await markCostResolutionFailed(updateClient, costLogId, `HTTP ${response.status}`);
145
+ }
146
+ continue;
147
+ }
148
+ const generation = await response.json();
149
+ const totalCost = generation.data?.total_cost ?? null;
150
+ if (totalCost == null) {
151
+ console.warn(`OpenRouter returned no total_cost for generation ${generationId}`);
152
+ if (attempt === delays.length - 1) {
153
+ await markCostResolutionFailed(updateClient, costLogId, 'no total_cost in response');
154
+ }
155
+ continue;
156
+ }
157
+ const { error } = await updateClient
158
+ .from('cost_logs')
159
+ .update({
160
+ cost_usd: totalCost,
161
+ cost_resolved: true,
162
+ resolved_at: new Date().toISOString(),
163
+ cost_resolution_status: 'resolved',
164
+ })
165
+ .eq('id', costLogId);
166
+ if (error) {
167
+ console.error(`cost_logs update failed for row ${costLogId}:`, error.message);
168
+ }
169
+ return;
170
+ }
171
+ catch (error) {
172
+ if (attempt === delays.length - 1) {
173
+ console.error(`Cost resolution failed for ${generationId}:`, error.message);
174
+ await markCostResolutionFailed(updateClient, costLogId, error.message);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ async function markCostResolutionFailed(updateClient, costLogId, reason) {
180
+ try {
181
+ await updateClient
182
+ .from('cost_logs')
183
+ .update({ cost_resolution_status: 'failed' })
184
+ .eq('id', costLogId);
185
+ console.error(`Cost resolution marked as failed for ${costLogId}: ${reason}`);
186
+ }
187
+ catch {
188
+ // best-effort — don't fail the caller
189
+ }
190
+ }
191
+ /**
192
+ * Retry resolution for previously failed cost logs.
193
+ * Designed to be called by a reconciliation cron job.
194
+ */
195
+ export async function reconcileUnresolvedCosts(supabase, apiKey) {
196
+ const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
197
+ const { data: failedLogs, error } = await supabase
198
+ .from('cost_logs')
199
+ .select('id, generation_id')
200
+ .eq('cost_resolution_status', 'failed')
201
+ .gt('created_at', sevenDaysAgo)
202
+ .not('generation_id', 'is', null)
203
+ .limit(100);
204
+ if (error || !failedLogs?.length) {
205
+ return { retried: 0, resolved: 0 };
206
+ }
207
+ let resolved = 0;
208
+ for (const log of failedLogs) {
209
+ try {
210
+ const response = await fetch(`https://openrouter.ai/api/v1/generation?id=${log.generation_id}`, { headers: { Authorization: `Bearer ${apiKey}` } });
211
+ if (!response.ok)
212
+ continue;
213
+ const generation = await response.json();
214
+ const totalCost = generation.data?.total_cost ?? null;
215
+ if (totalCost == null)
216
+ continue;
217
+ await supabase
218
+ .from('cost_logs')
219
+ .update({
220
+ cost_usd: totalCost,
221
+ cost_resolved: true,
222
+ resolved_at: new Date().toISOString(),
223
+ cost_resolution_status: 'resolved',
224
+ })
225
+ .eq('id', log.id);
226
+ resolved += 1;
227
+ }
228
+ catch {
229
+ // skip this entry, will be retried next run
230
+ }
231
+ }
232
+ return { retried: failedLogs.length, resolved };
233
+ }
234
+ export function resolveAllCosts(supabase, entries, apiKey, serviceClient) {
235
+ const toResolve = entries.filter((entry) => entry.generationId != null);
236
+ if (toResolve.length === 0) {
237
+ return Promise.resolve();
238
+ }
239
+ const updateClient = serviceClient ?? supabase;
240
+ return Promise.allSettled(toResolve.map((entry) => resolveCost(updateClient, entry.generationId, entry.costLogId, apiKey))).then(() => undefined);
241
+ }
242
+ //# sourceMappingURL=cost-logging.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Cost resolution monitoring — exposes metrics for alerting
3
+ * when the failure rate of OpenRouter cost lookups exceeds thresholds.
4
+ */
5
+ import type { SupabaseClient } from '@supabase/supabase-js';
6
+ export interface CostResolutionStats {
7
+ /** Number of cost logs with status 'failed' in the last hour. */
8
+ failedLastHour: number;
9
+ /** Total cost logs created in the last hour. */
10
+ totalLastHour: number;
11
+ /** Failure rate as a percentage (0-100). */
12
+ failureRate: number;
13
+ /** Number of cost logs still pending (older than 5 minutes). */
14
+ stalePending: number;
15
+ }
16
+ export declare function getCostResolutionStats(supabase: SupabaseClient): Promise<CostResolutionStats>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Cost resolution monitoring — exposes metrics for alerting
3
+ * when the failure rate of OpenRouter cost lookups exceeds thresholds.
4
+ */
5
+ export async function getCostResolutionStats(supabase) {
6
+ const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
7
+ const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
8
+ const [failedResult, totalResult, staleResult] = await Promise.all([
9
+ supabase
10
+ .from('cost_logs')
11
+ .select('*', { count: 'exact', head: true })
12
+ .eq('cost_resolution_status', 'failed')
13
+ .gt('created_at', oneHourAgo),
14
+ supabase
15
+ .from('cost_logs')
16
+ .select('*', { count: 'exact', head: true })
17
+ .gt('created_at', oneHourAgo)
18
+ .not('generation_id', 'is', null),
19
+ supabase
20
+ .from('cost_logs')
21
+ .select('*', { count: 'exact', head: true })
22
+ .eq('cost_resolution_status', 'pending')
23
+ .lt('created_at', fiveMinutesAgo)
24
+ .not('generation_id', 'is', null),
25
+ ]);
26
+ const failedLastHour = failedResult.count ?? 0;
27
+ const totalLastHour = totalResult.count ?? 0;
28
+ const stalePending = staleResult.count ?? 0;
29
+ const failureRate = totalLastHour > 0
30
+ ? Math.round((failedLastHour / totalLastHour) * 10000) / 100
31
+ : 0;
32
+ return { failedLastHour, totalLastHour, failureRate, stalePending };
33
+ }
34
+ //# sourceMappingURL=cost-resolution-monitor.js.map
@@ -0,0 +1,5 @@
1
+ import type { LoginCredentials } from "./types.js";
2
+ export declare function resolveCredentialTemplates(value: string | undefined, credentials?: LoginCredentials): string | undefined;
3
+ export declare function ensureNoCredentialTemplate(field: string, value: string | undefined): void;
4
+ export declare function sanitizeCredentialParams(params: Record<string, unknown>, credentials?: LoginCredentials): Record<string, unknown>;
5
+ export declare function resolveActionCredentialArgs(action: string, args: Record<string, unknown>, credentials?: LoginCredentials): Record<string, unknown>;