auvrynt 1.0.2 → 1.0.5

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 (550) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +366 -233
  3. package/addons/auvrynt_bridge/auvrynt_bridge.gd +500 -0
  4. package/addons/auvrynt_bridge/plugin.cfg +7 -0
  5. package/bin/auvrynt-launcher.cjs +77 -0
  6. package/dist/artifact-registry.js +1 -0
  7. package/dist/aseprite-tools.js +1 -0
  8. package/dist/auth/connection-registry.js +145 -0
  9. package/dist/auth/connection-status.js +176 -0
  10. package/dist/auth/oauth-provider.js +617 -0
  11. package/dist/auth/room-registry.js +131 -0
  12. package/dist/auth/session-registry.js +531 -0
  13. package/dist/background-lifecycle.js +1 -0
  14. package/dist/blender-client.js +1 -109
  15. package/dist/blender-tools.js +1 -930
  16. package/dist/browser-stability.js +1 -0
  17. package/dist/cli/commands/help-command.js +10 -0
  18. package/dist/cli/commands/init-command.js +190 -0
  19. package/dist/cli/commands/integration-commands.js +88 -0
  20. package/dist/cli/commands/serve-command.js +83 -0
  21. package/dist/cli/commands/setup-command.js +81 -0
  22. package/dist/cli/commands/status-commands.js +166 -0
  23. package/dist/cli/commands/tunnel-command.js +127 -0
  24. package/dist/cli/commands/user-config-commands.js +105 -0
  25. package/dist/cli/dashboard-actions.js +516 -0
  26. package/dist/cli/dashboard-native-actions.js +282 -0
  27. package/dist/cli/foreground-server.js +270 -0
  28. package/dist/cli/instance-control.js +21 -0
  29. package/dist/cli/instance-lock.js +123 -0
  30. package/dist/cli/integration-bootstrap.js +206 -0
  31. package/dist/cli/lifecycle-manager.js +493 -0
  32. package/dist/cli/runtime-support.js +74 -0
  33. package/dist/cli/serena-detected-default.js +27 -0
  34. package/dist/cli/tunnel-health-monitor.js +182 -0
  35. package/dist/cli.js +82 -739
  36. package/dist/config.js +1 -243
  37. package/dist/connection-registry.js +1 -73
  38. package/dist/connection-status.js +1 -139
  39. package/dist/control-retry.js +1 -0
  40. package/dist/crash-recovery.js +1 -0
  41. package/dist/db/client.js +16 -3
  42. package/dist/db/schema.js +30 -0
  43. package/dist/dotnet-tools.js +1 -223
  44. package/dist/git-worktrees.js +1 -134
  45. package/dist/git.js +1 -41
  46. package/dist/godot-animation-tilemap.js +1 -82
  47. package/dist/godot-csharp-build.js +1 -93
  48. package/dist/godot-csharp-project.js +1 -157
  49. package/dist/godot-csharp-runner.js +1 -146
  50. package/dist/godot-csharp-semantic.js +1 -161
  51. package/dist/godot-csharp-validate.js +1 -71
  52. package/dist/godot-dotnet-env.js +1 -169
  53. package/dist/godot-editor-bridge.js +1 -140
  54. package/dist/godot-gdscript.js +1 -824
  55. package/dist/godot-project-settings.js +1 -223
  56. package/dist/godot-runtime-testing.js +1 -249
  57. package/dist/godot-tools.js +1 -183
  58. package/dist/http-rate-limit.js +1 -0
  59. package/dist/http-server-hardening.js +1 -0
  60. package/dist/image-tools.js +1 -157
  61. package/dist/infrastructure/command-reference.js +32 -0
  62. package/dist/infrastructure/config.js +424 -0
  63. package/dist/infrastructure/context7-auth.js +55 -0
  64. package/dist/infrastructure/http-rate-limit.js +42 -0
  65. package/dist/infrastructure/http-server-hardening.js +23 -0
  66. package/dist/infrastructure/integration-queue.js +46 -0
  67. package/dist/infrastructure/logger.js +129 -0
  68. package/dist/infrastructure/mcp-event-store.js +103 -0
  69. package/dist/infrastructure/ngrok-auth-pool.js +208 -0
  70. package/dist/infrastructure/request-context.js +14 -0
  71. package/dist/infrastructure/shutdown.js +37 -0
  72. package/dist/infrastructure/sse-heartbeat.js +47 -0
  73. package/dist/infrastructure/tool-capabilities.js +67 -0
  74. package/dist/infrastructure/tool-result-budget.js +175 -0
  75. package/dist/infrastructure/user-config.js +167 -0
  76. package/dist/integration-discovery.js +1 -98
  77. package/dist/integration-queue.js +1 -0
  78. package/dist/integrations/agent-reach/agent-reach-tools.js +196 -0
  79. package/dist/integrations/agent-reach/native-channels.js +468 -0
  80. package/dist/integrations/aseprite/aseprite-analysis-tools.js +368 -0
  81. package/dist/integrations/aseprite/aseprite-editing-tools.js +647 -0
  82. package/dist/integrations/aseprite/aseprite-live-tools.js +297 -0
  83. package/dist/integrations/aseprite/aseprite-safety-tools.js +594 -0
  84. package/dist/integrations/aseprite/aseprite-specialized-tools.js +289 -0
  85. package/dist/integrations/aseprite/aseprite-tools.js +837 -0
  86. package/dist/integrations/blender/blender-catalog-mesh.js +593 -0
  87. package/dist/integrations/blender/blender-catalog-object.js +906 -0
  88. package/dist/integrations/blender/blender-catalog-scene.js +266 -0
  89. package/dist/integrations/blender/blender-catalog-select.js +581 -0
  90. package/dist/integrations/blender/blender-catalog-stubs.js +7512 -0
  91. package/dist/integrations/blender/blender-catalog.js +165 -0
  92. package/dist/integrations/blender/blender-client.js +105 -0
  93. package/dist/integrations/blender/blender-tools.js +2243 -0
  94. package/dist/integrations/context7/context7-tools.js +106 -0
  95. package/dist/integrations/dotnet/dotnet-tools.js +195 -0
  96. package/dist/integrations/godot/godot-animation-tilemap.js +74 -0
  97. package/dist/integrations/godot/godot-csharp-build.js +91 -0
  98. package/dist/integrations/godot/godot-csharp-project.js +155 -0
  99. package/dist/integrations/godot/godot-csharp-runner.js +118 -0
  100. package/dist/integrations/godot/godot-csharp-semantic.js +146 -0
  101. package/dist/integrations/godot/godot-csharp-validate.js +71 -0
  102. package/dist/integrations/godot/godot-dotnet-env.js +134 -0
  103. package/dist/integrations/godot/godot-editor-bridge.js +328 -0
  104. package/dist/integrations/godot/godot-gdscript.js +536 -0
  105. package/dist/integrations/godot/godot-project-settings.js +178 -0
  106. package/dist/integrations/godot/godot-runtime-testing.js +263 -0
  107. package/dist/integrations/godot/godot-tools.js +193 -0
  108. package/dist/integrations/images/image-tools.js +414 -0
  109. package/dist/integrations/images/png-codec.js +251 -0
  110. package/dist/integrations/images/view-image.js +99 -0
  111. package/dist/integrations/images/window-capture.js +148 -0
  112. package/dist/integrations/integration-discovery.js +185 -0
  113. package/dist/integrations/obsidian/obsidian-tools.js +205 -0
  114. package/dist/integrations/playwright/browser-session.js +516 -0
  115. package/dist/integrations/playwright/browser-stability.js +36 -0
  116. package/dist/integrations/playwright/playwright-runtime.js +105 -0
  117. package/dist/integrations/playwright/web-tools.js +504 -0
  118. package/dist/integrations/roblox/roblox-helpers.js +381 -0
  119. package/dist/integrations/roblox/roblox-studio-client.js +112 -0
  120. package/dist/integrations/roblox/roblox-tools.js +106 -0
  121. package/dist/integrations/serena/serena-manager.js +464 -0
  122. package/dist/integrations/serena/serena-tools.js +411 -0
  123. package/dist/logger.js +1 -76
  124. package/dist/mcp-event-store.js +1 -0
  125. package/dist/oauth-provider.js +1 -377
  126. package/dist/pi-tools.js +1 -81
  127. package/dist/playwright-runtime.js +1 -0
  128. package/dist/process/background-lifecycle.js +189 -0
  129. package/dist/process/control-retry.js +42 -0
  130. package/dist/process/crash-recovery.js +23 -0
  131. package/dist/process/processes.js +456 -0
  132. package/dist/processes.js +1 -218
  133. package/dist/request-context.js +1 -0
  134. package/dist/review-checkpoints.js +1 -142
  135. package/dist/room-registry.js +1 -0
  136. package/dist/roots.js +1 -53
  137. package/dist/search-discovery.js +1 -271
  138. package/dist/serena-manager.js +1 -443
  139. package/dist/serena-tools.js +1 -411
  140. package/dist/server/dashboard-page-styles.js +331 -0
  141. package/dist/server/dashboard-page.js +1509 -0
  142. package/dist/server/dashboard-routes.js +46 -0
  143. package/dist/server/dashboard.js +240 -0
  144. package/dist/server/http-helpers.js +30 -0
  145. package/dist/server/integration-profile-update.js +37 -0
  146. package/dist/server/mcp-policy.js +169 -0
  147. package/dist/server/mcp-server-factory.js +127 -0
  148. package/dist/server/mcp-tool-registrar.js +182 -0
  149. package/dist/server/openai-session-hint.js +14 -0
  150. package/dist/server/tool-groups.js +29 -0
  151. package/dist/server/tool-logging.js +33 -0
  152. package/dist/server/tool-registration-shared.js +59 -0
  153. package/dist/server/tools/aseprite-advanced-tools-registration.js +547 -0
  154. package/dist/server/tools/aseprite-tools-registration.js +213 -0
  155. package/dist/server/tools/blender-tools-registration.js +690 -0
  156. package/dist/server/tools/core-file-tools-registration.js +459 -0
  157. package/dist/server/tools/dotnet-godot-window-tools-registration.js +120 -0
  158. package/dist/server/tools/godot-csharp-tools-registration.js +252 -0
  159. package/dist/server/tools/godot-gdscript-tools-registration.js +189 -0
  160. package/dist/server/tools/process-project-tools-registration.js +155 -0
  161. package/dist/server/tools/roblox-tools-registration.js +514 -0
  162. package/dist/server/tools/web-image-tools-registration.js +326 -0
  163. package/dist/server/tools/workspace-lifecycle-tools-registration.js +130 -0
  164. package/dist/server/ui-assets.js +97 -0
  165. package/dist/server/workspace-analytics.js +92 -0
  166. package/dist/server.js +453 -2713
  167. package/dist/session-registry.js +1 -0
  168. package/dist/shutdown.js +1 -0
  169. package/dist/skills.js +1 -45
  170. package/dist/sse-heartbeat.js +1 -0
  171. package/dist/tool-capabilities.js +1 -0
  172. package/dist/tool-result-budget.js +1 -0
  173. package/dist/tools/pi-tools.js +81 -0
  174. package/dist/tunnels/cloudflare-tunnel.js +217 -0
  175. package/dist/tunnels/cloudflared-service.js +174 -0
  176. package/dist/tunnels/ngrok-tunnel.js +220 -0
  177. package/dist/tunnels/tunnel-manager.js +186 -0
  178. package/dist/tunnels/tunnel-types.js +1 -0
  179. package/dist/tunnels/tunnel-utils.js +62 -0
  180. package/dist/ui/.vite/manifest.json +9 -2487
  181. package/dist/ui/assets/heavy-payload-D42A9ceL.js +1 -0
  182. package/dist/ui/assets/lightweight-diff-Cv4g9Hdr.js +13 -0
  183. package/dist/ui/assets/review-payload-DKIg1cKF.js +1 -0
  184. package/dist/ui/assets/workspace-app-CeK3NhmB.css +1 -0
  185. package/dist/ui/assets/{workspace-app-C5QsE314.js → workspace-app-CkUXnpl4.js} +6 -6
  186. package/dist/ui/brand-theme.js +23 -0
  187. package/dist/ui/workspace-app.html +15 -15
  188. package/dist/user-config.js +1 -57
  189. package/dist/view-image.js +1 -104
  190. package/dist/web-tools.js +1 -200
  191. package/dist/window-capture.js +1 -74
  192. package/dist/workspace/artifact-registry.js +69 -0
  193. package/dist/workspace/git-worktrees.js +141 -0
  194. package/dist/workspace/git.js +42 -0
  195. package/dist/workspace/review-checkpoints.js +142 -0
  196. package/dist/workspace/roots.js +56 -0
  197. package/dist/workspace/search-discovery.js +264 -0
  198. package/dist/workspace/skills.js +45 -0
  199. package/dist/workspace/workspace-store.js +219 -0
  200. package/dist/workspace/workspaces.js +314 -0
  201. package/dist/workspace-store.js +1 -125
  202. package/dist/workspaces.js +1 -226
  203. package/docs/CREATOR_STORE_FREE_ASSETS.md +162 -0
  204. package/docs/DEVELOPMENT_TOOLS.md +136 -93
  205. package/docs/assets/auvrynt-icon.png +0 -0
  206. package/docs/assets/auvrynt-icon.svg +20 -0
  207. package/docs/chatgpt-coding-workflow.md +143 -143
  208. package/docs/configuration.md +156 -124
  209. package/docs/diagnostics.md +79 -0
  210. package/docs/gotchas.md +244 -202
  211. package/docs/multi-agent-and-long-projects.md +91 -0
  212. package/docs/security.md +138 -100
  213. package/docs/setup.md +171 -131
  214. package/docs/source-layout.md +60 -0
  215. package/extensions/auvrynt_bridge/auvrynt_bridge.lua +594 -0
  216. package/extensions/auvrynt_bridge/live_analysis.lua +181 -0
  217. package/extensions/auvrynt_bridge/live_animation.lua +134 -0
  218. package/extensions/auvrynt_bridge/live_commands.lua +24 -0
  219. package/extensions/auvrynt_bridge/live_common.lua +130 -0
  220. package/extensions/auvrynt_bridge/live_document.lua +78 -0
  221. package/extensions/auvrynt_bridge/live_drawing.lua +125 -0
  222. package/extensions/auvrynt_bridge/live_effects.lua +91 -0
  223. package/extensions/auvrynt_bridge/live_palette_template.lua +167 -0
  224. package/extensions/auvrynt_bridge/live_selection.lua +110 -0
  225. package/extensions/auvrynt_bridge/live_structure.lua +129 -0
  226. package/extensions/auvrynt_bridge/package.json +17 -0
  227. package/guides/ASEPRITE_AGENT_GUIDE.md +133 -0
  228. package/guides/BLENDER_AGENT_GUIDE.md +472 -0
  229. package/guides/GODOT_AGENT_GUIDE.md +298 -0
  230. package/guides/PROJECT_ROUTER.md +165 -0
  231. package/guides/ROBLOX_AGENT_GUIDE.md +75 -0
  232. package/guides/SOFTWARE_AGENT_GUIDE.md +104 -0
  233. package/guides/WEB_AGENT_GUIDE.md +287 -0
  234. package/package.json +78 -70
  235. package/scripts/dev-server.mjs +121 -120
  236. package/scripts/live-tunnel-smoke.mjs +538 -0
  237. package/scripts/repo-hygiene.mjs +74 -0
  238. package/scripts/restart-after-crash.mjs +49 -0
  239. package/scripts/run-tests.mjs +166 -0
  240. package/dist/ui/assets/abap-CLvhMVsD.js +0 -1
  241. package/dist/ui/assets/actionscript-3--17pq3dv.js +0 -1
  242. package/dist/ui/assets/ada-C5qYipkI.js +0 -1
  243. package/dist/ui/assets/andromeeda-vGVdxbeo.js +0 -1
  244. package/dist/ui/assets/angular-html-BJqQFYI6.js +0 -1
  245. package/dist/ui/assets/angular-ts-CMJECO0Y.js +0 -1
  246. package/dist/ui/assets/apache-U0d_L8uA.js +0 -1
  247. package/dist/ui/assets/apex-CGTLDQj6.js +0 -1
  248. package/dist/ui/assets/apl-DWeXpBBr.js +0 -1
  249. package/dist/ui/assets/applescript-CCn79oCD.js +0 -1
  250. package/dist/ui/assets/ara-4CJ0cIlV.js +0 -1
  251. package/dist/ui/assets/asciidoc-DE70LPWp.js +0 -1
  252. package/dist/ui/assets/asm-Cmm7eHzH.js +0 -1
  253. package/dist/ui/assets/astro-CLbttLOq.js +0 -1
  254. package/dist/ui/assets/aurora-x-CDeNXAV0.js +0 -1
  255. package/dist/ui/assets/awk-BWXHIvNe.js +0 -1
  256. package/dist/ui/assets/ayu-dark-DluEY0Gj.js +0 -1
  257. package/dist/ui/assets/ayu-light-C3h-C4tm.js +0 -1
  258. package/dist/ui/assets/ayu-mirage-Bqwy1Gya.js +0 -1
  259. package/dist/ui/assets/ballerina-B7ZEbQpA.js +0 -1
  260. package/dist/ui/assets/bat-Bo4NYOV-.js +0 -1
  261. package/dist/ui/assets/beancount-D-usSTwE.js +0 -1
  262. package/dist/ui/assets/berry-DKpUyyne.js +0 -1
  263. package/dist/ui/assets/bibtex-Ci_nEsc7.js +0 -1
  264. package/dist/ui/assets/bicep-CUHmPFLl.js +0 -1
  265. package/dist/ui/assets/bird2-C2hNVINV.js +0 -1
  266. package/dist/ui/assets/blade-BOzY0P2V.js +0 -1
  267. package/dist/ui/assets/bsl-BkkzgIyY.js +0 -1
  268. package/dist/ui/assets/c-BG-8uJvh.js +0 -1
  269. package/dist/ui/assets/c3-CnJL0r0V.js +0 -1
  270. package/dist/ui/assets/cadence-CQ2zXKGN.js +0 -1
  271. package/dist/ui/assets/cairo-DLTphjLi.js +0 -1
  272. package/dist/ui/assets/catppuccin-frappe-3VR1Za6u.js +0 -1
  273. package/dist/ui/assets/catppuccin-latte-DwIHMF0Q.js +0 -1
  274. package/dist/ui/assets/catppuccin-macchiato-DYnBP6_5.js +0 -1
  275. package/dist/ui/assets/catppuccin-mocha-DYhrFGRu.js +0 -1
  276. package/dist/ui/assets/clarity-SemFz856.js +0 -1
  277. package/dist/ui/assets/clojure-DqKBuwfJ.js +0 -1
  278. package/dist/ui/assets/cmake-Bj61d0ZC.js +0 -1
  279. package/dist/ui/assets/cobol-I_17IuGe.js +0 -1
  280. package/dist/ui/assets/codeowners-C8r90Shi.js +0 -1
  281. package/dist/ui/assets/codeql-oeQT6MSM.js +0 -1
  282. package/dist/ui/assets/coffee-C0B8hmok.js +0 -1
  283. package/dist/ui/assets/common-lisp-Cv5bFMCO.js +0 -1
  284. package/dist/ui/assets/coq-BrsZFFmf.js +0 -1
  285. package/dist/ui/assets/cpp-DDJXt6Vs.js +0 -1
  286. package/dist/ui/assets/crystal-Bexqace6.js +0 -1
  287. package/dist/ui/assets/csharp-Ct8U2NOr.js +0 -1
  288. package/dist/ui/assets/css-DSQhw_9R.js +0 -1
  289. package/dist/ui/assets/csv-Dx-8-gkx.js +0 -1
  290. package/dist/ui/assets/cue-CE9AQfxI.js +0 -1
  291. package/dist/ui/assets/cypher-ClKdZ_lG.js +0 -1
  292. package/dist/ui/assets/d-qD-0Kul2.js +0 -1
  293. package/dist/ui/assets/dark-plus-Cs2F2srj.js +0 -1
  294. package/dist/ui/assets/dart-DkHntEIa.js +0 -1
  295. package/dist/ui/assets/dax-BkyTk9wS.js +0 -1
  296. package/dist/ui/assets/desktop-Dlh5hvp9.js +0 -1
  297. package/dist/ui/assets/diff-woXpYk--.js +0 -1
  298. package/dist/ui/assets/docker-IyjqRm3v.js +0 -1
  299. package/dist/ui/assets/dotenv-_5a1GRtc.js +0 -1
  300. package/dist/ui/assets/dracula-BHWKrbxM.js +0 -1
  301. package/dist/ui/assets/dracula-soft-5eyTD99u.js +0 -1
  302. package/dist/ui/assets/dream-maker-DW3nJb8Q.js +0 -1
  303. package/dist/ui/assets/edge-Z8YYkHOr.js +0 -1
  304. package/dist/ui/assets/elixir-CG9uKtAF.js +0 -1
  305. package/dist/ui/assets/elm-GfWG2IfT.js +0 -1
  306. package/dist/ui/assets/emacs-lisp-C9PiwqqW.js +0 -1
  307. package/dist/ui/assets/erb-xe89e73J.js +0 -1
  308. package/dist/ui/assets/erlang-Cphh6RMH.js +0 -1
  309. package/dist/ui/assets/everforest-dark-sB-x3p7T.js +0 -1
  310. package/dist/ui/assets/everforest-light-Df2xbC6M.js +0 -1
  311. package/dist/ui/assets/fennel-DQxkIbk2.js +0 -1
  312. package/dist/ui/assets/fish-BJitypiv.js +0 -1
  313. package/dist/ui/assets/fluent-C03EYrpw.js +0 -1
  314. package/dist/ui/assets/fortran-fixed-form-DEKoE2YW.js +0 -1
  315. package/dist/ui/assets/fortran-free-form-CYNrtFtB.js +0 -1
  316. package/dist/ui/assets/fsharp-D13ZGOAj.js +0 -1
  317. package/dist/ui/assets/gdresource-C0sCabJj.js +0 -1
  318. package/dist/ui/assets/gdscript-Cp2uCuqX.js +0 -1
  319. package/dist/ui/assets/gdshader-CBce3t8t.js +0 -1
  320. package/dist/ui/assets/genie-CV2tkWYe.js +0 -1
  321. package/dist/ui/assets/gherkin-DExj1W_8.js +0 -1
  322. package/dist/ui/assets/git-commit-BSykSTBG.js +0 -1
  323. package/dist/ui/assets/git-rebase-CoTL_uSE.js +0 -1
  324. package/dist/ui/assets/github-dark-C-LZuMrd.js +0 -1
  325. package/dist/ui/assets/github-dark-default-DXG-b-1a.js +0 -1
  326. package/dist/ui/assets/github-dark-dimmed-Bx1FflLF.js +0 -1
  327. package/dist/ui/assets/github-dark-high-contrast-B_tTalzw.js +0 -1
  328. package/dist/ui/assets/github-light-EUqPIrTm.js +0 -1
  329. package/dist/ui/assets/github-light-default-BXViO-2h.js +0 -1
  330. package/dist/ui/assets/github-light-high-contrast-B68TUdTA.js +0 -1
  331. package/dist/ui/assets/gleam-CSRkHgEL.js +0 -1
  332. package/dist/ui/assets/glimmer-js-DAn4UCSu.js +0 -1
  333. package/dist/ui/assets/glimmer-ts-Tjyi6Gh5.js +0 -1
  334. package/dist/ui/assets/glsl-CtTDZoqR.js +0 -1
  335. package/dist/ui/assets/gn-ilITqXS6.js +0 -1
  336. package/dist/ui/assets/gnuplot-7GGW24-e.js +0 -1
  337. package/dist/ui/assets/go-BJwz_mda.js +0 -1
  338. package/dist/ui/assets/graphql-D5gcC_Jk.js +0 -1
  339. package/dist/ui/assets/groovy-CacY0gHj.js +0 -1
  340. package/dist/ui/assets/gruvbox-dark-hard-C820rvS2.js +0 -1
  341. package/dist/ui/assets/gruvbox-dark-medium-BPjhmG05.js +0 -1
  342. package/dist/ui/assets/gruvbox-dark-soft-MrdJrrXF.js +0 -1
  343. package/dist/ui/assets/gruvbox-light-hard-BC_s9l72.js +0 -1
  344. package/dist/ui/assets/gruvbox-light-medium-BAWPOn9u.js +0 -1
  345. package/dist/ui/assets/gruvbox-light-soft-BSMLrYjP.js +0 -1
  346. package/dist/ui/assets/hack-BJXY27dt.js +0 -1
  347. package/dist/ui/assets/haml-BLfFoZf7.js +0 -1
  348. package/dist/ui/assets/handlebars-Dw6CWkvN.js +0 -1
  349. package/dist/ui/assets/haskell-D8IpX4py.js +0 -1
  350. package/dist/ui/assets/haxe-OTjmBuCE.js +0 -1
  351. package/dist/ui/assets/hcl-Dh228itO.js +0 -1
  352. package/dist/ui/assets/heavy-payload-Dcg0-F-S.js +0 -4
  353. package/dist/ui/assets/hjson-CxZEssPk.js +0 -1
  354. package/dist/ui/assets/hlsl-Cvrh5tZx.js +0 -1
  355. package/dist/ui/assets/horizon-CE9ld1lL.js +0 -1
  356. package/dist/ui/assets/horizon-bright-Br1oVSNq.js +0 -1
  357. package/dist/ui/assets/houston-CsvMBhTu.js +0 -1
  358. package/dist/ui/assets/html-CAcAWvoN.js +0 -1
  359. package/dist/ui/assets/html-derivative-8aSW2E_7.js +0 -1
  360. package/dist/ui/assets/http-B2YR_3V1.js +0 -1
  361. package/dist/ui/assets/hurl-BnDJ9Zhf.js +0 -1
  362. package/dist/ui/assets/hxml-B0Qn7Nwc.js +0 -1
  363. package/dist/ui/assets/hy-CZbG8q4J.js +0 -1
  364. package/dist/ui/assets/imba-DsUTQ-LC.js +0 -1
  365. package/dist/ui/assets/ini-B5eOa1yu.js +0 -1
  366. package/dist/ui/assets/java-Doxi4W8u.js +0 -1
  367. package/dist/ui/assets/javascript-BbRdEqti.js +0 -1
  368. package/dist/ui/assets/jinja-CS3Tpv_Q.js +0 -1
  369. package/dist/ui/assets/jison-BkPGYVHR.js +0 -1
  370. package/dist/ui/assets/json-BMt1m3m8.js +0 -1
  371. package/dist/ui/assets/json5-BR5RXkoi.js +0 -1
  372. package/dist/ui/assets/jsonc-CYpm1nAK.js +0 -1
  373. package/dist/ui/assets/jsonl-CmCQp5Yx.js +0 -1
  374. package/dist/ui/assets/jsonnet-CJTPZ8u_.js +0 -1
  375. package/dist/ui/assets/jssm-DXw9l8Rf.js +0 -1
  376. package/dist/ui/assets/jsx-CQKrJXXY.js +0 -1
  377. package/dist/ui/assets/julia-BuN7i2du.js +0 -1
  378. package/dist/ui/assets/just-BswEPFPu.js +0 -1
  379. package/dist/ui/assets/kanagawa-dragon-CXtmUGW6.js +0 -1
  380. package/dist/ui/assets/kanagawa-lotus-BN08jTvb.js +0 -1
  381. package/dist/ui/assets/kanagawa-wave-CTweb8Dz.js +0 -1
  382. package/dist/ui/assets/kdl-CsD5j6eV.js +0 -1
  383. package/dist/ui/assets/kotlin-DhhofPvG.js +0 -1
  384. package/dist/ui/assets/kusto-BUv0MjJC.js +0 -1
  385. package/dist/ui/assets/laserwave-C_8bwKvT.js +0 -1
  386. package/dist/ui/assets/latex-DU2VyFfA.js +0 -1
  387. package/dist/ui/assets/lean-CewbzKMR.js +0 -1
  388. package/dist/ui/assets/less-DVTAwKKz.js +0 -1
  389. package/dist/ui/assets/light-plus-DVQuIRkW.js +0 -1
  390. package/dist/ui/assets/liquid-Cmz1L3dq.js +0 -1
  391. package/dist/ui/assets/llvm-Cm23YOpf.js +0 -1
  392. package/dist/ui/assets/log-BNLmms1o.js +0 -1
  393. package/dist/ui/assets/logo-Cluzi2Zq.js +0 -1
  394. package/dist/ui/assets/lua-CfVXhkaJ.js +0 -1
  395. package/dist/ui/assets/luau-CNKltnaQ.js +0 -1
  396. package/dist/ui/assets/make-Dixweg8N.js +0 -1
  397. package/dist/ui/assets/markdown-BYOwaDjH.js +0 -1
  398. package/dist/ui/assets/marko-C5bfqGiA.js +0 -1
  399. package/dist/ui/assets/material-theme-Bm3Qr25_.js +0 -1
  400. package/dist/ui/assets/material-theme-darker-2IIEA8gg.js +0 -1
  401. package/dist/ui/assets/material-theme-lighter-uhdI0v04.js +0 -1
  402. package/dist/ui/assets/material-theme-ocean-CHQ94UKr.js +0 -1
  403. package/dist/ui/assets/material-theme-palenight-B5W6OYN7.js +0 -1
  404. package/dist/ui/assets/matlab-D7qyCx1q.js +0 -1
  405. package/dist/ui/assets/mdc-CVBXYfyq.js +0 -1
  406. package/dist/ui/assets/mdx-DQZ5AkYe.js +0 -1
  407. package/dist/ui/assets/mermaid-Bk4SNUv9.js +0 -1
  408. package/dist/ui/assets/min-dark-BSWPekZh.js +0 -1
  409. package/dist/ui/assets/min-light-DDpmG2fV.js +0 -1
  410. package/dist/ui/assets/mipsasm-BMqwQI7S.js +0 -1
  411. package/dist/ui/assets/mojo-BgCJLMeH.js +0 -1
  412. package/dist/ui/assets/monokai-CdkpiU2Y.js +0 -1
  413. package/dist/ui/assets/moonbit-CaWjb8XO.js +0 -1
  414. package/dist/ui/assets/move-B1IS1UjX.js +0 -1
  415. package/dist/ui/assets/narrat-_X_XdTYD.js +0 -1
  416. package/dist/ui/assets/nextflow-Bbiyy34d.js +0 -1
  417. package/dist/ui/assets/nextflow-groovy-Dc_ddanL.js +0 -1
  418. package/dist/ui/assets/nginx-CzJafxLa.js +0 -1
  419. package/dist/ui/assets/night-owl-DhmEMT88.js +0 -1
  420. package/dist/ui/assets/night-owl-light-eJ-hLW7d.js +0 -1
  421. package/dist/ui/assets/nim-DABKtHtM.js +0 -1
  422. package/dist/ui/assets/nix-IvuFDN5E.js +0 -1
  423. package/dist/ui/assets/nord-Cb4Vim4T.js +0 -1
  424. package/dist/ui/assets/nushell-DcLAeLz5.js +0 -1
  425. package/dist/ui/assets/objective-c-D1A_Heim.js +0 -1
  426. package/dist/ui/assets/objective-cpp-BsSzOQcm.js +0 -1
  427. package/dist/ui/assets/ocaml-O90oeIOV.js +0 -1
  428. package/dist/ui/assets/odin-B1RWQWA5.js +0 -1
  429. package/dist/ui/assets/one-dark-pro-CLwyXe_n.js +0 -1
  430. package/dist/ui/assets/one-light-D7Lr4KcI.js +0 -1
  431. package/dist/ui/assets/openscad-BUDT5pXO.js +0 -1
  432. package/dist/ui/assets/pascal-4ZHwLPI5.js +0 -1
  433. package/dist/ui/assets/perl-CBh7L3lZ.js +0 -1
  434. package/dist/ui/assets/php-DFbI9q5p.js +0 -1
  435. package/dist/ui/assets/pierre-dark-sU4Zdns8.js +0 -1
  436. package/dist/ui/assets/pierre-dark-soft-HXgun5vs.js +0 -1
  437. package/dist/ui/assets/pierre-light-DWBk51d8.js +0 -1
  438. package/dist/ui/assets/pierre-light-soft-CodEzPxf.js +0 -1
  439. package/dist/ui/assets/pkl-ot-7Btpt.js +0 -1
  440. package/dist/ui/assets/plastic-DQwYfKfQ.js +0 -1
  441. package/dist/ui/assets/plsql-DGHpHOYJ.js +0 -1
  442. package/dist/ui/assets/po-BiJDBrnU.js +0 -1
  443. package/dist/ui/assets/poimandres-DRFjx7u4.js +0 -1
  444. package/dist/ui/assets/polar-C7UOKdEL.js +0 -1
  445. package/dist/ui/assets/postcss-BXeXVLqQ.js +0 -1
  446. package/dist/ui/assets/powerquery-DNMTfnFr.js +0 -1
  447. package/dist/ui/assets/powershell-DshXNtvi.js +0 -1
  448. package/dist/ui/assets/prisma-BsRQq5mF.js +0 -1
  449. package/dist/ui/assets/prolog-iXnhIJG7.js +0 -1
  450. package/dist/ui/assets/proto-DB4EqR-F.js +0 -1
  451. package/dist/ui/assets/pug-C_PxC7gO.js +0 -1
  452. package/dist/ui/assets/puppet-CDv2pdJW.js +0 -1
  453. package/dist/ui/assets/purescript-9MfHhQsQ.js +0 -1
  454. package/dist/ui/assets/python-gzcpVVnB.js +0 -1
  455. package/dist/ui/assets/qml-DtngoFFK.js +0 -1
  456. package/dist/ui/assets/qmldir-DCQb3MpD.js +0 -1
  457. package/dist/ui/assets/qss-Fe1Jh2GI.js +0 -1
  458. package/dist/ui/assets/r-DohkcevW.js +0 -1
  459. package/dist/ui/assets/racket-DcIDlBhZ.js +0 -1
  460. package/dist/ui/assets/raku-B3gFvitq.js +0 -1
  461. package/dist/ui/assets/razor-C4t8J0_E.js +0 -1
  462. package/dist/ui/assets/red-CJ3rzSJv.js +0 -1
  463. package/dist/ui/assets/reg-CRGYupPL.js +0 -1
  464. package/dist/ui/assets/regexp-OGbTbjgY.js +0 -1
  465. package/dist/ui/assets/rel-BtDbiS_P.js +0 -1
  466. package/dist/ui/assets/review-payload-C5Qz60Kq.js +0 -1
  467. package/dist/ui/assets/riscv-Ckw8ddFX.js +0 -1
  468. package/dist/ui/assets/ron-VUp2lXgN.js +0 -1
  469. package/dist/ui/assets/rose-pine-BthvhNj6.js +0 -1
  470. package/dist/ui/assets/rose-pine-dawn-Dg85fqjY.js +0 -1
  471. package/dist/ui/assets/rose-pine-moon-hon4tzzS.js +0 -1
  472. package/dist/ui/assets/rosmsg-CAekHB0j.js +0 -1
  473. package/dist/ui/assets/rst-CfBbbcvC.js +0 -1
  474. package/dist/ui/assets/ruby-Bw0mpxHY.js +0 -1
  475. package/dist/ui/assets/rust-Cfkwpbl8.js +0 -1
  476. package/dist/ui/assets/sas-Cs3s9JOf.js +0 -1
  477. package/dist/ui/assets/sass-DXrisJhu.js +0 -1
  478. package/dist/ui/assets/scala-DKOlJaKm.js +0 -1
  479. package/dist/ui/assets/scheme-DQCgrYNe.js +0 -1
  480. package/dist/ui/assets/scss-sRxNJS54.js +0 -1
  481. package/dist/ui/assets/sdbl-bTVj8UrX.js +0 -1
  482. package/dist/ui/assets/shaderlab-TOUzSsQk.js +0 -1
  483. package/dist/ui/assets/shellscript-B0X6wh0R.js +0 -1
  484. package/dist/ui/assets/shellsession-D2FdOioR.js +0 -1
  485. package/dist/ui/assets/slack-dark-DnToyrRv.js +0 -1
  486. package/dist/ui/assets/slack-ochin-B2OO5cIa.js +0 -1
  487. package/dist/ui/assets/smalltalk-B16xEiuN.js +0 -1
  488. package/dist/ui/assets/snazzy-light-4G7pJPwS.js +0 -1
  489. package/dist/ui/assets/solarized-dark-DV17i1UV.js +0 -1
  490. package/dist/ui/assets/solarized-light-DSh2HLQt.js +0 -1
  491. package/dist/ui/assets/solidity-CKzVLygQ.js +0 -1
  492. package/dist/ui/assets/soy-s6g9OjJ1.js +0 -1
  493. package/dist/ui/assets/sparql-D_iOobhT.js +0 -1
  494. package/dist/ui/assets/splunk-BC2Px7Mm.js +0 -1
  495. package/dist/ui/assets/sql-DVTnh5V-.js +0 -1
  496. package/dist/ui/assets/ssh-config-BgfXC-Er.js +0 -1
  497. package/dist/ui/assets/stata-DFIDiXF9.js +0 -1
  498. package/dist/ui/assets/stylus-B6D30XZt.js +0 -1
  499. package/dist/ui/assets/surrealql-DVfrsLyg.js +0 -1
  500. package/dist/ui/assets/svelte-D5GsxESq.js +0 -1
  501. package/dist/ui/assets/swift-DonLKvLd.js +0 -1
  502. package/dist/ui/assets/synthwave-84-nFMaYfgc.js +0 -1
  503. package/dist/ui/assets/system-verilog-DJ5XKQeo.js +0 -1
  504. package/dist/ui/assets/systemd-BxMlprV5.js +0 -1
  505. package/dist/ui/assets/talonscript-CohzipZa.js +0 -1
  506. package/dist/ui/assets/tasl-DMoTqEGO.js +0 -1
  507. package/dist/ui/assets/tcl-CZd0xW_V.js +0 -1
  508. package/dist/ui/assets/templ-DI-Zyqzd.js +0 -1
  509. package/dist/ui/assets/terraform-DswuEJGm.js +0 -1
  510. package/dist/ui/assets/tex-WHHmlbJI.js +0 -1
  511. package/dist/ui/assets/tokyo-night-oM2G3aXe.js +0 -1
  512. package/dist/ui/assets/toml-CcmNWLt0.js +0 -1
  513. package/dist/ui/assets/ts-tags-Dwpo2RFJ.js +0 -1
  514. package/dist/ui/assets/tsv-sltzmVWM.js +0 -1
  515. package/dist/ui/assets/tsx-ph_idaAW.js +0 -1
  516. package/dist/ui/assets/turtle-ByJddavk.js +0 -1
  517. package/dist/ui/assets/twig-YO17jFjR.js +0 -1
  518. package/dist/ui/assets/typescript-CbAxjhkl.js +0 -1
  519. package/dist/ui/assets/typespec-B88KGewJ.js +0 -1
  520. package/dist/ui/assets/typst-DI99ib-x.js +0 -1
  521. package/dist/ui/assets/useFileDiffInstance-B2DLX3Vg.js +0 -244
  522. package/dist/ui/assets/v-DETTlOr0.js +0 -1
  523. package/dist/ui/assets/vala-zf12oZj6.js +0 -1
  524. package/dist/ui/assets/vb-Djn5o6TS.js +0 -1
  525. package/dist/ui/assets/verilog-CiiDBU1e.js +0 -1
  526. package/dist/ui/assets/vesper-D5bVUKB1.js +0 -1
  527. package/dist/ui/assets/vhdl-BroJfC0k.js +0 -1
  528. package/dist/ui/assets/viml-DvXPmvsu.js +0 -1
  529. package/dist/ui/assets/vitesse-black-fwtXNY1n.js +0 -1
  530. package/dist/ui/assets/vitesse-dark-BZCL-v6S.js +0 -1
  531. package/dist/ui/assets/vitesse-light-VbXTXTou.js +0 -1
  532. package/dist/ui/assets/vue-CraHL-Ps.js +0 -1
  533. package/dist/ui/assets/vue-html-e9tyZNtS.js +0 -1
  534. package/dist/ui/assets/vue-vine-CofmxaUb.js +0 -1
  535. package/dist/ui/assets/vyper-CgoNMtux.js +0 -1
  536. package/dist/ui/assets/wasm-BnjxR4X6.js +0 -1
  537. package/dist/ui/assets/wasm-ByWQv1Qj.js +0 -1
  538. package/dist/ui/assets/wenyan-C8pVoKbM.js +0 -1
  539. package/dist/ui/assets/wgsl-BsKzXJz4.js +0 -1
  540. package/dist/ui/assets/wikitext-ClFFjSW2.js +0 -1
  541. package/dist/ui/assets/wit-DdvCle-K.js +0 -1
  542. package/dist/ui/assets/wolfram-DLL8P-h_.js +0 -1
  543. package/dist/ui/assets/workspace-app-8--oTbCd.css +0 -1
  544. package/dist/ui/assets/xml-BWSdPZHm.js +0 -1
  545. package/dist/ui/assets/xsl-BtCt7Hx8.js +0 -1
  546. package/dist/ui/assets/yaml-BZiTw7cQ.js +0 -1
  547. package/dist/ui/assets/zenscript-BnlCZFoB.js +0 -1
  548. package/dist/ui/assets/zig-CMLA9XwU.js +0 -1
  549. package/docs/assets/auvrynt-logo-light.png +0 -0
  550. package/docs/assets/auvrynt-screenshot.png +0 -0
@@ -0,0 +1,837 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFile } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { copyFile, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
5
+ import { dirname, join, relative } from "node:path";
6
+ import { promisify } from "node:util";
7
+ import { discoverLocalIntegrations, processDetected } from "../integration-discovery.js";
8
+ const execFileAsync = promisify(execFile);
9
+ const RESULT_PREFIX = "AUVRYNT_RESULT:";
10
+ const ASEPRITE_TIMEOUT_MS = 60_000;
11
+ const MAX_PIXEL_EDITS = 8_192;
12
+ const MAX_SHAPES = 1_024;
13
+ const ASEPRITE_PALETTE_PRESETS = {
14
+ gameboy: ["#0F380F", "#306230", "#8BAC0F", "#9BBC0F"],
15
+ cga: ["#000000", "#55FFFF", "#FF55FF", "#FFFFFF"],
16
+ c64: ["#000000", "#FFFFFF", "#883932", "#67B6BD", "#8B3F96", "#55A049", "#40318D", "#BFCE72", "#8B5429", "#574200", "#B86962", "#505050", "#787878", "#94E089", "#7869C4", "#ABABAB"],
17
+ grayscale_4: ["#000000", "#555555", "#AAAAAA", "#FFFFFF"],
18
+ monochrome: ["#000000", "#FFFFFF"],
19
+ dawnbringer16: ["#140C1C", "#442434", "#30346D", "#4E4A4E", "#854C30", "#346524", "#D04648", "#757161", "#597DCE", "#D27D2C", "#8595A1", "#6DAA2C", "#D2AA99", "#6DC2CA", "#DAD45E", "#DEEED6"],
20
+ pico8: ["#000000", "#1D2B53", "#7E2553", "#008751", "#AB5236", "#5F574F", "#C2C3C7", "#FFF1E8", "#FF004D", "#FFA300", "#FFEC27", "#00E436", "#29ADFF", "#83769C", "#FF77A8", "#FFCCAA"],
21
+ };
22
+ export function getAsepritePalettePreset(name) {
23
+ return [...ASEPRITE_PALETTE_PRESETS[name]];
24
+ }
25
+ export function textResponse(value) {
26
+ return {
27
+ content: [{
28
+ type: "text",
29
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
30
+ }],
31
+ };
32
+ }
33
+ export function errorResponse(message) {
34
+ return { content: [{ type: "text", text: message }], isError: true };
35
+ }
36
+ // Third-party Aseprite extensions (e.g. pixellab and other community scripts)
37
+ // print their own Lua tracebacks straight to stdout/stderr when they error,
38
+ // and those extensions run inside the same headless Aseprite process we
39
+ // shell out to. Left unfiltered, that noise bleeds into our own tool output
40
+ // on every call that captures stdout/stderr. Strip any line that looks like
41
+ // a third-party extension's Lua traceback before returning CLI output.
42
+ const EXTENSION_TRACEBACK_LINE = /extensions[\\/].*\.lua:\d+/i;
43
+ export function sanitizeCliOutput(output) {
44
+ if (!output)
45
+ return output;
46
+ return output
47
+ .split(/\r?\n/)
48
+ .filter((line) => !EXTENSION_TRACEBACK_LINE.test(line))
49
+ .join("\n")
50
+ .trim();
51
+ }
52
+ function cleanPath(value) {
53
+ const cleaned = value?.trim().replace(/^["']|["']$/g, "").trim();
54
+ return cleaned || undefined;
55
+ }
56
+ function sourceExecutableCandidates(sourcePath) {
57
+ if (!sourcePath)
58
+ return [];
59
+ return process.platform === "win32"
60
+ ? [
61
+ join(sourcePath, "build", "bin", "aseprite.exe"),
62
+ join(sourcePath, "bin", "aseprite.exe"),
63
+ join(sourcePath, "aseprite.exe"),
64
+ ]
65
+ : [
66
+ join(sourcePath, "build", "bin", "aseprite"),
67
+ join(sourcePath, "bin", "aseprite"),
68
+ join(sourcePath, "aseprite"),
69
+ ];
70
+ }
71
+ async function findOnPath() {
72
+ try {
73
+ const command = process.platform === "win32" ? "where.exe" : "which";
74
+ const { stdout } = await execFileAsync(command, ["aseprite"], {
75
+ timeout: 5_000,
76
+ windowsHide: true,
77
+ });
78
+ return stdout.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
84
+ export async function resolveAsepriteExecutable(config) {
85
+ const direct = cleanPath(config.executables.aseprite ?? process.env.AUVRYNT_ASEPRITE_PATH);
86
+ if (direct && existsSync(direct))
87
+ return direct;
88
+ const sourcePath = cleanPath(config.executables.asepriteSource ?? process.env.AUVRYNT_ASEPRITE_SOURCE_PATH);
89
+ for (const candidate of sourceExecutableCandidates(sourcePath)) {
90
+ if (existsSync(candidate))
91
+ return candidate;
92
+ }
93
+ const discovered = await findOnPath();
94
+ if (discovered && existsSync(discovered))
95
+ return discovered;
96
+ throw new Error("Aseprite executable was not found. Configure executables.aseprite, "
97
+ + "executables.asepriteSource, AUVRYNT_ASEPRITE_PATH, or AUVRYNT_ASEPRITE_SOURCE_PATH.");
98
+ }
99
+ export function luaString(value) {
100
+ return `"${value
101
+ .replace(/\\/g, "\\\\")
102
+ .replace(/"/g, "\\\"")
103
+ .replace(/\r/g, "\\r")
104
+ .replace(/\n/g, "\\n")
105
+ .replace(/\t/g, "\\t")}"`;
106
+ }
107
+ export function parseColor(value) {
108
+ const normalized = value.trim().replace(/^#/, "");
109
+ if (!/^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(normalized)) {
110
+ throw new Error(`Invalid color ${value}. Use #RRGGBB or #RRGGBBAA.`);
111
+ }
112
+ return {
113
+ r: Number.parseInt(normalized.slice(0, 2), 16),
114
+ g: Number.parseInt(normalized.slice(2, 4), 16),
115
+ b: Number.parseInt(normalized.slice(4, 6), 16),
116
+ a: normalized.length === 8 ? Number.parseInt(normalized.slice(6, 8), 16) : 255,
117
+ };
118
+ }
119
+ function luaColor(value) {
120
+ const color = parseColor(value);
121
+ return `{r=${color.r},g=${color.g},b=${color.b},a=${color.a}}`;
122
+ }
123
+ export function assertInteger(value, label) {
124
+ if (!Number.isInteger(value))
125
+ throw new Error(`${label} must be an integer.`);
126
+ return value;
127
+ }
128
+ export function assertPositiveInteger(value, label) {
129
+ assertInteger(value, label);
130
+ if (value < 1)
131
+ throw new Error(`${label} must be at least 1.`);
132
+ return value;
133
+ }
134
+ export function luaCommonPrelude() {
135
+ return `
136
+ local function fail(message)
137
+ error(message, 0)
138
+ end
139
+
140
+ local function color_mode_name(mode)
141
+ if mode == ColorMode.RGB then return "rgb" end
142
+ if mode == ColorMode.GRAY then return "grayscale" end
143
+ if mode == ColorMode.INDEXED then return "indexed" end
144
+ return tostring(mode)
145
+ end
146
+
147
+ local function ani_dir_name(direction)
148
+ if direction == AniDir.REVERSE then return "reverse" end
149
+ if direction == AniDir.PING_PONG then return "ping_pong" end
150
+ return "forward"
151
+ end
152
+
153
+ local function find_layer(layers, name)
154
+ for _, layer in ipairs(layers) do
155
+ if layer.name == name then return layer end
156
+ if layer.isGroup then
157
+ local nested = find_layer(layer.layers, name)
158
+ if nested then return nested end
159
+ end
160
+ end
161
+ return nil
162
+ end
163
+
164
+ local function color_pixel(sprite, color)
165
+ if sprite.colorMode == ColorMode.RGB then
166
+ return app.pixelColor.rgba(color.r, color.g, color.b, color.a)
167
+ elseif sprite.colorMode == ColorMode.GRAY then
168
+ local gray = math.floor((color.r * 299 + color.g * 587 + color.b * 114) / 1000)
169
+ return app.pixelColor.graya(gray, color.a)
170
+ else
171
+ local palette = sprite.palettes[1]
172
+ local best_index = 0
173
+ local best_distance = math.huge
174
+ for index = 0, #palette - 1 do
175
+ local candidate = palette:getColor(index)
176
+ local dr = candidate.red - color.r
177
+ local dg = candidate.green - color.g
178
+ local db = candidate.blue - color.b
179
+ local da = candidate.alpha - color.a
180
+ local distance = dr*dr + dg*dg + db*db + da*da
181
+ if distance < best_distance then
182
+ best_distance = distance
183
+ best_index = index
184
+ end
185
+ end
186
+ return best_index
187
+ end
188
+ end
189
+
190
+ local function pixel_rgba(sprite, pixel)
191
+ if sprite.colorMode == ColorMode.RGB then
192
+ return {
193
+ r = app.pixelColor.rgbaR(pixel),
194
+ g = app.pixelColor.rgbaG(pixel),
195
+ b = app.pixelColor.rgbaB(pixel),
196
+ a = app.pixelColor.rgbaA(pixel),
197
+ index = nil,
198
+ }
199
+ elseif sprite.colorMode == ColorMode.GRAY then
200
+ local gray = app.pixelColor.grayaV(pixel)
201
+ return {
202
+ r = gray,
203
+ g = gray,
204
+ b = gray,
205
+ a = app.pixelColor.grayaA(pixel),
206
+ index = nil,
207
+ }
208
+ else
209
+ local palette = sprite.palettes[1]
210
+ local color = palette:getColor(pixel)
211
+ return {
212
+ r = color.red,
213
+ g = color.green,
214
+ b = color.blue,
215
+ a = pixel == sprite.transparentColor and 0 or color.alpha,
216
+ index = pixel,
217
+ }
218
+ end
219
+ end
220
+
221
+ local function rgba_hex(color)
222
+ return string.format("#%02X%02X%02X%02X", color.r, color.g, color.b, color.a)
223
+ end
224
+
225
+ local function canvas_pixel(sprite, layer, frame_number, x, y)
226
+ local cel = layer:cel(frame_number)
227
+ if cel == nil then return pixel_rgba(sprite, sprite.colorMode == ColorMode.INDEXED and sprite.transparentColor or 0) end
228
+ local local_x = x - cel.position.x
229
+ local local_y = y - cel.position.y
230
+ if local_x < 0 or local_y < 0 or local_x >= cel.image.width or local_y >= cel.image.height then
231
+ return pixel_rgba(sprite, sprite.colorMode == ColorMode.INDEXED and sprite.transparentColor or 0)
232
+ end
233
+ return pixel_rgba(sprite, cel.image:getPixel(local_x, local_y))
234
+ end
235
+
236
+ local function ensure_canvas_cel(sprite, layer, frame_number)
237
+ local cel = layer:cel(frame_number)
238
+ if cel == nil then
239
+ local image = Image(sprite.spec)
240
+ image:clear()
241
+ cel = sprite:newCel(layer, frame_number, image, Point(0, 0))
242
+ elseif cel.position.x ~= 0 or cel.position.y ~= 0 or
243
+ cel.image.width ~= sprite.width or cel.image.height ~= sprite.height then
244
+ local image = Image(sprite.spec)
245
+ image:clear()
246
+ image:drawImage(cel.image, cel.position)
247
+ cel.image = image
248
+ cel.position = Point(0, 0)
249
+ end
250
+ return cel
251
+ end
252
+
253
+ local function sprite_summary(sprite)
254
+ local layers = {}
255
+ local function append_layers(items, parent)
256
+ for _, layer in ipairs(items) do
257
+ table.insert(layers, {
258
+ name = layer.name,
259
+ parent = parent,
260
+ is_group = layer.isGroup,
261
+ is_visible = layer.isVisible,
262
+ is_editable = layer.isEditable,
263
+ opacity = layer.opacity,
264
+ blend_mode = tostring(layer.blendMode),
265
+ cel_count = #layer.cels,
266
+ })
267
+ if layer.isGroup then append_layers(layer.layers, layer.name) end
268
+ end
269
+ end
270
+ append_layers(sprite.layers, nil)
271
+
272
+ local frames = {}
273
+ for _, frame in ipairs(sprite.frames) do
274
+ table.insert(frames, {
275
+ frame = frame.frameNumber,
276
+ duration_ms = math.floor(frame.duration * 1000 + 0.5),
277
+ })
278
+ end
279
+
280
+ local tags = {}
281
+ for _, tag in ipairs(sprite.tags) do
282
+ table.insert(tags, {
283
+ name = tag.name,
284
+ from = tag.fromFrame.frameNumber,
285
+ to = tag.toFrame.frameNumber,
286
+ direction = ani_dir_name(tag.aniDir),
287
+ repeats = tag.repeats,
288
+ })
289
+ end
290
+
291
+ local slices = {}
292
+ for _, slice in ipairs(sprite.slices) do
293
+ table.insert(slices, { name = slice.name })
294
+ end
295
+
296
+ local palette_size = 0
297
+ if #sprite.palettes > 0 then palette_size = #sprite.palettes[1] end
298
+ return {
299
+ filename = sprite.filename,
300
+ width = sprite.width,
301
+ height = sprite.height,
302
+ color_mode = color_mode_name(sprite.colorMode),
303
+ frame_count = #sprite.frames,
304
+ layer_count = #layers,
305
+ palette_size = palette_size,
306
+ layers = layers,
307
+ frames = frames,
308
+ tags = tags,
309
+ slices = slices,
310
+ }
311
+ end
312
+
313
+ local function emit_result(value)
314
+ print(${luaString(RESULT_PREFIX)} .. json.encode(value))
315
+ end
316
+ `;
317
+ }
318
+ export async function runLua(config, registry, workspaceId, scriptBody) {
319
+ const workspace = registry.getWorkspace(workspaceId);
320
+ const executable = await resolveAsepriteExecutable(config);
321
+ const unique = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
322
+ const scriptPath = registry.resolveArtifactPath(workspace, `scripts/${unique}.lua`, "aseprite");
323
+ await mkdir(dirname(scriptPath), { recursive: true });
324
+ await writeFile(scriptPath, `${luaCommonPrelude()}\n${scriptBody}\n`, "utf8");
325
+ try {
326
+ const { stdout, stderr } = await execFileAsync(executable, ["-b", "--script", scriptPath], {
327
+ cwd: workspace.root,
328
+ timeout: ASEPRITE_TIMEOUT_MS,
329
+ maxBuffer: 10 * 1024 * 1024,
330
+ windowsHide: true,
331
+ });
332
+ const line = stdout.split(/\r?\n/).reverse().find((entry) => entry.startsWith(RESULT_PREFIX));
333
+ if (!line) {
334
+ const details = sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-4_000);
335
+ throw new Error(`Aseprite script returned no structured result.${details ? `\n${details}` : ""}`);
336
+ }
337
+ return JSON.parse(line.slice(RESULT_PREFIX.length));
338
+ }
339
+ catch (error) {
340
+ const stdout = typeof error?.stdout === "string" ? error.stdout.trim() : "";
341
+ const stderr = typeof error?.stderr === "string" ? error.stderr.trim() : "";
342
+ const detail = sanitizeCliOutput([stdout, stderr, error?.message].filter(Boolean).join("\n")).slice(-6_000);
343
+ throw new Error(`Aseprite operation failed: ${detail || "unknown error"}`);
344
+ }
345
+ finally {
346
+ await unlink(scriptPath).catch(() => undefined);
347
+ }
348
+ }
349
+ export function workspacePath(registry, workspaceId, inputPath) {
350
+ const workspace = registry.getWorkspace(workspaceId);
351
+ const absolute = registry.resolvePath(workspace, inputPath);
352
+ return { absolute, relative: relative(workspace.root, absolute).replace(/\\/g, "/") };
353
+ }
354
+ function safeCheckpointSegment(value) {
355
+ const normalized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
356
+ return normalized.slice(0, 120) || "sprite";
357
+ }
358
+ export async function prepareAsepriteSourceMutation(registry, workspaceId, filePath, expectedVersion, checkpoint, label) {
359
+ const workspace = registry.getWorkspace(workspaceId);
360
+ const file = workspacePath(registry, workspaceId, filePath);
361
+ const bytes = await readFile(file.absolute);
362
+ const version = createHash("sha256").update(bytes).digest("hex");
363
+ if (expectedVersion && expectedVersion !== version) {
364
+ throw new Error(`Aseprite file changed since it was inspected. Expected ${expectedVersion}, current ${version}.`);
365
+ }
366
+ if (checkpoint !== false) {
367
+ const root = registry.resolveArtifactPath(workspace, `checkpoints/${safeCheckpointSegment(file.relative)}`, "aseprite");
368
+ await mkdir(root, { recursive: true });
369
+ const id = `${new Date().toISOString().replace(/[:.]/g, "-")}-${version.slice(0, 12)}`;
370
+ const extension = file.absolute.match(/(\.[^./\\]+)$/)?.[1] ?? ".aseprite";
371
+ const copyName = `${id}${extension}`;
372
+ await copyFile(file.absolute, join(root, copyName));
373
+ await writeFile(join(root, `${id}.json`), JSON.stringify({
374
+ id,
375
+ file: file.relative,
376
+ version,
377
+ label,
378
+ createdAt: new Date().toISOString(),
379
+ copyFile: copyName,
380
+ }, null, 2), "utf8");
381
+ }
382
+ return version;
383
+ }
384
+ export function openSpriteScript(filePath) {
385
+ return `
386
+ local sprite = app.open(${luaString(filePath)})
387
+ if sprite == nil then fail("Unable to open sprite") end
388
+ app.activeSprite = sprite
389
+ `;
390
+ }
391
+ export function selectLayerAndFrameScript(layerName, frame) {
392
+ const frameNumber = frame === undefined ? 1 : assertPositiveInteger(frame, "frame");
393
+ return `
394
+ local layer = ${layerName ? `find_layer(sprite.layers, ${luaString(layerName)})` : "sprite.layers[1]"}
395
+ if layer == nil then fail("Layer not found") end
396
+ if layer.isGroup then fail("A group layer cannot contain pixels") end
397
+ if ${frameNumber} > #sprite.frames then fail("Frame is outside the sprite") end
398
+ local frame_number = ${frameNumber}
399
+ `;
400
+ }
401
+ export async function asepriteDetect(config, registry, input) {
402
+ try {
403
+ registry.getWorkspace(input.workspaceId);
404
+ const executable = await resolveAsepriteExecutable(config);
405
+ const { stdout, stderr } = await execFileAsync(executable, ["--version"], {
406
+ timeout: 10_000,
407
+ windowsHide: true,
408
+ });
409
+ const discovery = await discoverLocalIntegrations({ forceRefresh: true });
410
+ return textResponse({
411
+ detected: true,
412
+ executable,
413
+ sourcePath: cleanPath(config.executables.asepriteSource ?? process.env.AUVRYNT_ASEPRITE_SOURCE_PATH),
414
+ version: (stdout || stderr).split(/\r?\n/).map((line) => line.trim()).find((line) => /^Aseprite\s/i.test(line)) ?? (stdout || stderr).trim(),
415
+ running: processDetected(discovery, "aseprite"),
416
+ integration: "native-cli",
417
+ pluginRequired: false,
418
+ });
419
+ }
420
+ catch (error) {
421
+ return errorResponse(error instanceof Error ? error.message : String(error));
422
+ }
423
+ }
424
+ export async function asepriteInspectFile(config, registry, input) {
425
+ try {
426
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
427
+ const result = await runLua(config, registry, input.workspaceId, `
428
+ ${openSpriteScript(file.absolute)}
429
+ local result = sprite_summary(sprite)
430
+ result.filename = ${luaString(file.relative)}
431
+ emit_result(result)
432
+ sprite:close()
433
+ `);
434
+ return textResponse(result);
435
+ }
436
+ catch (error) {
437
+ return errorResponse(error instanceof Error ? error.message : String(error));
438
+ }
439
+ }
440
+ export async function asepriteCreateSprite(config, registry, input) {
441
+ try {
442
+ const output = workspacePath(registry, input.workspaceId, input.outputPath);
443
+ await mkdir(dirname(output.absolute), { recursive: true });
444
+ const width = assertPositiveInteger(input.width, "width");
445
+ const height = assertPositiveInteger(input.height, "height");
446
+ if (width > 8192 || height > 8192)
447
+ throw new Error("Sprite dimensions cannot exceed 8192x8192.");
448
+ const mode = input.colorMode ?? "rgb";
449
+ const modeLua = mode === "indexed" ? "ColorMode.INDEXED" : mode === "grayscale" ? "ColorMode.GRAY" : "ColorMode.RGB";
450
+ const background = input.backgroundColor ? luaColor(input.backgroundColor) : undefined;
451
+ const result = await runLua(config, registry, input.workspaceId, `
452
+ local sprite = Sprite(${width}, ${height}, ${modeLua})
453
+ sprite.layers[1].name = ${luaString(input.layerName?.trim() || "Layer 1")}
454
+ ${background ? `sprite.cels[1].image:clear(color_pixel(sprite, ${background}))` : "sprite.cels[1].image:clear()"}
455
+ if not sprite:saveAs(${luaString(output.absolute)}) then fail("Unable to save sprite") end
456
+ local result = sprite_summary(sprite)
457
+ result.filename = ${luaString(output.relative)}
458
+ emit_result(result)
459
+ sprite:close()
460
+ `);
461
+ return textResponse(result);
462
+ }
463
+ catch (error) {
464
+ return errorResponse(error instanceof Error ? error.message : String(error));
465
+ }
466
+ }
467
+ export async function asepriteSetPixels(config, registry, input) {
468
+ try {
469
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "set-pixels");
470
+ if (!Array.isArray(input.pixels) || input.pixels.length === 0)
471
+ throw new Error("pixels must not be empty.");
472
+ if (input.pixels.length > MAX_PIXEL_EDITS)
473
+ throw new Error(`A maximum of ${MAX_PIXEL_EDITS} pixels can be changed per call.`);
474
+ const pixels = input.pixels.map((pixel) => ({
475
+ x: assertInteger(pixel.x, "pixel.x"),
476
+ y: assertInteger(pixel.y, "pixel.y"),
477
+ color: parseColor(pixel.color),
478
+ }));
479
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
480
+ const pixelsLua = `{${pixels.map((pixel) => `{x=${pixel.x},y=${pixel.y},color={r=${pixel.color.r},g=${pixel.color.g},b=${pixel.color.b},a=${pixel.color.a}}}`).join(",")}}`;
481
+ const result = await runLua(config, registry, input.workspaceId, `
482
+ ${openSpriteScript(file.absolute)}
483
+ ${selectLayerAndFrameScript(input.layer, input.frame)}
484
+ local cel = ensure_canvas_cel(sprite, layer, frame_number)
485
+ local image = cel.image
486
+ local pixels = ${pixelsLua}
487
+ for _, pixel in ipairs(pixels) do
488
+ if pixel.x < 0 or pixel.x >= sprite.width or pixel.y < 0 or pixel.y >= sprite.height then
489
+ fail("Pixel coordinate is outside the sprite")
490
+ end
491
+ image:drawPixel(pixel.x, pixel.y, color_pixel(sprite, pixel.color))
492
+ end
493
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
494
+ emit_result({ file = ${luaString(file.relative)}, changed_pixels = #pixels, layer = layer.name, frame = frame_number })
495
+ sprite:close()
496
+ `);
497
+ return textResponse(result);
498
+ }
499
+ catch (error) {
500
+ return errorResponse(error instanceof Error ? error.message : String(error));
501
+ }
502
+ }
503
+ export async function asepriteDrawShapes(config, registry, input) {
504
+ try {
505
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "draw-shapes");
506
+ if (!Array.isArray(input.shapes) || input.shapes.length === 0)
507
+ throw new Error("shapes must not be empty.");
508
+ if (input.shapes.length > MAX_SHAPES)
509
+ throw new Error(`A maximum of ${MAX_SHAPES} shapes can be drawn per call.`);
510
+ const shapes = input.shapes.map((shape) => {
511
+ const color = parseColor(shape.color);
512
+ if (shape.type === "point")
513
+ return { ...shape, x: assertInteger(shape.x, "point.x"), y: assertInteger(shape.y, "point.y"), color };
514
+ if (shape.type === "line")
515
+ return {
516
+ ...shape,
517
+ x1: assertInteger(shape.x1, "line.x1"), y1: assertInteger(shape.y1, "line.y1"),
518
+ x2: assertInteger(shape.x2, "line.x2"), y2: assertInteger(shape.y2, "line.y2"), color,
519
+ };
520
+ return {
521
+ ...shape,
522
+ x: assertInteger(shape.x, "rect.x"), y: assertInteger(shape.y, "rect.y"),
523
+ width: assertPositiveInteger(shape.width, "rect.width"), height: assertPositiveInteger(shape.height, "rect.height"), color,
524
+ };
525
+ });
526
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
527
+ const shapesLua = `{${shapes.map((shape) => {
528
+ const color = `{r=${shape.color.r},g=${shape.color.g},b=${shape.color.b},a=${shape.color.a}}`;
529
+ if (shape.type === "point")
530
+ return `{type="point",x=${shape.x},y=${shape.y},color=${color}}`;
531
+ if (shape.type === "line")
532
+ return `{type="line",x1=${shape.x1},y1=${shape.y1},x2=${shape.x2},y2=${shape.y2},color=${color}}`;
533
+ return `{type="rect",x=${shape.x},y=${shape.y},width=${shape.width},height=${shape.height},fill=${shape.fill === true ? "true" : "false"},color=${color}}`;
534
+ }).join(",")}}`;
535
+ const result = await runLua(config, registry, input.workspaceId, `
536
+ ${openSpriteScript(file.absolute)}
537
+ ${selectLayerAndFrameScript(input.layer, input.frame)}
538
+ local cel = ensure_canvas_cel(sprite, layer, frame_number)
539
+ local image = cel.image
540
+ local shapes = ${shapesLua}
541
+ local function put(x, y, pixel)
542
+ if x >= 0 and x < sprite.width and y >= 0 and y < sprite.height then image:drawPixel(x, y, pixel) end
543
+ end
544
+ local function line(x0, y0, x1, y1, pixel)
545
+ local dx = math.abs(x1 - x0)
546
+ local sx = x0 < x1 and 1 or -1
547
+ local dy = -math.abs(y1 - y0)
548
+ local sy = y0 < y1 and 1 or -1
549
+ local err = dx + dy
550
+ while true do
551
+ put(x0, y0, pixel)
552
+ if x0 == x1 and y0 == y1 then break end
553
+ local e2 = 2 * err
554
+ if e2 >= dy then err = err + dy; x0 = x0 + sx end
555
+ if e2 <= dx then err = err + dx; y0 = y0 + sy end
556
+ end
557
+ end
558
+ for _, shape in ipairs(shapes) do
559
+ local pixel = color_pixel(sprite, shape.color)
560
+ if shape.type == "point" then
561
+ put(shape.x, shape.y, pixel)
562
+ elseif shape.type == "line" then
563
+ line(shape.x1, shape.y1, shape.x2, shape.y2, pixel)
564
+ elseif shape.fill then
565
+ for y = shape.y, shape.y + shape.height - 1 do
566
+ for x = shape.x, shape.x + shape.width - 1 do put(x, y, pixel) end
567
+ end
568
+ else
569
+ line(shape.x, shape.y, shape.x + shape.width - 1, shape.y, pixel)
570
+ line(shape.x, shape.y + shape.height - 1, shape.x + shape.width - 1, shape.y + shape.height - 1, pixel)
571
+ line(shape.x, shape.y, shape.x, shape.y + shape.height - 1, pixel)
572
+ line(shape.x + shape.width - 1, shape.y, shape.x + shape.width - 1, shape.y + shape.height - 1, pixel)
573
+ end
574
+ end
575
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
576
+ emit_result({ file = ${luaString(file.relative)}, shapes = #shapes, layer = layer.name, frame = frame_number })
577
+ sprite:close()
578
+ `);
579
+ return textResponse(result);
580
+ }
581
+ catch (error) {
582
+ return errorResponse(error instanceof Error ? error.message : String(error));
583
+ }
584
+ }
585
+ export async function asepriteManageLayers(config, registry, input) {
586
+ try {
587
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-layer-${input.action}`);
588
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
589
+ if (["rename", "delete", "set_visibility", "set_opacity"].includes(input.action) && !input.layer?.trim()) {
590
+ throw new Error("layer is required for this action.");
591
+ }
592
+ if (["add", "rename"].includes(input.action) && !input.name?.trim())
593
+ throw new Error("name is required for this action.");
594
+ if (input.opacity !== undefined && (!Number.isInteger(input.opacity) || input.opacity < 0 || input.opacity > 255)) {
595
+ throw new Error("opacity must be an integer from 0 to 255.");
596
+ }
597
+ const result = await runLua(config, registry, input.workspaceId, `
598
+ ${openSpriteScript(file.absolute)}
599
+ local action = ${luaString(input.action)}
600
+ local layer = ${input.layer ? `find_layer(sprite.layers, ${luaString(input.layer)})` : "nil"}
601
+ if action == "add" then
602
+ layer = sprite:newLayer()
603
+ layer.name = ${luaString(input.name?.trim() || "Layer")}
604
+ elseif layer == nil then
605
+ fail("Layer not found")
606
+ elseif action == "rename" then
607
+ layer.name = ${luaString(input.name?.trim() || "Layer")}
608
+ elseif action == "delete" then
609
+ sprite:deleteLayer(layer)
610
+ elseif action == "set_visibility" then
611
+ layer.isVisible = ${input.visible === false ? "false" : "true"}
612
+ elseif action == "set_opacity" then
613
+ layer.opacity = ${input.opacity ?? 255}
614
+ else
615
+ fail("Unsupported layer action")
616
+ end
617
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
618
+ emit_result(sprite_summary(sprite))
619
+ sprite:close()
620
+ `);
621
+ return textResponse(result);
622
+ }
623
+ catch (error) {
624
+ return errorResponse(error instanceof Error ? error.message : String(error));
625
+ }
626
+ }
627
+ export async function asepriteManageFrames(config, registry, input) {
628
+ try {
629
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-frame-${input.action}`);
630
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
631
+ const frame = input.frame === undefined ? 1 : assertPositiveInteger(input.frame, "frame");
632
+ if (input.durationMs !== undefined && (!Number.isInteger(input.durationMs) || input.durationMs < 1 || input.durationMs > 60_000)) {
633
+ throw new Error("durationMs must be an integer from 1 to 60000.");
634
+ }
635
+ const result = await runLua(config, registry, input.workspaceId, `
636
+ ${openSpriteScript(file.absolute)}
637
+ local action = ${luaString(input.action)}
638
+ local frame_number = ${frame}
639
+ if action == "add_empty" then
640
+ sprite:newEmptyFrame(math.min(frame_number, #sprite.frames + 1))
641
+ elseif frame_number > #sprite.frames then
642
+ fail("Frame is outside the sprite")
643
+ elseif action == "duplicate" then
644
+ sprite:newFrame(frame_number)
645
+ elseif action == "delete" then
646
+ if #sprite.frames <= 1 then fail("Cannot delete the only frame") end
647
+ sprite:deleteFrame(frame_number)
648
+ elseif action == "set_duration" then
649
+ sprite.frames[frame_number].duration = ${(input.durationMs ?? 100) / 1000}
650
+ else
651
+ fail("Unsupported frame action")
652
+ end
653
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
654
+ emit_result(sprite_summary(sprite))
655
+ sprite:close()
656
+ `);
657
+ return textResponse(result);
658
+ }
659
+ catch (error) {
660
+ return errorResponse(error instanceof Error ? error.message : String(error));
661
+ }
662
+ }
663
+ export async function asepriteManageTags(config, registry, input) {
664
+ try {
665
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, `manage-tag-${input.action}`);
666
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
667
+ if (!input.name.trim())
668
+ throw new Error("name is required.");
669
+ const from = input.from === undefined ? 1 : assertPositiveInteger(input.from, "from");
670
+ const to = input.to === undefined ? from : assertPositiveInteger(input.to, "to");
671
+ if (from > to)
672
+ throw new Error("from cannot be greater than to.");
673
+ if (input.action === "rename" && !input.newName?.trim())
674
+ throw new Error("newName is required when renaming a tag.");
675
+ const direction = input.direction ?? "forward";
676
+ const directionLua = direction === "reverse" ? "AniDir.REVERSE" : direction === "ping_pong" ? "AniDir.PING_PONG" : "AniDir.FORWARD";
677
+ const result = await runLua(config, registry, input.workspaceId, `
678
+ ${openSpriteScript(file.absolute)}
679
+ local action = ${luaString(input.action)}
680
+ local name = ${luaString(input.name.trim())}
681
+ local tag = nil
682
+ for _, candidate in ipairs(sprite.tags) do if candidate.name == name then tag = candidate; break end end
683
+ if action == "add" then
684
+ if ${to} > #sprite.frames then fail("Tag range is outside the sprite") end
685
+ if tag ~= nil then fail("Tag already exists") end
686
+ tag = sprite:newTag(${from}, ${to})
687
+ tag.name = name
688
+ tag.aniDir = ${directionLua}
689
+ elseif tag == nil then
690
+ fail("Tag not found")
691
+ elseif action == "rename" then
692
+ tag.name = ${luaString(input.newName?.trim() || input.name.trim())}
693
+ elseif action == "delete" then
694
+ sprite:deleteTag(tag)
695
+ else
696
+ fail("Unsupported tag action")
697
+ end
698
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
699
+ emit_result(sprite_summary(sprite))
700
+ sprite:close()
701
+ `);
702
+ return textResponse(result);
703
+ }
704
+ catch (error) {
705
+ return errorResponse(error instanceof Error ? error.message : String(error));
706
+ }
707
+ }
708
+ export async function asepriteSetPalette(config, registry, input) {
709
+ try {
710
+ await prepareAsepriteSourceMutation(registry, input.workspaceId, input.filePath, input.expectedVersion, input.checkpoint, "set-palette");
711
+ if (!Array.isArray(input.colors) || input.colors.length < 1 || input.colors.length > 256) {
712
+ throw new Error("colors must contain between 1 and 256 entries.");
713
+ }
714
+ const colors = input.colors.map(parseColor);
715
+ const file = workspacePath(registry, input.workspaceId, input.filePath);
716
+ const colorsLua = `{${colors.map((color) => `{r=${color.r},g=${color.g},b=${color.b},a=${color.a}}`).join(",")}}`;
717
+ const result = await runLua(config, registry, input.workspaceId, `
718
+ ${openSpriteScript(file.absolute)}
719
+ local colors = ${colorsLua}
720
+ local palette = Palette(#colors)
721
+ for index, color in ipairs(colors) do palette:setColor(index - 1, Color(color)) end
722
+ sprite:setPalette(palette)
723
+ if not sprite:saveAs(${luaString(file.absolute)}) then fail("Unable to save sprite") end
724
+ emit_result({ file = ${luaString(file.relative)}, palette_size = #colors })
725
+ sprite:close()
726
+ `);
727
+ return textResponse(result);
728
+ }
729
+ catch (error) {
730
+ return errorResponse(error instanceof Error ? error.message : String(error));
731
+ }
732
+ }
733
+ export async function asepriteExportSpriteSheet(config, registry, input) {
734
+ try {
735
+ const workspace = registry.getWorkspace(input.workspaceId);
736
+ const executable = await resolveAsepriteExecutable(config);
737
+ const source = workspacePath(registry, input.workspaceId, input.filePath);
738
+ const sheet = workspacePath(registry, input.workspaceId, input.sheetPath);
739
+ const data = input.dataPath ? workspacePath(registry, input.workspaceId, input.dataPath) : undefined;
740
+ await mkdir(dirname(sheet.absolute), { recursive: true });
741
+ if (data)
742
+ await mkdir(dirname(data.absolute), { recursive: true });
743
+ const args = ["-b", source.absolute, "--sheet", sheet.absolute, "--sheet-type", input.sheetType ?? "horizontal"];
744
+ if (data)
745
+ args.push("--data", data.absolute, "--format", "json-array");
746
+ if (input.columns !== undefined)
747
+ args.push("--sheet-columns", String(assertPositiveInteger(input.columns, "columns")));
748
+ if (input.rows !== undefined)
749
+ args.push("--sheet-rows", String(assertPositiveInteger(input.rows, "rows")));
750
+ if (input.tag)
751
+ args.push("--tag", input.tag);
752
+ if (input.layer)
753
+ args.push("--layer", input.layer);
754
+ if (input.trim)
755
+ args.push("--trim");
756
+ if (input.mergeDuplicates)
757
+ args.push("--merge-duplicates");
758
+ if (input.borderPadding !== undefined)
759
+ args.push("--border-padding", String(Math.max(0, assertInteger(input.borderPadding, "borderPadding"))));
760
+ if (input.shapePadding !== undefined)
761
+ args.push("--shape-padding", String(Math.max(0, assertInteger(input.shapePadding, "shapePadding"))));
762
+ if (input.innerPadding !== undefined)
763
+ args.push("--inner-padding", String(Math.max(0, assertInteger(input.innerPadding, "innerPadding"))));
764
+ const { stdout, stderr } = await execFileAsync(executable, args, {
765
+ cwd: workspace.root,
766
+ timeout: ASEPRITE_TIMEOUT_MS,
767
+ maxBuffer: 10 * 1024 * 1024,
768
+ windowsHide: true,
769
+ });
770
+ return textResponse({
771
+ success: true,
772
+ source: source.relative,
773
+ sheet: sheet.relative,
774
+ data: data?.relative,
775
+ output: sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-2_000),
776
+ });
777
+ }
778
+ catch (error) {
779
+ return errorResponse(`Aseprite export failed: ${sanitizeCliOutput(String(error?.stderr || error?.message || String(error)))}`);
780
+ }
781
+ }
782
+ export async function asepriteConvertFile(config, registry, input) {
783
+ try {
784
+ const workspace = registry.getWorkspace(input.workspaceId);
785
+ const executable = await resolveAsepriteExecutable(config);
786
+ const source = workspacePath(registry, input.workspaceId, input.filePath);
787
+ const output = workspacePath(registry, input.workspaceId, input.outputPath);
788
+ await mkdir(dirname(output.absolute), { recursive: true });
789
+ const args = ["-b", source.absolute];
790
+ if (input.scale !== undefined) {
791
+ if (!Number.isFinite(input.scale) || input.scale <= 0 || input.scale > 64)
792
+ throw new Error("scale must be greater than 0 and no more than 64.");
793
+ args.push("--scale", String(input.scale));
794
+ }
795
+ if (input.colorMode)
796
+ args.push("--color-mode", input.colorMode === "grayscale" ? "grayscale" : input.colorMode);
797
+ if (input.palettePath)
798
+ args.push("--palette", workspacePath(registry, input.workspaceId, input.palettePath).absolute);
799
+ if (input.trim)
800
+ args.push("--trim");
801
+ if (input.crop) {
802
+ args.push("--crop", [
803
+ assertInteger(input.crop.x, "crop.x"),
804
+ assertInteger(input.crop.y, "crop.y"),
805
+ assertPositiveInteger(input.crop.width, "crop.width"),
806
+ assertPositiveInteger(input.crop.height, "crop.height"),
807
+ ].join(","));
808
+ }
809
+ if (input.frameRange) {
810
+ const from = assertPositiveInteger(input.frameRange.from, "frameRange.from");
811
+ const to = assertPositiveInteger(input.frameRange.to, "frameRange.to");
812
+ if (from > to)
813
+ throw new Error("frameRange.from cannot be greater than frameRange.to.");
814
+ args.push("--frame-range", `${from},${to}`);
815
+ }
816
+ if (input.tag)
817
+ args.push("--tag", input.tag);
818
+ if (input.layer)
819
+ args.push("--layer", input.layer);
820
+ args.push("--save-as", output.absolute);
821
+ const { stdout, stderr } = await execFileAsync(executable, args, {
822
+ cwd: workspace.root,
823
+ timeout: ASEPRITE_TIMEOUT_MS,
824
+ maxBuffer: 10 * 1024 * 1024,
825
+ windowsHide: true,
826
+ });
827
+ return textResponse({
828
+ success: true,
829
+ source: source.relative,
830
+ output: output.relative,
831
+ details: sanitizeCliOutput([stdout.trim(), stderr.trim()].filter(Boolean).join("\n")).slice(-2_000),
832
+ });
833
+ }
834
+ catch (error) {
835
+ return errorResponse(`Aseprite conversion failed: ${sanitizeCliOutput(String(error?.stderr || error?.message || String(error)))}`);
836
+ }
837
+ }