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,34 @@
1
+ /**
2
+ * Capture Agent — CLI Runner
3
+ *
4
+ * Entry point for deterministic execution from the CLI.
5
+ * Flow:
6
+ * 1. Authenticate via stored CLI key
7
+ * 2. Fetch compiled program from server
8
+ * 3. Validate program against Zod schema
9
+ * 4. Launch local Playwright browser per variant
10
+ * 5. Execute program via opcode-runner
11
+ * 6. Upload artifacts + telemetry to server
12
+ */
13
+ import { type ProgressEvent } from './opcode-runner.js';
14
+ import type { ExecutionProgram, RunResult } from './execution-types.js';
15
+ export interface CLIRunnerOptions {
16
+ /** Preset ID to run */
17
+ presetId: string;
18
+ /** Override: provide program directly instead of fetching from server */
19
+ program?: ExecutionProgram;
20
+ /** Selector memory map (fetched from server or cached locally) */
21
+ selectorMemory?: Record<string, string[]>;
22
+ /** Show browser window. Default: false (headless) */
23
+ headed?: boolean;
24
+ /** Abort signal for cancellation */
25
+ abortSignal?: AbortSignal;
26
+ /** Progress callback */
27
+ onProgress?: (event: ProgressEvent) => void;
28
+ }
29
+ export interface CLIRunResult {
30
+ success: boolean;
31
+ runResult?: RunResult;
32
+ error?: string;
33
+ }
34
+ export declare function runCapture(options: CLIRunnerOptions): Promise<CLIRunResult>;
@@ -0,0 +1,580 @@
1
+ /**
2
+ * Capture Agent — CLI Runner
3
+ *
4
+ * Entry point for deterministic execution from the CLI.
5
+ * Flow:
6
+ * 1. Authenticate via stored CLI key
7
+ * 2. Fetch compiled program from server
8
+ * 3. Validate program against Zod schema
9
+ * 4. Launch local Playwright browser per variant
10
+ * 5. Execute program via opcode-runner
11
+ * 6. Upload artifacts + telemetry to server
12
+ */
13
+ import fs from 'node:fs/promises';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import { randomUUID } from 'node:crypto';
17
+ import { Browser } from './browser.js';
18
+ import { API_BASE_URL_ENV_VAR, requireConfig } from './cli-config.js';
19
+ import { WebPlaywrightLocal } from './web-playwright-local.js';
20
+ import { executeProgram } from './opcode-runner.js';
21
+ import { RecoveryChainImpl } from './recovery-chain.js';
22
+ import { parseProgram } from './execution-schema.js';
23
+ import { buildCursorOverlayScript } from './cursor-overlay-script.js';
24
+ import { CLI_VERSION_HEADER } from './cli-contract.js';
25
+ import { logger } from './logger.js';
26
+ import { callLLM } from './llm-provider.js';
27
+ import { APP_VERSION } from './version.js';
28
+ import { normalizeAllowedOrigins, normalizeHttpOrigin, verifySignedExecutionProgramEnvelope, } from './program-signing.js';
29
+ const HEALER_SYSTEM_PROMPT = 'You repair failed deterministic browser opcodes. Respond only with JSON.';
30
+ // ── Main entry point ────────────────────────────────────────────────
31
+ export async function runCapture(options) {
32
+ const config = await requireConfig();
33
+ // Step 1: Get the compiled program
34
+ let resolvedProgram;
35
+ if (options.program) {
36
+ let parsedProgram;
37
+ try {
38
+ parsedProgram = parseProgram(options.program);
39
+ }
40
+ catch (err) {
41
+ return { success: false, error: `program validation failed: ${err instanceof Error ? err.message : String(err)}` };
42
+ }
43
+ resolvedProgram = {
44
+ program: parsedProgram,
45
+ security: deriveUnsignedProgramSecurity(parsedProgram, config.apiBaseUrl),
46
+ };
47
+ }
48
+ else {
49
+ const fetched = await fetchProgram(config, options.presetId);
50
+ if (!fetched.success) {
51
+ return { success: false, error: fetched.error };
52
+ }
53
+ resolvedProgram = {
54
+ program: fetched.program,
55
+ security: fetched.security,
56
+ };
57
+ try {
58
+ // Step 2: Validate the program fetched from the server.
59
+ resolvedProgram = {
60
+ ...resolvedProgram,
61
+ program: parseProgram(resolvedProgram.program),
62
+ };
63
+ }
64
+ catch (err) {
65
+ return { success: false, error: `program validation failed: ${err instanceof Error ? err.message : String(err)}` };
66
+ }
67
+ }
68
+ let { program } = resolvedProgram;
69
+ // Persist against the canonical preset identifier selected by the CLI,
70
+ // even if the compiled program carries a human-readable preset slug/name.
71
+ if (program.presetId !== options.presetId) {
72
+ program = {
73
+ ...program,
74
+ presetId: options.presetId,
75
+ };
76
+ }
77
+ try {
78
+ assertProgramNavigationScope(program, resolvedProgram.security);
79
+ }
80
+ catch (error) {
81
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
82
+ }
83
+ logger.info(`[capture] Running preset "${options.presetId}" — ${program.steps.length} opcodes, ${program.variants.length} variant(s)`);
84
+ logger.info(`[capture] Resolved API origin ${resolvedProgram.security.expectedApiOrigin}; navigation scope: ${resolvedProgram.security.allowedNavigationOrigins.join(', ')}`);
85
+ const llmConfig = resolveCliLLMConfig(resolvedProgram.security);
86
+ // Step 3: Set up recovery chain
87
+ const recoveryChain = new RecoveryChainImpl({
88
+ selectorMemory: options.selectorMemory,
89
+ programSteps: program.steps,
90
+ llmProvider: llmConfig ? createHealerLLMProvider(llmConfig) : undefined,
91
+ credentials: program.preconditions.credentials,
92
+ });
93
+ // Step 4: Execute the program
94
+ const runOptions = {
95
+ recoveryChain,
96
+ abortSignal: options.abortSignal,
97
+ maxParallelVariants: program.maxParallelCaptures,
98
+ llmConfig,
99
+ presetName: program.presetId,
100
+ onProgress: (event) => {
101
+ if (!options.onProgress) {
102
+ logProgress(event);
103
+ }
104
+ options.onProgress?.(event);
105
+ },
106
+ };
107
+ const captureStart = Date.now();
108
+ if (program.maxParallelCaptures) {
109
+ logger.info(`[capture] Concurrency cap resolved to ${program.maxParallelCaptures} parallel variant(s)`);
110
+ }
111
+ const createAdapter = async (variant) => {
112
+ const browserOptions = {
113
+ headed: options.headed ?? false,
114
+ viewport: variant.viewport,
115
+ deviceScaleFactor: variant.deviceScaleFactor ?? program.outputScale,
116
+ lang: variant.locale,
117
+ colorScheme: variant.theme,
118
+ storageState: program.preconditions.storageState,
119
+ };
120
+ const recordable = program.mediaMode === 'clip';
121
+ let recordingDir;
122
+ let browser;
123
+ logger.info(`[capture] Launching browser${browserOptions.headed ? ' (headed)' : ''}…`);
124
+ if (recordable) {
125
+ recordingDir = await fs.mkdtemp(path.join(os.tmpdir(), `autokap-${program.mediaMode}-`));
126
+ browser = await Browser.forClipCapture(browserOptions, buildCursorOverlayScript(program.artifactPlan.cursorTheme ?? 'minimal'));
127
+ }
128
+ else if (browserOptions.headed) {
129
+ // Headed mode: standalone browser (pool is always headless)
130
+ browser = new Browser(browserOptions);
131
+ await browser.launch();
132
+ }
133
+ else {
134
+ browser = await Browser.fromPool(browserOptions);
135
+ await browser.launch();
136
+ }
137
+ await applyPreconditions(browser, program);
138
+ return new WebPlaywrightLocal(browser, recordingDir);
139
+ };
140
+ const runResult = await executeProgram(program, createAdapter, runOptions);
141
+ if (runResult.success) {
142
+ logger.info(`[capture] Run completed successfully — ${runResult.telemetry.totalOpcodes} opcodes, ${runResult.telemetry.recoveredOpcodes} recovered, ${runResult.totalDurationMs}ms`);
143
+ }
144
+ else {
145
+ logger.error(`[capture] Run failed: ${runResult.error}`);
146
+ }
147
+ try {
148
+ logger.info('[capture] Saving captures, might take a few seconds...');
149
+ await uploadResults(config, program, runResult);
150
+ const totalDurationSec = ((Date.now() - captureStart) / 1000).toFixed(1);
151
+ logger.info(`[capture] Captures saved successfully — total ${totalDurationSec}s`);
152
+ }
153
+ catch (err) {
154
+ logger.error(`[capture] Failed to upload results: ${err instanceof Error ? err.message : String(err)}`);
155
+ }
156
+ return { success: runResult.success, runResult };
157
+ }
158
+ // ── Server communication ────────────────────────────────────────────
159
+ async function fetchProgram(config, presetId) {
160
+ try {
161
+ const url = `${config.apiBaseUrl}/api/cli/programs/${presetId}`;
162
+ const response = await fetch(url, {
163
+ headers: {
164
+ 'Authorization': `Bearer ${config.apiKey}`,
165
+ 'Content-Type': 'application/json',
166
+ [CLI_VERSION_HEADER]: APP_VERSION,
167
+ },
168
+ });
169
+ if (!response.ok) {
170
+ return { success: false, error: await formatServerError(response, url) };
171
+ }
172
+ const data = await response.json();
173
+ const envelope = verifySignedExecutionProgramEnvelope({
174
+ apiKey: config.apiKey,
175
+ envelope: data,
176
+ expectedApiOrigin: safeOrigin(config.apiBaseUrl),
177
+ });
178
+ if (envelope.meta?.stale) {
179
+ logger.warn('[capture] Program is stale — langs/themes changed since last compilation. Recompile to apply changes.');
180
+ }
181
+ return { success: true, program: envelope.program, security: envelope.security };
182
+ }
183
+ catch (err) {
184
+ return { success: false, error: `failed to fetch program: ${err instanceof Error ? err.message : String(err)}` };
185
+ }
186
+ }
187
+ async function uploadResults(config, program, result) {
188
+ const runId = randomUUID();
189
+ const totalArtifacts = result.variantResults.reduce((sum, v) => sum + v.artifacts.length, 0);
190
+ let uploadedCount = 0;
191
+ // Upload artifacts
192
+ for (const variant of result.variantResults) {
193
+ const variantSpec = program.variants.find((entry) => entry.id === variant.variantId);
194
+ for (const artifact of variant.artifacts) {
195
+ const formData = new FormData();
196
+ const filename = buildArtifactFilename(program.presetId, variant.variantId, artifact);
197
+ uploadedCount += 1;
198
+ const label = artifact.captureName ?? artifact.clipName ?? artifact.fragmentName ?? artifact.stateName ?? filename;
199
+ logger.info(`[capture] Exporting capture ${uploadedCount}/${totalArtifacts}: ${label}`);
200
+ formData.append('file', new Blob([new Uint8Array(artifact.buffer)], { type: artifact.mimeType }), filename);
201
+ formData.append('presetId', program.presetId);
202
+ formData.append('programVersion', String(program.programVersion));
203
+ formData.append('compileFingerprint', program.compileFingerprint);
204
+ formData.append('runId', runId);
205
+ formData.append('variantId', variant.variantId);
206
+ formData.append('targetId', variantSpec?.targetId ?? variant.variantId);
207
+ formData.append('targetLabel', variantSpec?.targetLabel ?? variantSpec?.deviceFrame ?? variant.variantId);
208
+ formData.append('mediaMode', artifact.mediaMode);
209
+ formData.append('mimeType', artifact.mimeType);
210
+ formData.append('captureType', artifact.captureType ?? 'fullpage');
211
+ formData.append('captureUrl', artifact.captureUrl ?? program.baseUrl);
212
+ formData.append('lang', variantSpec?.locale ?? 'en');
213
+ formData.append('theme', variantSpec?.theme ?? 'light');
214
+ if (variantSpec?.deviceFrame) {
215
+ formData.append('deviceFrame', variantSpec.deviceFrame);
216
+ }
217
+ const deviceScaleFactor = variantSpec?.deviceScaleFactor ?? program.outputScale;
218
+ if (Number.isFinite(deviceScaleFactor)) {
219
+ formData.append('deviceScaleFactor', String(deviceScaleFactor));
220
+ }
221
+ formData.append('viewport', JSON.stringify(variantSpec?.viewport ?? null));
222
+ formData.append('artifactPlan', JSON.stringify(program.artifactPlan));
223
+ if (artifact.altText) {
224
+ formData.append('altText', artifact.altText);
225
+ }
226
+ if (artifact.elementSelector) {
227
+ formData.append('elementSelector', artifact.elementSelector);
228
+ }
229
+ if (artifact.captureId) {
230
+ formData.append('captureId', artifact.captureId);
231
+ }
232
+ if (artifact.captureName) {
233
+ formData.append('captureName', artifact.captureName);
234
+ }
235
+ if (artifact.clipId) {
236
+ formData.append('clipId', artifact.clipId);
237
+ }
238
+ if (artifact.clipName) {
239
+ formData.append('clipName', artifact.clipName);
240
+ }
241
+ if (artifact.stepDescription) {
242
+ formData.append('stepDescription', artifact.stepDescription);
243
+ }
244
+ if (typeof artifact.stepIndex === 'number') {
245
+ formData.append('stepIndex', String(artifact.stepIndex));
246
+ }
247
+ if (artifact.tabIconData) {
248
+ formData.append('tabIcon', new Blob([new Uint8Array(artifact.tabIconData)], { type: artifact.tabIconMimeType ?? 'image/png' }), 'favicon');
249
+ }
250
+ if (typeof artifact.durationMs === 'number') {
251
+ formData.append('durationMs', String(artifact.durationMs));
252
+ }
253
+ if (typeof artifact.trimStartMs === 'number') {
254
+ formData.append('trimStartMs', String(artifact.trimStartMs));
255
+ }
256
+ // ── Interactive demo (mediaMode === 'dom') ──
257
+ if (artifact.stateName) {
258
+ formData.append('stateName', artifact.stateName);
259
+ }
260
+ if (artifact.domAssetUrls && artifact.domAssetUrls.length > 0) {
261
+ formData.append('domAssetUrls', JSON.stringify(artifact.domAssetUrls));
262
+ }
263
+ if (artifact.domThumbnailBuffer) {
264
+ formData.append('domThumbnail', new Blob([new Uint8Array(artifact.domThumbnailBuffer)], { type: 'image/png' }), 'thumbnail.png');
265
+ }
266
+ // Phase 5: fragment fields
267
+ if (artifact.fragmentName) {
268
+ formData.append('fragmentName', artifact.fragmentName);
269
+ }
270
+ // Phase 8: variant of the fragment capture (defaults to 'default'
271
+ // server-side when omitted, but we always send it explicitly so the
272
+ // upload row's unique key includes it).
273
+ if (artifact.fragmentVariantName) {
274
+ formData.append('fragmentVariantName', artifact.fragmentVariantName);
275
+ }
276
+ if (artifact.parentStateName) {
277
+ formData.append('parentStateName', artifact.parentStateName);
278
+ }
279
+ if (artifact.mountStrategy) {
280
+ formData.append('mountStrategy', artifact.mountStrategy);
281
+ }
282
+ if (artifact.mountTargetSelector) {
283
+ formData.append('mountTargetSelector', artifact.mountTargetSelector);
284
+ }
285
+ const response = await fetch(`${config.apiBaseUrl}/api/cli/artifacts`, {
286
+ method: 'POST',
287
+ headers: { 'Authorization': `Bearer ${config.apiKey}` },
288
+ body: formData,
289
+ });
290
+ if (!response.ok) {
291
+ throw new Error(`artifact upload failed for ${variant.variantId}: ${await formatServerError(response, `${config.apiBaseUrl}/api/cli/artifacts`)}`);
292
+ }
293
+ }
294
+ }
295
+ // Strip binary buffers from artifacts before sending. The raw PNG/video
296
+ // buffers were already uploaded via /api/cli/artifacts above, and the
297
+ // server strips them again before persisting. Sending them in the JSON
298
+ // body bloats the payload (each Buffer serializes as a giant int array,
299
+ // ~3-4× the original byte size) and overflows Next.js' request body limit.
300
+ const strippedRunResult = {
301
+ ...result,
302
+ variantResults: result.variantResults.map((variant) => ({
303
+ ...variant,
304
+ artifacts: variant.artifacts.map((artifact) => {
305
+ const { buffer: _buffer, tabIconData: _tabIconData, ...rest } = artifact;
306
+ const sanitizedArtifact = sanitizeArtifactForTelemetry(rest);
307
+ return sanitizedArtifact;
308
+ }),
309
+ error: redactTelemetryText(variant.error),
310
+ opcodeResults: variant.opcodeResults.map((opcodeResult) => ({
311
+ ...opcodeResult,
312
+ error: redactTelemetryText(opcodeResult.error),
313
+ })),
314
+ })),
315
+ };
316
+ const sanitizedHealerPatches = sanitizeHealerPatches(result.healerPatches);
317
+ // Upload telemetry + healer patches
318
+ const telemetryResponse = await fetch(`${config.apiBaseUrl}/api/cli/telemetry`, {
319
+ method: 'POST',
320
+ headers: {
321
+ 'Authorization': `Bearer ${config.apiKey}`,
322
+ 'Content-Type': 'application/json',
323
+ },
324
+ body: JSON.stringify({
325
+ runId,
326
+ presetId: program.presetId,
327
+ programVersion: program.programVersion,
328
+ compileFingerprint: program.compileFingerprint,
329
+ success: result.success,
330
+ mediaMode: program.mediaMode,
331
+ telemetry: result.telemetry,
332
+ healerPatches: sanitizedHealerPatches,
333
+ runResult: strippedRunResult,
334
+ totalDurationMs: result.totalDurationMs,
335
+ variantSummaries: result.variantResults.map(v => ({
336
+ variantId: v.variantId,
337
+ success: v.success,
338
+ opcodeCount: v.opcodeResults.length,
339
+ artifactCount: v.artifacts.length,
340
+ durationMs: v.durationMs,
341
+ error: v.error,
342
+ })),
343
+ }),
344
+ });
345
+ if (!telemetryResponse.ok) {
346
+ throw new Error(`telemetry upload failed: ${await formatServerError(telemetryResponse, `${config.apiBaseUrl}/api/cli/telemetry`)}`);
347
+ }
348
+ }
349
+ async function formatServerError(response, requestedUrl) {
350
+ const contentType = response.headers?.get?.('content-type') ?? '';
351
+ const rawBody = (await response.text()).trim();
352
+ if (!rawBody) {
353
+ return `server returned ${response.status}`;
354
+ }
355
+ if (contentType.includes('application/json') || rawBody.startsWith('{')) {
356
+ try {
357
+ const parsed = JSON.parse(rawBody);
358
+ const message = typeof parsed.error === 'string'
359
+ ? parsed.error
360
+ : typeof parsed.message === 'string'
361
+ ? parsed.message
362
+ : null;
363
+ if (message) {
364
+ return `server returned ${response.status}: ${message}`;
365
+ }
366
+ }
367
+ catch {
368
+ // Fall back to a condensed text preview below.
369
+ }
370
+ }
371
+ const isHtml = contentType.includes('text/html') || /^<!doctype html>/i.test(rawBody);
372
+ if (response.status === 404 && isHtml) {
373
+ return [
374
+ `server returned 404 for ${requestedUrl}.`,
375
+ `The configured AutoKap server (${safeOrigin(requestedUrl)}) does not expose this CLI route yet.`,
376
+ 'This usually means the web app deployment is older than the CLI.',
377
+ `Point the CLI at your local dev server with \`autokap run --local <preset-id>\`, \`${API_BASE_URL_ENV_VAR}=http://localhost:3000 autokap run <preset-id>\`, or re-initialize against a local server with \`autokap init --cli-key <key> --api-base-url http://localhost:3000\`.`,
378
+ ].join(' ');
379
+ }
380
+ if (isHtml) {
381
+ return `server returned ${response.status}: unexpected HTML response from ${requestedUrl}`;
382
+ }
383
+ return `server returned ${response.status}: ${rawBody.replace(/\s+/g, ' ').slice(0, 240)}`;
384
+ }
385
+ function safeOrigin(url) {
386
+ try {
387
+ return new URL(url).origin;
388
+ }
389
+ catch {
390
+ return url;
391
+ }
392
+ }
393
+ async function applyPreconditions(browser, program) {
394
+ const { preconditions } = program;
395
+ // Auth bootstrap auto-adapts to whatever is configured on the preset.
396
+ // - cookies / sessionStorage / storageState are injected into the browser
397
+ // context so the page starts already authenticated.
398
+ // - credentials are NOT applied here; they're substituted into
399
+ // {{email}}/{{password}}/{{loginUrl}} placeholders inside opcodes by the
400
+ // opcode runner, and the program performs the UI login itself.
401
+ // - If neither is set, the run is anonymous.
402
+ if (preconditions.cookies && preconditions.cookies.length > 0) {
403
+ await browser.addCookies(preconditions.cookies);
404
+ }
405
+ if (preconditions.sessionStorage && Object.keys(preconditions.sessionStorage).length > 0) {
406
+ await browser.prepareSessionStorage(preconditions.sessionStorage, { replace: false });
407
+ }
408
+ }
409
+ function resolveCliLLMConfig(security) {
410
+ if (!security.runtimeLlmAllowed) {
411
+ return undefined;
412
+ }
413
+ const enabled = process.env.AUTOKAP_ENABLE_RUNTIME_LLM?.trim().toLowerCase();
414
+ if (enabled !== '1' && enabled !== 'true') {
415
+ return undefined;
416
+ }
417
+ const apiKey = process.env.OPENROUTER_API_KEY?.trim();
418
+ if (!apiKey)
419
+ return undefined;
420
+ return {
421
+ apiKey,
422
+ model: process.env.AUTOKAP_LLM_MODEL?.trim() || undefined,
423
+ };
424
+ }
425
+ function createHealerLLMProvider(llmConfig) {
426
+ return {
427
+ call: async (prompt, screenshot) => {
428
+ const llmResult = await callLLM({
429
+ ...llmConfig,
430
+ maxTokens: Math.max(llmConfig.maxTokens ?? 400, 400),
431
+ timeoutMs: llmConfig.timeoutMs ?? 20000,
432
+ }, HEALER_SYSTEM_PROMPT, prompt, screenshot);
433
+ return {
434
+ response: llmResult.text,
435
+ llmResult,
436
+ };
437
+ },
438
+ };
439
+ }
440
+ function buildArtifactFilename(presetId, variantId, artifact) {
441
+ const ext = artifact.mediaMode === 'dom'
442
+ ? 'html'
443
+ : artifact.mimeType === 'image/jpeg'
444
+ ? 'jpg'
445
+ : artifact.mimeType === 'image/png'
446
+ ? 'png'
447
+ : artifact.mimeType.includes('gif')
448
+ ? 'gif'
449
+ : artifact.mimeType.includes('mp4')
450
+ ? 'mp4'
451
+ : 'webm';
452
+ const stepToken = typeof artifact.stepIndex === 'number' ? `-${artifact.stepIndex}` : '';
453
+ return `${sanitizeArtifactToken(presetId)}-${sanitizeArtifactToken(variantId)}${stepToken}.${ext}`;
454
+ }
455
+ function sanitizeArtifactToken(value) {
456
+ const cleaned = value.trim().replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/-+/g, '-');
457
+ return cleaned.length > 0 ? cleaned.slice(0, 80) : 'artifact';
458
+ }
459
+ function deriveUnsignedProgramSecurity(program, apiBaseUrl) {
460
+ return {
461
+ expectedApiOrigin: safeOrigin(apiBaseUrl),
462
+ allowedNavigationOrigins: collectAllowedNavigationOrigins(program),
463
+ runtimeLlmAllowed: true,
464
+ };
465
+ }
466
+ function collectAllowedNavigationOrigins(program) {
467
+ const candidates = [program.baseUrl];
468
+ if (program.preconditions.credentials?.loginUrl) {
469
+ candidates.push(program.preconditions.credentials.loginUrl);
470
+ }
471
+ for (const step of program.steps) {
472
+ if (step.kind === 'NAVIGATE') {
473
+ candidates.push(step.url);
474
+ }
475
+ }
476
+ return normalizeAllowedOrigins(candidates
477
+ .map((value) => normalizeHttpOrigin(value))
478
+ .filter((value) => Boolean(value)));
479
+ }
480
+ function assertProgramNavigationScope(program, security) {
481
+ const allowedOrigins = new Set(normalizeAllowedOrigins(security.allowedNavigationOrigins));
482
+ if (allowedOrigins.size === 0) {
483
+ throw new Error('program security metadata does not declare any navigation origins');
484
+ }
485
+ for (const step of program.steps) {
486
+ if (step.kind !== 'NAVIGATE')
487
+ continue;
488
+ const origin = normalizeHttpOrigin(step.url);
489
+ if (!origin || !allowedOrigins.has(origin)) {
490
+ throw new Error(`program navigation target ${step.url} is outside the signed scope (${Array.from(allowedOrigins).join(', ')})`);
491
+ }
492
+ }
493
+ }
494
+ function sanitizeArtifactForTelemetry(artifact) {
495
+ return {
496
+ ...artifact,
497
+ altText: redactTelemetryText(artifact.altText),
498
+ captureUrl: redactUrl(artifact.captureUrl),
499
+ elementSelector: undefined,
500
+ domAssetUrls: artifact.domAssetUrls
501
+ ?.map((entry) => redactUrl(entry))
502
+ .filter((entry) => Boolean(entry)),
503
+ };
504
+ }
505
+ function sanitizeHealerPatches(patches) {
506
+ return patches.map((patch) => ({
507
+ ...patch,
508
+ reason: redactTelemetryText(patch.reason) ?? patch.reason,
509
+ originalOpcode: redactOpcodeForTelemetry(patch.originalOpcode),
510
+ replacementOpcodes: patch.replacementOpcodes.map((opcode) => redactOpcodeForTelemetry(opcode)),
511
+ }));
512
+ }
513
+ function redactOpcodeForTelemetry(opcode) {
514
+ const base = {
515
+ ...opcode,
516
+ description: redactTelemetryText(opcode.description) ?? opcode.description,
517
+ };
518
+ if ('selector' in base) {
519
+ return {
520
+ ...base,
521
+ selector: '<redacted-selector>',
522
+ ...('selectorAlternates' in base ? { selectorAlternates: [] } : {}),
523
+ };
524
+ }
525
+ return base;
526
+ }
527
+ function redactTelemetryText(value) {
528
+ if (!value)
529
+ return value;
530
+ return value
531
+ .replace(/\{\{(email|password|loginUrl)\}\}/g, '<credential-placeholder>')
532
+ .replace(/https?:\/\/[^\s"'<>]+/gi, (match) => redactUrl(match) ?? '<redacted-url>')
533
+ .replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, '<redacted-email>')
534
+ .slice(0, 512);
535
+ }
536
+ function redactUrl(value) {
537
+ if (!value)
538
+ return value;
539
+ try {
540
+ const parsed = new URL(value);
541
+ parsed.username = '';
542
+ parsed.password = '';
543
+ parsed.search = parsed.search ? '?<redacted>' : '';
544
+ parsed.hash = parsed.hash ? '#<redacted>' : '';
545
+ return parsed.toString();
546
+ }
547
+ catch {
548
+ return value.slice(0, 256);
549
+ }
550
+ }
551
+ // ── Progress logging ────────────────────────────────────────────────
552
+ function logProgress(event) {
553
+ const prefix = `[capture][${event.variantId}]`;
554
+ switch (event.type) {
555
+ case 'variant_start':
556
+ logger.info(`${prefix} Starting variant`);
557
+ break;
558
+ case 'variant_end':
559
+ logger.info(`${prefix} ${event.status === 'ok' ? 'Completed' : 'Failed'}: ${event.message}`);
560
+ break;
561
+ case 'opcode_start':
562
+ logger.debug(`${prefix}[${event.opcodeIndex}] ${event.opcodeKind}: ${event.message}`);
563
+ break;
564
+ case 'opcode_end':
565
+ if (event.status === 'failed') {
566
+ logger.error(`${prefix}[${event.opcodeIndex}] FAILED: ${event.message}`);
567
+ }
568
+ else if (event.status === 'recovered') {
569
+ logger.warn(`${prefix}[${event.opcodeIndex}] RECOVERED: ${event.message}`);
570
+ }
571
+ break;
572
+ case 'recovery':
573
+ logger.warn(`${prefix}[${event.opcodeIndex}] Recovery: ${event.message}`);
574
+ break;
575
+ case 'breaker_trip':
576
+ logger.error(`${prefix} Circuit breaker tripped: ${event.message}`);
577
+ break;
578
+ }
579
+ }
580
+ //# sourceMappingURL=cli-runner.js.map
@@ -1,6 +1,5 @@
1
1
  export declare function replaceSkillPlaceholders(content: string, opts: {
2
2
  projectUrl?: string;
3
3
  projectId?: string;
4
- apiKey?: string;
5
4
  apiBaseUrl?: string;
6
5
  }): string;
package/dist/cli-utils.js CHANGED
@@ -6,12 +6,9 @@ export function replaceSkillPlaceholders(content, opts) {
6
6
  if (opts.projectId) {
7
7
  result = result.replace(/\[AUTOKAP_PROJECT_ID\]/g, opts.projectId);
8
8
  }
9
- if (opts.apiKey) {
10
- result = result.replace(/YOUR_API_KEY/g, opts.apiKey);
9
+ if (opts.apiBaseUrl) {
10
+ result = result.replace(/https:\/\/autokap\.app/g, opts.apiBaseUrl);
11
11
  }
12
- const baseUrl = opts.apiBaseUrl ?? 'https://autokap.app';
13
- result = result.replace(/https:\/\/app\.autokap\.com/g, baseUrl);
14
- result = result.replace(/https:\/\/autokap\.com/g, baseUrl);
15
12
  return result;
16
13
  }
17
14
  //# sourceMappingURL=cli-utils.js.map