@tangle-network/browser-agent-driver 0.33.3 → 0.35.0

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 (489) hide show
  1. package/README.md +42 -1
  2. package/discovery/llms.txt +27 -0
  3. package/discovery/tangle-agent.json +36 -0
  4. package/dist/brain/action-parse.d.ts +16 -0
  5. package/dist/brain/action-parse.d.ts.map +1 -0
  6. package/dist/brain/action-parse.js +191 -0
  7. package/dist/brain/action-parse.js.map +1 -0
  8. package/dist/brain/decide.d.ts +58 -0
  9. package/dist/brain/decide.d.ts.map +1 -0
  10. package/dist/brain/decide.js +418 -0
  11. package/dist/brain/decide.js.map +1 -0
  12. package/dist/brain/history-compact.d.ts +19 -0
  13. package/dist/brain/history-compact.d.ts.map +1 -0
  14. package/dist/brain/history-compact.js +153 -0
  15. package/dist/brain/history-compact.js.map +1 -0
  16. package/dist/brain/index.d.ts +78 -152
  17. package/dist/brain/index.d.ts.map +1 -1
  18. package/dist/brain/index.js +62 -2268
  19. package/dist/brain/index.js.map +1 -1
  20. package/dist/brain/model-client.d.ts +101 -0
  21. package/dist/brain/model-client.d.ts.map +1 -0
  22. package/dist/brain/model-client.js +391 -0
  23. package/dist/brain/model-client.js.map +1 -0
  24. package/dist/brain/plan.d.ts +57 -0
  25. package/dist/brain/plan.d.ts.map +1 -0
  26. package/dist/brain/plan.js +194 -0
  27. package/dist/brain/plan.js.map +1 -0
  28. package/dist/brain/prompts.d.ts +37 -0
  29. package/dist/brain/prompts.d.ts.map +1 -0
  30. package/dist/brain/prompts.js +455 -0
  31. package/dist/brain/prompts.js.map +1 -0
  32. package/dist/brain/provider-fetch.d.ts +26 -0
  33. package/dist/brain/provider-fetch.d.ts.map +1 -0
  34. package/dist/brain/provider-fetch.js +45 -0
  35. package/dist/brain/provider-fetch.js.map +1 -0
  36. package/dist/brain/snapshot-budget.d.ts +20 -0
  37. package/dist/brain/snapshot-budget.d.ts.map +1 -0
  38. package/dist/brain/snapshot-budget.js +202 -0
  39. package/dist/brain/snapshot-budget.js.map +1 -0
  40. package/dist/brain/system-prompt.d.ts +67 -0
  41. package/dist/brain/system-prompt.d.ts.map +1 -0
  42. package/dist/brain/system-prompt.js +128 -0
  43. package/dist/brain/system-prompt.js.map +1 -0
  44. package/dist/brain/tasks/design-audit.d.ts +33 -0
  45. package/dist/brain/tasks/design-audit.d.ts.map +1 -0
  46. package/dist/brain/tasks/design-audit.js +100 -0
  47. package/dist/brain/tasks/design-audit.js.map +1 -0
  48. package/dist/brain/tasks/evaluate.d.ts +25 -0
  49. package/dist/brain/tasks/evaluate.d.ts.map +1 -0
  50. package/dist/brain/tasks/evaluate.js +55 -0
  51. package/dist/brain/tasks/evaluate.js.map +1 -0
  52. package/dist/brain/tasks/goal-verification.d.ts +32 -0
  53. package/dist/brain/tasks/goal-verification.d.ts.map +1 -0
  54. package/dist/brain/tasks/goal-verification.js +68 -0
  55. package/dist/brain/tasks/goal-verification.js.map +1 -0
  56. package/dist/brain/tasks/knowledge.d.ts +26 -0
  57. package/dist/brain/tasks/knowledge.d.ts.map +1 -0
  58. package/dist/brain/tasks/knowledge.js +56 -0
  59. package/dist/brain/tasks/knowledge.js.map +1 -0
  60. package/dist/brain/tasks/link-scout.d.ts +37 -0
  61. package/dist/brain/tasks/link-scout.d.ts.map +1 -0
  62. package/dist/brain/tasks/link-scout.js +69 -0
  63. package/dist/brain/tasks/link-scout.js.map +1 -0
  64. package/dist/brain/types.d.ts +55 -0
  65. package/dist/brain/types.d.ts.map +1 -0
  66. package/dist/brain/types.js +7 -0
  67. package/dist/brain/types.js.map +1 -0
  68. package/dist/browser/stealth-init-script.d.ts +9 -0
  69. package/dist/browser/stealth-init-script.d.ts.map +1 -0
  70. package/dist/browser/stealth-init-script.js +128 -0
  71. package/dist/browser/stealth-init-script.js.map +1 -0
  72. package/dist/browser/storage-state.d.ts +29 -0
  73. package/dist/browser/storage-state.d.ts.map +1 -0
  74. package/dist/browser/storage-state.js +47 -0
  75. package/dist/browser/storage-state.js.map +1 -0
  76. package/dist/browser-launch.d.ts +6 -0
  77. package/dist/browser-launch.d.ts.map +1 -1
  78. package/dist/browser-launch.js +3 -0
  79. package/dist/browser-launch.js.map +1 -1
  80. package/dist/cli/args.d.ts +141 -0
  81. package/dist/cli/args.d.ts.map +1 -0
  82. package/dist/cli/args.js +167 -0
  83. package/dist/cli/args.js.map +1 -0
  84. package/dist/cli/benchmark-sync.d.ts +9 -0
  85. package/dist/cli/benchmark-sync.d.ts.map +1 -0
  86. package/dist/cli/benchmark-sync.js +43 -0
  87. package/dist/cli/benchmark-sync.js.map +1 -0
  88. package/dist/cli/build-config.d.ts +11 -0
  89. package/dist/cli/build-config.d.ts.map +1 -0
  90. package/dist/cli/build-config.js +355 -0
  91. package/dist/cli/build-config.js.map +1 -0
  92. package/dist/cli/commands/auth.d.ts +3 -0
  93. package/dist/cli/commands/auth.d.ts.map +1 -0
  94. package/dist/cli/commands/auth.js +36 -0
  95. package/dist/cli/commands/auth.js.map +1 -0
  96. package/dist/cli/commands/chrome-debug.d.ts +7 -0
  97. package/dist/cli/commands/chrome-debug.d.ts.map +1 -0
  98. package/dist/cli/commands/chrome-debug.js +11 -0
  99. package/dist/cli/commands/chrome-debug.js.map +1 -0
  100. package/dist/cli/commands/design-audit.d.ts +3 -0
  101. package/dist/cli/commands/design-audit.d.ts.map +1 -0
  102. package/dist/cli/commands/design-audit.js +68 -0
  103. package/dist/cli/commands/design-audit.js.map +1 -0
  104. package/dist/cli/commands/preview.d.ts +14 -0
  105. package/dist/cli/commands/preview.d.ts.map +1 -0
  106. package/dist/cli/commands/preview.js +31 -0
  107. package/dist/cli/commands/preview.js.map +1 -0
  108. package/dist/cli/commands/run.d.ts +3 -0
  109. package/dist/cli/commands/run.d.ts.map +1 -0
  110. package/dist/cli/commands/run.js +740 -0
  111. package/dist/cli/commands/run.js.map +1 -0
  112. package/dist/cli/commands/runs.d.ts +8 -0
  113. package/dist/cli/commands/runs.d.ts.map +1 -0
  114. package/dist/cli/commands/runs.js +35 -0
  115. package/dist/cli/commands/runs.js.map +1 -0
  116. package/dist/cli/commands/share.d.ts +10 -0
  117. package/dist/cli/commands/share.d.ts.map +1 -0
  118. package/dist/cli/commands/share.js +32 -0
  119. package/dist/cli/commands/share.js.map +1 -0
  120. package/dist/cli/commands/showcase.d.ts +17 -0
  121. package/dist/cli/commands/showcase.d.ts.map +1 -0
  122. package/dist/cli/commands/showcase.js +20 -0
  123. package/dist/cli/commands/showcase.js.map +1 -0
  124. package/dist/cli/commands/snapshot.d.ts +12 -0
  125. package/dist/cli/commands/snapshot.d.ts.map +1 -0
  126. package/dist/cli/commands/snapshot.js +24 -0
  127. package/dist/cli/commands/snapshot.js.map +1 -0
  128. package/dist/cli/commands/view.d.ts +7 -0
  129. package/dist/cli/commands/view.d.ts.map +1 -0
  130. package/dist/cli/commands/view.js +23 -0
  131. package/dist/cli/commands/view.js.map +1 -0
  132. package/dist/cli/constants.d.ts +6 -0
  133. package/dist/cli/constants.d.ts.map +1 -0
  134. package/dist/cli/constants.js +3 -0
  135. package/dist/cli/constants.js.map +1 -0
  136. package/dist/cli/version.d.ts +7 -0
  137. package/dist/cli/version.d.ts.map +1 -0
  138. package/dist/cli/version.js +16 -0
  139. package/dist/cli/version.js.map +1 -0
  140. package/dist/cli-design-audit.d.ts +44 -36
  141. package/dist/cli-design-audit.d.ts.map +1 -1
  142. package/dist/cli-design-audit.js +137 -1646
  143. package/dist/cli-design-audit.js.map +1 -1
  144. package/dist/cli-jobs.js +2 -1
  145. package/dist/cli-jobs.js.map +1 -1
  146. package/dist/cli.js +66 -1697
  147. package/dist/cli.js.map +1 -1
  148. package/dist/config.d.ts +12 -0
  149. package/dist/config.d.ts.map +1 -1
  150. package/dist/config.js +20 -2
  151. package/dist/config.js.map +1 -1
  152. package/dist/design/audit/evolve/agent.d.ts +23 -0
  153. package/dist/design/audit/evolve/agent.d.ts.map +1 -0
  154. package/dist/design/audit/evolve/agent.js +273 -0
  155. package/dist/design/audit/evolve/agent.js.map +1 -0
  156. package/dist/design/audit/evolve/css.d.ts +10 -0
  157. package/dist/design/audit/evolve/css.d.ts.map +1 -0
  158. package/dist/design/audit/evolve/css.js +206 -0
  159. package/dist/design/audit/evolve/css.js.map +1 -0
  160. package/dist/design/audit/evolve/index.d.ts +13 -0
  161. package/dist/design/audit/evolve/index.d.ts.map +1 -0
  162. package/dist/design/audit/evolve/index.js +12 -0
  163. package/dist/design/audit/evolve/index.js.map +1 -0
  164. package/dist/design/audit/evolve/report.d.ts +3 -0
  165. package/dist/design/audit/evolve/report.d.ts.map +1 -0
  166. package/dist/design/audit/evolve/report.js +45 -0
  167. package/dist/design/audit/evolve/report.js.map +1 -0
  168. package/dist/design/audit/evolve/types.d.ts +14 -0
  169. package/dist/design/audit/evolve/types.d.ts.map +1 -0
  170. package/dist/design/audit/evolve/types.js +2 -0
  171. package/dist/design/audit/evolve/types.js.map +1 -0
  172. package/dist/design/audit/pipeline.d.ts +12 -0
  173. package/dist/design/audit/pipeline.d.ts.map +1 -1
  174. package/dist/design/audit/pipeline.js +43 -27
  175. package/dist/design/audit/pipeline.js.map +1 -1
  176. package/dist/design/audit/reference/artifact/build.d.ts +33 -0
  177. package/dist/design/audit/reference/artifact/build.d.ts.map +1 -0
  178. package/dist/design/audit/reference/artifact/build.js +64 -0
  179. package/dist/design/audit/reference/artifact/build.js.map +1 -0
  180. package/dist/design/audit/reference/artifact/render.d.ts +39 -0
  181. package/dist/design/audit/reference/artifact/render.d.ts.map +1 -0
  182. package/dist/design/audit/reference/artifact/render.js +213 -0
  183. package/dist/design/audit/reference/artifact/render.js.map +1 -0
  184. package/dist/design/audit/reference/artifact/text.d.ts +11 -0
  185. package/dist/design/audit/reference/artifact/text.d.ts.map +1 -0
  186. package/dist/design/audit/reference/artifact/text.js +20 -0
  187. package/dist/design/audit/reference/artifact/text.js.map +1 -0
  188. package/dist/design/audit/reference/artifact/to-findings.d.ts +57 -0
  189. package/dist/design/audit/reference/artifact/to-findings.d.ts.map +1 -0
  190. package/dist/design/audit/reference/artifact/to-findings.js +314 -0
  191. package/dist/design/audit/reference/artifact/to-findings.js.map +1 -0
  192. package/dist/design/audit/reference/config.d.ts +50 -0
  193. package/dist/design/audit/reference/config.d.ts.map +1 -0
  194. package/dist/design/audit/reference/config.js +108 -0
  195. package/dist/design/audit/reference/config.js.map +1 -0
  196. package/dist/design/audit/reference/contracts.d.ts +1030 -0
  197. package/dist/design/audit/reference/contracts.d.ts.map +1 -0
  198. package/dist/design/audit/reference/contracts.js +28 -0
  199. package/dist/design/audit/reference/contracts.js.map +1 -0
  200. package/dist/design/audit/reference/corpus/build.d.ts +115 -0
  201. package/dist/design/audit/reference/corpus/build.d.ts.map +1 -0
  202. package/dist/design/audit/reference/corpus/build.js +152 -0
  203. package/dist/design/audit/reference/corpus/build.js.map +1 -0
  204. package/dist/design/audit/reference/corpus/schema.d.ts +33 -0
  205. package/dist/design/audit/reference/corpus/schema.d.ts.map +1 -0
  206. package/dist/design/audit/reference/corpus/schema.js +249 -0
  207. package/dist/design/audit/reference/corpus/schema.js.map +1 -0
  208. package/dist/design/audit/reference/corpus/store.d.ts +26 -0
  209. package/dist/design/audit/reference/corpus/store.d.ts.map +1 -0
  210. package/dist/design/audit/reference/corpus/store.js +104 -0
  211. package/dist/design/audit/reference/corpus/store.js.map +1 -0
  212. package/dist/design/audit/reference/dna/delta.d.ts +21 -0
  213. package/dist/design/audit/reference/dna/delta.d.ts.map +1 -0
  214. package/dist/design/audit/reference/dna/delta.js +126 -0
  215. package/dist/design/audit/reference/dna/delta.js.map +1 -0
  216. package/dist/design/audit/reference/dna/derive.d.ts +31 -0
  217. package/dist/design/audit/reference/dna/derive.d.ts.map +1 -0
  218. package/dist/design/audit/reference/dna/derive.js +381 -0
  219. package/dist/design/audit/reference/dna/derive.js.map +1 -0
  220. package/dist/design/audit/reference/dna/descriptor.d.ts +36 -0
  221. package/dist/design/audit/reference/dna/descriptor.d.ts.map +1 -0
  222. package/dist/design/audit/reference/dna/descriptor.js +209 -0
  223. package/dist/design/audit/reference/dna/descriptor.js.map +1 -0
  224. package/dist/design/audit/reference/dna/page-adapter.d.ts +17 -0
  225. package/dist/design/audit/reference/dna/page-adapter.d.ts.map +1 -0
  226. package/dist/design/audit/reference/dna/page-adapter.js +35 -0
  227. package/dist/design/audit/reference/dna/page-adapter.js.map +1 -0
  228. package/dist/design/audit/reference/dna/scroll-capture.d.ts +93 -0
  229. package/dist/design/audit/reference/dna/scroll-capture.d.ts.map +1 -0
  230. package/dist/design/audit/reference/dna/scroll-capture.js +374 -0
  231. package/dist/design/audit/reference/dna/scroll-capture.js.map +1 -0
  232. package/dist/design/audit/reference/engine/budget.d.ts +63 -0
  233. package/dist/design/audit/reference/engine/budget.d.ts.map +1 -0
  234. package/dist/design/audit/reference/engine/budget.js +110 -0
  235. package/dist/design/audit/reference/engine/budget.js.map +1 -0
  236. package/dist/design/audit/reference/engine/core.d.ts +32 -0
  237. package/dist/design/audit/reference/engine/core.d.ts.map +1 -0
  238. package/dist/design/audit/reference/engine/core.js +261 -0
  239. package/dist/design/audit/reference/engine/core.js.map +1 -0
  240. package/dist/design/audit/reference/engine/guard.d.ts +43 -0
  241. package/dist/design/audit/reference/engine/guard.d.ts.map +1 -0
  242. package/dist/design/audit/reference/engine/guard.js +43 -0
  243. package/dist/design/audit/reference/engine/guard.js.map +1 -0
  244. package/dist/design/audit/reference/engine/score-core.d.ts +48 -0
  245. package/dist/design/audit/reference/engine/score-core.d.ts.map +1 -0
  246. package/dist/design/audit/reference/engine/score-core.js +134 -0
  247. package/dist/design/audit/reference/engine/score-core.js.map +1 -0
  248. package/dist/design/audit/reference/engine/wiring.d.ts +35 -0
  249. package/dist/design/audit/reference/engine/wiring.d.ts.map +1 -0
  250. package/dist/design/audit/reference/engine/wiring.js +80 -0
  251. package/dist/design/audit/reference/engine/wiring.js.map +1 -0
  252. package/dist/design/audit/reference/eval/taste-core.d.ts +77 -0
  253. package/dist/design/audit/reference/eval/taste-core.d.ts.map +1 -0
  254. package/dist/design/audit/reference/eval/taste-core.js +95 -0
  255. package/dist/design/audit/reference/eval/taste-core.js.map +1 -0
  256. package/dist/design/audit/reference/generate/generator.d.ts +49 -0
  257. package/dist/design/audit/reference/generate/generator.d.ts.map +1 -0
  258. package/dist/design/audit/reference/generate/generator.js +100 -0
  259. package/dist/design/audit/reference/generate/generator.js.map +1 -0
  260. package/dist/design/audit/reference/generate/parse.d.ts +28 -0
  261. package/dist/design/audit/reference/generate/parse.d.ts.map +1 -0
  262. package/dist/design/audit/reference/generate/parse.js +220 -0
  263. package/dist/design/audit/reference/generate/parse.js.map +1 -0
  264. package/dist/design/audit/reference/generate/prompt.d.ts +36 -0
  265. package/dist/design/audit/reference/generate/prompt.d.ts.map +1 -0
  266. package/dist/design/audit/reference/generate/prompt.js +172 -0
  267. package/dist/design/audit/reference/generate/prompt.js.map +1 -0
  268. package/dist/design/audit/reference/index.d.ts +26 -0
  269. package/dist/design/audit/reference/index.d.ts.map +1 -0
  270. package/dist/design/audit/reference/index.js +31 -0
  271. package/dist/design/audit/reference/index.js.map +1 -0
  272. package/dist/design/audit/reference/judge/image-clamp.d.ts +34 -0
  273. package/dist/design/audit/reference/judge/image-clamp.d.ts.map +1 -0
  274. package/dist/design/audit/reference/judge/image-clamp.js +88 -0
  275. package/dist/design/audit/reference/judge/image-clamp.js.map +1 -0
  276. package/dist/design/audit/reference/judge/pairwise.d.ts +34 -0
  277. package/dist/design/audit/reference/judge/pairwise.d.ts.map +1 -0
  278. package/dist/design/audit/reference/judge/pairwise.js +105 -0
  279. package/dist/design/audit/reference/judge/pairwise.js.map +1 -0
  280. package/dist/design/audit/reference/judge/parse.d.ts +31 -0
  281. package/dist/design/audit/reference/judge/parse.d.ts.map +1 -0
  282. package/dist/design/audit/reference/judge/parse.js +113 -0
  283. package/dist/design/audit/reference/judge/parse.js.map +1 -0
  284. package/dist/design/audit/reference/judge/prompt.d.ts +38 -0
  285. package/dist/design/audit/reference/judge/prompt.d.ts.map +1 -0
  286. package/dist/design/audit/reference/judge/prompt.js +123 -0
  287. package/dist/design/audit/reference/judge/prompt.js.map +1 -0
  288. package/dist/design/audit/reference/judge/quality.d.ts +28 -0
  289. package/dist/design/audit/reference/judge/quality.d.ts.map +1 -0
  290. package/dist/design/audit/reference/judge/quality.js +62 -0
  291. package/dist/design/audit/reference/judge/quality.js.map +1 -0
  292. package/dist/design/audit/reference/judge/rank.d.ts +41 -0
  293. package/dist/design/audit/reference/judge/rank.d.ts.map +1 -0
  294. package/dist/design/audit/reference/judge/rank.js +196 -0
  295. package/dist/design/audit/reference/judge/rank.js.map +1 -0
  296. package/dist/design/audit/reference/judge/text-judge.d.ts +31 -0
  297. package/dist/design/audit/reference/judge/text-judge.d.ts.map +1 -0
  298. package/dist/design/audit/reference/judge/text-judge.js +43 -0
  299. package/dist/design/audit/reference/judge/text-judge.js.map +1 -0
  300. package/dist/design/audit/reference/judge/vision-judge.d.ts +59 -0
  301. package/dist/design/audit/reference/judge/vision-judge.d.ts.map +1 -0
  302. package/dist/design/audit/reference/judge/vision-judge.js +150 -0
  303. package/dist/design/audit/reference/judge/vision-judge.js.map +1 -0
  304. package/dist/design/audit/reference/judge/vision-model.d.ts +65 -0
  305. package/dist/design/audit/reference/judge/vision-model.d.ts.map +1 -0
  306. package/dist/design/audit/reference/judge/vision-model.js +110 -0
  307. package/dist/design/audit/reference/judge/vision-model.js.map +1 -0
  308. package/dist/design/audit/reference/pipeline/evaluate-reference.d.ts +68 -0
  309. package/dist/design/audit/reference/pipeline/evaluate-reference.d.ts.map +1 -0
  310. package/dist/design/audit/reference/pipeline/evaluate-reference.js +52 -0
  311. package/dist/design/audit/reference/pipeline/evaluate-reference.js.map +1 -0
  312. package/dist/design/audit/reference/reference-context.d.ts +31 -0
  313. package/dist/design/audit/reference/reference-context.d.ts.map +1 -0
  314. package/dist/design/audit/reference/reference-context.js +73 -0
  315. package/dist/design/audit/reference/reference-context.js.map +1 -0
  316. package/dist/design/audit/reference/retrieval/embedding-hash.d.ts +30 -0
  317. package/dist/design/audit/reference/retrieval/embedding-hash.d.ts.map +1 -0
  318. package/dist/design/audit/reference/retrieval/embedding-hash.js +87 -0
  319. package/dist/design/audit/reference/retrieval/embedding-hash.js.map +1 -0
  320. package/dist/design/audit/reference/retrieval/embedding-openai.d.ts +38 -0
  321. package/dist/design/audit/reference/retrieval/embedding-openai.d.ts.map +1 -0
  322. package/dist/design/audit/reference/retrieval/embedding-openai.js +73 -0
  323. package/dist/design/audit/reference/retrieval/embedding-openai.js.map +1 -0
  324. package/dist/design/audit/reference/retrieval/matcher.d.ts +34 -0
  325. package/dist/design/audit/reference/retrieval/matcher.d.ts.map +1 -0
  326. package/dist/design/audit/reference/retrieval/matcher.js +107 -0
  327. package/dist/design/audit/reference/retrieval/matcher.js.map +1 -0
  328. package/dist/design/audit/reference/run.d.ts +59 -0
  329. package/dist/design/audit/reference/run.d.ts.map +1 -0
  330. package/dist/design/audit/reference/run.js +99 -0
  331. package/dist/design/audit/reference/run.js.map +1 -0
  332. package/dist/design/audit/report.d.ts +35 -0
  333. package/dist/design/audit/report.d.ts.map +1 -0
  334. package/dist/design/audit/report.js +195 -0
  335. package/dist/design/audit/report.js.map +1 -0
  336. package/dist/design/audit/tokens/extract.d.ts +71 -0
  337. package/dist/design/audit/tokens/extract.d.ts.map +1 -0
  338. package/dist/design/audit/tokens/extract.js +1034 -0
  339. package/dist/design/audit/tokens/extract.js.map +1 -0
  340. package/dist/design/audit/types.d.ts +8 -0
  341. package/dist/design/audit/types.d.ts.map +1 -1
  342. package/dist/design/compare.d.ts.map +1 -1
  343. package/dist/design/compare.js +2 -6
  344. package/dist/design/compare.js.map +1 -1
  345. package/dist/design/cookie-consent.d.ts +3 -0
  346. package/dist/design/cookie-consent.d.ts.map +1 -0
  347. package/dist/design/cookie-consent.js +26 -0
  348. package/dist/design/cookie-consent.js.map +1 -0
  349. package/dist/design/viewports.d.ts +18 -0
  350. package/dist/design/viewports.d.ts.map +1 -0
  351. package/dist/design/viewports.js +10 -0
  352. package/dist/design/viewports.js.map +1 -0
  353. package/dist/index.d.ts +2 -2
  354. package/dist/index.d.ts.map +1 -1
  355. package/dist/index.js +1 -1
  356. package/dist/index.js.map +1 -1
  357. package/dist/memory/store.d.ts +19 -1
  358. package/dist/memory/store.d.ts.map +1 -1
  359. package/dist/memory/store.js +47 -0
  360. package/dist/memory/store.js.map +1 -1
  361. package/dist/provider-defaults.d.ts +18 -0
  362. package/dist/provider-defaults.d.ts.map +1 -1
  363. package/dist/provider-defaults.js +33 -0
  364. package/dist/provider-defaults.js.map +1 -1
  365. package/dist/runner/allowed-domains.d.ts +11 -0
  366. package/dist/runner/allowed-domains.d.ts.map +1 -0
  367. package/dist/runner/allowed-domains.js +14 -0
  368. package/dist/runner/allowed-domains.js.map +1 -0
  369. package/dist/runner/batch-fill.d.ts +28 -0
  370. package/dist/runner/batch-fill.d.ts.map +1 -0
  371. package/dist/runner/batch-fill.js +80 -0
  372. package/dist/runner/batch-fill.js.map +1 -0
  373. package/dist/runner/completion-policy.d.ts +26 -0
  374. package/dist/runner/completion-policy.d.ts.map +1 -0
  375. package/dist/runner/completion-policy.js +76 -0
  376. package/dist/runner/completion-policy.js.map +1 -0
  377. package/dist/runner/constants.d.ts +14 -0
  378. package/dist/runner/constants.d.ts.map +1 -0
  379. package/dist/runner/constants.js +23 -0
  380. package/dist/runner/constants.js.map +1 -0
  381. package/dist/runner/decision-screenshot.d.ts +24 -0
  382. package/dist/runner/decision-screenshot.d.ts.map +1 -0
  383. package/dist/runner/decision-screenshot.js +23 -0
  384. package/dist/runner/decision-screenshot.js.map +1 -0
  385. package/dist/runner/domain-boundary.d.ts +36 -0
  386. package/dist/runner/domain-boundary.d.ts.map +1 -0
  387. package/dist/runner/domain-boundary.js +81 -0
  388. package/dist/runner/domain-boundary.js.map +1 -0
  389. package/dist/runner/effect-verify.d.ts +28 -0
  390. package/dist/runner/effect-verify.d.ts.map +1 -0
  391. package/dist/runner/effect-verify.js +42 -0
  392. package/dist/runner/effect-verify.js.map +1 -0
  393. package/dist/runner/execute-plan.d.ts +58 -0
  394. package/dist/runner/execute-plan.d.ts.map +1 -0
  395. package/dist/runner/execute-plan.js +426 -0
  396. package/dist/runner/execute-plan.js.map +1 -0
  397. package/dist/runner/max-turns-extension.d.ts +19 -0
  398. package/dist/runner/max-turns-extension.d.ts.map +1 -0
  399. package/dist/runner/max-turns-extension.js +25 -0
  400. package/dist/runner/max-turns-extension.js.map +1 -0
  401. package/dist/runner/micro-plan.d.ts +11 -0
  402. package/dist/runner/micro-plan.d.ts.map +1 -0
  403. package/dist/runner/micro-plan.js +29 -0
  404. package/dist/runner/micro-plan.js.map +1 -0
  405. package/dist/runner/prompt-snippets.d.ts +5 -0
  406. package/dist/runner/prompt-snippets.d.ts.map +1 -0
  407. package/dist/runner/prompt-snippets.js +13 -0
  408. package/dist/runner/prompt-snippets.js.map +1 -0
  409. package/dist/runner/replay/contracts.d.ts +300 -0
  410. package/dist/runner/replay/contracts.d.ts.map +1 -0
  411. package/dist/runner/replay/contracts.js +42 -0
  412. package/dist/runner/replay/contracts.js.map +1 -0
  413. package/dist/runner/replay/controller.d.ts +27 -0
  414. package/dist/runner/replay/controller.d.ts.map +1 -0
  415. package/dist/runner/replay/controller.js +197 -0
  416. package/dist/runner/replay/controller.js.map +1 -0
  417. package/dist/runner/replay/guard.d.ts +28 -0
  418. package/dist/runner/replay/guard.d.ts.map +1 -0
  419. package/dist/runner/replay/guard.js +100 -0
  420. package/dist/runner/replay/guard.js.map +1 -0
  421. package/dist/runner/runner.d.ts +44 -51
  422. package/dist/runner/runner.d.ts.map +1 -1
  423. package/dist/runner/runner.js +155 -851
  424. package/dist/runner/runner.js.map +1 -1
  425. package/dist/runner/scout-feedback.d.ts +51 -0
  426. package/dist/runner/scout-feedback.d.ts.map +1 -0
  427. package/dist/runner/scout-feedback.js +149 -0
  428. package/dist/runner/scout-feedback.js.map +1 -0
  429. package/dist/skills/macro-loader.d.ts +1 -1
  430. package/dist/test-runner.d.ts.map +1 -1
  431. package/dist/test-runner.js +27 -1
  432. package/dist/test-runner.js.map +1 -1
  433. package/dist/types/actions.d.ts +241 -0
  434. package/dist/types/actions.d.ts.map +1 -0
  435. package/dist/types/actions.js +5 -0
  436. package/dist/types/actions.js.map +1 -0
  437. package/dist/types/config.d.ts +211 -0
  438. package/dist/types/config.d.ts.map +1 -0
  439. package/dist/types/config.js +2 -0
  440. package/dist/types/config.js.map +1 -0
  441. package/dist/types/design-audit.d.ts +92 -0
  442. package/dist/types/design-audit.d.ts.map +1 -0
  443. package/dist/types/design-audit.js +5 -0
  444. package/dist/types/design-audit.js.map +1 -0
  445. package/dist/types/design-tokens.d.ts +138 -0
  446. package/dist/types/design-tokens.d.ts.map +1 -0
  447. package/dist/types/design-tokens.js +5 -0
  448. package/dist/types/design-tokens.js.map +1 -0
  449. package/dist/types/page.d.ts +16 -0
  450. package/dist/types/page.d.ts.map +1 -0
  451. package/dist/types/page.js +2 -0
  452. package/dist/types/page.js.map +1 -0
  453. package/dist/types/plan.d.ts +50 -0
  454. package/dist/types/plan.d.ts.map +1 -0
  455. package/dist/types/plan.js +2 -0
  456. package/dist/types/plan.js.map +1 -0
  457. package/dist/types/preview.d.ts +9 -0
  458. package/dist/types/preview.d.ts.map +1 -0
  459. package/dist/types/preview.js +5 -0
  460. package/dist/types/preview.js.map +1 -0
  461. package/dist/types/result.d.ts +62 -0
  462. package/dist/types/result.d.ts.map +1 -0
  463. package/dist/types/result.js +2 -0
  464. package/dist/types/result.js.map +1 -0
  465. package/dist/types/scenario.d.ts +20 -0
  466. package/dist/types/scenario.d.ts.map +1 -0
  467. package/dist/types/scenario.js +5 -0
  468. package/dist/types/scenario.js.map +1 -0
  469. package/dist/types/test-runner.d.ts +145 -0
  470. package/dist/types/test-runner.d.ts.map +1 -0
  471. package/dist/types/test-runner.js +10 -0
  472. package/dist/types/test-runner.js.map +1 -0
  473. package/dist/types/trajectory.d.ts +38 -0
  474. package/dist/types/trajectory.d.ts.map +1 -0
  475. package/dist/types/trajectory.js +2 -0
  476. package/dist/types/trajectory.js.map +1 -0
  477. package/dist/types/turn.d.ts +50 -0
  478. package/dist/types/turn.d.ts.map +1 -0
  479. package/dist/types/turn.js +2 -0
  480. package/dist/types/turn.js.map +1 -0
  481. package/dist/types.d.ts +16 -1017
  482. package/dist/types.d.ts.map +1 -1
  483. package/dist/types.js +15 -8
  484. package/dist/types.js.map +1 -1
  485. package/dist/wallet/rpc-interception.d.ts +15 -0
  486. package/dist/wallet/rpc-interception.d.ts.map +1 -0
  487. package/dist/wallet/rpc-interception.js +95 -0
  488. package/dist/wallet/rpc-interception.js.map +1 -0
  489. package/package.json +23 -5
@@ -0,0 +1,1034 @@
1
+ /**
2
+ * Design token extraction — pure DOM mining, no LLM calls.
3
+ *
4
+ * Leaf module: loads a page at multiple viewports, mines colours, typography,
5
+ * spacing, components, and brand/asset references inside `page.evaluate`,
6
+ * clusters them into a `DesignTokens` artifact, downloads referenced assets,
7
+ * and zips the result. Imported (often lazily) by the CLI design-audit handler,
8
+ * the design-compare diff, and the reference-DNA page adapter.
9
+ */
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { chromium } from 'playwright';
13
+ import { createScrollCapturer } from '../reference/dna/scroll-capture.js';
14
+ import { VIEWPORTS } from '../../viewports.js';
15
+ import { dismissCookieBanners } from '../../cookie-consent.js';
16
+ // The in-page extraction function — runs inside page.evaluate()
17
+ function extractTokensFromDOM() {
18
+ const MAX_ELEMENTS = 50_000;
19
+ // --- CSS Custom Properties & Font Files ---
20
+ const customProperties = {};
21
+ const fontFiles = [];
22
+ const externalStylesheetUrls = [];
23
+ for (const sheet of document.styleSheets) {
24
+ if (sheet.href && (sheet.href.startsWith('http://') || sheet.href.startsWith('https://'))) {
25
+ externalStylesheetUrls.push(sheet.href);
26
+ }
27
+ try {
28
+ for (const rule of sheet.cssRules) {
29
+ if (rule instanceof CSSStyleRule &&
30
+ (rule.selectorText === ':root' || rule.selectorText?.includes('html') || rule.selectorText?.includes('body'))) {
31
+ for (let i = 0; i < rule.style.length; i++) {
32
+ const prop = rule.style[i];
33
+ if (prop.startsWith('--')) {
34
+ customProperties[prop] = rule.style.getPropertyValue(prop).trim();
35
+ }
36
+ }
37
+ }
38
+ if (rule instanceof CSSFontFaceRule) {
39
+ const family = rule.style.getPropertyValue('font-family').replace(/['"]/g, '').trim();
40
+ const weight = rule.style.getPropertyValue('font-weight') || '400';
41
+ const style = rule.style.getPropertyValue('font-style') || 'normal';
42
+ const src = rule.style.getPropertyValue('src');
43
+ const urlMatch = src.match(/url\(["']?([^"')]+)["']?\)/);
44
+ const formatMatch = src.match(/format\(["']?([^"')]+)["']?\)/);
45
+ if (urlMatch) {
46
+ fontFiles.push({
47
+ family,
48
+ weight,
49
+ style,
50
+ src: urlMatch[1],
51
+ format: formatMatch?.[1] || 'unknown',
52
+ });
53
+ }
54
+ }
55
+ }
56
+ }
57
+ catch { /* cross-origin stylesheet */ }
58
+ }
59
+ // Supplement with document.fonts API (catches cross-origin @font-face)
60
+ const seenFontSrcs = new Set(fontFiles.map(f => f.src));
61
+ try {
62
+ for (const face of document.fonts) {
63
+ if (face.status === 'loaded') {
64
+ // face.family is the CSS font-family, face.weight/style are strings
65
+ // Check if we already captured this via @font-face rules
66
+ const family = face.family.replace(/['"]/g, '').trim();
67
+ const weight = face.weight || '400';
68
+ const style = face.style || 'normal';
69
+ // document.fonts doesn't expose the URL, but we can match against loaded resources
70
+ if (!fontFiles.some(f => f.family === family && f.weight === weight && f.style === style)) {
71
+ // Will be supplemented by Performance API below
72
+ }
73
+ }
74
+ }
75
+ }
76
+ catch { /* older browser */ }
77
+ // Use Performance API to find all loaded font resources (works cross-origin)
78
+ try {
79
+ const resources = performance.getEntriesByType('resource');
80
+ for (const r of resources) {
81
+ if (/\.(woff2?|ttf|otf|eot)(\?|$)/i.test(r.name) && !seenFontSrcs.has(r.name)) {
82
+ seenFontSrcs.add(r.name);
83
+ const formatMatch = r.name.match(/\.(woff2?|ttf|otf|eot)/i);
84
+ fontFiles.push({
85
+ family: '', // can't determine from URL alone
86
+ weight: '',
87
+ style: '',
88
+ src: r.name,
89
+ format: formatMatch?.[1] || 'unknown',
90
+ });
91
+ }
92
+ }
93
+ }
94
+ catch { /* Performance API not available */ }
95
+ // --- Element traversal ---
96
+ const colorMap = new Map();
97
+ const spacingMap = new Map();
98
+ const borderRadiusMap = new Map();
99
+ const shadowMap = new Map();
100
+ const fontFamilyMap = new Map();
101
+ const typeScaleMap = new Map();
102
+ const animationMap = new Map();
103
+ const backgroundImageUrls = new Set();
104
+ function addColor(value, property) {
105
+ if (!value || value === 'transparent' || value === 'rgba(0, 0, 0, 0)' || value === 'inherit' || value === 'initial')
106
+ return;
107
+ const entry = colorMap.get(value);
108
+ if (entry) {
109
+ entry.count++;
110
+ entry.properties.add(property);
111
+ }
112
+ else {
113
+ colorMap.set(value, { count: 1, properties: new Set([property]) });
114
+ }
115
+ }
116
+ function addSpacing(value, property) {
117
+ if (!value || value === '0px' || value === 'auto' || value === 'normal')
118
+ return;
119
+ // Normalize multi-value shorthands — take individual values
120
+ const vals = value.split(/\s+/).filter(v => v !== '0px' && v !== 'auto');
121
+ for (const v of vals) {
122
+ if (!v.endsWith('px') && !v.endsWith('rem') && !v.endsWith('em'))
123
+ continue;
124
+ const entry = spacingMap.get(v);
125
+ if (entry) {
126
+ entry.count++;
127
+ entry.properties.add(property);
128
+ }
129
+ else {
130
+ spacingMap.set(v, { count: 1, properties: new Set([property]) });
131
+ }
132
+ }
133
+ }
134
+ function parseGradientColors(bg) {
135
+ const colorRegex = /(?:rgba?\([^)]+\)|hsla?\([^)]+\)|#[0-9a-fA-F]{3,8})/g;
136
+ const matches = bg.match(colorRegex) || [];
137
+ for (const c of matches)
138
+ addColor(c, 'gradient');
139
+ }
140
+ function parseShadowColors(shadow) {
141
+ if (shadow === 'none')
142
+ return;
143
+ const colorRegex = /(?:rgba?\([^)]+\)|hsla?\([^)]+\)|#[0-9a-fA-F]{3,8})/g;
144
+ const matches = shadow.match(colorRegex) || [];
145
+ for (const c of matches)
146
+ addColor(c, 'boxShadow');
147
+ }
148
+ const HEADING_TAGS = new Set(['H1', 'H2', 'H3', 'H4', 'H5', 'H6']);
149
+ const MONO_FAMILIES = /mono|consolas|courier|fira\s*code|jetbrains|source\s*code/i;
150
+ let elementCount = 0;
151
+ // Per-element extraction — shared between light DOM walker and shadow DOM walker
152
+ function processElement(el) {
153
+ const htmlEl = el;
154
+ if (htmlEl.offsetWidth === 0 && htmlEl.offsetHeight === 0)
155
+ return;
156
+ const cs = getComputedStyle(el);
157
+ // Colors
158
+ addColor(cs.color, 'color');
159
+ addColor(cs.backgroundColor, 'backgroundColor');
160
+ addColor(cs.borderTopColor, 'borderColor');
161
+ addColor(cs.borderRightColor, 'borderColor');
162
+ addColor(cs.borderBottomColor, 'borderColor');
163
+ addColor(cs.borderLeftColor, 'borderColor');
164
+ addColor(cs.outlineColor, 'outlineColor');
165
+ if (cs.backgroundImage.includes('gradient'))
166
+ parseGradientColors(cs.backgroundImage);
167
+ if (cs.backgroundImage.includes('url(')) {
168
+ const urlMatches = cs.backgroundImage.matchAll(/url\(["']?([^"')]+)["']?\)/g);
169
+ for (const m of urlMatches) {
170
+ const u = m[1];
171
+ if (u && !u.startsWith('data:') && !u.startsWith('blob:'))
172
+ backgroundImageUrls.add(u);
173
+ }
174
+ }
175
+ if (cs.boxShadow !== 'none')
176
+ parseShadowColors(cs.boxShadow);
177
+ // Spacing
178
+ addSpacing(cs.paddingTop, 'padding');
179
+ addSpacing(cs.paddingRight, 'padding');
180
+ addSpacing(cs.paddingBottom, 'padding');
181
+ addSpacing(cs.paddingLeft, 'padding');
182
+ addSpacing(cs.marginTop, 'margin');
183
+ addSpacing(cs.marginRight, 'margin');
184
+ addSpacing(cs.marginBottom, 'margin');
185
+ addSpacing(cs.marginLeft, 'margin');
186
+ addSpacing(cs.gap, 'gap');
187
+ addSpacing(cs.rowGap, 'gap');
188
+ addSpacing(cs.columnGap, 'gap');
189
+ // Border radius
190
+ const br = cs.borderRadius;
191
+ if (br && br !== '0px') {
192
+ borderRadiusMap.set(br, (borderRadiusMap.get(br) ?? 0) + 1);
193
+ }
194
+ // Shadows
195
+ if (cs.boxShadow !== 'none') {
196
+ shadowMap.set(cs.boxShadow, (shadowMap.get(cs.boxShadow) ?? 0) + 1);
197
+ }
198
+ // Typography — only for elements with text content
199
+ if (el.childNodes.length > 0 && Array.from(el.childNodes).some(n => n.nodeType === Node.TEXT_NODE && n.textContent?.trim())) {
200
+ const family = cs.fontFamily;
201
+ const weight = parseInt(cs.fontWeight) || 400;
202
+ const isHeading = HEADING_TAGS.has(el.tagName);
203
+ const isMono = MONO_FAMILIES.test(family);
204
+ const fEntry = fontFamilyMap.get(family);
205
+ if (fEntry) {
206
+ fEntry.weights.add(weight);
207
+ if (isHeading)
208
+ fEntry.headingUse = true;
209
+ if (isMono)
210
+ fEntry.monoUse = true;
211
+ }
212
+ else {
213
+ fontFamilyMap.set(family, { weights: new Set([weight]), headingUse: isHeading, monoUse: isMono });
214
+ }
215
+ const scaleKey = `${cs.fontSize}|${cs.fontWeight}|${cs.lineHeight}|${cs.letterSpacing}|${family}`;
216
+ const scaleEntry = typeScaleMap.get(scaleKey);
217
+ if (scaleEntry) {
218
+ scaleEntry.count++;
219
+ }
220
+ else {
221
+ typeScaleMap.set(scaleKey, {
222
+ fontSize: cs.fontSize,
223
+ fontWeight: cs.fontWeight,
224
+ lineHeight: cs.lineHeight,
225
+ letterSpacing: cs.letterSpacing,
226
+ fontFamily: family,
227
+ tag: el.tagName.toLowerCase(),
228
+ count: 1,
229
+ });
230
+ }
231
+ }
232
+ // Animations/transitions
233
+ if (cs.transition && cs.transition !== 'all 0s ease 0s' && cs.transition !== 'none') {
234
+ const key = cs.transition;
235
+ const aEntry = animationMap.get(key);
236
+ if (aEntry) {
237
+ aEntry.count++;
238
+ }
239
+ else {
240
+ animationMap.set(key, { property: 'transition', value: cs.transition, count: 1 });
241
+ }
242
+ }
243
+ if (cs.animationName && cs.animationName !== 'none') {
244
+ const key = `animation:${cs.animationName}`;
245
+ const aEntry = animationMap.get(key);
246
+ if (aEntry) {
247
+ aEntry.count++;
248
+ }
249
+ else {
250
+ animationMap.set(key, { property: 'animation', value: `${cs.animationName} ${cs.animationDuration} ${cs.animationTimingFunction}`, count: 1 });
251
+ }
252
+ }
253
+ }
254
+ // --- Light DOM walk ---
255
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
256
+ let el = walker.currentNode;
257
+ while (el && elementCount < MAX_ELEMENTS) {
258
+ elementCount++;
259
+ processElement(el);
260
+ el = walker.nextNode();
261
+ }
262
+ // --- Shadow DOM walk ---
263
+ function walkShadowRoots(root) {
264
+ const shadowWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
265
+ // currentNode starts at root (ShadowRoot, not Element) — advance to first element
266
+ let shadowEl = shadowWalker.nextNode();
267
+ while (shadowEl && elementCount < MAX_ELEMENTS) {
268
+ elementCount++;
269
+ processElement(shadowEl);
270
+ if (shadowEl.shadowRoot) {
271
+ walkShadowRoots(shadowEl.shadowRoot);
272
+ }
273
+ shadowEl = shadowWalker.nextNode();
274
+ }
275
+ }
276
+ const allElements = document.body.querySelectorAll('*');
277
+ for (const bodyEl of allElements) {
278
+ if (bodyEl.shadowRoot && elementCount < MAX_ELEMENTS) {
279
+ walkShadowRoots(bodyEl.shadowRoot);
280
+ }
281
+ }
282
+ // --- Components: Buttons ---
283
+ const buttonStyles = [];
284
+ const btnFingerprints = new Map();
285
+ const BUTTON_STYLE_KEYS = ['backgroundColor', 'color', 'borderRadius', 'padding', 'fontSize', 'fontWeight', 'border', 'boxShadow', 'textTransform', 'letterSpacing'];
286
+ for (const btn of document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]')) {
287
+ const htmlBtn = btn;
288
+ if (htmlBtn.offsetWidth === 0 && htmlBtn.offsetHeight === 0)
289
+ continue;
290
+ const cs = getComputedStyle(btn);
291
+ const styles = {};
292
+ for (const k of BUTTON_STYLE_KEYS)
293
+ styles[k] = cs[k];
294
+ const fp = JSON.stringify(styles);
295
+ const existing = btnFingerprints.get(fp);
296
+ if (existing) {
297
+ existing.count++;
298
+ }
299
+ else {
300
+ const entry = { fingerprint: fp, count: 1, exampleText: htmlBtn.textContent?.trim().slice(0, 60) || '', styles };
301
+ btnFingerprints.set(fp, entry);
302
+ }
303
+ }
304
+ buttonStyles.push(...btnFingerprints.values());
305
+ // --- Components: Inputs ---
306
+ const inputStyles = [];
307
+ const inputFingerprints = new Map();
308
+ const INPUT_STYLE_KEYS = ['backgroundColor', 'color', 'borderRadius', 'padding', 'fontSize', 'border', 'boxShadow', 'outline'];
309
+ for (const inp of document.querySelectorAll('input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="number"], input[type="tel"], input[type="url"], textarea, select, [role="textbox"], [role="combobox"]')) {
310
+ const htmlInp = inp;
311
+ if (htmlInp.offsetWidth === 0 && htmlInp.offsetHeight === 0)
312
+ continue;
313
+ const cs = getComputedStyle(inp);
314
+ const styles = {};
315
+ for (const k of INPUT_STYLE_KEYS)
316
+ styles[k] = cs[k];
317
+ const fp = JSON.stringify(styles);
318
+ const existing = inputFingerprints.get(fp);
319
+ if (existing) {
320
+ existing.count++;
321
+ }
322
+ else {
323
+ const entry = { fingerprint: fp, count: 1, exampleText: inp.placeholder?.slice(0, 60) || '', styles };
324
+ inputFingerprints.set(fp, entry);
325
+ }
326
+ }
327
+ inputStyles.push(...inputFingerprints.values());
328
+ // --- Components: Cards (elements with shadow or border-radius + multiple children) ---
329
+ const cardStyles = [];
330
+ const cardFingerprints = new Map();
331
+ const CARD_STYLE_KEYS = ['borderRadius', 'boxShadow', 'padding', 'backgroundColor', 'border'];
332
+ for (const el of document.querySelectorAll('div, section, article, li')) {
333
+ const htmlEl = el;
334
+ if (htmlEl.offsetWidth === 0 || htmlEl.children.length < 2)
335
+ continue;
336
+ const cs = getComputedStyle(el);
337
+ if (cs.boxShadow === 'none' && cs.borderRadius === '0px' && cs.border === '0px none rgb(0, 0, 0)')
338
+ continue;
339
+ const styles = {};
340
+ for (const k of CARD_STYLE_KEYS)
341
+ styles[k] = cs[k];
342
+ const fp = JSON.stringify(styles);
343
+ const existing = cardFingerprints.get(fp);
344
+ if (existing) {
345
+ existing.count++;
346
+ }
347
+ else {
348
+ const entry = { fingerprint: fp, count: 1, styles };
349
+ cardFingerprints.set(fp, entry);
350
+ }
351
+ }
352
+ // Only keep card patterns with 2+ instances (skip one-off containers)
353
+ for (const c of cardFingerprints.values()) {
354
+ if (c.count >= 2)
355
+ cardStyles.push(c);
356
+ }
357
+ // --- Components: Navigation ---
358
+ const navPatterns = [];
359
+ for (const nav of document.querySelectorAll('nav, [role="navigation"], header')) {
360
+ const cs = getComputedStyle(nav);
361
+ const links = nav.querySelectorAll('a');
362
+ if (links.length === 0)
363
+ continue;
364
+ const firstLinkCs = getComputedStyle(links[0]);
365
+ navPatterns.push({
366
+ selector: nav.tagName.toLowerCase() + (nav.className ? '.' + nav.className.split(/\s+/)[0] : ''),
367
+ layout: {
368
+ display: cs.display,
369
+ flexDirection: cs.flexDirection,
370
+ gap: cs.gap,
371
+ alignItems: cs.alignItems,
372
+ justifyContent: cs.justifyContent,
373
+ backgroundColor: cs.backgroundColor,
374
+ padding: cs.padding,
375
+ },
376
+ linkCount: links.length,
377
+ linkStyles: {
378
+ color: firstLinkCs.color,
379
+ fontSize: firstLinkCs.fontSize,
380
+ fontWeight: firstLinkCs.fontWeight,
381
+ textDecoration: firstLinkCs.textDecoration,
382
+ },
383
+ });
384
+ }
385
+ // --- Logos & Icons ---
386
+ const logos = [];
387
+ const icons = [];
388
+ // SVG logos (in header/nav or with logo-related attributes)
389
+ for (const svg of document.querySelectorAll('svg')) {
390
+ const parent = svg.closest('header, nav, [role="banner"]');
391
+ const classes = (svg.getAttribute('class') || '') + ' ' + (svg.parentElement?.getAttribute('class') || '');
392
+ const ariaLabel = svg.getAttribute('aria-label') || '';
393
+ const isLogo = parent !== null || /logo|brand|mark/i.test(classes) || /logo|brand/i.test(ariaLabel);
394
+ if (isLogo) {
395
+ const content = svg.outerHTML.slice(0, 4096);
396
+ logos.push({
397
+ type: 'svg',
398
+ width: svg.width?.baseVal?.value || undefined,
399
+ height: svg.height?.baseVal?.value || undefined,
400
+ svgContent: content,
401
+ });
402
+ }
403
+ else if (svg.outerHTML.length < 2048) {
404
+ // Collect as icon (small SVGs)
405
+ icons.push({
406
+ selector: svg.tagName + (svg.getAttribute('class') ? '.' + svg.getAttribute('class').split(/\s+/)[0] : ''),
407
+ viewBox: svg.getAttribute('viewBox') || undefined,
408
+ width: svg.width?.baseVal?.value || undefined,
409
+ height: svg.height?.baseVal?.value || undefined,
410
+ content: svg.outerHTML,
411
+ });
412
+ }
413
+ }
414
+ // IMG logos
415
+ for (const img of document.querySelectorAll('img')) {
416
+ const parent = img.closest('header, nav, [role="banner"]');
417
+ const src = img.src || '';
418
+ const alt = img.alt || '';
419
+ const classes = img.className || '';
420
+ if (parent !== null || /logo|brand|mark/i.test(src) || /logo|brand/i.test(alt) || /logo|brand/i.test(classes)) {
421
+ logos.push({
422
+ type: 'img',
423
+ src: img.src,
424
+ alt: img.alt || undefined,
425
+ width: img.naturalWidth || undefined,
426
+ height: img.naturalHeight || undefined,
427
+ });
428
+ }
429
+ }
430
+ // Limit icons to top 50 unique by viewBox
431
+ const seenViewBoxes = new Set();
432
+ const dedupedIcons = icons.filter(icon => {
433
+ const key = icon.viewBox || icon.content.slice(0, 100);
434
+ if (seenViewBoxes.has(key))
435
+ return false;
436
+ seenViewBoxes.add(key);
437
+ return true;
438
+ }).slice(0, 50);
439
+ // --- Brand Meta ---
440
+ const getMeta = (sel) => document.querySelector(sel)?.getAttribute('content') || undefined;
441
+ const getLink = (sel) => {
442
+ const el = document.querySelector(sel);
443
+ return el ? el.href || el.getAttribute('href') || undefined : undefined;
444
+ };
445
+ const brand = {
446
+ title: getMeta('meta[property="og:title"]') || document.title || undefined,
447
+ description: getMeta('meta[property="og:description"]') || getMeta('meta[name="description"]'),
448
+ themeColor: getMeta('meta[name="theme-color"]'),
449
+ favicon: getLink('link[rel="icon"]') || getLink('link[rel="shortcut icon"]'),
450
+ ogImage: getMeta('meta[property="og:image"]'),
451
+ appleTouchIcon: getLink('link[rel="apple-touch-icon"]'),
452
+ manifestUrl: getLink('link[rel="manifest"]'),
453
+ };
454
+ // --- All Image URLs ---
455
+ const imageUrls = new Set();
456
+ for (const img of document.querySelectorAll('img')) {
457
+ const src = img.src;
458
+ if (src && !src.startsWith('data:') && !src.startsWith('blob:'))
459
+ imageUrls.add(src);
460
+ const srcset = img.srcset;
461
+ if (srcset) {
462
+ for (const part of srcset.split(',')) {
463
+ const url = part.trim().split(/\s+/)[0];
464
+ if (url && !url.startsWith('data:') && !url.startsWith('blob:'))
465
+ imageUrls.add(url);
466
+ }
467
+ }
468
+ }
469
+ // --- Video URLs ---
470
+ const videoUrls = new Set();
471
+ for (const video of document.querySelectorAll('video')) {
472
+ const src = video.src;
473
+ if (src && !src.startsWith('data:') && !src.startsWith('blob:'))
474
+ videoUrls.add(src);
475
+ const poster = video.poster;
476
+ if (poster && !poster.startsWith('data:') && !poster.startsWith('blob:'))
477
+ imageUrls.add(poster);
478
+ for (const source of video.querySelectorAll('source')) {
479
+ const ssrc = source.src;
480
+ if (ssrc && !ssrc.startsWith('data:') && !ssrc.startsWith('blob:'))
481
+ videoUrls.add(ssrc);
482
+ }
483
+ }
484
+ // Orphan <source> elements (audio, picture)
485
+ for (const source of document.querySelectorAll('source[src]')) {
486
+ const ssrc = source.src;
487
+ if (ssrc && /\.(mp4|webm|mov|ogg|avi|m3u8)/i.test(ssrc))
488
+ videoUrls.add(ssrc);
489
+ }
490
+ // <picture> <source> with srcset
491
+ for (const source of document.querySelectorAll('picture source[srcset]')) {
492
+ const srcset = source.srcset;
493
+ if (srcset) {
494
+ for (const part of srcset.split(',')) {
495
+ const url = part.trim().split(/\s+/)[0];
496
+ if (url && !url.startsWith('data:') && !url.startsWith('blob:'))
497
+ imageUrls.add(url);
498
+ }
499
+ }
500
+ }
501
+ // --- Lazy-load data attributes ---
502
+ for (const el of document.querySelectorAll('[data-src], [data-bg], [data-background-image], [data-poster]')) {
503
+ for (const attr of ['data-src', 'data-bg', 'data-background-image', 'data-poster']) {
504
+ const val = el.getAttribute(attr);
505
+ if (!val || val.startsWith('data:') || val.startsWith('blob:'))
506
+ continue;
507
+ try {
508
+ const resolved = new URL(val, document.location.href).href;
509
+ if (/\.(mp4|webm|mov|ogg|avi|m3u8)/i.test(val))
510
+ videoUrls.add(resolved);
511
+ else
512
+ imageUrls.add(resolved);
513
+ }
514
+ catch { /* bad URL */ }
515
+ }
516
+ }
517
+ // --- Link Assets (favicons, icons, manifests) ---
518
+ const linkAssetUrls = [];
519
+ for (const link of document.querySelectorAll('link[href]')) {
520
+ const href = link.href;
521
+ const rel = link.rel || '';
522
+ if (href && !href.startsWith('data:') && !href.startsWith('blob:') &&
523
+ /^(icon|shortcut icon|apple-touch-icon|manifest|preload)$/i.test(rel)) {
524
+ linkAssetUrls.push({ url: href, rel });
525
+ }
526
+ }
527
+ // --- Inline script library detection ---
528
+ const detectedLibraries = [];
529
+ const seenLibs = new Set();
530
+ for (const script of document.querySelectorAll('script')) {
531
+ const src = script.src || '';
532
+ const text = script.textContent || '';
533
+ const content = src + ' ' + text.slice(0, 10_000);
534
+ const checks = [
535
+ [/gsap|ScrollTrigger|ScrollSmoother/i, 'gsap'],
536
+ [/new\s+p5\b|p5\.setup|p5\.draw|p5\.js/i, 'p5.js'],
537
+ [/THREE\.|new\s+THREE\.|from\s+['"]three/i, 'three.js'],
538
+ [/lottie|bodymovin/i, 'lottie'],
539
+ [/anime\s*\(|animejs/i, 'anime.js'],
540
+ [/framer-motion|motion\.div/i, 'framer-motion'],
541
+ [/webflow/i, 'webflow'],
542
+ [/swiper/i, 'swiper'],
543
+ [/locomotive.?scroll/i, 'locomotive-scroll'],
544
+ [/rive\.?app|@rive-app/i, 'rive'],
545
+ [/spline/i, 'spline'],
546
+ ];
547
+ for (const [regex, name] of checks) {
548
+ if (!seenLibs.has(name) && regex.test(content)) {
549
+ seenLibs.add(name);
550
+ detectedLibraries.push(name);
551
+ }
552
+ }
553
+ }
554
+ // --- Assemble result ---
555
+ return {
556
+ customProperties,
557
+ colors: Array.from(colorMap.entries())
558
+ .map(([value, data]) => ({ value, count: data.count, properties: Array.from(data.properties) }))
559
+ .sort((a, b) => b.count - a.count),
560
+ typography: {
561
+ families: Array.from(fontFamilyMap.entries()).map(([family, data]) => ({
562
+ family,
563
+ weights: Array.from(data.weights).sort((a, b) => a - b),
564
+ headingUse: data.headingUse,
565
+ monoUse: data.monoUse,
566
+ })),
567
+ scale: Array.from(typeScaleMap.values()).sort((a, b) => b.count - a.count),
568
+ },
569
+ brand,
570
+ logos,
571
+ icons: dedupedIcons,
572
+ spacing: Array.from(spacingMap.entries())
573
+ .map(([value, data]) => ({ value, count: data.count, properties: Array.from(data.properties) }))
574
+ .sort((a, b) => b.count - a.count),
575
+ borders: Array.from(borderRadiusMap.entries())
576
+ .map(([borderRadius, count]) => ({ borderRadius, count }))
577
+ .sort((a, b) => b.count - a.count),
578
+ shadows: Array.from(shadowMap.entries())
579
+ .map(([value, count]) => ({ value, count }))
580
+ .sort((a, b) => b.count - a.count),
581
+ components: {
582
+ buttons: buttonStyles.sort((a, b) => b.count - a.count),
583
+ inputs: inputStyles.sort((a, b) => b.count - a.count),
584
+ cards: cardStyles.sort((a, b) => b.count - a.count),
585
+ nav: navPatterns,
586
+ },
587
+ animations: Array.from(animationMap.values()).sort((a, b) => b.count - a.count),
588
+ fontFiles,
589
+ imageUrls: Array.from(imageUrls),
590
+ backgroundImageUrls: Array.from(backgroundImageUrls),
591
+ videoUrls: Array.from(videoUrls),
592
+ externalStylesheetUrls,
593
+ linkAssetUrls,
594
+ detectedLibraries,
595
+ };
596
+ }
597
+ // ---------------------------------------------------------------------------
598
+ // Color clustering — assign semantic roles to extracted colors
599
+ // ---------------------------------------------------------------------------
600
+ function rgbaToHsl(rgba) {
601
+ const match = rgba.match(/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)/);
602
+ if (!match)
603
+ return null;
604
+ const r = parseFloat(match[1]) / 255;
605
+ const g = parseFloat(match[2]) / 255;
606
+ const b = parseFloat(match[3]) / 255;
607
+ const a = match[4] !== undefined ? parseFloat(match[4]) : 1;
608
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
609
+ const l = (max + min) / 2;
610
+ if (max === min)
611
+ return { h: 0, s: 0, l, a };
612
+ const d = max - min;
613
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
614
+ let h = 0;
615
+ if (max === r)
616
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
617
+ else if (max === g)
618
+ h = ((b - r) / d + 2) / 6;
619
+ else
620
+ h = ((r - g) / d + 4) / 6;
621
+ return { h: h * 360, s: s * 100, l: l * 100, a };
622
+ }
623
+ function rgbaToHex(rgba) {
624
+ const match = rgba.match(/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)/);
625
+ if (!match)
626
+ return rgba;
627
+ const r = Math.round(parseFloat(match[1]));
628
+ const g = Math.round(parseFloat(match[2]));
629
+ const b = Math.round(parseFloat(match[3]));
630
+ const a = match[4] !== undefined ? parseFloat(match[4]) : 1;
631
+ const hex = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
632
+ if (a < 1)
633
+ return `${hex}${Math.round(a * 255).toString(16).padStart(2, '0')}`;
634
+ return hex;
635
+ }
636
+ function clusterColors(colors) {
637
+ const result = [];
638
+ let primaryHue = -1;
639
+ let secondaryHue = -1;
640
+ for (const c of colors) {
641
+ const hsl = rgbaToHsl(c.value);
642
+ const hex = rgbaToHex(c.value);
643
+ let cluster;
644
+ if (!hsl || hsl.s < 10) {
645
+ // Low saturation = neutral (grays, black, white)
646
+ if (hsl && hsl.l > 90)
647
+ cluster = 'background';
648
+ else
649
+ cluster = 'neutral';
650
+ }
651
+ else if (c.properties.includes('borderColor') && !c.properties.includes('color') && !c.properties.includes('backgroundColor')) {
652
+ cluster = 'border';
653
+ }
654
+ else if (primaryHue < 0) {
655
+ primaryHue = hsl.h;
656
+ cluster = 'primary';
657
+ }
658
+ else if (Math.abs(hsl.h - primaryHue) < 30 || Math.abs(hsl.h - primaryHue) > 330) {
659
+ cluster = 'primary';
660
+ }
661
+ else if (secondaryHue < 0) {
662
+ secondaryHue = hsl.h;
663
+ cluster = 'secondary';
664
+ }
665
+ else if (Math.abs(hsl.h - secondaryHue) < 30 || Math.abs(hsl.h - secondaryHue) > 330) {
666
+ cluster = 'secondary';
667
+ }
668
+ else {
669
+ cluster = 'accent';
670
+ }
671
+ result.push({ value: c.value, hex, count: c.count, properties: c.properties, cluster });
672
+ }
673
+ return result;
674
+ }
675
+ // ---------------------------------------------------------------------------
676
+ // Grid base unit detection
677
+ // ---------------------------------------------------------------------------
678
+ function detectGridUnit(spacingValues) {
679
+ // Build frequency-weighted pairs
680
+ const pairs = spacingValues
681
+ .map(s => ({ px: parseFloat(s.value), count: s.count }))
682
+ .filter(p => p.px > 0 && p.px <= 200 && Number.isInteger(p.px));
683
+ if (pairs.length < 3)
684
+ return undefined;
685
+ // Score common grid units by how much of the total usage they explain
686
+ const CANDIDATES = [4, 5, 6, 8, 10];
687
+ let bestUnit = 0;
688
+ let bestScore = 0;
689
+ for (const unit of CANDIDATES) {
690
+ let aligned = 0;
691
+ let total = 0;
692
+ for (const p of pairs) {
693
+ total += p.count;
694
+ if (p.px % unit === 0)
695
+ aligned += p.count;
696
+ }
697
+ const score = aligned / total;
698
+ if (score > bestScore) {
699
+ bestScore = score;
700
+ bestUnit = unit;
701
+ }
702
+ }
703
+ // Require at least 60% of usage aligns with the grid
704
+ return bestScore >= 0.6 ? bestUnit : undefined;
705
+ }
706
+ /**
707
+ * Extract design tokens from a URL at multiple viewports.
708
+ * Pure DOM extraction — no LLM calls.
709
+ *
710
+ * ```typescript
711
+ * import { extractDesignTokens } from '@tangle-network/browser-agent-driver'
712
+ *
713
+ * const { tokens } = await extractDesignTokens({ url: 'https://stripe.com' })
714
+ * console.log(tokens.colors.filter(c => c.cluster === 'primary'))
715
+ * console.log(tokens.typography.families)
716
+ * console.log(tokens.brand)
717
+ * ```
718
+ */
719
+ /**
720
+ * Reduce a candidate asset filename to a safe, separator-free basename so a
721
+ * DOM-controlled value (e.g. a hostile `@font-face` family `../../../tmp/evil`)
722
+ * can never escape the output directory when joined to it. Strips any path
723
+ * components and leading dots (no traversal, no dotfiles); returns `fallback`
724
+ * when nothing usable remains.
725
+ */
726
+ export function safeAssetFilename(raw, fallback) {
727
+ return path.basename(raw).replace(/^[.\s]+/, '') || fallback;
728
+ }
729
+ export async function extractDesignTokens(opts) {
730
+ const viewports = opts.viewports ?? [...VIEWPORTS];
731
+ const outputDir = opts.outputDir ?? `./audit-results/${new URL(opts.url).hostname}-tokens-${Date.now()}`;
732
+ fs.mkdirSync(outputDir, { recursive: true });
733
+ const screenshotDir = path.join(outputDir, 'screenshots');
734
+ fs.mkdirSync(screenshotDir, { recursive: true });
735
+ const browser = await chromium.launch({ headless: opts.headless ?? true });
736
+ const allCustomProps = {};
737
+ const allColors = new Map();
738
+ const allFamilies = new Map();
739
+ const allScaleEntries = [];
740
+ let brand = {};
741
+ const allLogos = [];
742
+ const allIcons = [];
743
+ const allFontFiles = new Map();
744
+ const allImageUrls = new Set();
745
+ const allBackgroundImageUrls = new Set();
746
+ const allVideoUrls = new Set();
747
+ const allExternalStylesheetUrls = new Set();
748
+ const allLinkAssetUrls = new Map(); // url -> rel
749
+ const allDetectedLibraries = new Set();
750
+ const responsiveTokens = {};
751
+ const screenshotPaths = {};
752
+ let scrollMotion;
753
+ for (const vp of viewports) {
754
+ const context = await browser.newContext({ viewport: { width: vp.width, height: vp.height } });
755
+ const page = await context.newPage();
756
+ try {
757
+ await page.goto(opts.url, { waitUntil: 'networkidle', timeout: 20_000 }).catch(() => page.goto(opts.url, { waitUntil: 'domcontentloaded', timeout: 15_000 }));
758
+ await page.waitForTimeout(2000);
759
+ await dismissCookieBanners(page);
760
+ await page.waitForTimeout(500);
761
+ // Opt-in live scroll-motion capture — runs FIRST on the freshly loaded
762
+ // page, BEFORE the fullPage screenshot scrolls it, so one-shot scroll
763
+ // reveals are observed as they actually fire. Desktop only (long-scroll is
764
+ // a desktop signal); the capturer restores scroll to the top when done.
765
+ if (opts.captureScrollMotion && vp.name === 'desktop') {
766
+ scrollMotion = await createScrollCapturer().capture(page).catch(() => undefined);
767
+ }
768
+ const screenshotPath = path.join(screenshotDir, `${vp.name}.png`);
769
+ await page.screenshot({ path: screenshotPath, fullPage: true }).catch(() => page.screenshot({ path: screenshotPath, fullPage: false }));
770
+ screenshotPaths[vp.name] = screenshotPath;
771
+ const raw = await page.evaluate(extractTokensFromDOM);
772
+ Object.assign(allCustomProps, raw.customProperties);
773
+ for (const c of raw.colors) {
774
+ const existing = allColors.get(c.value);
775
+ if (existing) {
776
+ existing.count += c.count;
777
+ for (const p of c.properties)
778
+ existing.properties.add(p);
779
+ }
780
+ else {
781
+ allColors.set(c.value, { count: c.count, properties: new Set(c.properties) });
782
+ }
783
+ }
784
+ for (const f of raw.typography.families) {
785
+ const existing = allFamilies.get(f.family);
786
+ if (existing) {
787
+ for (const w of f.weights)
788
+ existing.weights.add(w);
789
+ if (f.headingUse)
790
+ existing.headingUse = true;
791
+ if (f.monoUse)
792
+ existing.monoUse = true;
793
+ }
794
+ else {
795
+ allFamilies.set(f.family, { weights: new Set(f.weights), headingUse: f.headingUse, monoUse: f.monoUse });
796
+ }
797
+ }
798
+ allScaleEntries.push(...raw.typography.scale);
799
+ if (vp.name === 'desktop' || !brand.title) {
800
+ brand = raw.brand;
801
+ }
802
+ for (const logo of raw.logos) {
803
+ if (!allLogos.some(l => l.src === logo.src && l.svgContent === logo.svgContent)) {
804
+ allLogos.push(logo);
805
+ }
806
+ }
807
+ for (const icon of raw.icons) {
808
+ if (!allIcons.some(i => i.content === icon.content)) {
809
+ allIcons.push(icon);
810
+ }
811
+ }
812
+ for (const ff of raw.fontFiles) {
813
+ if (!allFontFiles.has(ff.src)) {
814
+ allFontFiles.set(ff.src, ff);
815
+ }
816
+ }
817
+ for (const u of raw.imageUrls)
818
+ allImageUrls.add(u);
819
+ for (const u of raw.backgroundImageUrls)
820
+ allBackgroundImageUrls.add(u);
821
+ for (const u of raw.videoUrls)
822
+ allVideoUrls.add(u);
823
+ for (const u of raw.externalStylesheetUrls)
824
+ allExternalStylesheetUrls.add(u);
825
+ for (const la of raw.linkAssetUrls)
826
+ allLinkAssetUrls.set(la.url, la.rel);
827
+ for (const lib of raw.detectedLibraries)
828
+ allDetectedLibraries.add(lib);
829
+ responsiveTokens[vp.name] = {
830
+ width: vp.width,
831
+ height: vp.height,
832
+ spacing: raw.spacing,
833
+ gridBaseUnit: detectGridUnit(raw.spacing),
834
+ borders: raw.borders,
835
+ shadows: raw.shadows,
836
+ components: raw.components,
837
+ animations: raw.animations,
838
+ screenshotPath,
839
+ };
840
+ opts.onProgress?.(vp.name, vp.width, vp.height, {
841
+ colors: raw.colors.length,
842
+ fonts: raw.typography.families.length,
843
+ buttons: raw.components.buttons.length,
844
+ inputs: raw.components.inputs.length,
845
+ cards: raw.components.cards.length,
846
+ });
847
+ }
848
+ finally {
849
+ await context.close();
850
+ }
851
+ }
852
+ await browser.close();
853
+ // Deduplicate type scale
854
+ const scaleMap = new Map();
855
+ for (const entry of allScaleEntries) {
856
+ const key = `${entry.fontSize}|${entry.fontWeight}|${entry.lineHeight}|${entry.fontFamily}`;
857
+ const existing = scaleMap.get(key);
858
+ if (existing) {
859
+ existing.count += entry.count;
860
+ }
861
+ else {
862
+ const isHeading = /^h[1-6]$/.test(entry.tag);
863
+ const fontSize = parseFloat(entry.fontSize);
864
+ let usage = 'body';
865
+ if (isHeading)
866
+ usage = 'heading';
867
+ else if (fontSize <= 12)
868
+ usage = 'caption';
869
+ else if (fontSize <= 14 && entry.fontWeight >= '500')
870
+ usage = 'label';
871
+ scaleMap.set(key, { fontSize: entry.fontSize, fontWeight: entry.fontWeight, lineHeight: entry.lineHeight, letterSpacing: entry.letterSpacing, fontFamily: entry.fontFamily, usage, tag: entry.tag, count: entry.count });
872
+ }
873
+ }
874
+ const mergedColors = Array.from(allColors.entries())
875
+ .map(([value, data]) => ({ value, count: data.count, properties: Array.from(data.properties) }))
876
+ .sort((a, b) => b.count - a.count);
877
+ const clusteredColors = clusterColors(mergedColors);
878
+ const families = Array.from(allFamilies.entries()).map(([family, data]) => ({
879
+ family,
880
+ weights: Array.from(data.weights).sort((a, b) => a - b),
881
+ classification: data.monoUse ? 'mono' :
882
+ (data.headingUse && allFamilies.size > 1) ? 'heading' :
883
+ data.headingUse ? 'display' : 'body',
884
+ }));
885
+ // Download font files
886
+ const downloadedFontFiles = [];
887
+ if (allFontFiles.size > 0) {
888
+ const fontDir = path.join(outputDir, 'fonts');
889
+ fs.mkdirSync(fontDir, { recursive: true });
890
+ for (const ff of allFontFiles.values()) {
891
+ try {
892
+ const res = await fetch(ff.src);
893
+ if (!res.ok)
894
+ continue;
895
+ const buffer = Buffer.from(await res.arrayBuffer());
896
+ const urlPath = decodeURIComponent(new URL(ff.src).pathname);
897
+ const rawName = path.basename(urlPath) || `${ff.family.replace(/\s+/g, '-')}-${ff.weight}-${ff.style}.${ff.format === 'unknown' ? 'bin' : ff.format}`;
898
+ const localPath = path.join(fontDir, safeAssetFilename(rawName, 'font.bin'));
899
+ fs.writeFileSync(localPath, buffer);
900
+ downloadedFontFiles.push({ ...ff, localPath });
901
+ }
902
+ catch {
903
+ downloadedFontFiles.push({ ...ff });
904
+ }
905
+ }
906
+ }
907
+ // Download images
908
+ const downloadedImages = [];
909
+ const allAssetUrls = new Map();
910
+ for (const u of allImageUrls)
911
+ allAssetUrls.set(u, 'img');
912
+ for (const u of allBackgroundImageUrls)
913
+ allAssetUrls.set(u, 'background');
914
+ // Brand assets
915
+ if (brand.favicon)
916
+ allAssetUrls.set(brand.favicon, 'favicon');
917
+ if (brand.ogImage)
918
+ allAssetUrls.set(brand.ogImage, 'og-image');
919
+ if (brand.appleTouchIcon)
920
+ allAssetUrls.set(brand.appleTouchIcon, 'icon');
921
+ for (const [url, rel] of allLinkAssetUrls) {
922
+ if (/icon/i.test(rel))
923
+ allAssetUrls.set(url, 'favicon');
924
+ }
925
+ if (allAssetUrls.size > 0) {
926
+ const imageDir = path.join(outputDir, 'images');
927
+ fs.mkdirSync(imageDir, { recursive: true });
928
+ for (const [url, type] of allAssetUrls) {
929
+ try {
930
+ const res = await fetch(url);
931
+ if (!res.ok) {
932
+ downloadedImages.push({ url, type });
933
+ continue;
934
+ }
935
+ const buffer = Buffer.from(await res.arrayBuffer());
936
+ const urlPath = decodeURIComponent(new URL(url).pathname);
937
+ const filename = path.basename(urlPath) || `asset-${downloadedImages.length}.bin`;
938
+ const localPath = path.join(imageDir, filename);
939
+ fs.writeFileSync(localPath, buffer);
940
+ downloadedImages.push({
941
+ url, type, localPath,
942
+ mimeType: res.headers.get('content-type') || undefined,
943
+ sizeBytes: buffer.length,
944
+ });
945
+ }
946
+ catch {
947
+ downloadedImages.push({ url, type });
948
+ }
949
+ }
950
+ }
951
+ // Download external stylesheets
952
+ const downloadedStylesheets = [];
953
+ if (allExternalStylesheetUrls.size > 0) {
954
+ const cssDir = path.join(outputDir, 'stylesheets');
955
+ fs.mkdirSync(cssDir, { recursive: true });
956
+ for (const url of allExternalStylesheetUrls) {
957
+ try {
958
+ const res = await fetch(url);
959
+ if (!res.ok) {
960
+ downloadedStylesheets.push({ url });
961
+ continue;
962
+ }
963
+ const text = await res.text();
964
+ const urlPath = decodeURIComponent(new URL(url).pathname);
965
+ const filename = path.basename(urlPath) || `style-${downloadedStylesheets.length}.css`;
966
+ const localPath = path.join(cssDir, filename);
967
+ fs.writeFileSync(localPath, text);
968
+ downloadedStylesheets.push({ url, localPath });
969
+ }
970
+ catch {
971
+ downloadedStylesheets.push({ url });
972
+ }
973
+ }
974
+ }
975
+ // Download videos
976
+ const downloadedVideos = [];
977
+ if (allVideoUrls.size > 0) {
978
+ const videoDir = path.join(outputDir, 'videos');
979
+ fs.mkdirSync(videoDir, { recursive: true });
980
+ for (const url of allVideoUrls) {
981
+ try {
982
+ const res = await fetch(url);
983
+ if (!res.ok) {
984
+ downloadedVideos.push({ url, type: 'video' });
985
+ continue;
986
+ }
987
+ const buffer = Buffer.from(await res.arrayBuffer());
988
+ const urlPath = decodeURIComponent(new URL(url).pathname);
989
+ const filename = path.basename(urlPath) || `video-${downloadedVideos.length}.mp4`;
990
+ const localPath = path.join(videoDir, filename);
991
+ fs.writeFileSync(localPath, buffer);
992
+ downloadedVideos.push({
993
+ url, type: 'video', localPath,
994
+ mimeType: res.headers.get('content-type') || undefined,
995
+ sizeBytes: buffer.length,
996
+ });
997
+ }
998
+ catch {
999
+ downloadedVideos.push({ url, type: 'video' });
1000
+ }
1001
+ }
1002
+ }
1003
+ const tokens = {
1004
+ url: opts.url,
1005
+ extractedAt: new Date().toISOString(),
1006
+ viewportsAudited: viewports.map(v => v.name),
1007
+ customProperties: allCustomProps,
1008
+ colors: clusteredColors,
1009
+ typography: { families, scale: Array.from(scaleMap.values()).sort((a, b) => b.count - a.count) },
1010
+ brand,
1011
+ logos: allLogos,
1012
+ icons: allIcons.slice(0, 50),
1013
+ fontFiles: downloadedFontFiles,
1014
+ images: downloadedImages,
1015
+ videos: downloadedVideos,
1016
+ stylesheets: downloadedStylesheets,
1017
+ responsive: responsiveTokens,
1018
+ detectedLibraries: Array.from(allDetectedLibraries),
1019
+ };
1020
+ const tokenPath = path.join(outputDir, 'tokens.json');
1021
+ fs.writeFileSync(tokenPath, JSON.stringify(tokens, null, 2));
1022
+ return { tokens, outputDir, screenshotPaths, scrollMotion };
1023
+ }
1024
+ // ---------------------------------------------------------------------------
1025
+ // Zip packaging
1026
+ // ---------------------------------------------------------------------------
1027
+ export async function createZipBundle(outputDir, tokens) {
1028
+ const { execSync } = await import('node:child_process');
1029
+ const zipPath = `${outputDir}.zip`;
1030
+ // Use system zip (available on macOS/Linux)
1031
+ execSync(`cd "${path.dirname(outputDir)}" && zip -r "${path.basename(zipPath)}" "${path.basename(outputDir)}"`, { stdio: 'pipe' });
1032
+ return zipPath;
1033
+ }
1034
+ //# sourceMappingURL=extract.js.map