autokap 1.0.7 → 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 (278) 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 +607 -0
  4. package/assets/skill/README.md +39 -0
  5. package/assets/skill/SKILL.md +453 -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 +164 -0
  26. package/dist/benchmark.d.ts +59 -0
  27. package/dist/benchmark.js +135 -0
  28. package/dist/browser-bar.d.ts +14 -6
  29. package/dist/browser-bar.js +145 -8
  30. package/dist/browser-pool.d.ts +7 -0
  31. package/dist/browser-pool.js +15 -5
  32. package/dist/browser-utils.d.ts +31 -0
  33. package/dist/browser-utils.js +97 -0
  34. package/dist/browser.d.ts +51 -1
  35. package/dist/browser.js +1481 -31
  36. package/dist/capture-alt-text.js +2 -1
  37. package/dist/capture-language-preflight.js +14 -0
  38. package/dist/capture-llm-page-identity.js +22 -10
  39. package/dist/capture-page-identity.d.ts +5 -7
  40. package/dist/capture-page-identity.js +211 -78
  41. package/dist/capture-preset-credentials.d.ts +50 -0
  42. package/dist/capture-preset-credentials.js +127 -0
  43. package/dist/capture-request-plan.d.ts +2 -2
  44. package/dist/capture-request-plan.js +64 -16
  45. package/dist/capture-run-optimizer.js +48 -33
  46. package/dist/capture-selector-memory.d.ts +5 -0
  47. package/dist/capture-selector-memory.js +18 -0
  48. package/dist/capture-strategy.d.ts +36 -0
  49. package/dist/capture-strategy.js +95 -0
  50. package/dist/capture-studio-sync.d.ts +1 -0
  51. package/dist/capture-studio-sync.js +9 -3
  52. package/dist/capture-surface-contract.d.ts +36 -0
  53. package/dist/capture-surface-contract.js +299 -0
  54. package/dist/capture-transition-engine.d.ts +28 -0
  55. package/dist/capture-transition-engine.js +292 -0
  56. package/dist/capture-variant-state.d.ts +2 -0
  57. package/dist/capture-variant-state.js +26 -0
  58. package/dist/capture-verification.d.ts +35 -0
  59. package/dist/capture-verification.js +95 -0
  60. package/dist/capture-viewport-lock.d.ts +48 -0
  61. package/dist/capture-viewport-lock.js +74 -0
  62. package/dist/circuit-breaker.d.ts +42 -0
  63. package/dist/circuit-breaker.js +119 -0
  64. package/dist/cli-config.d.ts +8 -1
  65. package/dist/cli-config.js +62 -6
  66. package/dist/cli-contract.d.ts +15 -0
  67. package/dist/cli-contract.js +167 -0
  68. package/dist/cli-runner-local.d.ts +12 -0
  69. package/dist/cli-runner-local.js +102 -0
  70. package/dist/cli-runner.d.ts +34 -0
  71. package/dist/cli-runner.js +433 -0
  72. package/dist/cli-utils.d.ts +0 -1
  73. package/dist/cli-utils.js +2 -5
  74. package/dist/cli.js +1005 -267
  75. package/dist/clip-orchestrator.js +9 -2
  76. package/dist/clip-postprocess.js +25 -16
  77. package/dist/cookie-dismiss.d.ts +2 -0
  78. package/dist/cookie-dismiss.js +48 -13
  79. package/dist/cost-logging.d.ts +8 -0
  80. package/dist/cost-logging.js +160 -46
  81. package/dist/cost-resolution-monitor.d.ts +16 -0
  82. package/dist/cost-resolution-monitor.js +34 -0
  83. package/dist/credential-templates.js +2 -2
  84. package/dist/cursor-overlay-script.d.ts +6 -0
  85. package/dist/cursor-overlay-script.js +169 -0
  86. package/dist/dom-css-purger.d.ts +65 -0
  87. package/dist/dom-css-purger.js +333 -0
  88. package/dist/dom-font-inliner.d.ts +45 -0
  89. package/dist/dom-font-inliner.js +148 -0
  90. package/dist/dom-patch-resolver.d.ts +52 -0
  91. package/dist/dom-patch-resolver.js +242 -0
  92. package/dist/dom-serializer.d.ts +82 -0
  93. package/dist/dom-serializer.js +378 -0
  94. package/dist/element-capture.d.ts +1 -41
  95. package/dist/element-capture.js +202 -446
  96. package/dist/env-validation.d.ts +5 -0
  97. package/dist/env-validation.js +29 -0
  98. package/dist/execution-schema.d.ts +4423 -0
  99. package/dist/execution-schema.js +507 -0
  100. package/dist/execution-types.d.ts +886 -0
  101. package/dist/execution-types.js +65 -0
  102. package/dist/fonts-loader.d.ts +14 -0
  103. package/dist/fonts-loader.js +55 -0
  104. package/dist/hybrid-navigator.js +12 -12
  105. package/dist/index.d.ts +9 -6
  106. package/dist/index.js +10 -4
  107. package/dist/legacy/agent-action-recovery.d.ts +45 -0
  108. package/dist/legacy/agent-action-recovery.js +370 -0
  109. package/dist/legacy/agent-message-utils.d.ts +21 -0
  110. package/dist/legacy/agent-message-utils.js +77 -0
  111. package/dist/legacy/agent-url-utils.d.ts +30 -0
  112. package/dist/legacy/agent-url-utils.js +138 -0
  113. package/dist/legacy/agent.d.ts +226 -0
  114. package/dist/legacy/agent.js +6666 -0
  115. package/dist/legacy/clip-orchestrator.d.ts +148 -0
  116. package/dist/legacy/clip-orchestrator.js +957 -0
  117. package/dist/legacy/credential-templates.d.ts +5 -0
  118. package/dist/legacy/credential-templates.js +60 -0
  119. package/dist/legacy/hybrid-navigator.d.ts +138 -0
  120. package/dist/legacy/hybrid-navigator.js +468 -0
  121. package/dist/legacy/llm-usage.d.ts +17 -0
  122. package/dist/legacy/llm-usage.js +45 -0
  123. package/dist/legacy/prompt-cache.d.ts +10 -0
  124. package/dist/legacy/prompt-cache.js +24 -0
  125. package/dist/legacy/prompts.d.ts +175 -0
  126. package/dist/legacy/prompts.js +1038 -0
  127. package/dist/legacy/tools.d.ts +4 -0
  128. package/dist/legacy/tools.js +216 -0
  129. package/dist/legacy/video-agent.d.ts +143 -0
  130. package/dist/legacy/video-agent.js +4788 -0
  131. package/dist/legacy/video-observation.d.ts +36 -0
  132. package/dist/legacy/video-observation.js +192 -0
  133. package/dist/legacy/video-planner.d.ts +12 -0
  134. package/dist/legacy/video-planner.js +501 -0
  135. package/dist/legacy/video-prompts.d.ts +37 -0
  136. package/dist/legacy/video-prompts.js +569 -0
  137. package/dist/legacy/video-tools.d.ts +3 -0
  138. package/dist/legacy/video-tools.js +59 -0
  139. package/dist/legacy/video-variant-state.d.ts +29 -0
  140. package/dist/legacy/video-variant-state.js +80 -0
  141. package/dist/legacy/vision-model.d.ts +17 -0
  142. package/dist/legacy/vision-model.js +74 -0
  143. package/dist/llm-healer.d.ts +63 -0
  144. package/dist/llm-healer.js +166 -0
  145. package/dist/llm-provider.d.ts +29 -0
  146. package/dist/llm-provider.js +80 -0
  147. package/dist/logger.d.ts +6 -2
  148. package/dist/logger.js +15 -1
  149. package/dist/mockup-html.js +35 -25
  150. package/dist/mockup.d.ts +95 -2
  151. package/dist/mockup.js +427 -166
  152. package/dist/mouse-animation.d.ts +2 -2
  153. package/dist/mouse-animation.js +34 -20
  154. package/dist/opcode-actions.d.ts +42 -0
  155. package/dist/opcode-actions.js +511 -0
  156. package/dist/opcode-runner.d.ts +51 -0
  157. package/dist/opcode-runner.js +770 -0
  158. package/dist/openrouter-client.d.ts +40 -0
  159. package/dist/openrouter-client.js +16 -0
  160. package/dist/overlay-engine.d.ts +24 -0
  161. package/dist/overlay-engine.js +176 -0
  162. package/dist/postcondition.d.ts +16 -0
  163. package/dist/postcondition.js +269 -0
  164. package/dist/program-patcher.d.ts +25 -0
  165. package/dist/program-patcher.js +44 -0
  166. package/dist/prompts.d.ts +13 -5
  167. package/dist/prompts.js +224 -351
  168. package/dist/provider-config.d.ts +12 -0
  169. package/dist/provider-config.js +15 -0
  170. package/dist/recovery-chain.d.ts +37 -0
  171. package/dist/recovery-chain.js +350 -0
  172. package/dist/remote-browser.d.ts +28 -4
  173. package/dist/remote-browser.js +60 -5
  174. package/dist/safari-browser-bar.d.ts +15 -0
  175. package/dist/safari-browser-bar.js +95 -0
  176. package/dist/safari-toolbar-asset.d.ts +15 -0
  177. package/dist/safari-toolbar-asset.js +12 -0
  178. package/dist/security.d.ts +2 -1
  179. package/dist/security.js +49 -10
  180. package/dist/selector-resolver.d.ts +34 -0
  181. package/dist/selector-resolver.js +181 -0
  182. package/dist/semantic-resolver.d.ts +35 -0
  183. package/dist/semantic-resolver.js +161 -0
  184. package/dist/server-capture-runtime.d.ts +5 -3
  185. package/dist/server-capture-runtime.js +42 -95
  186. package/dist/server-credit-usage.d.ts +2 -2
  187. package/dist/server-project-webhooks.d.ts +15 -1
  188. package/dist/server-project-webhooks.js +34 -8
  189. package/dist/server-screenshot-watermark.js +27 -5
  190. package/dist/session-profile.js +164 -1
  191. package/dist/sf-pro-symbols.d.ts +1 -0
  192. package/dist/sf-pro-symbols.js +55 -0
  193. package/dist/skill-packaging.d.ts +28 -0
  194. package/dist/skill-packaging.js +169 -0
  195. package/dist/smart-wait.d.ts +27 -0
  196. package/dist/smart-wait.js +81 -0
  197. package/dist/status-bar-render.d.ts +20 -0
  198. package/dist/status-bar-render.js +410 -0
  199. package/dist/status-bar.d.ts +9 -0
  200. package/dist/status-bar.js +298 -14
  201. package/dist/svg-browser-bar.d.ts +33 -0
  202. package/dist/svg-browser-bar.js +206 -0
  203. package/dist/svg-status-bar.d.ts +36 -0
  204. package/dist/svg-status-bar.js +597 -0
  205. package/dist/svg-text.d.ts +61 -0
  206. package/dist/svg-text.js +118 -0
  207. package/dist/tools.js +89 -451
  208. package/dist/types.d.ts +240 -5
  209. package/dist/types.js +23 -1
  210. package/dist/v2/action-verifier.d.ts +29 -0
  211. package/dist/v2/action-verifier.js +133 -0
  212. package/dist/v2/alt-text.d.ts +26 -0
  213. package/dist/v2/alt-text.js +55 -0
  214. package/dist/v2/benchmark.d.ts +59 -0
  215. package/dist/v2/benchmark.js +135 -0
  216. package/dist/v2/capture-strategy.d.ts +30 -0
  217. package/dist/v2/capture-strategy.js +67 -0
  218. package/dist/v2/capture-verification.d.ts +35 -0
  219. package/dist/v2/capture-verification.js +95 -0
  220. package/dist/v2/circuit-breaker.d.ts +42 -0
  221. package/dist/v2/circuit-breaker.js +119 -0
  222. package/dist/v2/cli-runner-local.d.ts +11 -0
  223. package/dist/v2/cli-runner-local.js +91 -0
  224. package/dist/v2/cli-runner.d.ts +34 -0
  225. package/dist/v2/cli-runner.js +300 -0
  226. package/dist/v2/compiler-prompts.d.ts +27 -0
  227. package/dist/v2/compiler-prompts.js +123 -0
  228. package/dist/v2/compiler.d.ts +37 -0
  229. package/dist/v2/compiler.js +147 -0
  230. package/dist/v2/explorer.d.ts +41 -0
  231. package/dist/v2/explorer.js +56 -0
  232. package/dist/v2/index.d.ts +37 -0
  233. package/dist/v2/index.js +31 -0
  234. package/dist/v2/llm-healer.d.ts +62 -0
  235. package/dist/v2/llm-healer.js +166 -0
  236. package/dist/v2/llm-provider.d.ts +29 -0
  237. package/dist/v2/llm-provider.js +80 -0
  238. package/dist/v2/opcode-runner.d.ts +47 -0
  239. package/dist/v2/opcode-runner.js +634 -0
  240. package/dist/v2/overlay-engine.d.ts +24 -0
  241. package/dist/v2/overlay-engine.js +150 -0
  242. package/dist/v2/postcondition.d.ts +16 -0
  243. package/dist/v2/postcondition.js +249 -0
  244. package/dist/v2/program-patcher.d.ts +25 -0
  245. package/dist/v2/program-patcher.js +44 -0
  246. package/dist/v2/recovery-chain.d.ts +30 -0
  247. package/dist/v2/recovery-chain.js +368 -0
  248. package/dist/v2/schema.d.ts +2580 -0
  249. package/dist/v2/schema.js +295 -0
  250. package/dist/v2/selector-resolver.d.ts +34 -0
  251. package/dist/v2/selector-resolver.js +181 -0
  252. package/dist/v2/semantic-resolver.d.ts +35 -0
  253. package/dist/v2/semantic-resolver.js +161 -0
  254. package/dist/v2/smart-wait.d.ts +27 -0
  255. package/dist/v2/smart-wait.js +81 -0
  256. package/dist/v2/types.d.ts +444 -0
  257. package/dist/v2/types.js +19 -0
  258. package/dist/v2/web-playwright-local.d.ts +69 -0
  259. package/dist/v2/web-playwright-local.js +392 -0
  260. package/dist/version.d.ts +1 -0
  261. package/dist/version.js +5 -0
  262. package/dist/video-agent.js +18 -13
  263. package/dist/video-planner.js +2 -1
  264. package/dist/video-prompts.js +3 -3
  265. package/dist/web-playwright-local.d.ts +126 -0
  266. package/dist/web-playwright-local.js +819 -0
  267. package/dist/ws-auth.js +4 -1
  268. package/dist/ws-broadcast.d.ts +34 -0
  269. package/dist/ws-broadcast.js +85 -0
  270. package/dist/ws-connection-limits.d.ts +12 -0
  271. package/dist/ws-connection-limits.js +44 -0
  272. package/dist/ws-handler-utils.d.ts +32 -0
  273. package/dist/ws-handler-utils.js +139 -0
  274. package/dist/ws-handler.js +294 -164
  275. package/dist/ws-metrics-server.d.ts +9 -0
  276. package/dist/ws-metrics-server.js +31 -0
  277. package/dist/ws-server.js +41 -1
  278. package/package.json +51 -34
@@ -6,8 +6,8 @@ export interface BezierMoveOptions {
6
6
  steps?: number;
7
7
  }
8
8
  /**
9
- * Move the mouse from `from` to `to` along a cubic Bezier curve with natural
10
- * human-like motion: ease-in-out timing, randomized control points, micro-jitter.
9
+ * Move the mouse from `from` to `to` along a cubic Bezier curve with
10
+ * smooth controlled motion: ease-in-out timing, gentle proportional curves.
11
11
  */
12
12
  export declare function moveMouse(page: Page, from: {
13
13
  x: number;
@@ -14,8 +14,8 @@ function randomOffset(scale) {
14
14
  return (Math.random() - 0.5) * 2 * scale;
15
15
  }
16
16
  /**
17
- * Move the mouse from `from` to `to` along a cubic Bezier curve with natural
18
- * human-like motion: ease-in-out timing, randomized control points, micro-jitter.
17
+ * Move the mouse from `from` to `to` along a cubic Bezier curve with
18
+ * smooth controlled motion: ease-in-out timing, gentle proportional curves.
19
19
  */
20
20
  export async function moveMouse(page, from, to, options = {}) {
21
21
  const dx = to.x - from.x;
@@ -23,35 +23,49 @@ export async function moveMouse(page, from, to, options = {}) {
23
23
  const distance = Math.sqrt(dx * dx + dy * dy);
24
24
  if (distance < 2)
25
25
  return; // Already there
26
- const steps = options.steps ?? 30;
27
- const durationMs = options.durationMs ?? Math.min(900, Math.max(350, distance * 1.2));
26
+ const durationMs = options.durationMs ?? Math.min(700, Math.max(80, Math.sqrt(distance) * 28));
27
+ const steps = options.steps ?? Math.max(4, Math.ceil(durationMs / 16));
28
28
  const msPerStep = durationMs / steps;
29
- // Randomize control points to create a natural curve.
30
- // Enforce a minimum arc so short-distance moves still curve visibly.
31
- const perpScale = Math.max(30, Math.min(distance * 0.3, 80));
32
- const p1 = {
33
- x: from.x + dx * 0.25 + randomOffset(perpScale),
34
- y: from.y + dy * 0.25 + randomOffset(perpScale),
35
- };
36
- const p2 = {
37
- x: from.x + dx * 0.75 + randomOffset(perpScale),
38
- y: from.y + dy * 0.75 + randomOffset(perpScale),
39
- };
29
+ // Control points: straight line for short moves, gentle proportional arc for longer ones.
30
+ let p1;
31
+ let p2;
32
+ if (distance < 30) {
33
+ p1 = { x: from.x + dx * 0.33, y: from.y + dy * 0.33 };
34
+ p2 = { x: from.x + dx * 0.67, y: from.y + dy * 0.67 };
35
+ }
36
+ else {
37
+ const perpScale = Math.min(distance * 0.12, 60);
38
+ p1 = {
39
+ x: from.x + dx * 0.25 + randomOffset(perpScale),
40
+ y: from.y + dy * 0.25 + randomOffset(perpScale),
41
+ };
42
+ p2 = {
43
+ x: from.x + dx * 0.75 + randomOffset(perpScale),
44
+ y: from.y + dy * 0.75 + randomOffset(perpScale),
45
+ };
46
+ }
40
47
  for (let i = 1; i <= steps; i++) {
41
48
  const linearT = i / steps;
42
49
  const t = easeInOut(linearT);
43
50
  const point = cubicBezier(from, p1, p2, to, t);
44
- // Add micro-jitter to simulate hand imprecision
45
- const jitter = Math.max(0, 1 - linearT) * 1.5; // jitter decreases near destination
46
- const jx = randomOffset(jitter);
47
- const jy = randomOffset(jitter);
48
- await page.mouse.move(point.x + jx, point.y + jy);
51
+ const mx = Math.round(point.x);
52
+ const my = Math.round(point.y);
53
+ await page.mouse.move(mx, my);
54
+ // CDP mouse.move doesn't fire DOM mousemove — update overlay directly
55
+ await page.evaluate(({ x, y }) => {
56
+ if (typeof window.__akMoveCursor === 'function')
57
+ window.__akMoveCursor(x, y);
58
+ }, { x: mx, y: my }).catch(() => { });
49
59
  if (msPerStep > 1) {
50
60
  await page.waitForTimeout(msPerStep);
51
61
  }
52
62
  }
53
63
  // Final move to exact destination (no jitter)
54
64
  await page.mouse.move(to.x, to.y);
65
+ await page.evaluate(({ x, y }) => {
66
+ if (typeof window.__akMoveCursor === 'function')
67
+ window.__akMoveCursor(x, y);
68
+ }, { x: Math.round(to.x), y: Math.round(to.y) }).catch(() => { });
55
69
  }
56
70
  /**
57
71
  * Move the mouse to `target` with Bezier curve animation and click.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Capture Agent — Shared Opcode Actions
3
+ *
4
+ * Deterministic action dispatch shared by the main opcode runner and the
5
+ * recovery chain so both paths execute the same behavior.
6
+ */
7
+ import type { ExecutionOpcode, MockDataGroup, RuntimeAdapter, VariantSpec } from './execution-types.js';
8
+ export interface OpcodeActionContext {
9
+ currentVariant?: VariantSpec;
10
+ /** Mock data groups available to INJECT_MOCK_DATA. Plumbed from ExecutionProgram. */
11
+ mockDataGroups?: MockDataGroup[];
12
+ /**
13
+ * Live credentials used to substitute {{email}}/{{password}}/{{loginUrl}}
14
+ * placeholders in TYPE / NAVIGATE opcodes. Plumbed from
15
+ * `program.preconditions.credentials`.
16
+ */
17
+ credentials?: {
18
+ email?: string;
19
+ password?: string;
20
+ loginUrl?: string;
21
+ };
22
+ }
23
+ /**
24
+ * Substitute credential placeholders inside opcode text fields.
25
+ * Only the {{email}}, {{password}} and {{loginUrl}} tokens are replaced.
26
+ * No-op if no credentials are present or the text contains no placeholder.
27
+ */
28
+ export declare function substituteCredentialPlaceholders(text: string, credentials?: OpcodeActionContext['credentials']): string;
29
+ /**
30
+ * Returns the list of credential placeholders (`{{email}}`, `{{password}}`,
31
+ * `{{loginUrl}}`) that the input string references but the provided
32
+ * `credentials` object does NOT have a non-empty value for. Used by the
33
+ * opcode dispatcher to fail loudly with a clear message when a NAVIGATE /
34
+ * TYPE / etc. would otherwise resolve to an empty string and crash
35
+ * Playwright with a confusing "Cannot navigate to invalid URL" error.
36
+ */
37
+ export declare function findUnresolvedCredentialPlaceholders(text: string, credentials?: OpcodeActionContext['credentials']): string[];
38
+ export interface OpcodeActionResult {
39
+ success: boolean;
40
+ error?: string;
41
+ }
42
+ export declare function executeOpcodeCoreAction(opcode: ExecutionOpcode, adapter: RuntimeAdapter, context?: OpcodeActionContext): Promise<OpcodeActionResult>;
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Capture Agent — Shared Opcode Actions
3
+ *
4
+ * Deterministic action dispatch shared by the main opcode runner and the
5
+ * recovery chain so both paths execute the same behavior.
6
+ */
7
+ import { VARIANT_PLACEHOLDER } from './execution-types.js';
8
+ import { dismissAllOverlays } from './overlay-engine.js';
9
+ /**
10
+ * Substitute credential placeholders inside opcode text fields.
11
+ * Only the {{email}}, {{password}} and {{loginUrl}} tokens are replaced.
12
+ * No-op if no credentials are present or the text contains no placeholder.
13
+ */
14
+ export function substituteCredentialPlaceholders(text, credentials) {
15
+ if (typeof text !== 'string' || !text.includes('{{')) {
16
+ return text;
17
+ }
18
+ return text
19
+ .replaceAll('{{email}}', credentials?.email ?? '')
20
+ .replaceAll('{{password}}', credentials?.password ?? '')
21
+ .replaceAll('{{loginUrl}}', credentials?.loginUrl ?? '');
22
+ }
23
+ /**
24
+ * Returns the list of credential placeholders (`{{email}}`, `{{password}}`,
25
+ * `{{loginUrl}}`) that the input string references but the provided
26
+ * `credentials` object does NOT have a non-empty value for. Used by the
27
+ * opcode dispatcher to fail loudly with a clear message when a NAVIGATE /
28
+ * TYPE / etc. would otherwise resolve to an empty string and crash
29
+ * Playwright with a confusing "Cannot navigate to invalid URL" error.
30
+ */
31
+ export function findUnresolvedCredentialPlaceholders(text, credentials) {
32
+ if (typeof text !== 'string' || !text.includes('{{'))
33
+ return [];
34
+ const missing = [];
35
+ if (text.includes('{{email}}') && !credentials?.email?.trim()) {
36
+ missing.push('{{email}}');
37
+ }
38
+ if (text.includes('{{password}}') && !credentials?.password) {
39
+ missing.push('{{password}}');
40
+ }
41
+ if (text.includes('{{loginUrl}}') && !credentials?.loginUrl?.trim()) {
42
+ missing.push('{{loginUrl}}');
43
+ }
44
+ return missing;
45
+ }
46
+ export async function executeOpcodeCoreAction(opcode, adapter, context = {}) {
47
+ try {
48
+ switch (opcode.kind) {
49
+ case 'NAVIGATE': {
50
+ const missing = findUnresolvedCredentialPlaceholders(opcode.url, context.credentials);
51
+ if (missing.length > 0) {
52
+ throw new Error(`NAVIGATE url contains placeholder(s) ${missing.join(', ')} but the preset's credentials are missing the matching value(s). ` +
53
+ `Open the preset editor and fill in the credentials (${missing.map((p) => p.replace(/[{}]/g, '')).join(', ')}), or remove the placeholder from the program.`);
54
+ }
55
+ const url = substituteCredentialPlaceholders(opcode.url, context.credentials);
56
+ if (!url.trim()) {
57
+ throw new Error(`NAVIGATE url resolved to an empty string (original: "${opcode.url}")`);
58
+ }
59
+ await adapter.navigate(url);
60
+ break;
61
+ }
62
+ case 'DISMISS_OVERLAYS':
63
+ await dismissAllOverlays(adapter);
64
+ break;
65
+ case 'CLICK':
66
+ try {
67
+ await adapter.click(opcode.selector, opcode.button ? { button: opcode.button } : undefined);
68
+ }
69
+ catch (error) {
70
+ if (!opcode.target || !adapter.clickByTarget)
71
+ throw error;
72
+ await adapter.clickByTarget({
73
+ selector: opcode.selector,
74
+ target: opcode.target,
75
+ selectorAlternates: opcode.selectorAlternates,
76
+ });
77
+ }
78
+ break;
79
+ case 'TYPE': {
80
+ const rawText = (opcode.textByLocale && context.currentVariant?.locale
81
+ ? opcode.textByLocale[context.currentVariant.locale] ?? opcode.text
82
+ : opcode.text);
83
+ const text = substituteCredentialPlaceholders(rawText, context.credentials);
84
+ try {
85
+ await adapter.type(opcode.selector, text, opcode.clearFirst);
86
+ }
87
+ catch (error) {
88
+ if (!opcode.target || !adapter.typeByTarget)
89
+ throw error;
90
+ await adapter.typeByTarget({
91
+ selector: opcode.selector,
92
+ target: opcode.target,
93
+ selectorAlternates: opcode.selectorAlternates,
94
+ }, text, opcode.clearFirst);
95
+ }
96
+ break;
97
+ }
98
+ case 'PRESS_KEY':
99
+ await adapter.pressKey(opcode.key);
100
+ break;
101
+ case 'WAIT_FOR': {
102
+ const waitSelector = opcode.selector;
103
+ if (waitSelector) {
104
+ const found = await adapter.waitFor({
105
+ selector: waitSelector,
106
+ state: opcode.state,
107
+ timeoutMs: opcode.timeoutMs,
108
+ });
109
+ if (!found) {
110
+ return { success: false, error: `element "${waitSelector}" not found within timeout` };
111
+ }
112
+ }
113
+ else if (opcode.target && adapter.waitForTarget) {
114
+ const found = await adapter.waitForTarget({ target: opcode.target }, opcode.timeoutMs);
115
+ if (!found) {
116
+ return { success: false, error: `target "${opcode.target.text ?? opcode.target.role ?? 'unknown'}" not found within timeout` };
117
+ }
118
+ }
119
+ else {
120
+ return { success: false, error: 'WAIT_FOR needs a selector or target' };
121
+ }
122
+ break;
123
+ }
124
+ case 'SET_LOCALE':
125
+ return applyLocaleOpcode(opcode, adapter, context.currentVariant);
126
+ case 'SET_THEME':
127
+ return applyThemeOpcode(opcode, adapter, context.currentVariant);
128
+ case 'SCROLL':
129
+ if (opcode.targetSelector) {
130
+ await adapter.scrollIntoView(opcode.targetSelector);
131
+ }
132
+ else if (opcode.target && adapter.scrollIntoViewByTarget) {
133
+ await adapter.scrollIntoViewByTarget({ target: opcode.target });
134
+ }
135
+ else {
136
+ await adapter.scroll(opcode.direction, opcode.amount);
137
+ }
138
+ break;
139
+ case 'HOVER':
140
+ if (!adapter.hover)
141
+ return { success: false, error: 'adapter does not support HOVER' };
142
+ try {
143
+ await adapter.hover(opcode.selector);
144
+ }
145
+ catch (error) {
146
+ if (!opcode.target || !adapter.hoverByTarget)
147
+ throw error;
148
+ await adapter.hoverByTarget({
149
+ selector: opcode.selector,
150
+ target: opcode.target,
151
+ selectorAlternates: opcode.selectorAlternates,
152
+ });
153
+ }
154
+ break;
155
+ case 'SELECT_OPTION':
156
+ if (!adapter.selectOption)
157
+ return { success: false, error: 'adapter does not support SELECT_OPTION' };
158
+ await adapter.selectOption(opcode.selector, {
159
+ label: opcode.optionLabel,
160
+ value: opcode.optionValue,
161
+ index: opcode.optionIndex,
162
+ });
163
+ break;
164
+ case 'CHECK':
165
+ if (!adapter.check)
166
+ return { success: false, error: 'adapter does not support CHECK' };
167
+ await adapter.check(opcode.selector, opcode.checked);
168
+ break;
169
+ case 'DOUBLE_CLICK':
170
+ if (!adapter.doubleClick)
171
+ return { success: false, error: 'adapter does not support DOUBLE_CLICK' };
172
+ await adapter.doubleClick(opcode.selector);
173
+ break;
174
+ case 'CLONE_ELEMENT':
175
+ if (!adapter.cloneElement) {
176
+ return { success: false, error: 'adapter does not support CLONE_ELEMENT' };
177
+ }
178
+ await adapter.cloneElement({
179
+ sourceSelector: opcode.sourceSelector,
180
+ containerSelector: opcode.containerSelector,
181
+ count: opcode.count,
182
+ removeSource: opcode.removeSource,
183
+ });
184
+ break;
185
+ case 'REMOVE_ELEMENT':
186
+ if (!adapter.removeElement) {
187
+ return { success: false, error: 'adapter does not support REMOVE_ELEMENT' };
188
+ }
189
+ await adapter.removeElement({ selector: opcode.selector });
190
+ break;
191
+ case 'SET_ATTRIBUTE':
192
+ if (!adapter.setAttribute) {
193
+ return { success: false, error: 'adapter does not support SET_ATTRIBUTE' };
194
+ }
195
+ await adapter.setAttribute({
196
+ selector: opcode.selector,
197
+ attribute: opcode.attribute,
198
+ value: opcode.value,
199
+ });
200
+ break;
201
+ case 'INJECT_MOCK_DATA':
202
+ return applyInjectMockDataOpcode(opcode, adapter, context);
203
+ case 'CAPTURE_SCREENSHOT':
204
+ case 'CAPTURE_DOM':
205
+ case 'CAPTURE_FRAGMENT':
206
+ case 'BEGIN_CLIP':
207
+ case 'END_CLIP':
208
+ case 'ASSERT_ROUTE':
209
+ case 'ASSERT_SURFACE':
210
+ break;
211
+ default: {
212
+ const _exhaustive = opcode;
213
+ return { success: false, error: `unknown opcode kind: ${opcode.kind}` };
214
+ }
215
+ }
216
+ return { success: true };
217
+ }
218
+ catch (err) {
219
+ return {
220
+ success: false,
221
+ error: err instanceof Error ? err.message : String(err),
222
+ };
223
+ }
224
+ }
225
+ function resolveVariantPlaceholder(value, replacement, fieldName) {
226
+ if (value !== VARIANT_PLACEHOLDER)
227
+ return { resolved: value };
228
+ if (!replacement) {
229
+ return { error: `${fieldName} uses ${VARIANT_PLACEHOLDER} but no variant value is available` };
230
+ }
231
+ return { resolved: replacement };
232
+ }
233
+ function resolveStorageHintValues(hints, replacement, fieldName) {
234
+ const resolvedHints = [];
235
+ for (const hint of hints) {
236
+ const result = resolveVariantPlaceholder(hint.value, replacement, fieldName);
237
+ if ('error' in result) {
238
+ return { error: result.error };
239
+ }
240
+ resolvedHints.push({ ...hint, value: result.resolved });
241
+ }
242
+ return { resolvedHints };
243
+ }
244
+ async function applyLocaleOpcode(opcode, adapter, currentVariant) {
245
+ const localeResult = resolveVariantPlaceholder(opcode.locale, currentVariant?.locale, 'SET_LOCALE.locale');
246
+ if ('error' in localeResult)
247
+ return { success: false, error: localeResult.error };
248
+ const resolvedLocale = localeResult.resolved;
249
+ if (opcode.method === 'browser_context') {
250
+ await adapter.setLocale(resolvedLocale);
251
+ if (adapter.reloadPage) {
252
+ await adapter.reloadPage();
253
+ }
254
+ return { success: true };
255
+ }
256
+ if (opcode.method === 'ui_interaction') {
257
+ if (!opcode.selector) {
258
+ return { success: false, error: 'SET_LOCALE ui_interaction requires selector' };
259
+ }
260
+ await adapter.click(opcode.selector);
261
+ return { success: true };
262
+ }
263
+ if (!opcode.storageHints || opcode.storageHints.length === 0 || !adapter.writeStorageHint) {
264
+ return { success: false, error: 'SET_LOCALE storage mode requires storageHints and adapter storage support' };
265
+ }
266
+ const resolvedHints = resolveStorageHintValues(opcode.storageHints, currentVariant?.locale, 'SET_LOCALE.storageHints.value');
267
+ if ('error' in resolvedHints) {
268
+ return { success: false, error: resolvedHints.error };
269
+ }
270
+ const writes = await Promise.all(resolvedHints.resolvedHints.map((hint) => adapter.writeStorageHint({
271
+ storage: hint.storage,
272
+ key: hint.key,
273
+ value: hint.value,
274
+ kind: 'locale',
275
+ })));
276
+ if (!writes.some(Boolean)) {
277
+ return { success: false, error: 'SET_LOCALE storage hints did not match any writable locale keys' };
278
+ }
279
+ if (adapter.reloadPage) {
280
+ await adapter.reloadPage();
281
+ }
282
+ return { success: true };
283
+ }
284
+ /**
285
+ * Orchestrates mock data injection for one group. Applies BOTH delivery
286
+ * mechanisms additively when their fields are provided:
287
+ *
288
+ * 1. **Clone**: clones a template DOM element N times and writes each slot
289
+ * value into the cloned descendants. Provides instant visual feedback
290
+ * even before the app re-renders.
291
+ *
292
+ * 2. **Trigger**: writes the JSON-encoded `defaultValues` into a hidden
293
+ * input the user's app exposed, then clicks a hidden trigger button. The
294
+ * user's `onClick` handler reads the input and re-renders the widget.
295
+ * Survives React re-renders that would otherwise clobber clones.
296
+ *
297
+ * The two mechanisms are independent and complementary. An opcode typically
298
+ * carries fields for both. Each runs in isolation: a failure of one does not
299
+ * prevent the other from running. As long as AT LEAST ONE produces a
300
+ * successful application, the group is recorded as 'applied'. If both fail
301
+ * (or neither was wired), the opcode returns failure and the runner's
302
+ * soft-skip branch converts it to `status: 'skipped'`.
303
+ */
304
+ async function applyInjectMockDataOpcode(opcode, adapter, context) {
305
+ const group = context.mockDataGroups?.find((g) => g.name === opcode.groupName);
306
+ if (!group) {
307
+ return {
308
+ success: false,
309
+ error: `INJECT_MOCK_DATA: group "${opcode.groupName}" not found in program.mockDataGroups`,
310
+ };
311
+ }
312
+ if (group.defaultValues.length === 0) {
313
+ return {
314
+ success: false,
315
+ error: `INJECT_MOCK_DATA: group "${opcode.groupName}" has no defaultValues`,
316
+ };
317
+ }
318
+ const hasCloneFields = Boolean(opcode.containerSelector && opcode.slotMappings && opcode.slotMappings.length > 0);
319
+ const hasTriggerFields = Boolean(opcode.inputSelector && opcode.triggerSelector);
320
+ if (!hasCloneFields && !hasTriggerFields) {
321
+ return {
322
+ success: false,
323
+ error: `INJECT_MOCK_DATA "${opcode.groupName}": no clone fields and no trigger fields provided`,
324
+ };
325
+ }
326
+ const errors = [];
327
+ let anySucceeded = false;
328
+ // Apply clone first so the user sees an instant paint of the mocked DOM,
329
+ // even before the app's state-driven re-render fires.
330
+ if (hasCloneFields) {
331
+ try {
332
+ const cloneResult = await applyInjectMockDataCloneStrategy(opcode, adapter, group);
333
+ if (cloneResult.success) {
334
+ anySucceeded = true;
335
+ }
336
+ else if (cloneResult.error) {
337
+ errors.push(`clone: ${cloneResult.error}`);
338
+ }
339
+ }
340
+ catch (err) {
341
+ errors.push(`clone: ${err instanceof Error ? err.message : String(err)}`);
342
+ }
343
+ }
344
+ // Apply trigger second so the app's state takes over and survives any
345
+ // future re-renders that would clobber the cloned DOM.
346
+ if (hasTriggerFields) {
347
+ try {
348
+ const triggerResult = await applyInjectMockDataTriggerStrategy(opcode, adapter, group);
349
+ if (triggerResult.success) {
350
+ anySucceeded = true;
351
+ }
352
+ else if (triggerResult.error) {
353
+ errors.push(`trigger: ${triggerResult.error}`);
354
+ }
355
+ }
356
+ catch (err) {
357
+ errors.push(`trigger: ${err instanceof Error ? err.message : String(err)}`);
358
+ }
359
+ }
360
+ if (anySucceeded) {
361
+ return { success: true };
362
+ }
363
+ return {
364
+ success: false,
365
+ error: `INJECT_MOCK_DATA "${opcode.groupName}" failed: ${errors.join('; ')}`,
366
+ };
367
+ }
368
+ async function applyInjectMockDataCloneStrategy(opcode, adapter, group) {
369
+ if (!adapter.cloneElement || !adapter.setAttribute || !adapter.setTextContent) {
370
+ return {
371
+ success: false,
372
+ error: 'adapter does not support cloneElement / setAttribute / setTextContent',
373
+ };
374
+ }
375
+ if (!opcode.containerSelector) {
376
+ return {
377
+ success: false,
378
+ error: 'containerSelector is required',
379
+ };
380
+ }
381
+ if (!opcode.slotMappings || opcode.slotMappings.length === 0) {
382
+ return {
383
+ success: false,
384
+ error: 'slotMappings are required',
385
+ };
386
+ }
387
+ const rowCount = opcode.count ?? group.defaultValues.length;
388
+ const templateSelector = opcode.templateSelector ?? `${opcode.containerSelector} > *:first-child`;
389
+ // Clone N copies of the template element. The runner short-circuits to
390
+ // 'skipped' if cloneElement throws (selector miss).
391
+ await adapter.cloneElement({
392
+ sourceSelector: templateSelector,
393
+ containerSelector: opcode.containerSelector,
394
+ count: rowCount,
395
+ removeSource: opcode.removeTemplate,
396
+ });
397
+ // If the group is configured to replace existing data, remove every child
398
+ // of the container that is NOT a clone (placeholders, real items, empty
399
+ // states). This must run AFTER cloning so we know which children to keep.
400
+ if (group.replaceExisting && adapter.removeElement) {
401
+ try {
402
+ await adapter.removeElement({
403
+ selector: `${opcode.containerSelector} > *:not([data-ak-mock-clone])`,
404
+ });
405
+ }
406
+ catch {
407
+ // non-blocking — if nothing to remove, skip
408
+ }
409
+ }
410
+ // Each clone is tagged by cloneElement with `data-ak-mock-clone="<i>"`,
411
+ // so we address them by that attribute instead of nth-child math. This
412
+ // is robust against the container holding non-template siblings (e.g.
413
+ // empty-state placeholders) that would otherwise shift indices.
414
+ for (let i = 0; i < rowCount; i++) {
415
+ const row = group.defaultValues[i % group.defaultValues.length];
416
+ const rowSelector = `${opcode.containerSelector} > [data-ak-mock-clone="${i}"]`;
417
+ for (const mapping of opcode.slotMappings) {
418
+ const value = row[mapping.slot];
419
+ if (value === undefined)
420
+ continue;
421
+ const targetSelector = `${rowSelector} ${mapping.selector}`;
422
+ if (mapping.attribute) {
423
+ await adapter.setAttribute({
424
+ selector: targetSelector,
425
+ attribute: mapping.attribute,
426
+ value,
427
+ });
428
+ }
429
+ else {
430
+ await adapter.setTextContent({
431
+ selector: targetSelector,
432
+ text: value,
433
+ });
434
+ }
435
+ }
436
+ }
437
+ return { success: true };
438
+ }
439
+ async function applyInjectMockDataTriggerStrategy(opcode, adapter, group) {
440
+ if (!adapter.setInputValue || !adapter.clickHidden) {
441
+ return {
442
+ success: false,
443
+ error: 'adapter does not support setInputValue / clickHidden',
444
+ };
445
+ }
446
+ if (!opcode.inputSelector) {
447
+ return {
448
+ success: false,
449
+ error: 'inputSelector is required',
450
+ };
451
+ }
452
+ if (!opcode.triggerSelector) {
453
+ return {
454
+ success: false,
455
+ error: 'triggerSelector is required',
456
+ };
457
+ }
458
+ // Serialize the seed values. The user's onClick handler will JSON.parse this
459
+ // and feed it into its render function (e.g. setChartData(parsed)).
460
+ const valueJson = JSON.stringify(group.defaultValues);
461
+ // Write into the hidden input via the React-aware native setter so the
462
+ // app's controlled-component bookkeeping picks up the change.
463
+ await adapter.setInputValue({
464
+ selector: opcode.inputSelector,
465
+ value: valueJson,
466
+ });
467
+ // Click the hidden trigger via the native .click() method so React's
468
+ // onClick handler fires even if the button is display:none.
469
+ await adapter.clickHidden({
470
+ selector: opcode.triggerSelector,
471
+ });
472
+ return { success: true };
473
+ }
474
+ async function applyThemeOpcode(opcode, adapter, currentVariant) {
475
+ const themeResult = resolveVariantPlaceholder(opcode.theme, currentVariant?.theme, 'SET_THEME.theme');
476
+ if ('error' in themeResult)
477
+ return { success: false, error: themeResult.error };
478
+ const resolvedTheme = themeResult.resolved;
479
+ if (opcode.method === 'color_scheme') {
480
+ await adapter.setColorScheme(resolvedTheme);
481
+ return { success: true };
482
+ }
483
+ if (opcode.method === 'ui_interaction') {
484
+ if (!opcode.selector) {
485
+ return { success: false, error: 'SET_THEME ui_interaction requires selector' };
486
+ }
487
+ await adapter.click(opcode.selector);
488
+ return { success: true };
489
+ }
490
+ if (!opcode.storageHints || opcode.storageHints.length === 0 || !adapter.writeStorageHint) {
491
+ return { success: false, error: 'SET_THEME storage mode requires storageHints and adapter storage support' };
492
+ }
493
+ const resolvedHints = resolveStorageHintValues(opcode.storageHints, currentVariant?.theme, 'SET_THEME.storageHints.value');
494
+ if ('error' in resolvedHints) {
495
+ return { success: false, error: resolvedHints.error };
496
+ }
497
+ const writes = await Promise.all(resolvedHints.resolvedHints.map((hint) => adapter.writeStorageHint({
498
+ storage: hint.storage,
499
+ key: hint.key,
500
+ value: hint.value,
501
+ kind: 'theme',
502
+ })));
503
+ if (!writes.some(Boolean)) {
504
+ return { success: false, error: 'SET_THEME storage hints did not match any writable theme keys' };
505
+ }
506
+ if (adapter.reloadPage) {
507
+ await adapter.reloadPage();
508
+ }
509
+ return { success: true };
510
+ }
511
+ //# sourceMappingURL=opcode-actions.js.map