@tomsun28/pizza 0.1.0 → 0.1.1

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 (382) hide show
  1. package/README.md +20 -143
  2. package/README.zh-CN.md +36 -0
  3. package/dist/bun/cli.d.ts +2 -0
  4. package/dist/bun/cli.js +6 -0
  5. package/dist/bun/register-bedrock.d.ts +1 -0
  6. package/dist/bun/register-bedrock.js +3 -0
  7. package/dist/cli/args.d.ts +48 -0
  8. package/dist/cli/args.js +330 -0
  9. package/dist/cli/config-selector.d.ts +13 -0
  10. package/dist/cli/config-selector.js +30 -0
  11. package/dist/cli/file-processor.d.ts +14 -0
  12. package/dist/cli/file-processor.js +82 -0
  13. package/dist/cli/initial-message.d.ts +17 -0
  14. package/dist/cli/initial-message.js +21 -0
  15. package/dist/cli/list-models.d.ts +8 -0
  16. package/dist/cli/list-models.js +96 -0
  17. package/dist/cli.d.ts +2 -0
  18. package/dist/cli.js +68 -0
  19. package/dist/config.d.ts +79 -0
  20. package/dist/config.js +223 -0
  21. package/dist/core/agent/index.d.ts +6 -0
  22. package/dist/core/agent/index.js +6 -0
  23. package/dist/core/agent/types.d.ts +202 -0
  24. package/dist/core/agent/types.js +51 -0
  25. package/dist/core/auth-storage.d.ts +135 -0
  26. package/dist/core/auth-storage.js +417 -0
  27. package/dist/core/bash-executor.d.ts +31 -0
  28. package/dist/core/bash-executor.js +107 -0
  29. package/dist/core/compaction/branch-summarization.d.ts +87 -0
  30. package/dist/core/compaction/branch-summarization.js +242 -0
  31. package/dist/core/compaction/compaction-engine.d.ts +40 -0
  32. package/dist/core/compaction/compaction-engine.js +292 -0
  33. package/dist/core/compaction/compaction.d.ts +106 -0
  34. package/dist/core/compaction/compaction.js +295 -0
  35. package/dist/core/compaction/index.d.ts +7 -0
  36. package/dist/core/compaction/index.js +7 -0
  37. package/dist/core/compaction/utils.d.ts +37 -0
  38. package/dist/core/compaction/utils.js +152 -0
  39. package/dist/core/defaults.d.ts +2 -0
  40. package/dist/core/defaults.js +1 -0
  41. package/dist/core/diagnostics.d.ts +14 -0
  42. package/dist/core/diagnostics.js +1 -0
  43. package/dist/core/event-bus.d.ts +8 -0
  44. package/dist/core/event-bus.js +24 -0
  45. package/dist/core/event-store/events.d.ts +465 -0
  46. package/dist/core/event-store/events.js +6 -0
  47. package/dist/core/event-store/index.d.ts +10 -0
  48. package/dist/core/event-store/index.js +10 -0
  49. package/dist/core/event-store/sqlite-store.d.ts +27 -0
  50. package/dist/core/event-store/sqlite-store.js +275 -0
  51. package/dist/core/event-store/store.d.ts +91 -0
  52. package/dist/core/event-store/store.js +7 -0
  53. package/dist/core/event-store/thread-scoped-store.d.ts +29 -0
  54. package/dist/core/event-store/thread-scoped-store.js +50 -0
  55. package/dist/core/event-store/types.d.ts +70 -0
  56. package/dist/core/event-store/types.js +7 -0
  57. package/dist/core/event-store/workspace.d.ts +37 -0
  58. package/dist/core/event-store/workspace.js +71 -0
  59. package/dist/core/exec.d.ts +28 -0
  60. package/dist/core/exec.js +74 -0
  61. package/dist/core/export-html/ansi-to-html.d.ts +21 -0
  62. package/dist/core/export-html/ansi-to-html.js +248 -0
  63. package/dist/core/export-html/index.d.ts +29 -0
  64. package/dist/core/export-html/index.js +186 -0
  65. package/dist/core/export-html/template.css +1017 -0
  66. package/dist/core/export-html/template.html +55 -0
  67. package/dist/core/export-html/template.js +1742 -0
  68. package/dist/core/export-html/tool-renderer.d.ts +39 -0
  69. package/dist/core/export-html/tool-renderer.js +94 -0
  70. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  71. package/dist/core/export-html/vendor/marked.min.js +6 -0
  72. package/dist/core/extensions/index.d.ts +12 -0
  73. package/dist/core/extensions/index.js +9 -0
  74. package/dist/core/extensions/loader.d.ts +24 -0
  75. package/dist/core/extensions/loader.js +492 -0
  76. package/dist/core/extensions/runner.d.ts +167 -0
  77. package/dist/core/extensions/runner.js +898 -0
  78. package/dist/core/extensions/session-context.d.ts +67 -0
  79. package/dist/core/extensions/session-context.js +272 -0
  80. package/dist/core/extensions/types.d.ts +1147 -0
  81. package/dist/core/extensions/types.js +44 -0
  82. package/dist/core/extensions/wrapper.d.ts +19 -0
  83. package/dist/core/extensions/wrapper.js +21 -0
  84. package/dist/core/footer-data-provider.d.ts +47 -0
  85. package/dist/core/footer-data-provider.js +321 -0
  86. package/dist/core/goal/index.d.ts +3 -0
  87. package/dist/core/goal/index.js +1 -0
  88. package/dist/core/goal/scheduler.d.ts +83 -0
  89. package/dist/core/goal/scheduler.js +154 -0
  90. package/dist/core/goal/types.d.ts +182 -0
  91. package/dist/core/goal/types.js +11 -0
  92. package/dist/core/index.d.ts +8 -0
  93. package/dist/core/index.js +8 -0
  94. package/dist/core/intent/classifier.d.ts +53 -0
  95. package/dist/core/intent/classifier.js +353 -0
  96. package/dist/core/intent/index.d.ts +8 -0
  97. package/dist/core/intent/index.js +8 -0
  98. package/dist/core/intent/tool-adapter.d.ts +31 -0
  99. package/dist/core/intent/tool-adapter.js +168 -0
  100. package/dist/core/intent/types.d.ts +71 -0
  101. package/dist/core/intent/types.js +6 -0
  102. package/dist/core/keybindings.d.ts +297 -0
  103. package/dist/core/keybindings.js +250 -0
  104. package/dist/core/messages.d.ts +30 -0
  105. package/dist/core/messages.js +134 -0
  106. package/dist/core/model-registry.d.ts +131 -0
  107. package/dist/core/model-registry.js +634 -0
  108. package/dist/core/model-resolver.d.ts +109 -0
  109. package/dist/core/model-resolver.js +486 -0
  110. package/dist/core/output-guard.d.ts +5 -0
  111. package/dist/core/output-guard.js +58 -0
  112. package/dist/core/package-manager.d.ts +196 -0
  113. package/dist/core/package-manager.js +1873 -0
  114. package/dist/core/projection/boundary-inferrer.d.ts +58 -0
  115. package/dist/core/projection/boundary-inferrer.js +122 -0
  116. package/dist/core/projection/event-to-message.d.ts +35 -0
  117. package/dist/core/projection/event-to-message.js +253 -0
  118. package/dist/core/projection/goal-projection.d.ts +67 -0
  119. package/dist/core/projection/goal-projection.js +339 -0
  120. package/dist/core/projection/index.d.ts +13 -0
  121. package/dist/core/projection/index.js +13 -0
  122. package/dist/core/projection/session-manager.d.ts +100 -0
  123. package/dist/core/projection/session-manager.js +331 -0
  124. package/dist/core/projection/session-projection.d.ts +58 -0
  125. package/dist/core/projection/session-projection.js +257 -0
  126. package/dist/core/projection/task-history.d.ts +13 -0
  127. package/dist/core/projection/task-history.js +61 -0
  128. package/dist/core/projection/timeline-projection.d.ts +82 -0
  129. package/dist/core/projection/timeline-projection.js +276 -0
  130. package/dist/core/projection/types.d.ts +69 -0
  131. package/dist/core/projection/types.js +6 -0
  132. package/dist/core/prompt-templates.d.ts +51 -0
  133. package/dist/core/prompt-templates.js +249 -0
  134. package/dist/core/resolve-config-value.d.ts +22 -0
  135. package/dist/core/resolve-config-value.js +125 -0
  136. package/dist/core/resource-loader.d.ts +193 -0
  137. package/dist/core/resource-loader.js +685 -0
  138. package/dist/core/runtime/ai-client.d.ts +12 -0
  139. package/dist/core/runtime/ai-client.js +153 -0
  140. package/dist/core/runtime/checkpoint.d.ts +12 -0
  141. package/dist/core/runtime/checkpoint.js +78 -0
  142. package/dist/core/runtime/index.d.ts +12 -0
  143. package/dist/core/runtime/index.js +12 -0
  144. package/dist/core/runtime/llm-types.d.ts +99 -0
  145. package/dist/core/runtime/llm-types.js +6 -0
  146. package/dist/core/runtime/local-runtime.d.ts +23 -0
  147. package/dist/core/runtime/local-runtime.js +59 -0
  148. package/dist/core/runtime/policies.d.ts +72 -0
  149. package/dist/core/runtime/policies.js +37 -0
  150. package/dist/core/runtime/reactor.d.ts +213 -0
  151. package/dist/core/runtime/reactor.js +992 -0
  152. package/dist/core/runtime/runtime.d.ts +191 -0
  153. package/dist/core/runtime/runtime.js +449 -0
  154. package/dist/core/runtime/types.d.ts +42 -0
  155. package/dist/core/runtime/types.js +1 -0
  156. package/dist/core/sdk.d.ts +12 -0
  157. package/dist/core/sdk.js +10 -0
  158. package/dist/core/session-context-builder.d.ts +9 -0
  159. package/dist/core/session-context-builder.js +87 -0
  160. package/dist/core/session-cwd.d.ts +18 -0
  161. package/dist/core/session-cwd.js +36 -0
  162. package/dist/core/session-facade-factory.d.ts +79 -0
  163. package/dist/core/session-facade-factory.js +460 -0
  164. package/dist/core/session-facade.d.ts +57 -0
  165. package/dist/core/session-facade.js +91 -0
  166. package/dist/core/session-listing.d.ts +30 -0
  167. package/dist/core/session-listing.js +168 -0
  168. package/dist/core/session-reader.d.ts +19 -0
  169. package/dist/core/session-reader.js +101 -0
  170. package/dist/core/session-ref.d.ts +7 -0
  171. package/dist/core/session-ref.js +25 -0
  172. package/dist/core/session-services.d.ts +51 -0
  173. package/dist/core/session-services.js +90 -0
  174. package/dist/core/session-stats.d.ts +43 -0
  175. package/dist/core/session-stats.js +41 -0
  176. package/dist/core/settings-manager.d.ts +236 -0
  177. package/dist/core/settings-manager.js +706 -0
  178. package/dist/core/skill-block-parser.d.ts +12 -0
  179. package/dist/core/skill-block-parser.js +15 -0
  180. package/dist/core/skills.d.ts +59 -0
  181. package/dist/core/skills.js +408 -0
  182. package/dist/core/slash-commands.d.ts +13 -0
  183. package/dist/core/slash-commands.js +17 -0
  184. package/dist/core/source-info.d.ts +17 -0
  185. package/dist/core/source-info.js +18 -0
  186. package/dist/core/system-prompt.d.ts +27 -0
  187. package/dist/core/system-prompt.js +179 -0
  188. package/dist/core/timings.d.ts +7 -0
  189. package/dist/core/timings.js +30 -0
  190. package/dist/core/tools/bash.d.ts +95 -0
  191. package/dist/core/tools/bash.js +513 -0
  192. package/dist/core/tools/builtin-commands.d.ts +75 -0
  193. package/dist/core/tools/builtin-commands.js +503 -0
  194. package/dist/core/tools/compat-commands.d.ts +12 -0
  195. package/dist/core/tools/compat-commands.js +1280 -0
  196. package/dist/core/tools/edit-diff.d.ts +59 -0
  197. package/dist/core/tools/edit-diff.js +263 -0
  198. package/dist/core/tools/edit.d.ts +49 -0
  199. package/dist/core/tools/edit.js +340 -0
  200. package/dist/core/tools/file-mutation-queue.d.ts +5 -0
  201. package/dist/core/tools/file-mutation-queue.js +36 -0
  202. package/dist/core/tools/find.d.ts +34 -0
  203. package/dist/core/tools/find.js +459 -0
  204. package/dist/core/tools/grep.d.ts +36 -0
  205. package/dist/core/tools/grep.js +422 -0
  206. package/dist/core/tools/index.d.ts +70 -0
  207. package/dist/core/tools/index.js +129 -0
  208. package/dist/core/tools/line-anchors.d.ts +33 -0
  209. package/dist/core/tools/line-anchors.js +151 -0
  210. package/dist/core/tools/ls.d.ts +36 -0
  211. package/dist/core/tools/ls.js +268 -0
  212. package/dist/core/tools/path-utils.d.ts +7 -0
  213. package/dist/core/tools/path-utils.js +80 -0
  214. package/dist/core/tools/read.d.ts +35 -0
  215. package/dist/core/tools/read.js +244 -0
  216. package/dist/core/tools/render-utils.d.ts +20 -0
  217. package/dist/core/tools/render-utils.js +48 -0
  218. package/dist/core/tools/session-split.d.ts +9 -0
  219. package/dist/core/tools/session-split.js +77 -0
  220. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -0
  221. package/dist/core/tools/tool-definition-wrapper.js +32 -0
  222. package/dist/core/tools/truncate.d.ts +69 -0
  223. package/dist/core/tools/truncate.js +204 -0
  224. package/dist/core/tools/write.d.ts +25 -0
  225. package/dist/core/tools/write.js +211 -0
  226. package/dist/core/types/session-types.d.ts +123 -0
  227. package/dist/core/types/session-types.js +8 -0
  228. package/dist/index.d.ts +27 -0
  229. package/dist/index.js +38 -0
  230. package/dist/main.d.ts +11 -2
  231. package/dist/main.js +577 -149
  232. package/dist/modes/event-mapper.d.ts +115 -0
  233. package/dist/modes/event-mapper.js +200 -0
  234. package/dist/modes/gui/server.d.ts +14 -0
  235. package/dist/modes/gui/server.js +1032 -0
  236. package/dist/modes/index.d.ts +10 -0
  237. package/dist/modes/index.js +9 -0
  238. package/dist/modes/interactive/assets/clankolas.png +0 -0
  239. package/dist/modes/interactive/components/armin.d.ts +33 -0
  240. package/dist/modes/interactive/components/armin.js +328 -0
  241. package/dist/modes/interactive/components/assistant-message.d.ts +19 -0
  242. package/dist/modes/interactive/components/assistant-message.js +115 -0
  243. package/dist/modes/interactive/components/bash-execution.d.ts +33 -0
  244. package/dist/modes/interactive/components/bash-execution.js +169 -0
  245. package/dist/modes/interactive/components/bordered-loader.d.ts +15 -0
  246. package/dist/modes/interactive/components/bordered-loader.js +50 -0
  247. package/dist/modes/interactive/components/branch-summary-message.d.ts +15 -0
  248. package/dist/modes/interactive/components/branch-summary-message.js +41 -0
  249. package/dist/modes/interactive/components/brand-header.d.ts +33 -0
  250. package/dist/modes/interactive/components/brand-header.js +235 -0
  251. package/dist/modes/interactive/components/compaction-summary-message.d.ts +15 -0
  252. package/dist/modes/interactive/components/compaction-summary-message.js +42 -0
  253. package/dist/modes/interactive/components/config-selector.d.ts +70 -0
  254. package/dist/modes/interactive/components/config-selector.js +469 -0
  255. package/dist/modes/interactive/components/countdown-timer.d.ts +13 -0
  256. package/dist/modes/interactive/components/countdown-timer.js +27 -0
  257. package/dist/modes/interactive/components/custom-editor.d.ts +27 -0
  258. package/dist/modes/interactive/components/custom-editor.js +94 -0
  259. package/dist/modes/interactive/components/custom-message.d.ts +19 -0
  260. package/dist/modes/interactive/components/custom-message.js +73 -0
  261. package/dist/modes/interactive/components/daxnuts.d.ts +22 -0
  262. package/dist/modes/interactive/components/daxnuts.js +137 -0
  263. package/dist/modes/interactive/components/diff.d.ts +11 -0
  264. package/dist/modes/interactive/components/diff.js +132 -0
  265. package/dist/modes/interactive/components/dynamic-border.d.ts +14 -0
  266. package/dist/modes/interactive/components/dynamic-border.js +19 -0
  267. package/dist/modes/interactive/components/earendil-announcement.d.ts +4 -0
  268. package/dist/modes/interactive/components/earendil-announcement.js +39 -0
  269. package/dist/modes/interactive/components/extension-editor.d.ts +19 -0
  270. package/dist/modes/interactive/components/extension-editor.js +105 -0
  271. package/dist/modes/interactive/components/extension-input.d.ts +22 -0
  272. package/dist/modes/interactive/components/extension-input.js +54 -0
  273. package/dist/modes/interactive/components/extension-selector.d.ts +23 -0
  274. package/dist/modes/interactive/components/extension-selector.js +70 -0
  275. package/dist/modes/interactive/components/footer.d.ts +55 -0
  276. package/dist/modes/interactive/components/footer.js +207 -0
  277. package/dist/modes/interactive/components/index.d.ts +31 -0
  278. package/dist/modes/interactive/components/index.js +32 -0
  279. package/dist/modes/interactive/components/keybinding-hints.d.ts +7 -0
  280. package/dist/modes/interactive/components/keybinding-hints.js +21 -0
  281. package/dist/modes/interactive/components/login-dialog.d.ts +41 -0
  282. package/dist/modes/interactive/components/login-dialog.js +138 -0
  283. package/dist/modes/interactive/components/model-selector.d.ts +32 -0
  284. package/dist/modes/interactive/components/model-selector.js +209 -0
  285. package/dist/modes/interactive/components/oauth-selector.d.ts +18 -0
  286. package/dist/modes/interactive/components/oauth-selector.js +91 -0
  287. package/dist/modes/interactive/components/settings-selector.d.ts +57 -0
  288. package/dist/modes/interactive/components/settings-selector.js +299 -0
  289. package/dist/modes/interactive/components/show-images-selector.d.ts +9 -0
  290. package/dist/modes/interactive/components/show-images-selector.js +37 -0
  291. package/dist/modes/interactive/components/skill-invocation-message.d.ts +16 -0
  292. package/dist/modes/interactive/components/skill-invocation-message.js +44 -0
  293. package/dist/modes/interactive/components/theme-selector.d.ts +10 -0
  294. package/dist/modes/interactive/components/theme-selector.js +47 -0
  295. package/dist/modes/interactive/components/thinking-selector.d.ts +10 -0
  296. package/dist/modes/interactive/components/thinking-selector.js +50 -0
  297. package/dist/modes/interactive/components/timeline-view.d.ts +31 -0
  298. package/dist/modes/interactive/components/timeline-view.js +81 -0
  299. package/dist/modes/interactive/components/tool-execution.d.ts +64 -0
  300. package/dist/modes/interactive/components/tool-execution.js +305 -0
  301. package/dist/modes/interactive/components/tree-selector.d.ts +88 -0
  302. package/dist/modes/interactive/components/tree-selector.js +1072 -0
  303. package/dist/modes/interactive/components/user-message-selector.d.ts +29 -0
  304. package/dist/modes/interactive/components/user-message-selector.js +110 -0
  305. package/dist/modes/interactive/components/user-message.d.ts +9 -0
  306. package/dist/modes/interactive/components/user-message.js +27 -0
  307. package/dist/modes/interactive/components/visual-truncate.d.ts +23 -0
  308. package/dist/modes/interactive/components/visual-truncate.js +32 -0
  309. package/dist/modes/interactive/interactive-mode.d.ts +394 -0
  310. package/dist/modes/interactive/interactive-mode.js +4116 -0
  311. package/dist/modes/interactive/theme/dark.json +85 -0
  312. package/dist/modes/interactive/theme/light.json +84 -0
  313. package/dist/modes/interactive/theme/theme-schema.json +335 -0
  314. package/dist/modes/interactive/theme/theme.d.ts +80 -0
  315. package/dist/modes/interactive/theme/theme.js +973 -0
  316. package/dist/modes/print-mode.d.ts +27 -0
  317. package/dist/modes/print-mode.js +102 -0
  318. package/dist/modes/rpc/jsonl.d.ts +16 -0
  319. package/dist/modes/rpc/jsonl.js +48 -0
  320. package/dist/modes/rpc/rpc-client.d.ts +219 -0
  321. package/dist/modes/rpc/rpc-client.js +405 -0
  322. package/dist/modes/rpc/rpc-mode.d.ts +19 -0
  323. package/dist/modes/rpc/rpc-mode.js +381 -0
  324. package/dist/modes/rpc/rpc-types.d.ts +406 -0
  325. package/dist/modes/rpc/rpc-types.js +7 -0
  326. package/dist/package-manager-cli.d.ts +3 -0
  327. package/dist/package-manager-cli.js +233 -0
  328. package/dist/utils/changelog.d.ts +20 -0
  329. package/dist/utils/changelog.js +86 -0
  330. package/dist/utils/child-process.d.ts +10 -0
  331. package/dist/utils/child-process.js +77 -0
  332. package/dist/utils/clipboard-image.d.ts +10 -0
  333. package/dist/utils/clipboard-image.js +244 -0
  334. package/dist/utils/clipboard-native.d.ts +7 -0
  335. package/dist/utils/clipboard-native.js +13 -0
  336. package/dist/utils/clipboard.d.ts +1 -0
  337. package/dist/utils/clipboard.js +77 -0
  338. package/dist/utils/exif-orientation.d.ts +4 -0
  339. package/dist/utils/exif-orientation.js +157 -0
  340. package/dist/utils/frontmatter.d.ts +7 -0
  341. package/dist/utils/frontmatter.js +25 -0
  342. package/dist/utils/git.d.ts +25 -0
  343. package/dist/utils/git.js +162 -0
  344. package/dist/utils/image-convert.d.ts +8 -0
  345. package/dist/utils/image-convert.js +38 -0
  346. package/dist/utils/image-resize.d.ts +35 -0
  347. package/dist/utils/image-resize.js +136 -0
  348. package/dist/utils/mime.d.ts +1 -0
  349. package/dist/utils/mime.js +25 -0
  350. package/dist/utils/path-shims.d.ts +1 -0
  351. package/dist/utils/path-shims.js +90 -0
  352. package/dist/utils/paths.d.ts +6 -0
  353. package/dist/utils/paths.js +18 -0
  354. package/dist/utils/photon.d.ts +20 -0
  355. package/dist/utils/photon.js +120 -0
  356. package/dist/utils/shell.d.ts +29 -0
  357. package/dist/utils/shell.js +189 -0
  358. package/dist/utils/sleep.d.ts +4 -0
  359. package/dist/utils/sleep.js +16 -0
  360. package/dist/utils/tools-manager.d.ts +3 -0
  361. package/dist/utils/tools-manager.js +101 -0
  362. package/dist/vendor/bin/darwin-arm64/fd +0 -0
  363. package/dist/vendor/bin/darwin-arm64/rg +0 -0
  364. package/dist/vendor/bin/darwin-x64/fd +0 -0
  365. package/dist/vendor/bin/darwin-x64/rg +0 -0
  366. package/dist/vendor/bin/linux-arm64/fd +0 -0
  367. package/dist/vendor/bin/linux-arm64/rg +0 -0
  368. package/dist/vendor/bin/linux-x64/fd +0 -0
  369. package/dist/vendor/bin/linux-x64/rg +0 -0
  370. package/dist/vendor/bin/win32-arm64/fd.exe +0 -0
  371. package/dist/vendor/bin/win32-arm64/rg.exe +0 -0
  372. package/dist/vendor/bin/win32-x64/fd.exe +0 -0
  373. package/dist/vendor/bin/win32-x64/rg.exe +0 -0
  374. package/package.json +90 -26
  375. package/dist/extension/core.d.ts +0 -69
  376. package/dist/extension/core.js +0 -270
  377. package/dist/extension/core.js.map +0 -1
  378. package/dist/extension/index.d.ts +0 -14
  379. package/dist/extension/index.js +0 -374
  380. package/dist/extension/index.js.map +0 -1
  381. package/dist/main.js.map +0 -1
  382. package/themes/pizza.json +0 -87
@@ -0,0 +1,157 @@
1
+ function readOrientationFromTiff(bytes, tiffStart) {
2
+ if (tiffStart + 8 > bytes.length)
3
+ return 1;
4
+ const byteOrder = (bytes[tiffStart] << 8) | bytes[tiffStart + 1];
5
+ const le = byteOrder === 0x4949;
6
+ const read16 = (pos) => {
7
+ if (le)
8
+ return bytes[pos] | (bytes[pos + 1] << 8);
9
+ return (bytes[pos] << 8) | bytes[pos + 1];
10
+ };
11
+ const read32 = (pos) => {
12
+ if (le)
13
+ return bytes[pos] | (bytes[pos + 1] << 8) | (bytes[pos + 2] << 16) | (bytes[pos + 3] << 24);
14
+ return ((bytes[pos] << 24) | (bytes[pos + 1] << 16) | (bytes[pos + 2] << 8) | bytes[pos + 3]) >>> 0;
15
+ };
16
+ const ifdOffset = read32(tiffStart + 4);
17
+ const ifdStart = tiffStart + ifdOffset;
18
+ if (ifdStart + 2 > bytes.length)
19
+ return 1;
20
+ const entryCount = read16(ifdStart);
21
+ for (let i = 0; i < entryCount; i++) {
22
+ const entryPos = ifdStart + 2 + i * 12;
23
+ if (entryPos + 12 > bytes.length)
24
+ return 1;
25
+ if (read16(entryPos) === 0x0112) {
26
+ const value = read16(entryPos + 8);
27
+ return value >= 1 && value <= 8 ? value : 1;
28
+ }
29
+ }
30
+ return 1;
31
+ }
32
+ function findJpegTiffOffset(bytes) {
33
+ let offset = 2;
34
+ while (offset < bytes.length - 1) {
35
+ if (bytes[offset] !== 0xff)
36
+ return -1;
37
+ const marker = bytes[offset + 1];
38
+ if (marker === 0xff) {
39
+ offset++;
40
+ continue;
41
+ }
42
+ if (marker === 0xe1) {
43
+ if (offset + 4 >= bytes.length)
44
+ return -1;
45
+ const segmentStart = offset + 4;
46
+ if (segmentStart + 6 > bytes.length)
47
+ return -1;
48
+ if (!hasExifHeader(bytes, segmentStart))
49
+ return -1;
50
+ return segmentStart + 6;
51
+ }
52
+ if (offset + 4 > bytes.length)
53
+ return -1;
54
+ const length = (bytes[offset + 2] << 8) | bytes[offset + 3];
55
+ offset += 2 + length;
56
+ }
57
+ return -1;
58
+ }
59
+ function findWebpTiffOffset(bytes) {
60
+ let offset = 12;
61
+ while (offset + 8 <= bytes.length) {
62
+ const chunkId = String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
63
+ const chunkSize = bytes[offset + 4] | (bytes[offset + 5] << 8) | (bytes[offset + 6] << 16) | (bytes[offset + 7] << 24);
64
+ const dataStart = offset + 8;
65
+ if (chunkId === "EXIF") {
66
+ if (dataStart + chunkSize > bytes.length)
67
+ return -1;
68
+ // Some WebP files have "Exif\0\0" prefix before the TIFF header
69
+ const tiffStart = chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;
70
+ return tiffStart;
71
+ }
72
+ // RIFF chunks are padded to even size
73
+ offset = dataStart + chunkSize + (chunkSize % 2);
74
+ }
75
+ return -1;
76
+ }
77
+ function hasExifHeader(bytes, offset) {
78
+ return (bytes[offset] === 0x45 &&
79
+ bytes[offset + 1] === 0x78 &&
80
+ bytes[offset + 2] === 0x69 &&
81
+ bytes[offset + 3] === 0x66 &&
82
+ bytes[offset + 4] === 0x00 &&
83
+ bytes[offset + 5] === 0x00);
84
+ }
85
+ function getExifOrientation(bytes) {
86
+ let tiffOffset = -1;
87
+ // JPEG: starts with FF D8
88
+ if (bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xd8) {
89
+ tiffOffset = findJpegTiffOffset(bytes);
90
+ }
91
+ // WebP: starts with RIFF....WEBP
92
+ else if (bytes.length >= 12 &&
93
+ bytes[0] === 0x52 &&
94
+ bytes[1] === 0x49 &&
95
+ bytes[2] === 0x46 &&
96
+ bytes[3] === 0x46 &&
97
+ bytes[8] === 0x57 &&
98
+ bytes[9] === 0x45 &&
99
+ bytes[10] === 0x42 &&
100
+ bytes[11] === 0x50) {
101
+ tiffOffset = findWebpTiffOffset(bytes);
102
+ }
103
+ if (tiffOffset === -1)
104
+ return 1;
105
+ return readOrientationFromTiff(bytes, tiffOffset);
106
+ }
107
+ function rotate90(photon, image, dstIndex) {
108
+ const w = image.get_width();
109
+ const h = image.get_height();
110
+ const src = image.get_raw_pixels();
111
+ const dst = new Uint8Array(src.length);
112
+ for (let y = 0; y < h; y++) {
113
+ for (let x = 0; x < w; x++) {
114
+ const srcIdx = (y * w + x) * 4;
115
+ const dstIdx = dstIndex(x, y, w, h) * 4;
116
+ dst[dstIdx] = src[srcIdx];
117
+ dst[dstIdx + 1] = src[srcIdx + 1];
118
+ dst[dstIdx + 2] = src[srcIdx + 2];
119
+ dst[dstIdx + 3] = src[srcIdx + 3];
120
+ }
121
+ }
122
+ return new photon.PhotonImage(dst, h, w);
123
+ }
124
+ // Flip orientations mutate in-place. Rotations return a new image (caller must free the old one if different).
125
+ export function applyExifOrientation(photon, image, originalBytes) {
126
+ const orientation = getExifOrientation(originalBytes);
127
+ if (orientation === 1)
128
+ return image;
129
+ switch (orientation) {
130
+ case 2:
131
+ photon.fliph(image);
132
+ return image;
133
+ case 3:
134
+ photon.fliph(image);
135
+ photon.flipv(image);
136
+ return image;
137
+ case 4:
138
+ photon.flipv(image);
139
+ return image;
140
+ case 5: {
141
+ const rotated = rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));
142
+ photon.fliph(rotated);
143
+ return rotated;
144
+ }
145
+ case 6:
146
+ return rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));
147
+ case 7: {
148
+ const rotated = rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);
149
+ photon.fliph(rotated);
150
+ return rotated;
151
+ }
152
+ case 8:
153
+ return rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);
154
+ default:
155
+ return image;
156
+ }
157
+ }
@@ -0,0 +1,7 @@
1
+ type ParsedFrontmatter<T extends Record<string, unknown>> = {
2
+ frontmatter: T;
3
+ body: string;
4
+ };
5
+ export declare const parseFrontmatter: <T extends Record<string, unknown> = Record<string, unknown>>(content: string) => ParsedFrontmatter<T>;
6
+ export declare const stripFrontmatter: (content: string) => string;
7
+ export {};
@@ -0,0 +1,25 @@
1
+ import { parse } from "yaml";
2
+ const normalizeNewlines = (value) => value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
3
+ const extractFrontmatter = (content) => {
4
+ const normalized = normalizeNewlines(content);
5
+ if (!normalized.startsWith("---")) {
6
+ return { yamlString: null, body: normalized };
7
+ }
8
+ const endIndex = normalized.indexOf("\n---", 3);
9
+ if (endIndex === -1) {
10
+ return { yamlString: null, body: normalized };
11
+ }
12
+ return {
13
+ yamlString: normalized.slice(4, endIndex),
14
+ body: normalized.slice(endIndex + 4).trim(),
15
+ };
16
+ };
17
+ export const parseFrontmatter = (content) => {
18
+ const { yamlString, body } = extractFrontmatter(content);
19
+ if (!yamlString) {
20
+ return { frontmatter: {}, body };
21
+ }
22
+ const parsed = parse(yamlString);
23
+ return { frontmatter: (parsed ?? {}), body };
24
+ };
25
+ export const stripFrontmatter = (content) => parseFrontmatter(content).body;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Parsed git URL information.
3
+ */
4
+ export type GitSource = {
5
+ /** Always "git" for git sources */
6
+ type: "git";
7
+ /** Clone URL (always valid for git clone, without ref suffix) */
8
+ repo: string;
9
+ /** Git host domain (e.g., "github.com") */
10
+ host: string;
11
+ /** Repository path (e.g., "user/repo") */
12
+ path: string;
13
+ /** Git ref (branch, tag, commit) if specified */
14
+ ref?: string;
15
+ /** True if ref was specified (package won't be auto-updated) */
16
+ pinned: boolean;
17
+ };
18
+ /**
19
+ * Parse git source into a GitSource.
20
+ *
21
+ * Rules:
22
+ * - With git: prefix, accept all historical shorthand forms.
23
+ * - Without git: prefix, only accept explicit protocol URLs.
24
+ */
25
+ export declare function parseGitUrl(source: string): GitSource | null;
@@ -0,0 +1,162 @@
1
+ import hostedGitInfo from "hosted-git-info";
2
+ function splitRef(url) {
3
+ const scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);
4
+ if (scpLikeMatch) {
5
+ const pathWithMaybeRef = scpLikeMatch[2] ?? "";
6
+ const refSeparator = pathWithMaybeRef.indexOf("@");
7
+ if (refSeparator < 0)
8
+ return { repo: url };
9
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
10
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
11
+ if (!repoPath || !ref)
12
+ return { repo: url };
13
+ return {
14
+ repo: `git@${scpLikeMatch[1] ?? ""}:${repoPath}`,
15
+ ref,
16
+ };
17
+ }
18
+ if (url.includes("://")) {
19
+ try {
20
+ const parsed = new URL(url);
21
+ const pathWithMaybeRef = parsed.pathname.replace(/^\/+/, "");
22
+ const refSeparator = pathWithMaybeRef.indexOf("@");
23
+ if (refSeparator < 0)
24
+ return { repo: url };
25
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
26
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
27
+ if (!repoPath || !ref)
28
+ return { repo: url };
29
+ parsed.pathname = `/${repoPath}`;
30
+ return {
31
+ repo: parsed.toString().replace(/\/$/, ""),
32
+ ref,
33
+ };
34
+ }
35
+ catch {
36
+ return { repo: url };
37
+ }
38
+ }
39
+ const slashIndex = url.indexOf("/");
40
+ if (slashIndex < 0) {
41
+ return { repo: url };
42
+ }
43
+ const host = url.slice(0, slashIndex);
44
+ const pathWithMaybeRef = url.slice(slashIndex + 1);
45
+ const refSeparator = pathWithMaybeRef.indexOf("@");
46
+ if (refSeparator < 0) {
47
+ return { repo: url };
48
+ }
49
+ const repoPath = pathWithMaybeRef.slice(0, refSeparator);
50
+ const ref = pathWithMaybeRef.slice(refSeparator + 1);
51
+ if (!repoPath || !ref) {
52
+ return { repo: url };
53
+ }
54
+ return {
55
+ repo: `${host}/${repoPath}`,
56
+ ref,
57
+ };
58
+ }
59
+ function parseGenericGitUrl(url) {
60
+ const { repo: repoWithoutRef, ref } = splitRef(url);
61
+ let repo = repoWithoutRef;
62
+ let host = "";
63
+ let path = "";
64
+ const scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/);
65
+ if (scpLikeMatch) {
66
+ host = scpLikeMatch[1] ?? "";
67
+ path = scpLikeMatch[2] ?? "";
68
+ }
69
+ else if (repoWithoutRef.startsWith("https://") ||
70
+ repoWithoutRef.startsWith("http://") ||
71
+ repoWithoutRef.startsWith("ssh://") ||
72
+ repoWithoutRef.startsWith("git://")) {
73
+ try {
74
+ const parsed = new URL(repoWithoutRef);
75
+ host = parsed.hostname;
76
+ path = parsed.pathname.replace(/^\/+/, "");
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ else {
83
+ const slashIndex = repoWithoutRef.indexOf("/");
84
+ if (slashIndex < 0) {
85
+ return null;
86
+ }
87
+ host = repoWithoutRef.slice(0, slashIndex);
88
+ path = repoWithoutRef.slice(slashIndex + 1);
89
+ if (!host.includes(".") && host !== "localhost") {
90
+ return null;
91
+ }
92
+ repo = `https://${repoWithoutRef}`;
93
+ }
94
+ const normalizedPath = path.replace(/\.git$/, "").replace(/^\/+/, "");
95
+ if (!host || !normalizedPath || normalizedPath.split("/").length < 2) {
96
+ return null;
97
+ }
98
+ return {
99
+ type: "git",
100
+ repo,
101
+ host,
102
+ path: normalizedPath,
103
+ ref,
104
+ pinned: Boolean(ref),
105
+ };
106
+ }
107
+ /**
108
+ * Parse git source into a GitSource.
109
+ *
110
+ * Rules:
111
+ * - With git: prefix, accept all historical shorthand forms.
112
+ * - Without git: prefix, only accept explicit protocol URLs.
113
+ */
114
+ export function parseGitUrl(source) {
115
+ const trimmed = source.trim();
116
+ const hasGitPrefix = trimmed.startsWith("git:");
117
+ const url = hasGitPrefix ? trimmed.slice(4).trim() : trimmed;
118
+ if (!hasGitPrefix && !/^(https?|ssh|git):\/\//i.test(url)) {
119
+ return null;
120
+ }
121
+ const split = splitRef(url);
122
+ const hostedCandidates = [split.ref ? `${split.repo}#${split.ref}` : undefined, url].filter((value) => Boolean(value));
123
+ for (const candidate of hostedCandidates) {
124
+ const info = hostedGitInfo.fromUrl(candidate);
125
+ if (info) {
126
+ if (split.ref && info.project?.includes("@")) {
127
+ continue;
128
+ }
129
+ const useHttpsPrefix = !split.repo.startsWith("http://") &&
130
+ !split.repo.startsWith("https://") &&
131
+ !split.repo.startsWith("ssh://") &&
132
+ !split.repo.startsWith("git://") &&
133
+ !split.repo.startsWith("git@");
134
+ return {
135
+ type: "git",
136
+ repo: useHttpsPrefix ? `https://${split.repo}` : split.repo,
137
+ host: info.domain || "",
138
+ path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
139
+ ref: info.committish || split.ref || undefined,
140
+ pinned: Boolean(info.committish || split.ref),
141
+ };
142
+ }
143
+ }
144
+ const httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter((value) => Boolean(value));
145
+ for (const candidate of httpsCandidates) {
146
+ const info = hostedGitInfo.fromUrl(candidate);
147
+ if (info) {
148
+ if (split.ref && info.project?.includes("@")) {
149
+ continue;
150
+ }
151
+ return {
152
+ type: "git",
153
+ repo: `https://${split.repo}`,
154
+ host: info.domain || "",
155
+ path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
156
+ ref: info.committish || split.ref || undefined,
157
+ pinned: Boolean(info.committish || split.ref),
158
+ };
159
+ }
160
+ }
161
+ return parseGenericGitUrl(url);
162
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Convert image to PNG format for terminal display.
3
+ * Kitty graphics protocol requires PNG format (f=100).
4
+ */
5
+ export declare function convertToPng(base64Data: string, mimeType: string): Promise<{
6
+ data: string;
7
+ mimeType: string;
8
+ } | null>;
@@ -0,0 +1,38 @@
1
+ import { applyExifOrientation } from "./exif-orientation.js";
2
+ import { loadPhoton } from "./photon.js";
3
+ /**
4
+ * Convert image to PNG format for terminal display.
5
+ * Kitty graphics protocol requires PNG format (f=100).
6
+ */
7
+ export async function convertToPng(base64Data, mimeType) {
8
+ // Already PNG, no conversion needed
9
+ if (mimeType === "image/png") {
10
+ return { data: base64Data, mimeType };
11
+ }
12
+ const photon = await loadPhoton();
13
+ if (!photon) {
14
+ // Photon not available, can't convert
15
+ return null;
16
+ }
17
+ try {
18
+ const bytes = new Uint8Array(Buffer.from(base64Data, "base64"));
19
+ const rawImage = photon.PhotonImage.new_from_byteslice(bytes);
20
+ const image = applyExifOrientation(photon, rawImage, bytes);
21
+ if (image !== rawImage)
22
+ rawImage.free();
23
+ try {
24
+ const pngBuffer = image.get_bytes();
25
+ return {
26
+ data: Buffer.from(pngBuffer).toString("base64"),
27
+ mimeType: "image/png",
28
+ };
29
+ }
30
+ finally {
31
+ image.free();
32
+ }
33
+ }
34
+ catch {
35
+ // Conversion failed
36
+ return null;
37
+ }
38
+ }
@@ -0,0 +1,35 @@
1
+ import type { ImageContent } from "@earendil-works/pi-ai/compat";
2
+ export interface ImageResizeOptions {
3
+ maxWidth?: number;
4
+ maxHeight?: number;
5
+ maxBytes?: number;
6
+ jpegQuality?: number;
7
+ }
8
+ export interface ResizedImage {
9
+ data: string;
10
+ mimeType: string;
11
+ originalWidth: number;
12
+ originalHeight: number;
13
+ width: number;
14
+ height: number;
15
+ wasResized: boolean;
16
+ }
17
+ /**
18
+ * Resize an image to fit within the specified max dimensions and encoded file size.
19
+ * Returns null if the image cannot be resized below maxBytes.
20
+ *
21
+ * Uses Photon (Rust/WASM) for image processing. If Photon is not available,
22
+ * returns null.
23
+ *
24
+ * Strategy for staying under maxBytes:
25
+ * 1. First resize to maxWidth/maxHeight
26
+ * 2. Try both PNG and JPEG formats, pick the smaller one
27
+ * 3. If still too large, try JPEG with decreasing quality
28
+ * 4. If still too large, progressively reduce dimensions until 1x1
29
+ */
30
+ export declare function resizeImage(img: ImageContent, options?: ImageResizeOptions): Promise<ResizedImage | null>;
31
+ /**
32
+ * Format a dimension note for resized images.
33
+ * This helps the model understand the coordinate mapping.
34
+ */
35
+ export declare function formatDimensionNote(result: ResizedImage): string | undefined;
@@ -0,0 +1,136 @@
1
+ import { applyExifOrientation } from "./exif-orientation.js";
2
+ import { loadPhoton } from "./photon.js";
3
+ // 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.
4
+ const DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;
5
+ const DEFAULT_OPTIONS = {
6
+ maxWidth: 2000,
7
+ maxHeight: 2000,
8
+ maxBytes: DEFAULT_MAX_BYTES,
9
+ jpegQuality: 80,
10
+ };
11
+ function encodeCandidate(buffer, mimeType) {
12
+ const data = Buffer.from(buffer).toString("base64");
13
+ return {
14
+ data,
15
+ encodedSize: Buffer.byteLength(data, "utf-8"),
16
+ mimeType,
17
+ };
18
+ }
19
+ /**
20
+ * Resize an image to fit within the specified max dimensions and encoded file size.
21
+ * Returns null if the image cannot be resized below maxBytes.
22
+ *
23
+ * Uses Photon (Rust/WASM) for image processing. If Photon is not available,
24
+ * returns null.
25
+ *
26
+ * Strategy for staying under maxBytes:
27
+ * 1. First resize to maxWidth/maxHeight
28
+ * 2. Try both PNG and JPEG formats, pick the smaller one
29
+ * 3. If still too large, try JPEG with decreasing quality
30
+ * 4. If still too large, progressively reduce dimensions until 1x1
31
+ */
32
+ export async function resizeImage(img, options) {
33
+ const opts = { ...DEFAULT_OPTIONS, ...options };
34
+ const inputBuffer = Buffer.from(img.data, "base64");
35
+ const inputBase64Size = Buffer.byteLength(img.data, "utf-8");
36
+ const photon = await loadPhoton();
37
+ if (!photon) {
38
+ return null;
39
+ }
40
+ let image;
41
+ try {
42
+ const inputBytes = new Uint8Array(inputBuffer);
43
+ const rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);
44
+ image = applyExifOrientation(photon, rawImage, inputBytes);
45
+ if (image !== rawImage)
46
+ rawImage.free();
47
+ const originalWidth = image.get_width();
48
+ const originalHeight = image.get_height();
49
+ const format = img.mimeType?.split("/")[1] ?? "png";
50
+ // Check if already within all limits (dimensions AND encoded size)
51
+ if (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {
52
+ return {
53
+ data: img.data,
54
+ mimeType: img.mimeType ?? `image/${format}`,
55
+ originalWidth,
56
+ originalHeight,
57
+ width: originalWidth,
58
+ height: originalHeight,
59
+ wasResized: false,
60
+ };
61
+ }
62
+ // Calculate initial dimensions respecting max limits
63
+ let targetWidth = originalWidth;
64
+ let targetHeight = originalHeight;
65
+ if (targetWidth > opts.maxWidth) {
66
+ targetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);
67
+ targetWidth = opts.maxWidth;
68
+ }
69
+ if (targetHeight > opts.maxHeight) {
70
+ targetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);
71
+ targetHeight = opts.maxHeight;
72
+ }
73
+ function tryEncodings(width, height, jpegQualities) {
74
+ const resized = photon.resize(image, width, height, photon.SamplingFilter.Lanczos3);
75
+ try {
76
+ const candidates = [encodeCandidate(resized.get_bytes(), "image/png")];
77
+ for (const quality of jpegQualities) {
78
+ candidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), "image/jpeg"));
79
+ }
80
+ return candidates;
81
+ }
82
+ finally {
83
+ resized.free();
84
+ }
85
+ }
86
+ const qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));
87
+ let currentWidth = targetWidth;
88
+ let currentHeight = targetHeight;
89
+ while (true) {
90
+ const candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);
91
+ for (const candidate of candidates) {
92
+ if (candidate.encodedSize < opts.maxBytes) {
93
+ return {
94
+ data: candidate.data,
95
+ mimeType: candidate.mimeType,
96
+ originalWidth,
97
+ originalHeight,
98
+ width: currentWidth,
99
+ height: currentHeight,
100
+ wasResized: true,
101
+ };
102
+ }
103
+ }
104
+ if (currentWidth === 1 && currentHeight === 1) {
105
+ break;
106
+ }
107
+ const nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));
108
+ const nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));
109
+ if (nextWidth === currentWidth && nextHeight === currentHeight) {
110
+ break;
111
+ }
112
+ currentWidth = nextWidth;
113
+ currentHeight = nextHeight;
114
+ }
115
+ return null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ finally {
121
+ if (image) {
122
+ image.free();
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * Format a dimension note for resized images.
128
+ * This helps the model understand the coordinate mapping.
129
+ */
130
+ export function formatDimensionNote(result) {
131
+ if (!result.wasResized) {
132
+ return undefined;
133
+ }
134
+ const scale = result.originalWidth / result.width;
135
+ return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
136
+ }
@@ -0,0 +1 @@
1
+ export declare function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null>;
@@ -0,0 +1,25 @@
1
+ import { open } from "node:fs/promises";
2
+ import { fileTypeFromBuffer } from "file-type";
3
+ const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
4
+ const FILE_TYPE_SNIFF_BYTES = 4100;
5
+ export async function detectSupportedImageMimeTypeFromFile(filePath) {
6
+ const fileHandle = await open(filePath, "r");
7
+ try {
8
+ const buffer = Buffer.alloc(FILE_TYPE_SNIFF_BYTES);
9
+ const { bytesRead } = await fileHandle.read(buffer, 0, FILE_TYPE_SNIFF_BYTES, 0);
10
+ if (bytesRead === 0) {
11
+ return null;
12
+ }
13
+ const fileType = await fileTypeFromBuffer(buffer.subarray(0, bytesRead));
14
+ if (!fileType) {
15
+ return null;
16
+ }
17
+ if (!IMAGE_MIME_TYPES.has(fileType.mime)) {
18
+ return null;
19
+ }
20
+ return fileType.mime;
21
+ }
22
+ finally {
23
+ await fileHandle.close();
24
+ }
25
+ }
@@ -0,0 +1 @@
1
+ export declare function injectPizzaPathShims(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;