autokap 1.0.7 → 1.0.10

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 (301) hide show
  1. package/assets/cursors/macos.svg +4 -0
  2. package/assets/cursors/windows.svg +15 -0
  3. package/assets/skill/OPCODE-REFERENCE.md +635 -0
  4. package/assets/skill/README.md +39 -0
  5. package/assets/skill/SKILL.md +454 -468
  6. package/assets/skill/STUDIO-SKILL.md +476 -0
  7. package/assets/skill/references/examples.md +104 -0
  8. package/assets/skill/references/interactive-demo.md +225 -0
  9. package/assets/skill/references/mock-data.md +178 -0
  10. package/dist/action-verifier.d.ts +29 -0
  11. package/dist/action-verifier.js +133 -0
  12. package/dist/agent-action-recovery.d.ts +45 -0
  13. package/dist/agent-action-recovery.js +370 -0
  14. package/dist/agent-message-utils.d.ts +21 -0
  15. package/dist/agent-message-utils.js +77 -0
  16. package/dist/agent-url-utils.d.ts +30 -0
  17. package/dist/agent-url-utils.js +138 -0
  18. package/dist/agent.d.ts +92 -8
  19. package/dist/agent.js +2936 -781
  20. package/dist/ak-tree.d.ts +39 -0
  21. package/dist/ak-tree.js +368 -0
  22. package/dist/alt-text.d.ts +26 -0
  23. package/dist/alt-text.js +55 -0
  24. package/dist/auth-capture.d.ts +17 -0
  25. package/dist/auth-capture.js +197 -0
  26. package/dist/benchmark.d.ts +59 -0
  27. package/dist/benchmark.js +135 -0
  28. package/dist/billing-operation-logging.d.ts +3 -1
  29. package/dist/billing-operation-logging.js +4 -0
  30. package/dist/browser-bar.d.ts +14 -6
  31. package/dist/browser-bar.js +145 -8
  32. package/dist/browser-pool.d.ts +7 -0
  33. package/dist/browser-pool.js +15 -5
  34. package/dist/browser-utils.d.ts +31 -0
  35. package/dist/browser-utils.js +97 -0
  36. package/dist/browser.d.ts +61 -11
  37. package/dist/browser.js +1513 -59
  38. package/dist/capture-alt-text.js +2 -1
  39. package/dist/capture-encryption.d.ts +3 -1
  40. package/dist/capture-encryption.js +21 -6
  41. package/dist/capture-language-preflight.js +14 -0
  42. package/dist/capture-llm-page-identity.js +22 -10
  43. package/dist/capture-page-identity.d.ts +5 -7
  44. package/dist/capture-page-identity.js +211 -78
  45. package/dist/capture-preset-credentials.d.ts +50 -0
  46. package/dist/capture-preset-credentials.js +127 -0
  47. package/dist/capture-request-plan.d.ts +2 -2
  48. package/dist/capture-request-plan.js +64 -16
  49. package/dist/capture-run-optimizer.js +48 -33
  50. package/dist/capture-selector-memory.d.ts +5 -0
  51. package/dist/capture-selector-memory.js +18 -0
  52. package/dist/capture-strategy.d.ts +36 -0
  53. package/dist/capture-strategy.js +96 -0
  54. package/dist/capture-studio-sync.d.ts +1 -0
  55. package/dist/capture-studio-sync.js +9 -3
  56. package/dist/capture-surface-contract.d.ts +36 -0
  57. package/dist/capture-surface-contract.js +299 -0
  58. package/dist/capture-transition-engine.d.ts +28 -0
  59. package/dist/capture-transition-engine.js +292 -0
  60. package/dist/capture-variant-state.d.ts +2 -0
  61. package/dist/capture-variant-state.js +26 -0
  62. package/dist/capture-verification.d.ts +35 -0
  63. package/dist/capture-verification.js +95 -0
  64. package/dist/capture-viewport-lock.d.ts +48 -0
  65. package/dist/capture-viewport-lock.js +74 -0
  66. package/dist/circuit-breaker.d.ts +42 -0
  67. package/dist/circuit-breaker.js +119 -0
  68. package/dist/cli-config.d.ts +9 -1
  69. package/dist/cli-config.js +112 -7
  70. package/dist/cli-contract.d.ts +19 -0
  71. package/dist/cli-contract.js +173 -0
  72. package/dist/cli-runner-local.d.ts +12 -0
  73. package/dist/cli-runner-local.js +115 -0
  74. package/dist/cli-runner.d.ts +34 -0
  75. package/dist/cli-runner.js +580 -0
  76. package/dist/cli-utils.d.ts +0 -1
  77. package/dist/cli-utils.js +2 -5
  78. package/dist/cli.js +1011 -267
  79. package/dist/clip-begin-frame-recorder.d.ts +44 -0
  80. package/dist/clip-begin-frame-recorder.js +250 -0
  81. package/dist/clip-capture-backend.d.ts +25 -0
  82. package/dist/clip-capture-backend.js +189 -0
  83. package/dist/clip-capture-loop.d.ts +61 -0
  84. package/dist/clip-capture-loop.js +111 -0
  85. package/dist/clip-frame-recorder.d.ts +63 -0
  86. package/dist/clip-frame-recorder.js +305 -0
  87. package/dist/clip-orchestrator.js +9 -2
  88. package/dist/clip-postprocess.d.ts +31 -2
  89. package/dist/clip-postprocess.js +194 -68
  90. package/dist/clip-runtime.d.ts +18 -0
  91. package/dist/clip-runtime.js +67 -0
  92. package/dist/clip-scale.d.ts +10 -0
  93. package/dist/clip-scale.js +21 -0
  94. package/dist/clip-screencast-recorder.d.ts +42 -0
  95. package/dist/clip-screencast-recorder.js +242 -0
  96. package/dist/clip-sidecar.d.ts +54 -0
  97. package/dist/clip-sidecar.js +208 -0
  98. package/dist/cookie-dismiss.d.ts +2 -0
  99. package/dist/cookie-dismiss.js +48 -13
  100. package/dist/cost-logging.d.ts +8 -0
  101. package/dist/cost-logging.js +160 -46
  102. package/dist/cost-resolution-monitor.d.ts +16 -0
  103. package/dist/cost-resolution-monitor.js +34 -0
  104. package/dist/credential-templates.js +2 -2
  105. package/dist/cursor-overlay-script.d.ts +6 -0
  106. package/dist/cursor-overlay-script.js +169 -0
  107. package/dist/dom-css-purger.d.ts +65 -0
  108. package/dist/dom-css-purger.js +333 -0
  109. package/dist/dom-font-inliner.d.ts +45 -0
  110. package/dist/dom-font-inliner.js +148 -0
  111. package/dist/dom-patch-resolver.d.ts +52 -0
  112. package/dist/dom-patch-resolver.js +242 -0
  113. package/dist/dom-serializer.d.ts +82 -0
  114. package/dist/dom-serializer.js +378 -0
  115. package/dist/element-capture.d.ts +1 -41
  116. package/dist/element-capture.js +202 -446
  117. package/dist/env-validation.d.ts +5 -0
  118. package/dist/env-validation.js +63 -0
  119. package/dist/execution-schema.d.ts +4753 -0
  120. package/dist/execution-schema.js +563 -0
  121. package/dist/execution-types.d.ts +936 -0
  122. package/dist/execution-types.js +66 -0
  123. package/dist/fonts-loader.d.ts +14 -0
  124. package/dist/fonts-loader.js +55 -0
  125. package/dist/hybrid-navigator.js +12 -12
  126. package/dist/index.d.ts +11 -6
  127. package/dist/index.js +11 -4
  128. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  129. package/dist/legacy/agent-action-recovery.js +370 -0
  130. package/dist/legacy/agent-message-utils.d.ts +21 -0
  131. package/dist/legacy/agent-message-utils.js +77 -0
  132. package/dist/legacy/agent-url-utils.d.ts +30 -0
  133. package/dist/legacy/agent-url-utils.js +138 -0
  134. package/dist/legacy/agent.d.ts +226 -0
  135. package/dist/legacy/agent.js +6666 -0
  136. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  137. package/dist/legacy/clip-orchestrator.js +957 -0
  138. package/dist/legacy/credential-templates.d.ts +5 -0
  139. package/dist/legacy/credential-templates.js +60 -0
  140. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  141. package/dist/legacy/hybrid-navigator.js +468 -0
  142. package/dist/legacy/llm-usage.d.ts +17 -0
  143. package/dist/legacy/llm-usage.js +45 -0
  144. package/dist/legacy/prompt-cache.d.ts +10 -0
  145. package/dist/legacy/prompt-cache.js +24 -0
  146. package/dist/legacy/prompts.d.ts +175 -0
  147. package/dist/legacy/prompts.js +1038 -0
  148. package/dist/legacy/tools.d.ts +4 -0
  149. package/dist/legacy/tools.js +216 -0
  150. package/dist/legacy/video-agent.d.ts +143 -0
  151. package/dist/legacy/video-agent.js +4788 -0
  152. package/dist/legacy/video-observation.d.ts +36 -0
  153. package/dist/legacy/video-observation.js +192 -0
  154. package/dist/legacy/video-planner.d.ts +12 -0
  155. package/dist/legacy/video-planner.js +501 -0
  156. package/dist/legacy/video-prompts.d.ts +37 -0
  157. package/dist/legacy/video-prompts.js +569 -0
  158. package/dist/legacy/video-tools.d.ts +3 -0
  159. package/dist/legacy/video-tools.js +59 -0
  160. package/dist/legacy/video-variant-state.d.ts +29 -0
  161. package/dist/legacy/video-variant-state.js +80 -0
  162. package/dist/legacy/vision-model.d.ts +17 -0
  163. package/dist/legacy/vision-model.js +74 -0
  164. package/dist/llm-healer.d.ts +55 -0
  165. package/dist/llm-healer.js +213 -0
  166. package/dist/llm-provider.d.ts +29 -0
  167. package/dist/llm-provider.js +83 -0
  168. package/dist/logger.d.ts +6 -2
  169. package/dist/logger.js +15 -1
  170. package/dist/mockup-html.js +35 -25
  171. package/dist/mockup.d.ts +95 -2
  172. package/dist/mockup.js +427 -166
  173. package/dist/mouse-animation.d.ts +2 -2
  174. package/dist/mouse-animation.js +34 -20
  175. package/dist/opcode-actions.d.ts +42 -0
  176. package/dist/opcode-actions.js +524 -0
  177. package/dist/opcode-runner.d.ts +51 -0
  178. package/dist/opcode-runner.js +779 -0
  179. package/dist/openrouter-client.d.ts +40 -0
  180. package/dist/openrouter-client.js +16 -0
  181. package/dist/overlay-engine.d.ts +24 -0
  182. package/dist/overlay-engine.js +176 -0
  183. package/dist/postcondition.d.ts +16 -0
  184. package/dist/postcondition.js +269 -0
  185. package/dist/program-patcher.d.ts +25 -0
  186. package/dist/program-patcher.js +44 -0
  187. package/dist/program-signing.d.ts +1094 -0
  188. package/dist/program-signing.js +140 -0
  189. package/dist/prompts.d.ts +13 -5
  190. package/dist/prompts.js +224 -351
  191. package/dist/provider-config.d.ts +17 -0
  192. package/dist/provider-config.js +42 -0
  193. package/dist/recovery-chain.d.ts +37 -0
  194. package/dist/recovery-chain.js +374 -0
  195. package/dist/remote-browser.d.ts +28 -4
  196. package/dist/remote-browser.js +60 -5
  197. package/dist/safari-browser-bar.d.ts +15 -0
  198. package/dist/safari-browser-bar.js +95 -0
  199. package/dist/safari-toolbar-asset.d.ts +15 -0
  200. package/dist/safari-toolbar-asset.js +12 -0
  201. package/dist/security.d.ts +2 -1
  202. package/dist/security.js +49 -10
  203. package/dist/selector-resolver.d.ts +34 -0
  204. package/dist/selector-resolver.js +181 -0
  205. package/dist/semantic-resolver.d.ts +35 -0
  206. package/dist/semantic-resolver.js +161 -0
  207. package/dist/server-capture-runtime.d.ts +5 -3
  208. package/dist/server-capture-runtime.js +42 -95
  209. package/dist/server-credit-usage.d.ts +2 -2
  210. package/dist/server-project-webhooks.d.ts +15 -1
  211. package/dist/server-project-webhooks.js +34 -8
  212. package/dist/server-screenshot-watermark.js +27 -5
  213. package/dist/session-profile.js +164 -1
  214. package/dist/sf-pro-symbols.d.ts +1 -0
  215. package/dist/sf-pro-symbols.js +55 -0
  216. package/dist/skill-packaging.d.ts +28 -0
  217. package/dist/skill-packaging.js +169 -0
  218. package/dist/smart-wait.d.ts +27 -0
  219. package/dist/smart-wait.js +81 -0
  220. package/dist/status-bar-render.d.ts +20 -0
  221. package/dist/status-bar-render.js +410 -0
  222. package/dist/status-bar.d.ts +9 -0
  223. package/dist/status-bar.js +298 -14
  224. package/dist/svg-browser-bar.d.ts +33 -0
  225. package/dist/svg-browser-bar.js +206 -0
  226. package/dist/svg-status-bar.d.ts +36 -0
  227. package/dist/svg-status-bar.js +597 -0
  228. package/dist/svg-text.d.ts +61 -0
  229. package/dist/svg-text.js +118 -0
  230. package/dist/tools.js +89 -451
  231. package/dist/types.d.ts +248 -7
  232. package/dist/types.js +23 -1
  233. package/dist/v2/action-verifier.d.ts +29 -0
  234. package/dist/v2/action-verifier.js +133 -0
  235. package/dist/v2/alt-text.d.ts +26 -0
  236. package/dist/v2/alt-text.js +55 -0
  237. package/dist/v2/benchmark.d.ts +59 -0
  238. package/dist/v2/benchmark.js +135 -0
  239. package/dist/v2/capture-strategy.d.ts +30 -0
  240. package/dist/v2/capture-strategy.js +67 -0
  241. package/dist/v2/capture-verification.d.ts +35 -0
  242. package/dist/v2/capture-verification.js +95 -0
  243. package/dist/v2/circuit-breaker.d.ts +42 -0
  244. package/dist/v2/circuit-breaker.js +119 -0
  245. package/dist/v2/cli-runner-local.d.ts +11 -0
  246. package/dist/v2/cli-runner-local.js +91 -0
  247. package/dist/v2/cli-runner.d.ts +34 -0
  248. package/dist/v2/cli-runner.js +300 -0
  249. package/dist/v2/compiler-prompts.d.ts +27 -0
  250. package/dist/v2/compiler-prompts.js +123 -0
  251. package/dist/v2/compiler.d.ts +37 -0
  252. package/dist/v2/compiler.js +147 -0
  253. package/dist/v2/explorer.d.ts +41 -0
  254. package/dist/v2/explorer.js +56 -0
  255. package/dist/v2/index.d.ts +37 -0
  256. package/dist/v2/index.js +31 -0
  257. package/dist/v2/llm-healer.d.ts +62 -0
  258. package/dist/v2/llm-healer.js +166 -0
  259. package/dist/v2/llm-provider.d.ts +29 -0
  260. package/dist/v2/llm-provider.js +80 -0
  261. package/dist/v2/opcode-runner.d.ts +47 -0
  262. package/dist/v2/opcode-runner.js +634 -0
  263. package/dist/v2/overlay-engine.d.ts +24 -0
  264. package/dist/v2/overlay-engine.js +150 -0
  265. package/dist/v2/postcondition.d.ts +16 -0
  266. package/dist/v2/postcondition.js +249 -0
  267. package/dist/v2/program-patcher.d.ts +25 -0
  268. package/dist/v2/program-patcher.js +44 -0
  269. package/dist/v2/recovery-chain.d.ts +30 -0
  270. package/dist/v2/recovery-chain.js +368 -0
  271. package/dist/v2/schema.d.ts +2580 -0
  272. package/dist/v2/schema.js +295 -0
  273. package/dist/v2/selector-resolver.d.ts +34 -0
  274. package/dist/v2/selector-resolver.js +181 -0
  275. package/dist/v2/semantic-resolver.d.ts +35 -0
  276. package/dist/v2/semantic-resolver.js +161 -0
  277. package/dist/v2/smart-wait.d.ts +27 -0
  278. package/dist/v2/smart-wait.js +81 -0
  279. package/dist/v2/types.d.ts +444 -0
  280. package/dist/v2/types.js +19 -0
  281. package/dist/v2/web-playwright-local.d.ts +69 -0
  282. package/dist/v2/web-playwright-local.js +392 -0
  283. package/dist/version.d.ts +1 -0
  284. package/dist/version.js +5 -0
  285. package/dist/video-agent.js +18 -13
  286. package/dist/video-planner.js +2 -1
  287. package/dist/video-prompts.js +3 -3
  288. package/dist/web-playwright-local.d.ts +156 -0
  289. package/dist/web-playwright-local.js +989 -0
  290. package/dist/ws-auth.js +4 -1
  291. package/dist/ws-broadcast.d.ts +34 -0
  292. package/dist/ws-broadcast.js +85 -0
  293. package/dist/ws-connection-limits.d.ts +12 -0
  294. package/dist/ws-connection-limits.js +44 -0
  295. package/dist/ws-handler-utils.d.ts +32 -0
  296. package/dist/ws-handler-utils.js +139 -0
  297. package/dist/ws-handler.js +294 -164
  298. package/dist/ws-metrics-server.d.ts +9 -0
  299. package/dist/ws-metrics-server.js +31 -0
  300. package/dist/ws-server.js +41 -1
  301. package/package.json +62 -35
@@ -0,0 +1,242 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { postProcessClipFrames } from './clip-postprocess.js';
5
+ import { buildCdpViewportClip } from './clip-frame-recorder.js';
6
+ import { resolvePhysicalCaptureSize } from './clip-scale.js';
7
+ const DEFAULT_CLIP_SOURCE_FPS = 30;
8
+ const DEFAULT_CLIP_OUTPUT_FPS = 30;
9
+ const SCREENCAST_FRAME_FORMAT = 'jpeg';
10
+ const SCREENCAST_FRAME_QUALITY = 92;
11
+ const MAX_PENDING_FRAME_WRITES = 8;
12
+ export class CdpScreencastClipRecorder {
13
+ page;
14
+ options;
15
+ sourceFps;
16
+ outputFps;
17
+ workingDirPromise;
18
+ frames = [];
19
+ startedAt = Date.now();
20
+ finalResult = null;
21
+ frameDimensions = null;
22
+ pendingWriteError = null;
23
+ pendingWrites = new Set();
24
+ cdpSession = null;
25
+ stopRequested = false;
26
+ started = false;
27
+ constructor(page, options) {
28
+ this.page = page;
29
+ this.options = options;
30
+ this.sourceFps = Math.max(1, Math.round(options.sourceFps ?? DEFAULT_CLIP_SOURCE_FPS));
31
+ this.outputFps = Math.max(1, Math.round(options.outputFps ?? DEFAULT_CLIP_OUTPUT_FPS));
32
+ this.workingDirPromise = options.baseDir
33
+ ? Promise.resolve(options.baseDir)
34
+ : fs.mkdtemp(path.join(os.tmpdir(), 'autokap-clip-screencast-'));
35
+ this.frameDimensions = resolvePhysicalCaptureSize(options.viewport, options.effectiveScale)
36
+ ?? {
37
+ width: Math.max(2, Math.round(options.viewport.width)) & ~1,
38
+ height: Math.max(2, Math.round(options.viewport.height)) & ~1,
39
+ };
40
+ }
41
+ async start() {
42
+ const workingDir = await this.workingDirPromise;
43
+ await fs.mkdir(path.join(workingDir, 'frames'), { recursive: true });
44
+ const context = this.page.context();
45
+ if (typeof context.newCDPSession !== 'function') {
46
+ throw new Error('CDP screencast is not available on this browser context');
47
+ }
48
+ this.cdpSession = await context.newCDPSession(this.page);
49
+ this.cdpSession.on('Page.screencastFrame', this.handleScreencastFrame);
50
+ await this.cdpSession.send('Page.enable').catch(() => undefined);
51
+ await this.captureInitialFrame();
52
+ await this.cdpSession.send('Page.startScreencast', {
53
+ format: SCREENCAST_FRAME_FORMAT,
54
+ quality: SCREENCAST_FRAME_QUALITY,
55
+ maxWidth: this.frameDimensions?.width,
56
+ maxHeight: this.frameDimensions?.height,
57
+ everyNthFrame: 1,
58
+ });
59
+ this.started = true;
60
+ }
61
+ async stop() {
62
+ if (this.finalResult) {
63
+ return this.finalResult;
64
+ }
65
+ if (!this.started || !this.cdpSession) {
66
+ throw new Error('CDP screencast recording was not started');
67
+ }
68
+ this.stopRequested = true;
69
+ const stopAt = Date.now();
70
+ try {
71
+ await this.cdpSession.send('Page.stopScreencast').catch(() => undefined);
72
+ await sleep(100);
73
+ await this.flushPendingWrites();
74
+ if (this.frames.length === 0) {
75
+ throw this.pendingWriteError ?? new Error('clip screencast ended before any frame was recorded');
76
+ }
77
+ if (this.pendingWriteError) {
78
+ throw this.pendingWriteError;
79
+ }
80
+ const workingDir = await this.workingDirPromise;
81
+ const outputDimensions = this.frameDimensions
82
+ ?? resolvePhysicalCaptureSize(this.options.viewport, this.options.effectiveScale)
83
+ ?? this.options.viewport;
84
+ const frameEntries = buildFrameEntries(this.frames, stopAt, this.sourceFps, this.outputFps);
85
+ const actualSourceFps = resolveActualSourceFps(this.frames, stopAt);
86
+ const processed = await postProcessClipFrames(frameEntries, workingDir, 'clip', {
87
+ ...this.options.clipOptions,
88
+ mp4Width: outputDimensions.width,
89
+ mp4Height: outputDimensions.height,
90
+ });
91
+ if (!processed.mp4Path || !processed.thumbnailPath) {
92
+ throw new Error('clip packaging failed: missing MP4 or thumbnail output');
93
+ }
94
+ const [mp4Buffer, gifBuffer, thumbnailBuffer] = await Promise.all([
95
+ fs.readFile(processed.mp4Path),
96
+ processed.gifPath ? fs.readFile(processed.gifPath) : Promise.resolve(null),
97
+ fs.readFile(processed.thumbnailPath),
98
+ ]);
99
+ const clipPackage = {
100
+ mp4: {
101
+ buffer: mp4Buffer,
102
+ mimeType: 'video/mp4',
103
+ },
104
+ ...(gifBuffer
105
+ ? {
106
+ gif: {
107
+ buffer: gifBuffer,
108
+ mimeType: 'image/gif',
109
+ },
110
+ }
111
+ : {}),
112
+ thumbnail: {
113
+ buffer: thumbnailBuffer,
114
+ mimeType: 'image/png',
115
+ },
116
+ captureMethod: 'cdp_screencast',
117
+ requestedScale: this.options.requestedScale,
118
+ effectiveScale: this.options.effectiveScale,
119
+ sourceFps: this.sourceFps,
120
+ actualSourceFps,
121
+ outputFps: this.outputFps,
122
+ frameCount: this.frames.length,
123
+ dimensions: outputDimensions,
124
+ };
125
+ this.finalResult = {
126
+ buffer: mp4Buffer,
127
+ mimeType: 'video/mp4',
128
+ durationMs: processed.durationMs,
129
+ trimStartMs: 0,
130
+ clipPackage,
131
+ };
132
+ return this.finalResult;
133
+ }
134
+ finally {
135
+ await this.cleanup();
136
+ }
137
+ }
138
+ async abort() {
139
+ this.stopRequested = true;
140
+ await this.cleanup();
141
+ }
142
+ async captureInitialFrame() {
143
+ const session = this.cdpSession;
144
+ if (!session) {
145
+ return;
146
+ }
147
+ const scrollOffset = await this.page.evaluate(() => ({
148
+ x: window.scrollX || window.pageXOffset || 0,
149
+ y: window.scrollY || window.pageYOffset || 0,
150
+ })).catch(() => ({ x: 0, y: 0 }));
151
+ const screenshot = await session.send('Page.captureScreenshot', {
152
+ format: SCREENCAST_FRAME_FORMAT,
153
+ quality: SCREENCAST_FRAME_QUALITY,
154
+ clip: buildCdpViewportClip(this.options.viewport, this.options.effectiveScale, scrollOffset),
155
+ }).catch(() => null);
156
+ if (!screenshot?.data) {
157
+ return;
158
+ }
159
+ await this.consumeFrame(Buffer.from(screenshot.data, 'base64'), this.startedAt);
160
+ }
161
+ handleScreencastFrame = (payload) => {
162
+ const session = this.cdpSession;
163
+ if (!session || this.stopRequested) {
164
+ return;
165
+ }
166
+ const ack = session.send('Page.screencastFrameAck', { sessionId: payload.sessionId }).catch(() => undefined);
167
+ void ack;
168
+ void this.consumeFrame(Buffer.from(payload.data, 'base64'), Date.now());
169
+ };
170
+ async consumeFrame(frameBuffer, capturedAt) {
171
+ if (this.pendingWriteError) {
172
+ return;
173
+ }
174
+ const workingDir = await this.workingDirPromise;
175
+ const frameIndex = this.frames.length;
176
+ const framePath = path.join(workingDir, 'frames', `frame-${String(frameIndex).padStart(6, '0')}.jpg`);
177
+ this.frames.push({ path: framePath, capturedAt });
178
+ await this.queueFrameWrite(framePath, frameBuffer);
179
+ }
180
+ async queueFrameWrite(framePath, frameBuffer) {
181
+ await this.waitForPendingWriteCapacity();
182
+ if (this.pendingWriteError) {
183
+ throw this.pendingWriteError;
184
+ }
185
+ let writePromise;
186
+ writePromise = fs.writeFile(framePath, frameBuffer)
187
+ .catch((error) => {
188
+ this.pendingWriteError = error instanceof Error ? error : new Error(String(error));
189
+ throw this.pendingWriteError;
190
+ })
191
+ .finally(() => {
192
+ this.pendingWrites.delete(writePromise);
193
+ });
194
+ this.pendingWrites.add(writePromise);
195
+ }
196
+ async waitForPendingWriteCapacity() {
197
+ while (this.pendingWrites.size >= MAX_PENDING_FRAME_WRITES) {
198
+ await Promise.race(this.pendingWrites);
199
+ if (this.pendingWriteError) {
200
+ throw this.pendingWriteError;
201
+ }
202
+ }
203
+ }
204
+ async flushPendingWrites() {
205
+ while (this.pendingWrites.size > 0) {
206
+ await Promise.allSettled(Array.from(this.pendingWrites));
207
+ }
208
+ }
209
+ async cleanup() {
210
+ const workingDir = await this.workingDirPromise;
211
+ this.cdpSession?.off?.('Page.screencastFrame', this.handleScreencastFrame);
212
+ await this.cdpSession?.detach().catch(() => undefined);
213
+ this.cdpSession = null;
214
+ this.started = false;
215
+ await fs.rm(workingDir, { recursive: true, force: true }).catch(() => undefined);
216
+ }
217
+ }
218
+ function buildFrameEntries(frames, stopAt, sourceFps, outputFps) {
219
+ const entries = [];
220
+ const minFrameDurationMs = Math.max(1, Math.min(Math.round(1000 / sourceFps), Math.round(1000 / outputFps)));
221
+ for (let index = 0; index < frames.length; index += 1) {
222
+ const frame = frames[index];
223
+ const nextTimestamp = frames[index + 1]?.capturedAt ?? stopAt;
224
+ entries.push({
225
+ path: frame.path,
226
+ durationMs: Math.max(minFrameDurationMs, nextTimestamp - frame.capturedAt),
227
+ });
228
+ }
229
+ return entries;
230
+ }
231
+ function resolveActualSourceFps(frames, stopAt) {
232
+ if (frames.length <= 1) {
233
+ return frames.length;
234
+ }
235
+ const firstTimestamp = frames[0].capturedAt;
236
+ const durationSec = Math.max(0.001, (stopAt - firstTimestamp) / 1000);
237
+ return Math.round((frames.length / durationSec) * 100) / 100;
238
+ }
239
+ function sleep(ms) {
240
+ return new Promise((resolve) => setTimeout(resolve, ms));
241
+ }
242
+ //# sourceMappingURL=clip-screencast-recorder.js.map
@@ -0,0 +1,54 @@
1
+ export interface ClipSidecarProbeResult {
2
+ supported: boolean;
3
+ supportsHeadless: boolean;
4
+ permissionGranted?: boolean;
5
+ executablePath?: string;
6
+ version?: string;
7
+ backend?: string;
8
+ reason?: string;
9
+ }
10
+ export interface NativeClipSidecarStartParams {
11
+ targetId: string;
12
+ outputPath: string;
13
+ viewport: {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ effectiveScale: number;
18
+ outputWidth: number;
19
+ outputHeight: number;
20
+ targetFps: number;
21
+ includeCursor: boolean;
22
+ headed: boolean;
23
+ windowTitle: string;
24
+ cropRect?: {
25
+ x: number;
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ };
30
+ }
31
+ export interface NativeClipSidecarStopResult {
32
+ mp4Path: string;
33
+ actualSourceFps?: number;
34
+ frameCount?: number;
35
+ width?: number;
36
+ height?: number;
37
+ durationMs?: number;
38
+ }
39
+ interface NativeClipSidecarSessionOptions {
40
+ executablePath: string;
41
+ }
42
+ export declare class NativeClipSidecarSession {
43
+ private readonly child;
44
+ private readonly pending;
45
+ private stderr;
46
+ private closed;
47
+ constructor(options: NativeClipSidecarSessionOptions);
48
+ request<T>(command: string, payload?: object): Promise<T>;
49
+ close(): Promise<void>;
50
+ private handleEnvelope;
51
+ private rejectAll;
52
+ }
53
+ export declare function probeNativeClipSidecar(): Promise<ClipSidecarProbeResult>;
54
+ export {};
@@ -0,0 +1,208 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { getConfigDir } from './cli-config.js';
6
+ const SIDECAR_BINARY_ENV_VAR = 'AUTOKAP_CLIP_SIDECAR_PATH';
7
+ const SIDECAR_BASE_URL_ENV_VAR = 'AUTOKAP_CLIP_SIDECAR_BASE_URL';
8
+ const DEFAULT_SIDECAR_BASE_URL = 'https://github.com/mangue-dev/screenshot-agent/releases/latest/download';
9
+ const SIDECAR_NAME = 'autokap-clip-capture-sidecar';
10
+ function resolveSidecarAssetSpec() {
11
+ const ext = process.platform === 'win32' ? '.exe' : '';
12
+ const platform = process.platform;
13
+ const arch = process.arch;
14
+ if (!['darwin', 'linux', 'win32'].includes(platform)) {
15
+ return null;
16
+ }
17
+ if (!['arm64', 'x64'].includes(arch)) {
18
+ return null;
19
+ }
20
+ const fileName = `${SIDECAR_NAME}-${platform}-${arch}${ext}`;
21
+ return {
22
+ fileName,
23
+ executableName: `${SIDECAR_NAME}${ext}`,
24
+ };
25
+ }
26
+ async function fileExists(filePath) {
27
+ try {
28
+ await fs.stat(filePath);
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ async function ensureDownloadedSidecar() {
36
+ const explicitPath = process.env[SIDECAR_BINARY_ENV_VAR]?.trim();
37
+ if (explicitPath) {
38
+ return explicitPath;
39
+ }
40
+ const workspaceBinary = path.resolve(process.cwd(), 'native', 'clip-capture-sidecar', 'target', 'release', resolveSidecarAssetSpec()?.executableName ?? SIDECAR_NAME);
41
+ if (await fileExists(workspaceBinary)) {
42
+ return workspaceBinary;
43
+ }
44
+ const asset = resolveSidecarAssetSpec();
45
+ if (!asset) {
46
+ throw new Error(`native clip sidecar is not available on ${process.platform}/${process.arch}`);
47
+ }
48
+ const sidecarDir = path.join(getConfigDir(), 'sidecars', 'clip-capture');
49
+ const binaryPath = path.join(sidecarDir, asset.executableName);
50
+ if (await fileExists(binaryPath)) {
51
+ return binaryPath;
52
+ }
53
+ const baseUrl = process.env[SIDECAR_BASE_URL_ENV_VAR]?.trim() || DEFAULT_SIDECAR_BASE_URL;
54
+ const response = await fetch(`${baseUrl.replace(/\/+$/, '')}/${asset.fileName}`);
55
+ if (!response.ok) {
56
+ throw new Error(`failed to download native clip sidecar (${response.status})`);
57
+ }
58
+ const bytes = new Uint8Array(await response.arrayBuffer());
59
+ await fs.mkdir(sidecarDir, { recursive: true });
60
+ await fs.writeFile(binaryPath, bytes);
61
+ if (process.platform !== 'win32') {
62
+ await fs.chmod(binaryPath, 0o755);
63
+ }
64
+ return binaryPath;
65
+ }
66
+ function parseLineDelimitedJson(stream, onMessage, onInvalid) {
67
+ let buffer = '';
68
+ stream.on('data', (chunk) => {
69
+ buffer += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
70
+ while (true) {
71
+ const newlineIndex = buffer.indexOf('\n');
72
+ if (newlineIndex === -1) {
73
+ break;
74
+ }
75
+ const line = buffer.slice(0, newlineIndex).trim();
76
+ buffer = buffer.slice(newlineIndex + 1);
77
+ if (!line) {
78
+ continue;
79
+ }
80
+ try {
81
+ onMessage(JSON.parse(line));
82
+ }
83
+ catch {
84
+ onInvalid(line);
85
+ }
86
+ }
87
+ });
88
+ }
89
+ export class NativeClipSidecarSession {
90
+ child;
91
+ pending = new Map();
92
+ stderr = '';
93
+ closed = false;
94
+ constructor(options) {
95
+ this.child = spawn(options.executablePath, [], {
96
+ stdio: ['pipe', 'pipe', 'pipe'],
97
+ windowsHide: true,
98
+ });
99
+ parseLineDelimitedJson(this.child.stdout, (payload) => this.handleEnvelope(payload), (raw) => this.rejectAll(new Error(`native sidecar emitted invalid JSON: ${raw}`)));
100
+ this.child.stderr.on('data', (chunk) => {
101
+ this.stderr += typeof chunk === 'string' ? chunk : chunk.toString('utf8');
102
+ });
103
+ this.child.on('error', (error) => {
104
+ this.rejectAll(error instanceof Error ? error : new Error(String(error)));
105
+ });
106
+ this.child.on('exit', (code, signal) => {
107
+ this.closed = true;
108
+ if (this.pending.size === 0) {
109
+ return;
110
+ }
111
+ const suffix = this.stderr.trim() ? ` — ${this.stderr.trim()}` : '';
112
+ this.rejectAll(new Error(`native sidecar exited before completing a request (code=${code ?? 'null'}, signal=${signal ?? 'null'})${suffix}`));
113
+ });
114
+ }
115
+ async request(command, payload = {}) {
116
+ if (this.closed) {
117
+ throw new Error('native sidecar session is already closed');
118
+ }
119
+ const id = randomUUID();
120
+ const request = JSON.stringify({
121
+ id,
122
+ command,
123
+ ...payload,
124
+ });
125
+ const responsePromise = new Promise((resolve, reject) => {
126
+ this.pending.set(id, {
127
+ resolve: (value) => resolve(value),
128
+ reject,
129
+ });
130
+ });
131
+ this.child.stdin.write(`${request}\n`);
132
+ return responsePromise;
133
+ }
134
+ async close() {
135
+ if (this.closed) {
136
+ return;
137
+ }
138
+ this.closed = true;
139
+ this.child.kill();
140
+ }
141
+ handleEnvelope(payload) {
142
+ if (!payload.id) {
143
+ return;
144
+ }
145
+ const pending = this.pending.get(payload.id);
146
+ if (!pending) {
147
+ return;
148
+ }
149
+ this.pending.delete(payload.id);
150
+ if (payload.ok) {
151
+ pending.resolve(payload.result);
152
+ return;
153
+ }
154
+ pending.reject(new Error(payload.error?.message ?? 'native sidecar request failed'));
155
+ }
156
+ rejectAll(error) {
157
+ for (const pending of this.pending.values()) {
158
+ pending.reject(error);
159
+ }
160
+ this.pending.clear();
161
+ }
162
+ }
163
+ export async function probeNativeClipSidecar() {
164
+ let executablePath;
165
+ try {
166
+ executablePath = await ensureDownloadedSidecar();
167
+ }
168
+ catch (error) {
169
+ return {
170
+ supported: false,
171
+ supportsHeadless: false,
172
+ reason: error instanceof Error ? error.message : String(error),
173
+ };
174
+ }
175
+ const session = new NativeClipSidecarSession({ executablePath });
176
+ try {
177
+ const result = await session.request('probe');
178
+ if (result.supported !== true || typeof result.supportsHeadless !== 'boolean') {
179
+ return {
180
+ supported: false,
181
+ supportsHeadless: false,
182
+ executablePath,
183
+ reason: 'native clip sidecar returned an invalid probe response',
184
+ };
185
+ }
186
+ return {
187
+ supported: true,
188
+ supportsHeadless: result.supportsHeadless,
189
+ permissionGranted: result.permissionGranted !== false,
190
+ executablePath,
191
+ version: result.version,
192
+ backend: result.backend,
193
+ reason: result.reason,
194
+ };
195
+ }
196
+ catch (error) {
197
+ return {
198
+ supported: false,
199
+ supportsHeadless: false,
200
+ executablePath,
201
+ reason: error instanceof Error ? error.message : String(error),
202
+ };
203
+ }
204
+ finally {
205
+ await session.close();
206
+ }
207
+ }
208
+ //# sourceMappingURL=clip-sidecar.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
@@ -21,6 +21,14 @@ export declare function updateCostLogCaptureContext(supabase: SupabaseClient, co
21
21
  viewportWidth?: number | null;
22
22
  viewportHeight?: number | null;
23
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
+ }>;
24
32
  export declare function resolveAllCosts(supabase: SupabaseClient, entries: Array<{
25
33
  costLogId: string;
26
34
  generationId: string | null;