@bastani/atomic 0.9.9-alpha.1 → 0.9.9-alpha.2

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 (534) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +3 -3
  4. package/dist/builtin/intercom/CHANGELOG.md +7 -0
  5. package/dist/builtin/intercom/package.json +3 -3
  6. package/dist/builtin/mcp/CHANGELOG.md +7 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +7 -0
  9. package/dist/builtin/subagents/package.json +5 -5
  10. package/dist/builtin/web-access/CHANGELOG.md +7 -0
  11. package/dist/builtin/web-access/package.json +3 -3
  12. package/dist/builtin/web-access/web-search-tool.ts +1 -2
  13. package/dist/builtin/workflows/CHANGELOG.md +7 -0
  14. package/dist/builtin/workflows/package.json +3 -3
  15. package/dist/builtin/workflows/src/shared/serializable.ts +1 -1
  16. package/dist/bun/cli.d.ts.map +1 -1
  17. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  18. package/dist/bun/register-bedrock.d.ts.map +1 -1
  19. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  20. package/dist/bun/split-loader.d.ts.map +1 -1
  21. package/dist/cli/args.d.ts.map +1 -1
  22. package/dist/cli/config-selector.d.ts.map +1 -1
  23. package/dist/cli/file-processor.d.ts.map +1 -1
  24. package/dist/cli/initial-message.d.ts.map +1 -1
  25. package/dist/cli/list-models.d.ts.map +1 -1
  26. package/dist/cli/project-trust.d.ts.map +1 -1
  27. package/dist/cli/session-picker.d.ts.map +1 -1
  28. package/dist/cli/startup-ui.d.ts.map +1 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/config-self-update.d.ts.map +1 -1
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  33. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  34. package/dist/core/agent-session-bash.d.ts.map +1 -1
  35. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  36. package/dist/core/agent-session-events.d.ts.map +1 -1
  37. package/dist/core/agent-session-export.d.ts.map +1 -1
  38. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  39. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  40. package/dist/core/agent-session-methods.d.ts.map +1 -1
  41. package/dist/core/agent-session-models.d.ts.map +1 -1
  42. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  43. package/dist/core/agent-session-retry.d.ts.map +1 -1
  44. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  45. package/dist/core/agent-session-services.d.ts.map +1 -1
  46. package/dist/core/agent-session-skill-block.d.ts.map +1 -1
  47. package/dist/core/agent-session-state.d.ts.map +1 -1
  48. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  49. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  50. package/dist/core/agent-session-tree.d.ts.map +1 -1
  51. package/dist/core/agent-session-types.d.ts.map +1 -1
  52. package/dist/core/agent-session.d.ts.map +1 -1
  53. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  54. package/dist/core/async/format.d.ts.map +1 -1
  55. package/dist/core/async/job-manager.d.ts.map +1 -1
  56. package/dist/core/async/session-manager.d.ts.map +1 -1
  57. package/dist/core/async/types.d.ts.map +1 -1
  58. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  59. package/dist/core/auth-guidance.d.ts.map +1 -1
  60. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  61. package/dist/core/auth-storage.d.ts +4 -0
  62. package/dist/core/auth-storage.d.ts.map +1 -1
  63. package/dist/core/bash-executor.d.ts.map +1 -1
  64. package/dist/core/builtin-packages.d.ts.map +1 -1
  65. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  66. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  67. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  68. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -1
  69. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  70. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  71. package/dist/core/compaction/compaction.d.ts.map +1 -1
  72. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -1
  73. package/dist/core/compaction/index.d.ts.map +1 -1
  74. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  75. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  76. package/dist/core/compaction/utils.d.ts.map +1 -1
  77. package/dist/core/context-window.d.ts.map +1 -1
  78. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -1
  79. package/dist/core/copilot-errors.d.ts.map +1 -1
  80. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
  81. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  82. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  83. package/dist/core/copilot-hosts.d.ts.map +1 -1
  84. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  85. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -1
  86. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  87. package/dist/core/defaults.d.ts.map +1 -1
  88. package/dist/core/diagnostics.d.ts.map +1 -1
  89. package/dist/core/event-bus.d.ts.map +1 -1
  90. package/dist/core/exec.d.ts.map +1 -1
  91. package/dist/core/experimental.d.ts.map +1 -1
  92. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  93. package/dist/core/export-html/index.d.ts.map +1 -1
  94. package/dist/core/export-html/template-script.d.ts.map +1 -1
  95. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  96. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  97. package/dist/core/extensions/api-types.d.ts.map +1 -1
  98. package/dist/core/extensions/command-types.d.ts.map +1 -1
  99. package/dist/core/extensions/context-types.d.ts.map +1 -1
  100. package/dist/core/extensions/event-results.d.ts.map +1 -1
  101. package/dist/core/extensions/event-types.d.ts.map +1 -1
  102. package/dist/core/extensions/index.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  104. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  105. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  106. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  107. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  108. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  109. package/dist/core/extensions/loader.d.ts.map +1 -1
  110. package/dist/core/extensions/message-types.d.ts.map +1 -1
  111. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  112. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  113. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  114. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  115. package/dist/core/extensions/runner-handlers.d.ts.map +1 -1
  116. package/dist/core/extensions/runner-project-trust.d.ts.map +1 -1
  117. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  118. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  119. package/dist/core/extensions/runner-ui.d.ts.map +1 -1
  120. package/dist/core/extensions/runner.d.ts.map +1 -1
  121. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  122. package/dist/core/extensions/session-events.d.ts.map +1 -1
  123. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  124. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  125. package/dist/core/extensions/types.d.ts.map +1 -1
  126. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  127. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  128. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  129. package/dist/core/footer-data-provider.d.ts.map +1 -1
  130. package/dist/core/http-dispatcher.d.ts.map +1 -1
  131. package/dist/core/index.d.ts.map +1 -1
  132. package/dist/core/keybindings.d.ts.map +1 -1
  133. package/dist/core/messages.d.ts.map +1 -1
  134. package/dist/core/model-registry-auth.d.ts.map +1 -1
  135. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  136. package/dist/core/model-registry-builtins.js.map +1 -1
  137. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  138. package/dist/core/model-registry-custom-loader.js.map +1 -1
  139. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  140. package/dist/core/model-registry-loader.d.ts.map +1 -1
  141. package/dist/core/model-registry-schemas.d.ts +65 -13
  142. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  143. package/dist/core/model-registry-schemas.js +5 -1
  144. package/dist/core/model-registry-schemas.js.map +1 -1
  145. package/dist/core/model-registry-types.d.ts.map +1 -1
  146. package/dist/core/model-registry.d.ts.map +1 -1
  147. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  148. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  149. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  150. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  151. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  152. package/dist/core/model-resolver-types.d.ts.map +1 -1
  153. package/dist/core/model-resolver.d.ts.map +1 -1
  154. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  155. package/dist/core/output-guard.d.ts.map +1 -1
  156. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  157. package/dist/core/package-manager-command.d.ts.map +1 -1
  158. package/dist/core/package-manager-constants.d.ts.map +1 -1
  159. package/dist/core/package-manager-env.d.ts.map +1 -1
  160. package/dist/core/package-manager-git.d.ts.map +1 -1
  161. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  162. package/dist/core/package-manager-npm.d.ts.map +1 -1
  163. package/dist/core/package-manager-operations.d.ts.map +1 -1
  164. package/dist/core/package-manager-paths.d.ts.map +1 -1
  165. package/dist/core/package-manager-progress.d.ts.map +1 -1
  166. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  167. package/dist/core/package-manager-resource-accumulator.d.ts.map +1 -1
  168. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  169. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  170. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  171. package/dist/core/package-manager-settings.d.ts.map +1 -1
  172. package/dist/core/package-manager-source.d.ts.map +1 -1
  173. package/dist/core/package-manager-types.d.ts.map +1 -1
  174. package/dist/core/package-manager.d.ts.map +1 -1
  175. package/dist/core/project-trust.d.ts.map +1 -1
  176. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  177. package/dist/core/prompt-templates.d.ts.map +1 -1
  178. package/dist/core/provider-attribution.d.ts.map +1 -1
  179. package/dist/core/provider-context-usage.d.ts.map +1 -1
  180. package/dist/core/provider-display-names.d.ts.map +1 -1
  181. package/dist/core/resolve-config-value.d.ts.map +1 -1
  182. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  183. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  184. package/dist/core/resource-loader-core.d.ts.map +1 -1
  185. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  186. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  187. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  188. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  189. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  190. package/dist/core/resource-loader-paths.d.ts.map +1 -1
  191. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  192. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  193. package/dist/core/resource-loader-types.d.ts.map +1 -1
  194. package/dist/core/resource-loader.d.ts.map +1 -1
  195. package/dist/core/sdk-exports.d.ts.map +1 -1
  196. package/dist/core/sdk-types.d.ts.map +1 -1
  197. package/dist/core/sdk.d.ts.map +1 -1
  198. package/dist/core/session-cwd.d.ts.map +1 -1
  199. package/dist/core/session-entry-normalization.d.ts.map +1 -1
  200. package/dist/core/session-manager-archive.d.ts.map +1 -1
  201. package/dist/core/session-manager-classification.d.ts.map +1 -1
  202. package/dist/core/session-manager-core.d.ts.map +1 -1
  203. package/dist/core/session-manager-entries.d.ts.map +1 -1
  204. package/dist/core/session-manager-history.d.ts.map +1 -1
  205. package/dist/core/session-manager-list.d.ts.map +1 -1
  206. package/dist/core/session-manager-migrations.d.ts.map +1 -1
  207. package/dist/core/session-manager-paths.d.ts.map +1 -1
  208. package/dist/core/session-manager-storage.d.ts.map +1 -1
  209. package/dist/core/session-manager-types.d.ts.map +1 -1
  210. package/dist/core/session-manager-validation.d.ts.map +1 -1
  211. package/dist/core/session-manager.d.ts.map +1 -1
  212. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  213. package/dist/core/settings-manager-core.d.ts.map +1 -1
  214. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  215. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  216. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  217. package/dist/core/settings-manager.d.ts.map +1 -1
  218. package/dist/core/settings-merge.d.ts.map +1 -1
  219. package/dist/core/settings-storage.d.ts.map +1 -1
  220. package/dist/core/settings-types.d.ts.map +1 -1
  221. package/dist/core/skills-async.d.ts.map +1 -1
  222. package/dist/core/skills.d.ts.map +1 -1
  223. package/dist/core/slash-commands.d.ts.map +1 -1
  224. package/dist/core/source-info.d.ts.map +1 -1
  225. package/dist/core/system-prompt.d.ts.map +1 -1
  226. package/dist/core/telemetry.d.ts.map +1 -1
  227. package/dist/core/thinking-blocks.d.ts.map +1 -1
  228. package/dist/core/timings.d.ts.map +1 -1
  229. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  230. package/dist/core/tools/artifacts.d.ts.map +1 -1
  231. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  232. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  233. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
  234. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  235. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -1
  236. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  237. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  238. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  239. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  240. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
  241. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
  242. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  243. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  244. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  245. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  246. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  247. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  248. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
  249. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  250. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
  251. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  252. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  253. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  254. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  255. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  256. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -1
  257. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
  258. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
  259. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  260. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  261. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  262. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  263. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  264. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -1
  265. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
  266. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  267. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  268. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  269. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  270. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  271. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  272. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  273. package/dist/core/tools/bash.d.ts.map +1 -1
  274. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  275. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  276. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  277. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -1
  278. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  279. package/dist/core/tools/edit.d.ts.map +1 -1
  280. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  281. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  282. package/dist/core/tools/find.d.ts.map +1 -1
  283. package/dist/core/tools/glob-path-utils.d.ts.map +1 -1
  284. package/dist/core/tools/grep.d.ts.map +1 -1
  285. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  286. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -1
  287. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -1
  288. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  289. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -1
  290. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -1
  291. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  292. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
  293. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -1
  294. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -1
  295. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
  296. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -1
  297. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -1
  298. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  299. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -1
  300. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -1
  301. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
  302. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -1
  303. package/dist/core/tools/hashline.d.ts.map +1 -1
  304. package/dist/core/tools/index.d.ts.map +1 -1
  305. package/dist/core/tools/ls.d.ts.map +1 -1
  306. package/dist/core/tools/notebook.d.ts.map +1 -1
  307. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  308. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
  309. package/dist/core/tools/path-utils.d.ts.map +1 -1
  310. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  311. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  312. package/dist/core/tools/read-url.d.ts.map +1 -1
  313. package/dist/core/tools/read.d.ts.map +1 -1
  314. package/dist/core/tools/render-utils.d.ts.map +1 -1
  315. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  316. package/dist/core/tools/search-details.d.ts.map +1 -1
  317. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  318. package/dist/core/tools/search-native.d.ts.map +1 -1
  319. package/dist/core/tools/search.d.ts.map +1 -1
  320. package/dist/core/tools/structured-output.d.ts.map +1 -1
  321. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  322. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  323. package/dist/core/tools/todos-model.d.ts.map +1 -1
  324. package/dist/core/tools/todos-mutations.d.ts.map +1 -1
  325. package/dist/core/tools/todos-paths.d.ts.map +1 -1
  326. package/dist/core/tools/todos-render.d.ts.map +1 -1
  327. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  328. package/dist/core/tools/todos-types.d.ts.map +1 -1
  329. package/dist/core/tools/todos-types.js +1 -1
  330. package/dist/core/tools/todos-types.js.map +1 -1
  331. package/dist/core/tools/todos.d.ts.map +1 -1
  332. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  333. package/dist/core/tools/tool-definition-wrapper.js +6 -2
  334. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  335. package/dist/core/tools/tool-limits.d.ts.map +1 -1
  336. package/dist/core/tools/truncate.d.ts.map +1 -1
  337. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  338. package/dist/core/tools/write.d.ts.map +1 -1
  339. package/dist/core/trust-manager.d.ts.map +1 -1
  340. package/dist/core/typebox-compat.d.ts +12 -0
  341. package/dist/core/typebox-compat.d.ts.map +1 -0
  342. package/dist/core/typebox-compat.js +10 -0
  343. package/dist/core/typebox-compat.js.map +1 -0
  344. package/dist/index-extensions.d.ts.map +1 -1
  345. package/dist/index.d.ts +1 -0
  346. package/dist/index.d.ts.map +1 -1
  347. package/dist/index.js +1 -0
  348. package/dist/index.js.map +1 -1
  349. package/dist/main-app-mode.d.ts.map +1 -1
  350. package/dist/main-deferred-startup.d.ts.map +1 -1
  351. package/dist/main-early-input.d.ts.map +1 -1
  352. package/dist/main-session-options.d.ts.map +1 -1
  353. package/dist/main-session.d.ts.map +1 -1
  354. package/dist/main-stdio.d.ts.map +1 -1
  355. package/dist/main.d.ts.map +1 -1
  356. package/dist/migrations-config-values.d.ts.map +1 -1
  357. package/dist/migrations.d.ts.map +1 -1
  358. package/dist/modes/index.d.ts.map +1 -1
  359. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  360. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  361. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  363. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  364. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  366. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  369. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  370. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  372. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  373. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  375. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  376. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  378. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  379. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  380. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -1
  382. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  383. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  384. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  386. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  387. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  389. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  390. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  392. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -1
  393. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  395. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  396. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  398. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  399. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/session-selector-delete.d.ts.map +1 -1
  401. package/dist/modes/interactive/components/session-selector-header.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  404. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -1
  405. package/dist/modes/interactive/components/session-selector-types.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/session-selector-utils.d.ts.map +1 -1
  407. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  408. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  409. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  410. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  411. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  412. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  414. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  415. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  417. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  418. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  419. package/dist/modes/interactive/components/tree-selector-component.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  421. package/dist/modes/interactive/components/tree-selector-help.d.ts.map +1 -1
  422. package/dist/modes/interactive/components/tree-selector-label-input.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/tree-selector-list.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  425. package/dist/modes/interactive/components/tree-selector-types.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  427. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  430. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  433. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  434. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  435. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  436. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  437. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  438. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -1
  439. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  440. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  441. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  442. package/dist/modes/interactive/interactive-extension-custom-ui.d.ts.map +1 -1
  443. package/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  444. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  445. package/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  446. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  447. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  448. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  449. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  450. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  451. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  452. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  455. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  456. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  457. package/dist/modes/interactive/interactive-queueing.d.ts.map +1 -1
  458. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  459. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  460. package/dist/modes/interactive/interactive-resource-paths.d.ts.map +1 -1
  461. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  463. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  464. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  465. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  467. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  468. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  469. package/dist/modes/interactive/theme/export-colors.d.ts.map +1 -1
  470. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  471. package/dist/modes/interactive/theme/terminal-detection.d.ts.map +1 -1
  472. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  473. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  474. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  475. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  476. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  477. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  478. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  479. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  480. package/dist/modes/print-mode.d.ts.map +1 -1
  481. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  482. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  483. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  484. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  485. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  486. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  487. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  488. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  489. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  490. package/dist/package-manager-cli-parser.d.ts.map +1 -1
  491. package/dist/package-manager-cli.d.ts.map +1 -1
  492. package/dist/rpc-entry.d.ts.map +1 -1
  493. package/dist/utils/ansi.d.ts.map +1 -1
  494. package/dist/utils/changelog.d.ts.map +1 -1
  495. package/dist/utils/child-process.d.ts.map +1 -1
  496. package/dist/utils/clipboard-image.d.ts.map +1 -1
  497. package/dist/utils/clipboard-native.d.ts.map +1 -1
  498. package/dist/utils/clipboard.d.ts.map +1 -1
  499. package/dist/utils/deprecation.d.ts.map +1 -1
  500. package/dist/utils/event-loop.d.ts.map +1 -1
  501. package/dist/utils/exif-orientation.d.ts.map +1 -1
  502. package/dist/utils/frontmatter.d.ts.map +1 -1
  503. package/dist/utils/fs-watch.d.ts.map +1 -1
  504. package/dist/utils/git-env.d.ts.map +1 -1
  505. package/dist/utils/git.d.ts.map +1 -1
  506. package/dist/utils/html.d.ts.map +1 -1
  507. package/dist/utils/image-convert.d.ts.map +1 -1
  508. package/dist/utils/image-process.d.ts.map +1 -1
  509. package/dist/utils/image-resize-core.d.ts.map +1 -1
  510. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  511. package/dist/utils/image-resize.d.ts.map +1 -1
  512. package/dist/utils/json.d.ts.map +1 -1
  513. package/dist/utils/markit.d.ts.map +1 -1
  514. package/dist/utils/mime.d.ts.map +1 -1
  515. package/dist/utils/module-require.d.ts.map +1 -1
  516. package/dist/utils/open-browser.d.ts.map +1 -1
  517. package/dist/utils/paths.d.ts.map +1 -1
  518. package/dist/utils/photon.d.ts.map +1 -1
  519. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  520. package/dist/utils/shell.d.ts.map +1 -1
  521. package/dist/utils/sleep.d.ts.map +1 -1
  522. package/dist/utils/split-launcher.d.ts.map +1 -1
  523. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  524. package/dist/utils/tools-manager.d.ts.map +1 -1
  525. package/dist/utils/version-check.d.ts.map +1 -1
  526. package/dist/utils/windows-self-update.d.ts.map +1 -1
  527. package/docs/custom-provider.md +7 -1
  528. package/docs/extensions.md +6 -4
  529. package/examples/extensions/README.md +3 -1
  530. package/examples/extensions/subagent/schemas.ts +1 -1
  531. package/examples/extensions/tic-tac-toe.ts +1 -2
  532. package/examples/extensions/todo.ts +1 -2
  533. package/npm-shrinkwrap.json +214 -98
  534. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"event-loop.d.ts","sourceRoot":"","sources":["../../src/utils/event-loop.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEtD;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F","sourcesContent":["import { setImmediate as waitForImmediate } from \"node:timers/promises\";\n\n/**\n * Yield to the macrotask queue so terminal input, timers, and render work can\n * run between otherwise synchronous startup chunks.\n */\nexport async function yieldToEventLoop(): Promise<void> {\n\tawait waitForImmediate();\n}\n\nexport async function yieldToEventLoopIfSlow(startedAt: number, thresholdMs = 16): Promise<void> {\n\tif (Date.now() - startedAt >= thresholdMs) {\n\t\tawait yieldToEventLoop();\n\t}\n}\n"]}
1
+ {"version":3,"file":"event-loop.d.ts","sourceRoot":"","sources":["../../src/utils/event-loop.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEtD;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F"}
@@ -1 +1 @@
1
- {"version":3,"file":"exif-orientation.d.ts","sourceRoot":"","sources":["../../src/utils/exif-orientation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,MAAM,GAAG,cAAc,4BAA4B,CAAC,CAAC;AAgJ1D,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,UAAU,GACvB,eAAe,CAgCjB","sourcesContent":["import type { PhotonImageType } from \"./photon.ts\";\n\ntype Photon = typeof import(\"@silvia-odwyer/photon-node\");\n\nfunction readOrientationFromTiff(bytes: Uint8Array, tiffStart: number): number {\n\tif (tiffStart + 8 > bytes.length) return 1;\n\n\tconst byteOrder = (bytes[tiffStart] << 8) | bytes[tiffStart + 1];\n\tconst le = byteOrder === 0x4949;\n\n\tconst read16 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8);\n\t\treturn (bytes[pos] << 8) | bytes[pos + 1];\n\t};\n\n\tconst read32 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8) | (bytes[pos + 2] << 16) | (bytes[pos + 3] << 24);\n\t\treturn ((bytes[pos] << 24) | (bytes[pos + 1] << 16) | (bytes[pos + 2] << 8) | bytes[pos + 3]) >>> 0;\n\t};\n\n\tconst ifdOffset = read32(tiffStart + 4);\n\tconst ifdStart = tiffStart + ifdOffset;\n\tif (ifdStart + 2 > bytes.length) return 1;\n\n\tconst entryCount = read16(ifdStart);\n\tfor (let i = 0; i < entryCount; i++) {\n\t\tconst entryPos = ifdStart + 2 + i * 12;\n\t\tif (entryPos + 12 > bytes.length) return 1;\n\n\t\tif (read16(entryPos) === 0x0112) {\n\t\t\tconst value = read16(entryPos + 8);\n\t\t\treturn value >= 1 && value <= 8 ? value : 1;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nfunction findJpegTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 2;\n\twhile (offset < bytes.length - 1) {\n\t\tif (bytes[offset] !== 0xff) return -1;\n\t\tconst marker = bytes[offset + 1];\n\t\tif (marker === 0xff) {\n\t\t\toffset++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (marker === 0xe1) {\n\t\t\tif (offset + 4 >= bytes.length) return -1;\n\t\t\tconst segmentStart = offset + 4;\n\t\t\tif (segmentStart + 6 > bytes.length) return -1;\n\t\t\tif (!hasExifHeader(bytes, segmentStart)) return -1;\n\t\t\treturn segmentStart + 6;\n\t\t}\n\n\t\tif (offset + 4 > bytes.length) return -1;\n\t\tconst length = (bytes[offset + 2] << 8) | bytes[offset + 3];\n\t\toffset += 2 + length;\n\t}\n\n\treturn -1;\n}\n\nfunction findWebpTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 12;\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst chunkId = String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);\n\t\tconst chunkSize =\n\t\t\tbytes[offset + 4] | (bytes[offset + 5] << 8) | (bytes[offset + 6] << 16) | (bytes[offset + 7] << 24);\n\t\tconst dataStart = offset + 8;\n\n\t\tif (chunkId === \"EXIF\") {\n\t\t\tif (dataStart + chunkSize > bytes.length) return -1;\n\t\t\t// Some WebP files have \"Exif\\0\\0\" prefix before the TIFF header\n\t\t\tconst tiffStart = chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;\n\t\t\treturn tiffStart;\n\t\t}\n\n\t\t// RIFF chunks are padded to even size\n\t\toffset = dataStart + chunkSize + (chunkSize % 2);\n\t}\n\n\treturn -1;\n}\n\nfunction hasExifHeader(bytes: Uint8Array, offset: number): boolean {\n\treturn (\n\t\tbytes[offset] === 0x45 &&\n\t\tbytes[offset + 1] === 0x78 &&\n\t\tbytes[offset + 2] === 0x69 &&\n\t\tbytes[offset + 3] === 0x66 &&\n\t\tbytes[offset + 4] === 0x00 &&\n\t\tbytes[offset + 5] === 0x00\n\t);\n}\n\nfunction getExifOrientation(bytes: Uint8Array): number {\n\tlet tiffOffset = -1;\n\n\t// JPEG: starts with FF D8\n\tif (bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xd8) {\n\t\ttiffOffset = findJpegTiffOffset(bytes);\n\t}\n\t// WebP: starts with RIFF....WEBP\n\telse if (\n\t\tbytes.length >= 12 &&\n\t\tbytes[0] === 0x52 &&\n\t\tbytes[1] === 0x49 &&\n\t\tbytes[2] === 0x46 &&\n\t\tbytes[3] === 0x46 &&\n\t\tbytes[8] === 0x57 &&\n\t\tbytes[9] === 0x45 &&\n\t\tbytes[10] === 0x42 &&\n\t\tbytes[11] === 0x50\n\t) {\n\t\ttiffOffset = findWebpTiffOffset(bytes);\n\t}\n\n\tif (tiffOffset === -1) return 1;\n\treturn readOrientationFromTiff(bytes, tiffOffset);\n}\n\ntype DstIndexFn = (x: number, y: number, w: number, h: number) => number;\n\nfunction rotate90(photon: Photon, image: PhotonImageType, dstIndex: DstIndexFn): PhotonImageType {\n\tconst w = image.get_width();\n\tconst h = image.get_height();\n\tconst src = image.get_raw_pixels();\n\tconst dst = new Uint8Array(src.length);\n\n\tfor (let y = 0; y < h; y++) {\n\t\tfor (let x = 0; x < w; x++) {\n\t\t\tconst srcIdx = (y * w + x) * 4;\n\t\t\tconst dstIdx = dstIndex(x, y, w, h) * 4;\n\t\t\tdst[dstIdx] = src[srcIdx];\n\t\t\tdst[dstIdx + 1] = src[srcIdx + 1];\n\t\t\tdst[dstIdx + 2] = src[srcIdx + 2];\n\t\t\tdst[dstIdx + 3] = src[srcIdx + 3];\n\t\t}\n\t}\n\n\treturn new photon.PhotonImage(dst, h, w);\n}\n\n// Flip orientations mutate in-place. Rotations return a new image (caller must free the old one if different).\nexport function applyExifOrientation(\n\tphoton: Photon,\n\timage: PhotonImageType,\n\toriginalBytes: Uint8Array,\n): PhotonImageType {\n\tconst orientation = getExifOrientation(originalBytes);\n\tif (orientation === 1) return image;\n\n\tswitch (orientation) {\n\t\tcase 2:\n\t\t\tphoton.fliph(image);\n\t\t\treturn image;\n\t\tcase 3:\n\t\t\tphoton.fliph(image);\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 4:\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 5: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 6:\n\t\t\treturn rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\tcase 7: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 8:\n\t\t\treturn rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\tdefault:\n\t\t\treturn image;\n\t}\n}\n"]}
1
+ {"version":3,"file":"exif-orientation.d.ts","sourceRoot":"","sources":["../../src/utils/exif-orientation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,MAAM,GAAG,cAAc,4BAA4B,CAAC,CAAC;AAgJ1D,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,UAAU,GACvB,eAAe,CAgCjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC3D,WAAW,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAsBF,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WAClF,MAAM,KACb,iBAAiB,CAAC,CAAC,CAOrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,YAAa,MAAM,KAAG,MAAwC,CAAC","sourcesContent":["import { parse } from \"yaml\";\n\ntype ParsedFrontmatter<T extends Record<string, unknown>> = {\n\tfrontmatter: T;\n\tbody: string;\n};\n\nconst normalizeNewlines = (value: string): string => value.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\nconst extractFrontmatter = (content: string): { yamlString: string | null; body: string } => {\n\tconst normalized = normalizeNewlines(content);\n\n\tif (!normalized.startsWith(\"---\")) {\n\t\treturn { yamlString: null, body: normalized };\n\t}\n\n\tconst endIndex = normalized.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { yamlString: null, body: normalized };\n\t}\n\n\treturn {\n\t\tyamlString: normalized.slice(4, endIndex),\n\t\tbody: normalized.slice(endIndex + 4).trim(),\n\t};\n};\n\nexport const parseFrontmatter = <T extends Record<string, unknown> = Record<string, unknown>>(\n\tcontent: string,\n): ParsedFrontmatter<T> => {\n\tconst { yamlString, body } = extractFrontmatter(content);\n\tif (!yamlString) {\n\t\treturn { frontmatter: {} as T, body };\n\t}\n\tconst parsed = parse(yamlString);\n\treturn { frontmatter: (parsed ?? {}) as T, body };\n};\n\nexport const stripFrontmatter = (content: string): string => parseFrontmatter(content).body;\n"]}
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC3D,WAAW,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAsBF,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WAClF,MAAM,KACb,iBAAiB,CAAC,CAAC,CAOrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,YAAa,MAAM,KAAG,MAAwC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"fs-watch.d.ts","sourceRoot":"","sources":["../../src/utils/fs-watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,aAAa,EAAS,MAAM,SAAS,CAAC;AAElF,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,qCAAqC,8CAA8C,CAAC;AACjG,eAAO,MAAM,uCAAuC,gDAAgD,CAAC;AAErG,MAAM,MAAM,oBAAoB,GAC7B,OAAO,qCAAqC,GAC5C,OAAO,uCAAuC,CAAC;AAElD,MAAM,WAAW,oBAAqB,SAAQ,KAAK;IAClD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,0BAA0B;IAC1C,KAAK,EAAE,oBAAoB,CAAC;CAC5B;AAED,MAAM,MAAM,yBAAyB,GAAG,qBAAqB,GAAG,0BAA0B,CAAC;AAE3F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;CACrE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAUxE;AAmBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,OAAO,CAQ5G;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAMpF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,yBAAyB,CAkChH;AAED,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAC/B,OAAO,GAAE,kBAAuB,GAC9B,SAAS,GAAG,IAAI,CAgBlB","sourcesContent":["import { type FSWatcher, realpathSync, type WatchListener, watch } from \"node:fs\";\n\nexport const FS_WATCH_RETRY_DELAY_MS = 5000;\n\nexport const SAFE_FS_WATCH_CANONICALIZATION_FAILED = \"ERR_SAFE_FS_WATCH_CANONICALIZATION_FAILED\";\nexport const SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH = \"ERR_SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH\";\n\nexport type SafeFsWatchErrorCode =\n\t| typeof SAFE_FS_WATCH_CANONICALIZATION_FAILED\n\t| typeof SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH;\n\nexport interface SafeFsWatchPathError extends Error {\n\tcode: SafeFsWatchErrorCode;\n\twatchedPath: string;\n\tresolvedPath?: string;\n}\n\nexport interface NativeWatchPathResult {\n\tpath: string;\n}\n\nexport interface NativeWatchPathErrorResult {\n\terror: SafeFsWatchPathError;\n}\n\nexport type NativeWatchPathResolution = NativeWatchPathResult | NativeWatchPathErrorResult;\n\nexport interface SafeFsWatchOptions {\n\tplatform?: NodeJS.Platform;\n\trealpathSyncNative?: (path: string) => string;\n\twatch?: (path: string, listener: WatchListener<string>) => FSWatcher;\n}\n\nexport function closeWatcher(watcher: FSWatcher | null | undefined): void {\n\tif (!watcher) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\twatcher.close();\n\t} catch {\n\t\t// Ignore watcher close errors\n\t}\n}\n\nfunction createSafeFsWatchPathError(\n\tcode: SafeFsWatchErrorCode,\n\twatchedPath: string,\n\tmessage: string,\n\tresolvedPath?: string,\n\tcause?: Error,\n): SafeFsWatchPathError {\n\tconst error = new Error(message, cause ? { cause } : undefined) as SafeFsWatchPathError;\n\terror.name = \"SafeFsWatchPathError\";\n\terror.code = code;\n\terror.watchedPath = watchedPath;\n\tif (resolvedPath !== undefined) {\n\t\terror.resolvedPath = resolvedPath;\n\t}\n\treturn error;\n}\n\nexport function isUnsafeWindowsShortPath(path: string, platform: NodeJS.Platform = process.platform): boolean {\n\tif (platform !== \"win32\") {\n\t\treturn false;\n\t}\n\n\treturn path\n\t\t.split(/[\\\\/]+/)\n\t\t.some((component) => /~\\d+(?:\\.|$)/i.test(component));\n}\n\nexport function isSafeFsWatchPathError(error: unknown): error is SafeFsWatchPathError {\n\tif (typeof error !== \"object\" || error === null || !(\"code\" in error)) {\n\t\treturn false;\n\t}\n\tconst code = (error as { code?: string }).code;\n\treturn code === SAFE_FS_WATCH_CANONICALIZATION_FAILED || code === SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH;\n}\n\nexport function resolveNativeWatchPath(path: string, options: SafeFsWatchOptions = {}): NativeWatchPathResolution {\n\tconst platform = options.platform ?? process.platform;\n\tif (platform !== \"win32\") {\n\t\treturn { path };\n\t}\n\n\tconst resolvePath = options.realpathSyncNative ?? realpathSync.native;\n\tlet resolvedPath: string;\n\ttry {\n\t\tresolvedPath = resolvePath(path);\n\t} catch (error) {\n\t\treturn {\n\t\t\terror: createSafeFsWatchPathError(\n\t\t\t\tSAFE_FS_WATCH_CANONICALIZATION_FAILED,\n\t\t\t\tpath,\n\t\t\t\t`Cannot canonicalize Windows fs.watch path '${path}'.`,\n\t\t\t\tundefined,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t};\n\t}\n\n\tif (isUnsafeWindowsShortPath(resolvedPath, platform)) {\n\t\treturn {\n\t\t\terror: createSafeFsWatchPathError(\n\t\t\t\tSAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH,\n\t\t\t\tpath,\n\t\t\t\t`Refusing native fs.watch for unsafe Windows short-name path '${resolvedPath}'.`,\n\t\t\t\tresolvedPath,\n\t\t\t),\n\t\t};\n\t}\n\n\treturn { path: resolvedPath };\n}\n\nexport function watchWithErrorHandler(\n\tpath: string,\n\tlistener: WatchListener<string>,\n\tonError: (error: Error) => void,\n\toptions: SafeFsWatchOptions = {},\n): FSWatcher | null {\n\tconst resolved = resolveNativeWatchPath(path, options);\n\tif (\"error\" in resolved) {\n\t\tonError(resolved.error);\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst watchPath = options.watch ?? watch;\n\t\tconst watcher = watchPath(resolved.path, listener);\n\t\twatcher.on(\"error\", onError);\n\t\treturn watcher;\n\t} catch (error) {\n\t\tonError(error instanceof Error ? error : new Error(String(error)));\n\t\treturn null;\n\t}\n}\n"]}
1
+ {"version":3,"file":"fs-watch.d.ts","sourceRoot":"","sources":["../../src/utils/fs-watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAgB,KAAK,aAAa,EAAS,MAAM,SAAS,CAAC;AAElF,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,qCAAqC,8CAA8C,CAAC;AACjG,eAAO,MAAM,uCAAuC,gDAAgD,CAAC;AAErG,MAAM,MAAM,oBAAoB,GAC7B,OAAO,qCAAqC,GAC5C,OAAO,uCAAuC,CAAC;AAElD,MAAM,WAAW,oBAAqB,SAAQ,KAAK;IAClD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,0BAA0B;IAC1C,KAAK,EAAE,oBAAoB,CAAC;CAC5B;AAED,MAAM,MAAM,yBAAyB,GAAG,qBAAqB,GAAG,0BAA0B,CAAC;AAE3F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;CACrE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAUxE;AAmBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,OAAO,CAQ5G;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAMpF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,yBAAyB,CAkChH;AAED,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAC/B,OAAO,GAAE,kBAAuB,GAC9B,SAAS,GAAG,IAAI,CAgBlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"git-env.d.ts","sourceRoot":"","sources":["../../src/utils/git-env.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,YAC9B,kCAAkC,EAClC,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,CACP,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CACnC,SAAS,CAAC,EAAE,MAAM,CAAC,UAAU,EAC7B,OAAO,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAKnB","sourcesContent":["export const GIT_LOCAL_ENV_VARS = [\n\t\"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n\t\"GIT_CONFIG\",\n\t\"GIT_CONFIG_PARAMETERS\",\n\t\"GIT_CONFIG_COUNT\",\n\t\"GIT_OBJECT_DIRECTORY\",\n\t\"GIT_DIR\",\n\t\"GIT_WORK_TREE\",\n\t\"GIT_IMPLICIT_WORK_TREE\",\n\t\"GIT_GRAFT_FILE\",\n\t\"GIT_INDEX_FILE\",\n\t\"GIT_NO_REPLACE_OBJECTS\",\n\t\"GIT_REPLACE_REF_BASE\",\n\t\"GIT_PREFIX\",\n\t\"GIT_SHALLOW_FILE\",\n\t\"GIT_COMMON_DIR\",\n] as const;\n\n/**\n * Create an environment for Git subprocesses that target an explicit cwd/path.\n *\n * Git honors repository-local environment variables over cwd and `git -C`, so\n * inherited values from hooks or unrelated worktrees can make a subprocess\n * inspect the wrong repository. This list mirrors `git rev-parse --local-env-vars`.\n */\nexport function createGitEnvironment(\n\toverrides?: NodeJS.ProcessEnv,\n\tbaseEnv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...baseEnv };\n\tif (overrides) Object.assign(env, overrides);\n\tfor (const key of GIT_LOCAL_ENV_VARS) delete env[key];\n\treturn env;\n}\n"]}
1
+ {"version":3,"file":"git-env.d.ts","sourceRoot":"","sources":["../../src/utils/git-env.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,YAC9B,kCAAkC,EAClC,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,SAAS,EACT,eAAe,EACf,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,CACP,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CACnC,SAAS,CAAC,EAAE,MAAM,CAAC,UAAU,EAC7B,OAAO,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAKnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACvB,mCAAmC;IACnC,IAAI,EAAE,KAAK,CAAC;IACZ,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAqJF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAsD5D","sourcesContent":["import hostedGitInfo from \"hosted-git-info\";\n\n/**\n * Clone URLs are eventually handed to `git clone`. Even though the package\n * manager always spawns git with an argument array (never a shell), constrain\n * the composed clone URL to characters that cannot carry shell or option\n * meaning, so a parsed source can never smuggle a metacharacter downstream.\n */\nconst SAFE_CLONE_URL = /^[A-Za-z0-9._~:@\\/%+-]+$/;\n\n/**\n * Parsed git URL information.\n */\nexport type GitSource = {\n\t/** Always \"git\" for git sources */\n\ttype: \"git\";\n\t/** Clone URL (always valid for git clone, without ref suffix) */\n\trepo: string;\n\t/** Git host domain (e.g., \"github.com\") */\n\thost: string;\n\t/** Repository path (e.g., \"user/repo\") */\n\tpath: string;\n\t/** Git ref (branch, tag, commit) if specified */\n\tref?: string;\n\t/** True if ref was specified (package won't be auto-updated) */\n\tpinned: boolean;\n};\n\nfunction splitRef(url: string): { repo: string; ref?: string } {\n\tconst scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\tconst pathWithMaybeRef = scpLikeMatch[2] ?? \"\";\n\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\tif (refSeparator < 0) return { repo: url };\n\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\tif (!repoPath || !ref) return { repo: url };\n\t\treturn {\n\t\t\trepo: `git@${scpLikeMatch[1] ?? \"\"}:${repoPath}`,\n\t\t\tref,\n\t\t};\n\t}\n\n\tif (url.includes(\"://\")) {\n\t\ttry {\n\t\t\tconst parsed = new URL(url);\n\t\t\tconst pathWithMaybeRef = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\t\t\tif (refSeparator < 0) return { repo: url };\n\t\t\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\t\t\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\t\t\tif (!repoPath || !ref) return { repo: url };\n\t\t\tparsed.pathname = `/${repoPath}`;\n\t\t\treturn {\n\t\t\t\trepo: parsed.toString().replace(/\\/$/, \"\"),\n\t\t\t\tref,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { repo: url };\n\t\t}\n\t}\n\n\tconst slashIndex = url.indexOf(\"/\");\n\tif (slashIndex < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst host = url.slice(0, slashIndex);\n\tconst pathWithMaybeRef = url.slice(slashIndex + 1);\n\tconst refSeparator = pathWithMaybeRef.indexOf(\"@\");\n\tif (refSeparator < 0) {\n\t\treturn { repo: url };\n\t}\n\tconst repoPath = pathWithMaybeRef.slice(0, refSeparator);\n\tconst ref = pathWithMaybeRef.slice(refSeparator + 1);\n\tif (!repoPath || !ref) {\n\t\treturn { repo: url };\n\t}\n\treturn {\n\t\trepo: `${host}/${repoPath}`,\n\t\tref,\n\t};\n}\n\nfunction decodeForValidation(value: string): string | null {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction hasUnsafeGitInstallPart(value: string, allowSlash: boolean): boolean {\n\tconst decoded = decodeForValidation(value);\n\tif (decoded === null) {\n\t\treturn true;\n\t}\n\tconst candidates = [value, decoded];\n\tfor (const candidate of candidates) {\n\t\tif (candidate.includes(\"\\0\") || candidate.includes(\"\\\\\") || candidate.startsWith(\"/\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (!allowSlash && candidate.includes(\"/\")) {\n\t\t\treturn true;\n\t\t}\n\t\tif (candidate.split(\"/\").includes(\"..\")) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction buildGitSource(args: { repo: string; host: string; path: string; ref?: string }): GitSource | null {\n\tif (args.path.startsWith(\"/\")) {\n\t\treturn null;\n\t}\n\tconst normalizedPath = args.path.replace(/\\.git$/, \"\").replace(/^\\/+/, \"\");\n\tif (!args.host || !normalizedPath || normalizedPath.split(\"/\").length < 2) {\n\t\treturn null;\n\t}\n\tif (hasUnsafeGitInstallPart(args.host, false) || hasUnsafeGitInstallPart(normalizedPath, true)) {\n\t\treturn null;\n\t}\n\tif (!SAFE_CLONE_URL.test(args.repo)) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttype: \"git\",\n\t\trepo: args.repo,\n\t\thost: args.host,\n\t\tpath: normalizedPath,\n\t\tref: args.ref,\n\t\tpinned: Boolean(args.ref),\n\t};\n}\n\nfunction parseGenericGitUrl(url: string): GitSource | null {\n\tconst { repo: repoWithoutRef, ref } = splitRef(url);\n\tlet repo = repoWithoutRef;\n\tlet host = \"\";\n\tlet path = \"\";\n\n\tconst scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/);\n\tif (scpLikeMatch) {\n\t\thost = scpLikeMatch[1] ?? \"\";\n\t\tpath = scpLikeMatch[2] ?? \"\";\n\t} else if (\n\t\trepoWithoutRef.startsWith(\"https://\") ||\n\t\trepoWithoutRef.startsWith(\"http://\") ||\n\t\trepoWithoutRef.startsWith(\"ssh://\") ||\n\t\trepoWithoutRef.startsWith(\"git://\")\n\t) {\n\t\ttry {\n\t\t\tconst parsed = new URL(repoWithoutRef);\n\t\t\thost = parsed.hostname;\n\t\t\tpath = parsed.pathname.replace(/^\\/+/, \"\");\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t} else {\n\t\tconst slashIndex = repoWithoutRef.indexOf(\"/\");\n\t\tif (slashIndex < 0) {\n\t\t\treturn null;\n\t\t}\n\t\thost = repoWithoutRef.slice(0, slashIndex);\n\t\tpath = repoWithoutRef.slice(slashIndex + 1);\n\t\tif (!host.includes(\".\") && host !== \"localhost\") {\n\t\t\treturn null;\n\t\t}\n\t\trepo = `https://${repoWithoutRef}`;\n\t}\n\n\treturn buildGitSource({ repo, host, path, ref });\n}\n\n/**\n * Parse git source into a GitSource.\n *\n * Rules:\n * - With git: prefix, accept all historical shorthand forms.\n * - Without git: prefix, only accept explicit protocol URLs.\n */\nexport function parseGitUrl(source: string): GitSource | null {\n\tconst trimmed = source.trim();\n\tconst hasGitPrefix = trimmed.startsWith(\"git:\");\n\tconst url = hasGitPrefix ? trimmed.slice(4).trim() : trimmed;\n\n\tif (!hasGitPrefix && !/^(https?|ssh|git):\\/\\//i.test(url)) {\n\t\treturn null;\n\t}\n\n\tconst split = splitRef(url);\n\n\tconst hostedCandidates = [split.ref ? `${split.repo}#${split.ref}` : undefined, url].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of hostedCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst useHttpsPrefix =\n\t\t\t\t!split.repo.startsWith(\"http://\") &&\n\t\t\t\t!split.repo.startsWith(\"https://\") &&\n\t\t\t\t!split.repo.startsWith(\"ssh://\") &&\n\t\t\t\t!split.repo.startsWith(\"git://\") &&\n\t\t\t\t!split.repo.startsWith(\"git@\");\n\t\t\treturn buildGitSource({\n\t\t\t\trepo: useHttpsPrefix ? `https://${split.repo}` : split.repo,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`,\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter(\n\t\t(value): value is string => Boolean(value),\n\t);\n\tfor (const candidate of httpsCandidates) {\n\t\tconst info = hostedGitInfo.fromUrl(candidate);\n\t\tif (info) {\n\t\t\tif (split.ref && info.project?.includes(\"@\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn buildGitSource({\n\t\t\t\trepo: `https://${split.repo}`,\n\t\t\t\thost: info.domain || \"\",\n\t\t\t\tpath: `${info.user}/${info.project}`,\n\t\t\t\tref: info.committish || split.ref || undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn parseGenericGitUrl(url);\n}\n"]}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACvB,mCAAmC;IACnC,IAAI,EAAE,KAAK,CAAC;IACZ,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAqJF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAsD5D"}
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/utils/html.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AASD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuBnE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAa7F","sourcesContent":["export interface DecodedHtmlEntity {\n\ttext: string;\n\tlength: number;\n}\n\nfunction decodeCodePoint(codePoint: number): string | undefined {\n\tif (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {\n\t\treturn undefined;\n\t}\n\treturn String.fromCodePoint(codePoint);\n}\n\nexport function decodeHtmlEntity(entity: string): string | undefined {\n\tswitch (entity) {\n\t\tcase \"amp\":\n\t\t\treturn \"&\";\n\t\tcase \"lt\":\n\t\t\treturn \"<\";\n\t\tcase \"gt\":\n\t\t\treturn \">\";\n\t\tcase \"quot\":\n\t\t\treturn '\"';\n\t\tcase \"apos\":\n\t\t\treturn \"'\";\n\t}\n\n\tif (entity.startsWith(\"#x\") || entity.startsWith(\"#X\")) {\n\t\treturn decodeCodePoint(Number.parseInt(entity.slice(2), 16));\n\t}\n\n\tif (entity.startsWith(\"#\")) {\n\t\treturn decodeCodePoint(Number.parseInt(entity.slice(1), 10));\n\t}\n\n\treturn undefined;\n}\n\nexport function decodeHtmlEntityAt(html: string, index: number): DecodedHtmlEntity | undefined {\n\tconst semicolonIndex = html.indexOf(\";\", index + 1);\n\tif (semicolonIndex === -1 || semicolonIndex - index > 16) {\n\t\treturn undefined;\n\t}\n\n\tconst entity = html.slice(index + 1, semicolonIndex);\n\tconst decoded = decodeHtmlEntity(entity);\n\tif (decoded === undefined) {\n\t\treturn undefined;\n\t}\n\n\treturn { text: decoded, length: semicolonIndex - index + 1 };\n}\n"]}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/utils/html.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AASD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuBnE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAa7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-convert.d.ts","sourceRoot":"","sources":["../../src/utils/image-convert.ts"],"names":[],"mappings":"AAGA,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoB1F;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAgBpD","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport async function convertImageBytesToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\t// Photon not available, can't convert\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(bytes);\n\t\tconst image = applyExifOrientation(photon, rawImage, bytes);\n\t\tif (image !== rawImage) rawImage.free();\n\t\ttry {\n\t\t\treturn new Uint8Array(image.get_bytes());\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\t// Conversion failed\n\t\treturn null;\n\t}\n}\n\n/**\n * Convert image to PNG format for terminal display.\n * Kitty graphics protocol requires PNG format (f=100).\n */\nexport async function convertToPng(\n\tbase64Data: string,\n\tmimeType: string,\n): Promise<{ data: string; mimeType: string } | null> {\n\t// Already PNG, no conversion needed\n\tif (mimeType === \"image/png\") {\n\t\treturn { data: base64Data, mimeType };\n\t}\n\n\tconst bytes = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tdata: Buffer.from(pngBytes).toString(\"base64\"),\n\t\tmimeType: \"image/png\",\n\t};\n}\n"]}
1
+ {"version":3,"file":"image-convert.d.ts","sourceRoot":"","sources":["../../src/utils/image-convert.ts"],"names":[],"mappings":"AAGA,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoB1F;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAgBpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-process.d.ts","sourceRoot":"","sources":["../../src/utils/image-process.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,kBAAkB,EAAe,MAAM,mBAAmB,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IACnC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED,MAAM,MAAM,kBAAkB,GAC3B;IACA,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACf,GACD;IACA,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACf,CAAC;AAmDL,wBAAsB,YAAY,CACjC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CA2C7B","sourcesContent":["import { convertImageBytesToPng } from \"./image-convert.ts\";\nimport { formatDimensionNote, type ImageResizeOptions, resizeImage } from \"./image-resize.ts\";\n\nexport interface ProcessImageOptions {\n\t/** Whether to resize images to inline provider limits. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Optional resize overrides. Uses resizeImage defaults when omitted. */\n\tresizeOptions?: ImageResizeOptions;\n}\n\nexport type ProcessImageResult =\n\t| {\n\t\t\tok: true;\n\t\t\tdata: string;\n\t\t\tmimeType: string;\n\t\t\thints: string[];\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\tmessage: string;\n\t };\n\ninterface NormalizedImage {\n\tbytes: Uint8Array;\n\tmimeType: string;\n\tconvertedFrom?: string;\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nfunction normalizeSupportedImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"image/png\";\n\t\tcase \"image/jpeg\":\n\t\tcase \"image/jpg\":\n\t\t\treturn \"image/jpeg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"image/gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"image/webp\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nasync function normalizeImage(bytes: Uint8Array, mimeType: string): Promise<NormalizedImage | null> {\n\tconst normalizedMimeType = normalizeSupportedImageMimeType(mimeType);\n\tif (normalizedMimeType) {\n\t\treturn { bytes, mimeType: normalizedMimeType };\n\t}\n\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tbytes: pngBytes,\n\t\tmimeType: \"image/png\",\n\t\tconvertedFrom: baseMimeType(mimeType),\n\t};\n}\n\nfunction conversionHint(from: string | undefined, to: string): string | undefined {\n\tif (!from || from === to) return undefined;\n\treturn `[Image converted from ${from} to ${to}.]`;\n}\n\nexport async function processImage(\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ProcessImageOptions,\n): Promise<ProcessImageResult> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst normalized = await normalizeImage(bytes, mimeType);\n\tif (!normalized) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: \"[Image omitted: could not be converted to a supported inline image format.]\",\n\t\t};\n\t}\n\n\tif (autoResizeImages) {\n\t\tconst resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);\n\t\tif (!resized) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: \"[Image omitted: could not be resized below the inline image size limit.]\",\n\t\t\t};\n\t\t}\n\n\t\tconst hints: string[] = [];\n\t\tconst convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);\n\t\tif (convertedHint) hints.push(convertedHint);\n\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\tif (dimensionNote) hints.push(dimensionNote);\n\n\t\treturn {\n\t\t\tok: true,\n\t\t\tdata: resized.data,\n\t\t\tmimeType: resized.mimeType,\n\t\t\thints,\n\t\t};\n\t}\n\n\tconst hints: string[] = [];\n\tconst convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);\n\tif (convertedHint) hints.push(convertedHint);\n\n\treturn {\n\t\tok: true,\n\t\tdata: Buffer.from(normalized.bytes).toString(\"base64\"),\n\t\tmimeType: normalized.mimeType,\n\t\thints,\n\t};\n}\n"]}
1
+ {"version":3,"file":"image-process.d.ts","sourceRoot":"","sources":["../../src/utils/image-process.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,kBAAkB,EAAe,MAAM,mBAAmB,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IACnC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED,MAAM,MAAM,kBAAkB,GAC3B;IACA,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACf,GACD;IACA,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACf,CAAC;AAmDL,wBAAsB,YAAY,CACjC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CA2C7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-resize-core.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACpB;AA2BD;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqG9B","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport interface ImageResizeOptions {\n\tmaxWidth?: number; // Default: 2000\n\tmaxHeight?: number; // Default: 2000\n\tmaxBytes?: number; // Default: 4.5MB of base64 payload (below Anthropic's 5MB limit)\n\tjpegQuality?: number; // Default: 80\n}\n\nexport interface ResizedImage {\n\tdata: string; // base64\n\tmimeType: string;\n\toriginalWidth: number;\n\toriginalHeight: number;\n\twidth: number;\n\theight: number;\n\twasResized: boolean;\n}\n\n// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.\nconst DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;\n\nconst DEFAULT_OPTIONS: Required<ImageResizeOptions> = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: DEFAULT_MAX_BYTES,\n\tjpegQuality: 80,\n};\n\ninterface EncodedCandidate {\n\tdata: string;\n\tencodedSize: number;\n\tmimeType: string;\n}\n\nfunction encodeCandidate(buffer: Uint8Array, mimeType: string): EncodedCandidate {\n\tconst data = Buffer.from(buffer).toString(\"base64\");\n\treturn {\n\t\tdata,\n\t\tencodedSize: Buffer.byteLength(data, \"utf-8\"),\n\t\tmimeType,\n\t};\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Returns null if the image cannot be resized below maxBytes.\n *\n * Uses Photon (Rust/WASM) for image processing. If Photon is not available,\n * returns null.\n *\n * Strategy for staying under maxBytes:\n * 1. First resize to maxWidth/maxHeight\n * 2. Try both PNG and JPEG formats, pick the smaller one\n * 3. If still too large, try JPEG with decreasing quality\n * 4. If still too large, progressively reduce dimensions until 1x1\n */\nexport async function resizeImageInProcess(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst opts = { ...DEFAULT_OPTIONS, ...options };\n\tconst inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;\n\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\tlet image: ReturnType<typeof photon.PhotonImage.new_from_byteslice> | undefined;\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);\n\t\timage = applyExifOrientation(photon, rawImage, inputBytes);\n\t\tif (image !== rawImage) rawImage.free();\n\n\t\tconst originalWidth = image.get_width();\n\t\tconst originalHeight = image.get_height();\n\t\tconst format = mimeType.split(\"/\")[1] ?? \"png\";\n\n\t\t// Check if already within all limits (dimensions AND encoded size)\n\t\tif (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tdata: Buffer.from(inputBytes).toString(\"base64\"),\n\t\t\t\tmimeType: mimeType || `image/${format}`,\n\t\t\t\toriginalWidth,\n\t\t\t\toriginalHeight,\n\t\t\t\twidth: originalWidth,\n\t\t\t\theight: originalHeight,\n\t\t\t\twasResized: false,\n\t\t\t};\n\t\t}\n\n\t\t// Calculate initial dimensions respecting max limits\n\t\tlet targetWidth = originalWidth;\n\t\tlet targetHeight = originalHeight;\n\n\t\tif (targetWidth > opts.maxWidth) {\n\t\t\ttargetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);\n\t\t\ttargetWidth = opts.maxWidth;\n\t\t}\n\t\tif (targetHeight > opts.maxHeight) {\n\t\t\ttargetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);\n\t\t\ttargetHeight = opts.maxHeight;\n\t\t}\n\n\t\tfunction tryEncodings(width: number, height: number, jpegQualities: number[]): EncodedCandidate[] {\n\t\t\tconst resized = photon!.resize(image!, width, height, photon!.SamplingFilter.Lanczos3);\n\n\t\t\ttry {\n\t\t\t\tconst candidates: EncodedCandidate[] = [encodeCandidate(resized.get_bytes(), \"image/png\")];\n\t\t\t\tfor (const quality of jpegQualities) {\n\t\t\t\t\tcandidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), \"image/jpeg\"));\n\t\t\t\t}\n\t\t\t\treturn candidates;\n\t\t\t} finally {\n\t\t\t\tresized.free();\n\t\t\t}\n\t\t}\n\n\t\tconst qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));\n\t\tlet currentWidth = targetWidth;\n\t\tlet currentHeight = targetHeight;\n\n\t\twhile (true) {\n\t\t\tconst candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tif (candidate.encodedSize < opts.maxBytes) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: candidate.data,\n\t\t\t\t\t\tmimeType: candidate.mimeType,\n\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\twidth: currentWidth,\n\t\t\t\t\t\theight: currentHeight,\n\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentWidth === 1 && currentHeight === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));\n\t\t\tconst nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));\n\t\t\tif (nextWidth === currentWidth && nextHeight === currentHeight) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrentWidth = nextWidth;\n\t\t\tcurrentHeight = nextHeight;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tif (image) {\n\t\t\timage.free();\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"image-resize-core.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACpB;AA2BD;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqG9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"image-resize-worker.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":"","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerRequest {\n\tinputBytes: Uint8Array;\n\tmimeType: string;\n\toptions?: ImageResizeOptions;\n}\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction isResizeImageWorkerRequest(value: unknown): value is ResizeImageWorkerRequest {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Record<string, unknown>;\n\treturn record.inputBytes instanceof Uint8Array && typeof record.mimeType === \"string\";\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"image resize worker requires parentPort\");\n}\n\nport.once(\"message\", (message: unknown) => {\n\tvoid (async () => {\n\t\ttry {\n\t\t\tif (!isResizeImageWorkerRequest(message)) {\n\t\t\t\tthrow new Error(\"Invalid image resize worker request\");\n\t\t\t}\n\t\t\tconst result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);\n\t\t\tconst response: ResizeImageWorkerResponse = { result };\n\t\t\tport.postMessage(response);\n\t\t} catch (error) {\n\t\t\tconst response: ResizeImageWorkerResponse = {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t\tport.postMessage(response);\n\t\t}\n\t})();\n});\n"]}
1
+ {"version":3,"file":"image-resize-worker.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAE1G,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0E/E;;;;;;GAMG;AACH,wBAAsB,WAAW,CAChC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqB9B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5E","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\nexport type { ImageResizeOptions, ResizedImage } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction toTransferableBytes(input: Uint8Array): Uint8Array<ArrayBuffer> {\n\t// Transfer detaches the buffer, so transfer a worker-owned copy and leave the\n\t// caller's bytes intact.\n\treturn new Uint8Array(input);\n}\n\nfunction isResizeImageWorkerResponse(value: unknown): value is ResizeImageWorkerResponse {\n\treturn value !== null && typeof value === \"object\";\n}\n\nfunction createResizeWorker(workerSpecifier: string | URL): Worker {\n\treturn new Worker(workerSpecifier);\n}\n\nasync function resizeImageInWorker(\n\tworkerSpecifier: string | URL,\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst worker = createResizeWorker(workerSpecifier);\n\ttry {\n\t\tconst inputBytesForWorker = toTransferableBytes(inputBytes);\n\t\treturn await new Promise<ResizedImage | null>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst settle = (result: ResizedImage | null): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tconst fail = (error: Error): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\treject(error);\n\t\t\t};\n\n\t\t\tworker.once(\"message\", (message: unknown) => {\n\t\t\t\tif (!isResizeImageWorkerResponse(message)) {\n\t\t\t\t\tfail(new Error(\"Invalid image resize worker response\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (message.error) {\n\t\t\t\t\tfail(new Error(message.error));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettle(message.result ?? null);\n\t\t\t});\n\t\t\tworker.once(\"error\", fail);\n\t\t\tworker.once(\"exit\", (code) => {\n\t\t\t\tif (!settled) {\n\t\t\t\t\tfail(new Error(`Image resize worker exited with code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\t\t\tworker.postMessage(\n\t\t\t\t{\n\t\t\t\t\tinputBytes: inputBytesForWorker,\n\t\t\t\t\tmimeType,\n\t\t\t\t\toptions,\n\t\t\t\t},\n\t\t\t\t[inputBytesForWorker.buffer],\n\t\t\t);\n\t\t});\n\t} finally {\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not\n * block the TUI event loop. If the worker cannot be loaded (for example in some\n * Bun compiled executable layouts), fall back to in-process resizing so image\n * reads still work.\n */\nexport async function resizeImage(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\tconst workerUrl = new URL(\n\t\tisTypeScriptRuntime ? \"./image-resize-worker.ts\" : \"./image-resize-worker.js\",\n\t\timport.meta.url,\n\t);\n\n\t// Bun compiled executables resolve worker entrypoints by string path, not via\n\t// new URL(..., import.meta.url). Try the string path first under Bun so the\n\t// release binary uses the embedded worker instead of falling back in-process.\n\tif (typeof process.versions.bun === \"string\") {\n\t\ttry {\n\t\t\treturn await resizeImageInWorker(\"./src/utils/image-resize-worker.ts\", inputBytes, mimeType, options);\n\t\t} catch {}\n\t}\n\n\ttry {\n\t\treturn await resizeImageInWorker(workerUrl, inputBytes, mimeType, options);\n\t} catch {\n\t\treturn resizeImageInProcess(inputBytes, mimeType, options);\n\t}\n}\n\n/**\n * Format a dimension note for resized images.\n * This helps the model understand the coordinate mapping.\n */\nexport function formatDimensionNote(result: ResizedImage): string | undefined {\n\tif (!result.wasResized) {\n\t\treturn undefined;\n\t}\n\n\tconst scale = result.originalWidth / result.width;\n\treturn `[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.]`;\n}\n"]}
1
+ {"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAE1G,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0E/E;;;;;;GAMG;AACH,wBAAsB,WAAW,CAChC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqB9B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n\n/** Strip one leading UTF-8 BOM (U+FEFF) before JSON parsing. */\nexport function stripJsonBom(input: string): string {\n\treturn input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;\n}\n\n/** Parse JSON read from disk, accepting files that start with a UTF-8 BOM. */\nexport function parseJsonFileContent(input: string): unknown {\n\treturn JSON.parse(stripJsonBom(input));\n}\n"]}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"markit.d.ts","sourceRoot":"","sources":["../../src/utils/markit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAoCxF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAGnH;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAK1I","sourcesContent":["import type { Markit, StreamInfo } from \"markit-ai\";\n\nexport interface MarkitConversionResult { content: string; ok: boolean; error?: string }\n\nlet markit: () => Markit | Promise<Markit> = async () => {\n\tconst promise = import(\"markit-ai\").then(({ Markit }) => {\n\t\tconst instance = new Markit();\n\t\tmarkit = () => instance;\n\t\treturn instance;\n\t});\n\tmarkit = () => promise;\n\treturn promise;\n};\n\nfunction normalizeExtension(extension: string): string {\n\tconst trimmed = extension.trim().toLowerCase();\n\treturn trimmed ? trimmed.startsWith(\".\") ? trimmed : `.${trimmed}` : \".bin\";\n}\n\nfunction normalizeError(error: unknown): string { return error instanceof Error && error.message.trim().length > 0 ? error.message.trim() : \"Conversion failed\"; }\n\nfunction abortError(): Error { const error = new Error(\"Aborted\"); error.name = \"AbortError\"; return error; }\n\nasync function runMarkitConversion<T>(task: (markit: Markit) => Promise<T>, signal?: AbortSignal): Promise<T> {\n\tif (signal?.aborted) throw abortError();\n\tconst instance = await markit();\n\tif (!signal) return task(instance);\n\treturn await new Promise<T>((resolve, reject) => {\n\t\tconst abort = () => reject(abortError());\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tvoid task(instance).then(resolve, reject).finally(() => signal.removeEventListener(\"abort\", abort));\n\t});\n}\n\nfunction finalizeConversion(markdown?: string): MarkitConversionResult {\n\treturn typeof markdown === \"string\" && markdown.length > 0 ? { content: markdown, ok: true } : { content: \"\", ok: false, error: \"Conversion produced no output\" };\n}\n\nexport async function convertFileWithMarkit(filePath: string, signal?: AbortSignal): Promise<MarkitConversionResult> {\n\ttry { return finalizeConversion((await runMarkitConversion((instance) => instance.convertFile(filePath), signal)).markdown); }\n\tcatch (error) { if (error instanceof Error && error.name === \"AbortError\") throw error; return { content: \"\", ok: false, error: normalizeError(error) }; }\n}\n\nexport async function convertBufferWithMarkit(buffer: Uint8Array, extension: string, signal?: AbortSignal): Promise<MarkitConversionResult> {\n\tconst normalizedExtension = normalizeExtension(extension);\n\tconst streamInfo: StreamInfo = { extension: normalizedExtension, filename: `input${normalizedExtension}` };\n\ttry { return finalizeConversion((await runMarkitConversion((instance) => instance.convert(Buffer.from(buffer), streamInfo), signal)).markdown); }\n\tcatch (error) { if (error instanceof Error && error.name === \"AbortError\") throw error; return { content: \"\", ok: false, error: normalizeError(error) }; }\n}\n"]}
1
+ {"version":3,"file":"markit.d.ts","sourceRoot":"","sources":["../../src/utils/markit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAoCxF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAGnH;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAK1I"}
@@ -1 +1 @@
1
- {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASnG","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"BM\") && isBmp(buffer)) {\n\t\treturn \"image/bmp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction isBmp(buffer: Uint8Array): boolean {\n\tif (buffer.length < 26) return false;\n\n\tconst declaredFileSize = readUint32LE(buffer, 2);\n\tconst pixelDataOffset = readUint32LE(buffer, 10);\n\tconst dibHeaderSize = readUint32LE(buffer, 14);\n\tif (declaredFileSize !== 0 && declaredFileSize < 26) return false;\n\tif (pixelDataOffset < 14 + dibHeaderSize) return false;\n\tif (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false;\n\n\tlet colorPlanes: number;\n\tlet bitsPerPixel: number;\n\tif (dibHeaderSize === 12) {\n\t\tcolorPlanes = readUint16LE(buffer, 22);\n\t\tbitsPerPixel = readUint16LE(buffer, 24);\n\t} else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {\n\t\tif (buffer.length < 30) return false;\n\t\tcolorPlanes = readUint16LE(buffer, 26);\n\t\tbitsPerPixel = readUint16LE(buffer, 28);\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);\n}\n\nfunction readUint16LE(buffer: Uint8Array, offset: number): number {\n\treturn (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction readUint32LE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) +\n\t\t((buffer[offset + 1] ?? 0) << 8) +\n\t\t((buffer[offset + 2] ?? 0) << 16) +\n\t\t(buffer[offset + 3] ?? 0) * 0x1000000\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASnG"}
@@ -1 +1 @@
1
- {"version":3,"file":"module-require.d.ts","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D","sourcesContent":["import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { isBunBinary } from \"../config.ts\";\n\n/**\n * createRequire anchored so bare-specifier resolution works in every runtime.\n *\n * In the compiled binary the bundle is CJS (bytecode), where import.meta.url\n * is rewritten to the original build-machine source path; requiring through it\n * would resolve node_modules against paths that do not exist on user machines.\n * Anchor to the executable instead so resolution walks from the node_modules\n * shipped next to the binary.\n */\nexport function createModuleRequire(moduleUrl: string): NodeJS.Require {\n\treturn createRequire(resolutionBaseUrl(moduleUrl));\n}\n\n/**\n * A resolution base URL safe for the current runtime.\n *\n * In compiled binaries (monolithic bun or the Atomic split launcher) the\n * bundled `import.meta.url` is the build machine's source URL — a foreign-OS\n * `file://` URL that `fileURLToPath` rejects on other platforms (notably a\n * macOS `file:///Users/...` URL on Windows). Any API that decodes the base\n * (`createRequire`, jiti's `createJiti`) then throws `ERR_INVALID_FILE_URL_PATH`.\n * Anchor to the executable instead, which is always a valid local file URL.\n */\nexport function resolutionBaseUrl(moduleUrl: string): string {\n\treturn isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl;\n}\n"]}
1
+ {"version":3,"file":"module-require.d.ts","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/utils/open-browser.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAchD","sourcesContent":["import { spawn } from \"node:child_process\";\n\n/**\n * Open a URL or file in the platform browser/default handler.\n *\n * This intentionally never invokes a shell. On Windows, do not use\n * `cmd /c start`: cmd.exe re-parses metacharacters (&, |, ^, ...) before\n * `start` runs, which would make attacker-controlled URLs injectable.\n */\nexport function openBrowser(target: string): void {\n\tconst [cmd, args]: [string, string[]] =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? [\"open\", [target]]\n\t\t\t: process.platform === \"win32\"\n\t\t\t\t? [\"rundll32\", [\"url.dll,FileProtocolHandler\", target]]\n\t\t\t\t: [\"xdg-open\", [target]];\n\n\t// spawn reports launcher failures (for example, missing xdg-open) via an\n\t// error event. Browser launch is best-effort: callers still present the target\n\t// to the user, so keep the launcher failure from becoming a process crash.\n\tspawn(cmd, args, { stdio: \"ignore\", detached: true })\n\t\t.on(\"error\", () => {})\n\t\t.unref();\n}\n"]}
1
+ {"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/utils/open-browser.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAchD"}
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAQnC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAclD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAuBnF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAIlH;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASpF;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvF;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAe7D","sourcesContent":["import { realpathSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, resolve as nodeResolvePath, relative, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnProcessSync } from \"./child-process.ts\";\n\nconst UNICODE_SPACES = /[\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\n\nexport interface PathInputOptions {\n\t/** Trim leading/trailing whitespace before normalization. */\n\ttrim?: boolean;\n\t/** Expand leading `~` to a home directory. Defaults to true. */\n\texpandTilde?: boolean;\n\t/** Home directory used for `~` expansion. Defaults to the live home environment or `os.homedir()`. */\n\thomeDir?: string;\n\t/** Strip a leading `@`, used for CLI @file paths. */\n\tstripAtPrefix?: boolean;\n\t/** Normalize unicode space variants to regular spaces. */\n\tnormalizeUnicodeSpaces?: boolean;\n}\n\nexport function getHomeDir(): string {\n\tif (process.platform === \"win32\") {\n\t\tif (process.env.USERPROFILE) return process.env.USERPROFILE;\n\t\tif (process.env.HOMEDRIVE && process.env.HOMEPATH) return `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`;\n\t\tif (process.env.HOME) return process.env.HOME;\n\t\treturn homedir();\n\t}\n\treturn process.env.HOME || process.env.USERPROFILE || homedir();\n}\n\n/**\n * Resolve a path to its canonical (real) form, following symlinks.\n * Falls back to the raw path if resolution fails (e.g. the target does\n * not exist yet), so that callers never crash on missing filesystem\n * entries.\n */\nexport function canonicalizePath(path: string): string {\n\ttry {\n\t\treturn realpathSync(path);\n\t} catch {\n\t\treturn path;\n\t}\n}\n\n/**\n * Returns true if the value is NOT a package source (npm:, git:, etc.)\n * or a remote URL protocol. Bare names, relative paths, and file: URLs\n * are considered local.\n */\nexport function isLocalPath(value: string): boolean {\n\tconst trimmed = value.trim();\n\t// Known non-local prefixes. file: URLs are local paths and are intentionally resolved by resolvePath().\n\tif (\n\t\ttrimmed.startsWith(\"npm:\") ||\n\t\ttrimmed.startsWith(\"git:\") ||\n\t\ttrimmed.startsWith(\"github:\") ||\n\t\ttrimmed.startsWith(\"http:\") ||\n\t\ttrimmed.startsWith(\"https:\") ||\n\t\ttrimmed.startsWith(\"ssh:\")\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport function normalizePath(input: string, options: PathInputOptions = {}): string {\n\tlet normalized = options.trim ? input.trim() : input;\n\tif (options.normalizeUnicodeSpaces) {\n\t\tnormalized = normalized.replace(UNICODE_SPACES, \" \");\n\t}\n\tif (options.stripAtPrefix && normalized.startsWith(\"@\")) {\n\t\tnormalized = normalized.slice(1);\n\t}\n\n\tif (options.expandTilde ?? true) {\n\t\tconst home = options.homeDir ?? getHomeDir();\n\t\tif (normalized === \"~\") return home;\n\t\tif (normalized.startsWith(\"~/\") || (process.platform === \"win32\" && normalized.startsWith(\"~\\\\\"))) {\n\t\t\treturn join(home, normalized.slice(2));\n\t\t}\n\t}\n\n\tif (/^file:\\/\\//.test(normalized)) {\n\t\tdecodeURI(normalized);\n\t\treturn fileURLToPath(normalized);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolvePath(input: string, baseDir: string = process.cwd(), options: PathInputOptions = {}): string {\n\tconst normalized = normalizePath(input, options);\n\tconst normalizedBaseDir = normalizePath(baseDir);\n\treturn isAbsolute(normalized) ? nodeResolvePath(normalized) : nodeResolvePath(normalizedBaseDir, normalized);\n}\n\nexport function getCwdRelativePath(filePath: string, cwd: string): string | undefined {\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst resolvedPath = resolvePath(filePath, resolvedCwd);\n\tconst relativePath = relative(resolvedCwd, resolvedPath);\n\tconst isInsideCwd =\n\t\trelativePath === \"\" ||\n\t\t(relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));\n\n\treturn isInsideCwd ? relativePath || \".\" : undefined;\n}\n\nexport function formatPathRelativeToCwdOrAbsolute(filePath: string, cwd: string): string {\n\tconst absolutePath = resolvePath(filePath, cwd);\n\treturn (getCwdRelativePath(absolutePath, cwd) ?? absolutePath).split(sep).join(\"/\");\n}\n\nexport function markPathIgnoredByCloudSync(path: string): void {\n\tconst attrs =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? [\"com.dropbox.ignored\", \"com.apple.fileprovider.ignore#P\"]\n\t\t\t: process.platform === \"linux\"\n\t\t\t\t? [\"user.com.dropbox.ignored\"]\n\t\t\t\t: [];\n\n\tfor (const attr of attrs) {\n\t\tif (process.platform === \"darwin\") {\n\t\t\tspawnProcessSync(\"xattr\", [\"-w\", attr, \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t} else {\n\t\t\tspawnProcessSync(\"setfattr\", [\"-n\", attr, \"-v\", \"1\", path], { encoding: \"utf-8\", stdio: \"ignore\" });\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sGAAsG;IACtG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAQnC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAclD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAuBnF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAIlH;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASpF;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvF;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAe7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"photon.d.ts","sourceRoot":"","sources":["../../src/utils/photon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,YAAY,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAuFjF;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,4BAA4B,CAAC,GAAG,IAAI,CAAC,CAuB9F","sourcesContent":["/**\n * Photon image processing wrapper.\n *\n * This module provides a unified interface to @silvia-odwyer/photon-node that works in:\n * 1. Node.js (development, npm run build)\n * 2. Bun compiled binaries (standalone distribution)\n *\n * The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm')\n * which bakes the build machine's absolute path into Bun compiled binaries.\n *\n * Solution:\n * 1. Patch fs.readFileSync to redirect missing photon_rs_bg.wasm reads\n * 2. Copy photon_rs_bg.wasm next to the executable in build:binary\n */\n\nimport type { PathOrFileDescriptor } from \"fs\";\nimport { createRequire } from \"module\";\nimport * as path from \"path\";\nimport { fileURLToPath } from \"url\";\n\nconst require = createRequire(import.meta.url);\nconst fs = require(\"fs\") as typeof import(\"fs\");\n\n// Re-export types from the main package\nexport type { PhotonImage as PhotonImageType } from \"@silvia-odwyer/photon-node\";\n\ntype ReadFileSync = typeof fs.readFileSync;\n\nconst WASM_FILENAME = \"photon_rs_bg.wasm\";\n\n// Lazy-loaded photon module\nlet photonModule: typeof import(\"@silvia-odwyer/photon-node\") | null = null;\nlet loadPromise: Promise<typeof import(\"@silvia-odwyer/photon-node\") | null> | null = null;\n\nfunction pathOrNull(file: PathOrFileDescriptor): string | null {\n\tif (typeof file === \"string\") {\n\t\treturn file;\n\t}\n\tif (file instanceof URL) {\n\t\treturn fileURLToPath(file);\n\t}\n\treturn null;\n}\n\nfunction getFallbackWasmPaths(): string[] {\n\tconst execDir = path.dirname(process.execPath);\n\treturn [\n\t\tpath.join(execDir, WASM_FILENAME),\n\t\tpath.join(execDir, \"photon\", WASM_FILENAME),\n\t\tpath.join(process.cwd(), WASM_FILENAME),\n\t];\n}\n\nfunction patchPhotonWasmRead(): () => void {\n\tconst originalReadFileSync: ReadFileSync = fs.readFileSync.bind(fs);\n\tconst fallbackPaths = getFallbackWasmPaths();\n\tconst mutableFs = fs as { readFileSync: ReadFileSync };\n\n\tconst patchedReadFileSync: ReadFileSync = ((...args: Parameters<ReadFileSync>) => {\n\t\tconst [file, options] = args;\n\t\tconst resolvedPath = pathOrNull(file);\n\n\t\tif (resolvedPath?.endsWith(WASM_FILENAME)) {\n\t\t\ttry {\n\t\t\t\treturn originalReadFileSync(...args);\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as NodeJS.ErrnoException;\n\t\t\t\tif (err?.code && err.code !== \"ENOENT\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\t\t\tif (!fs.existsSync(fallbackPath)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (options === undefined) {\n\t\t\t\t\t\treturn originalReadFileSync(fallbackPath);\n\t\t\t\t\t}\n\t\t\t\t\treturn originalReadFileSync(fallbackPath, options);\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn originalReadFileSync(...args);\n\t}) as ReadFileSync;\n\n\ttry {\n\t\tmutableFs.readFileSync = patchedReadFileSync;\n\t} catch {\n\t\tObject.defineProperty(fs, \"readFileSync\", {\n\t\t\tvalue: patchedReadFileSync,\n\t\t\twritable: true,\n\t\t\tconfigurable: true,\n\t\t});\n\t}\n\n\treturn () => {\n\t\ttry {\n\t\t\tmutableFs.readFileSync = originalReadFileSync;\n\t\t} catch {\n\t\t\tObject.defineProperty(fs, \"readFileSync\", {\n\t\t\t\tvalue: originalReadFileSync,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t}\n\t};\n}\n\n/**\n * Load the photon module asynchronously.\n * Returns cached module on subsequent calls.\n */\nexport async function loadPhoton(): Promise<typeof import(\"@silvia-odwyer/photon-node\") | null> {\n\tif (photonModule) {\n\t\treturn photonModule;\n\t}\n\n\tif (loadPromise) {\n\t\treturn loadPromise;\n\t}\n\n\tloadPromise = (async () => {\n\t\tconst restoreReadFileSync = patchPhotonWasmRead();\n\t\ttry {\n\t\t\tphotonModule = await import(\"@silvia-odwyer/photon-node\");\n\t\t\treturn photonModule;\n\t\t} catch {\n\t\t\tphotonModule = null;\n\t\t\treturn photonModule;\n\t\t} finally {\n\t\t\trestoreReadFileSync();\n\t\t}\n\t})();\n\n\treturn loadPromise;\n}\n"]}
1
+ {"version":3,"file":"photon.d.ts","sourceRoot":"","sources":["../../src/utils/photon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAWH,YAAY,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAuFjF;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,4BAA4B,CAAC,GAAG,IAAI,CAAC,CAuB9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"pi-user-agent.d.ts","sourceRoot":"","sources":["../../src/utils/pi-user-agent.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD","sourcesContent":["import { APP_NAME } from \"../config.ts\";\n\nexport function getPiUserAgent(version: string): string {\n\tconst runtime = process.versions.bun ? `bun/${process.versions.bun}` : `node/${process.version}`;\n\treturn `${APP_NAME}/${version} (${process.platform}; ${runtime}; ${process.arch})`;\n}\n"]}
1
+ {"version":3,"file":"pi-user-agent.d.ts","sourceRoot":"","sources":["../../src/utils/pi-user-agent.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AA8CD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,WAAW,CAqDpE;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAY/C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8BxD;AAQD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAwBjD","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { delimiter } from \"node:path\";\nimport { spawn, spawnSync } from \"child_process\";\nimport { getBinDir } from \"../config.ts\";\n\nexport interface ShellConfig {\n\tshell: string;\n\targs: string[];\n\tcommandTransport?: \"argv\" | \"stdin\";\n}\n\nfunction isLegacyWslBashPath(path: string): boolean {\n\tconst normalized = path.replace(/\\//g, \"\\\\\").toLowerCase();\n\treturn /^[a-z]:\\\\windows\\\\(?:system32|sysnative)\\\\bash\\.exe$/.test(normalized);\n}\n\nfunction getBashShellConfig(shell: string): ShellConfig {\n\treturn isLegacyWslBashPath(shell) ? { shell, args: [\"-s\"], commandTransport: \"stdin\" } : { shell, args: [\"-c\"] };\n}\n\n/**\n * Find bash executable on PATH (cross-platform)\n */\nfunction findBashOnPath(): string | null {\n\tif (process.platform === \"win32\") {\n\t\t// Windows: Use 'where' and verify file exists (where can return non-existent paths)\n\t\ttry {\n\t\t\tconst result = spawnSync(\"where\", [\"bash.exe\"], { encoding: \"utf-8\", timeout: 5000 });\n\t\t\tif (result.status === 0 && result.stdout) {\n\t\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\t\tif (firstMatch && existsSync(firstMatch)) {\n\t\t\t\t\treturn firstMatch;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Unix: Use 'which' and trust its output (handles Termux and special filesystems)\n\ttry {\n\t\tconst result = spawnSync(\"which\", [\"bash\"], { encoding: \"utf-8\", timeout: 5000 });\n\t\tif (result.status === 0 && result.stdout) {\n\t\t\tconst firstMatch = result.stdout.trim().split(/\\r?\\n/)[0];\n\t\t\tif (firstMatch) {\n\t\t\t\treturn firstMatch;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore errors\n\t}\n\treturn null;\n}\n\n/**\n * Resolve shell configuration based on platform and an optional explicit shell path.\n * Resolution order:\n * 1. User-specified shellPath\n * 2. On Windows: Git Bash in known locations, then bash on PATH\n * 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh\n */\nexport function getShellConfig(customShellPath?: string): ShellConfig {\n\t// 1. Check user-specified shell path\n\tif (customShellPath) {\n\t\tif (existsSync(customShellPath)) {\n\t\t\treturn getBashShellConfig(customShellPath);\n\t\t}\n\t\tthrow new Error(`Custom shell path not found: ${customShellPath}`);\n\t}\n\n\tif (process.platform === \"win32\") {\n\t\t// 2. Try Git Bash in known locations\n\t\tconst paths: string[] = [];\n\t\tconst programFiles = process.env.ProgramFiles;\n\t\tif (programFiles) {\n\t\t\tpaths.push(`${programFiles}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\t\tconst programFilesX86 = process.env[\"ProgramFiles(x86)\"];\n\t\tif (programFilesX86) {\n\t\t\tpaths.push(`${programFilesX86}\\\\Git\\\\bin\\\\bash.exe`);\n\t\t}\n\n\t\tfor (const path of paths) {\n\t\t\tif (existsSync(path)) {\n\t\t\t\treturn getBashShellConfig(path);\n\t\t\t}\n\t\t}\n\n\t\t// 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)\n\t\tconst bashOnPath = findBashOnPath();\n\t\tif (bashOnPath) {\n\t\t\treturn getBashShellConfig(bashOnPath);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`No bash shell found. Options:\\n` +\n\t\t\t\t` 1. Install Git for Windows: https://git-scm.com/download/win\\n` +\n\t\t\t\t` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\\n` +\n\t\t\t\t\" 3. Set shellPath in settings.json\\n\\n\" +\n\t\t\t\t`Searched Git Bash in:\\n${paths.map((p) => ` ${p}`).join(\"\\n\")}`,\n\t\t);\n\t}\n\n\t// Unix: try /bin/bash, then bash on PATH, then fallback to sh\n\tif (existsSync(\"/bin/bash\")) {\n\t\treturn getBashShellConfig(\"/bin/bash\");\n\t}\n\n\tconst bashOnPath = findBashOnPath();\n\tif (bashOnPath) {\n\t\treturn getBashShellConfig(bashOnPath);\n\t}\n\n\treturn { shell: \"sh\", args: [\"-c\"] };\n}\n\nexport function getShellEnv(): NodeJS.ProcessEnv {\n\tconst binDir = getBinDir();\n\tconst pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === \"path\") ?? \"PATH\";\n\tconst currentPath = process.env[pathKey] ?? \"\";\n\tconst pathEntries = currentPath.split(delimiter).filter(Boolean);\n\tconst hasBinDir = pathEntries.includes(binDir);\n\tconst updatedPath = hasBinDir ? currentPath : [binDir, currentPath].filter(Boolean).join(delimiter);\n\n\treturn {\n\t\t...process.env,\n\t\t[pathKey]: updatedPath,\n\t};\n}\n\n/**\n * Sanitize binary output for display/storage.\n * Removes characters that crash string-width or cause display issues:\n * - Control characters (except tab, newline, carriage return)\n * - Lone surrogates\n * - Unicode Format characters (crash string-width due to a bug)\n * - Characters with undefined code points\n */\nexport function sanitizeBinaryOutput(str: string): string {\n\t// Use Array.from to properly iterate over code points (not code units)\n\t// This handles surrogate pairs correctly and catches edge cases where\n\t// codePointAt() might return undefined\n\treturn Array.from(str)\n\t\t.filter((char) => {\n\t\t\t// Filter out characters that cause string-width to crash\n\t\t\t// This includes:\n\t\t\t// - Unicode format characters\n\t\t\t// - Lone surrogates (already filtered by Array.from)\n\t\t\t// - Control chars except \\t \\n \\r\n\t\t\t// - Characters with undefined code points\n\n\t\t\tconst code = char.codePointAt(0);\n\n\t\t\t// Skip if code point is undefined (edge case with invalid strings)\n\t\t\tif (code === undefined) return false;\n\n\t\t\t// Allow tab, newline, carriage return\n\t\t\tif (code === 0x09 || code === 0x0a || code === 0x0d) return true;\n\n\t\t\t// Filter out control characters (0x00-0x1F, except 0x09, 0x0a, 0x0x0d)\n\t\t\tif (code <= 0x1f) return false;\n\n\t\t\t// Filter out Unicode format characters\n\t\t\tif (code >= 0xfff9 && code <= 0xfffb) return false;\n\n\t\t\treturn true;\n\t\t})\n\t\t.join(\"\");\n}\n\n/**\n * Detached child processes must be tracked so they can be killed on parent\n * shutdown signals (SIGHUP/SIGTERM).\n */\nconst trackedDetachedChildPids = new Set<number>();\n\nexport function trackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.add(pid);\n}\n\nexport function untrackDetachedChildPid(pid: number): void {\n\ttrackedDetachedChildPids.delete(pid);\n}\n\nexport function killTrackedDetachedChildren(): void {\n\tfor (const pid of trackedDetachedChildPids) {\n\t\tkillProcessTree(pid);\n\t}\n\ttrackedDetachedChildPids.clear();\n}\n\n/**\n * Kill a process and all its children (cross-platform)\n */\nexport function killProcessTree(pid: number): void {\n\tif (process.platform === \"win32\") {\n\t\t// Use taskkill on Windows to kill process tree\n\t\ttry {\n\t\t\tspawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n\t\t\t\tstdio: \"ignore\",\n\t\t\t\tdetached: true,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors if taskkill fails\n\t\t}\n\t} else {\n\t\t// Use SIGKILL on Unix/Linux/Mac\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\t// Fallback to killing just the child if process group kill fails\n\t\t\ttry {\n\t\t\t\tprocess.kill(pid, \"SIGKILL\");\n\t\t\t} catch {\n\t\t\t\t// Process already dead\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/utils/shell.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AA8CD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,WAAW,CAqDpE;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAY/C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA8BxD;AAQD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAwBjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/utils/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAcrE","sourcesContent":["/**\n * Sleep helper that respects abort signal.\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Aborted\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst timeout = setTimeout(resolve, ms);\n\n\t\tsignal?.addEventListener(\"abort\", () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(new Error(\"Aborted\"));\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/utils/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAcrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"split-launcher.d.ts","sourceRoot":"","sources":["../../src/utils/split-launcher.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAKhD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,MAAM,EAAE,GAAG,MAAM,CAO/F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAOvF","sourcesContent":["import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Split-launcher / foreign-build `import.meta.url` handling.\n *\n * Atomic's release binaries use a split launcher: a tiny compiled `atomic` /\n * `atomic.exe` executable sets `ATOMIC_CODING_AGENT=true` and then dynamically\n * imports the bundled sidecar `app.js` (plus raw-TS built-in extensions) from\n * disk next to the executable.\n *\n * Unlike pi's single monolithic `bun --compile` binary — where `import.meta.url`\n * is always a `$bunfs://` virtual-FS URL — the split sidecar bundle carries the\n * build machine's source path baked in at bundle time (e.g. a macOS\n * `file:///Users/.../app.js` URL). On Windows, `fileURLToPath()` rejects that\n * URL with `ERR_INVALID_FILE_URL_PATH` (\"File URL path must be an absolute\n * path\") because `/Users/...` has no drive letter, so every site that decodes\n * `import.meta.url` to a filesystem path must tolerate it.\n *\n * These helpers centralize that tolerance: decode normally, and only when the\n * decode fails under the split launcher fall back to a path resolved relative to\n * the launcher executable (where the sidecar bundle and its assets actually\n * live). This mirrors pi's convention of a single detection point rather than\n * scattered ad-hoc guards.\n */\nexport function isSplitLauncherRuntime(): boolean {\n\treturn (\n\t\tprocess.env.ATOMIC_CODING_AGENT === \"true\" &&\n\t\t/(?:^|[\\\\/])atomic(?:\\.exe)?$/i.test(process.execPath)\n\t);\n}\n\n/** Directory containing the launcher executable and its sidecar bundle/assets. */\nexport function splitLauncherDir(): string {\n\treturn dirname(process.execPath);\n}\n\n/**\n * Resolve a module's directory from its `import.meta.url`, tolerating the\n * split-launcher case where the bundled URL is a foreign-OS build path.\n * `execRelativeSegments` locate the equivalent directory next to the launcher.\n */\nexport function moduleDirFromMetaUrl(metaUrl: string, ...execRelativeSegments: string[]): string {\n\ttry {\n\t\treturn dirname(fileURLToPath(metaUrl));\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), ...execRelativeSegments);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Resolve a module's filename from its `import.meta.url`, tolerating the\n * split-launcher case. `execRelativeFile` locates the equivalent file next to\n * the launcher (used as a stable resolution base, e.g. for `createRequire`).\n */\nexport function moduleFileFromMetaUrl(metaUrl: string, execRelativeFile: string): string {\n\ttry {\n\t\treturn fileURLToPath(metaUrl);\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), execRelativeFile);\n\t\tthrow error;\n\t}\n}\n"]}
1
+ {"version":3,"file":"split-launcher.d.ts","sourceRoot":"","sources":["../../src/utils/split-launcher.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAKhD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,MAAM,EAAE,GAAG,MAAM,CAO/F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAOvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,CAoDtF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAQ9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD","sourcesContent":["import hljs from \"highlight.js\";\nimport { decodeHtmlEntityAt } from \"./html.ts\";\n\nexport type HighlightFormatter = (text: string) => string;\nexport type HighlightTheme = Partial<Record<string, HighlightFormatter>>;\n\nexport interface HighlightOptions {\n\tlanguage?: string;\n\tignoreIllegals?: boolean;\n\tlanguageSubset?: string[];\n\ttheme?: HighlightTheme;\n}\n\nconst SPAN_CLOSE = \"</span>\";\nconst HIGHLIGHT_CLASS_PREFIX = \"hljs-\";\n\nfunction getScopeFromSpanTag(tag: string): string | undefined {\n\tconst match = /\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/.exec(tag);\n\tconst classValue = match?.[1] ?? match?.[2];\n\tif (!classValue) {\n\t\treturn undefined;\n\t}\n\n\tfor (const className of classValue.split(/\\s+/)) {\n\t\tif (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {\n\t\t\treturn className.slice(HIGHLIGHT_CLASS_PREFIX.length);\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getScopeFormatter(scope: string, theme: HighlightTheme): HighlightFormatter | undefined {\n\tconst exact = theme[scope];\n\tif (exact) {\n\t\treturn exact;\n\t}\n\n\tconst dotIndex = scope.indexOf(\".\");\n\tif (dotIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dotIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\tconst dashIndex = scope.indexOf(\"-\");\n\tif (dashIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dashIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getActiveFormatter(scopes: Array<string | undefined>, theme: HighlightTheme): HighlightFormatter | undefined {\n\tfor (let i = scopes.length - 1; i >= 0; i--) {\n\t\tconst scope = scopes[i];\n\t\tif (!scope) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst formatter = getScopeFormatter(scope, theme);\n\t\tif (formatter) {\n\t\t\treturn formatter;\n\t\t}\n\t}\n\treturn theme.default;\n}\n\nfunction isSpanOpenTagStart(html: string, index: number): boolean {\n\tif (!html.startsWith(\"<span\", index)) {\n\t\treturn false;\n\t}\n\tconst nextChar = html[index + \"<span\".length];\n\treturn nextChar === \">\" || nextChar === \" \" || nextChar === \"\\t\" || nextChar === \"\\n\" || nextChar === \"\\r\";\n}\n\nexport function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): string {\n\tlet output = \"\";\n\tlet textBuffer = \"\";\n\tconst scopes: Array<string | undefined> = [];\n\n\tconst flushText = () => {\n\t\tif (!textBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tconst formatter = getActiveFormatter(scopes, theme);\n\t\toutput += formatter ? formatter(textBuffer) : textBuffer;\n\t\ttextBuffer = \"\";\n\t};\n\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tif (isSpanOpenTagStart(html, index)) {\n\t\t\tconst tagEndIndex = html.indexOf(\">\", index + 5);\n\t\t\tif (tagEndIndex !== -1) {\n\t\t\t\tflushText();\n\t\t\t\tconst tag = html.slice(index, tagEndIndex + 1);\n\t\t\t\tconst scope = getScopeFromSpanTag(tag);\n\t\t\t\tscopes.push(scope);\n\t\t\t\tindex = tagEndIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (html.startsWith(SPAN_CLOSE, index)) {\n\t\t\tflushText();\n\t\t\tif (scopes.length > 0) {\n\t\t\t\tscopes.pop();\n\t\t\t}\n\t\t\tindex += SPAN_CLOSE.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (html[index] === \"&\") {\n\t\t\tconst decoded = decodeHtmlEntityAt(html, index);\n\t\t\tif (decoded) {\n\t\t\t\ttextBuffer += decoded.text;\n\t\t\t\tindex += decoded.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\ttextBuffer += html[index];\n\t\tindex++;\n\t}\n\n\tflushText();\n\treturn output;\n}\n\nexport function highlight(code: string, options: HighlightOptions = {}): string {\n\tconst html = options.language\n\t\t? hljs.highlight(code, {\n\t\t\t\tlanguage: options.language,\n\t\t\t\tignoreIllegals: options.ignoreIllegals,\n\t\t\t}).value\n\t\t: hljs.highlightAuto(code, options.languageSubset).value;\n\treturn renderHighlightedHtml(html, options.theme);\n}\n\nexport function supportsLanguage(name: string): boolean {\n\treturn hljs.getLanguage(name) !== undefined;\n}\n"]}
1
+ {"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,CAoDtF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAQ9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"AAkGA,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAsB5D;AAuQD,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuD7B","sourcesContent":["import chalk from \"chalk\";\nimport { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readdirSync,\n renameSync,\n rmSync,\n} from \"fs\";\nimport { writeFile } from \"fs/promises\";\nimport { arch, platform } from \"os\";\nimport { join } from \"path\";\nimport { APP_NAME, ENV_OFFLINE, getBinDir, getEnvValue } from \"../config.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst NETWORK_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\nfunction isOfflineModeEnabled(): boolean {\n const value = getEnvValue(ENV_OFFLINE);\n if (!value) return false;\n return (\n value === \"1\" ||\n value.toLowerCase() === \"true\" ||\n value.toLowerCase() === \"yes\"\n );\n}\n\ninterface ToolConfig {\n name: string;\n repo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n binaryName: string; // Name of the binary inside the archive\n systemBinaryNames?: string[]; // Alternative system command names to try before downloading\n tagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n getAssetName: (\n version: string,\n plat: string,\n architecture: string,\n ) => string | null;\n}\n\nconst TOOLS: Record<string, ToolConfig> = {\n fd: {\n name: \"fd\",\n repo: \"sharkdp/fd\",\n binaryName: \"fd\",\n systemBinaryNames: [\"fd\", \"fdfind\"],\n tagPrefix: \"v\",\n getAssetName: (version, plat, architecture) => {\n if (plat === \"darwin\") {\n const archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n return `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n } else if (plat === \"linux\") {\n const archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n return `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n } else if (plat === \"win32\") {\n const archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n return `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n }\n return null;\n },\n },\n rg: {\n name: \"ripgrep\",\n repo: \"BurntSushi/ripgrep\",\n binaryName: \"rg\",\n tagPrefix: \"\",\n getAssetName: (version, plat, architecture) => {\n if (plat === \"darwin\") {\n const archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n return `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n } else if (plat === \"linux\") {\n if (architecture === \"arm64\") {\n return `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n }\n return `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n } else if (plat === \"win32\") {\n const archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n return `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n }\n return null;\n },\n },\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n try {\n const result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n // Check for ENOENT error (command not found)\n return result.error === undefined || result.error === null;\n } catch {\n return false;\n }\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n const config = TOOLS[tool];\n if (!config) return null;\n\n // Check our tools directory first\n const localPath = join(\n TOOLS_DIR,\n config.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n );\n if (existsSync(localPath)) {\n return localPath;\n }\n\n // Check system PATH - if found, just return the command name (it's in PATH)\n const systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n for (const systemBinaryName of systemBinaryNames) {\n if (commandExists(systemBinaryName)) {\n return systemBinaryName;\n }\n }\n\n return null;\n}\n\n// Fetch latest release version from GitHub\nasync function getLatestVersion(repo: string): Promise<string> {\n const response = await fetch(\n `https://api.github.com/repos/${repo}/releases/latest`,\n {\n headers: { \"User-Agent\": APP_NAME },\n signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),\n },\n );\n\n if (!response.ok) {\n throw new Error(`GitHub API error: ${response.status}`);\n }\n\n const data = (await response.json()) as { tag_name: string };\n return data.tag_name.replace(/^v/, \"\");\n}\n\n// Download a file from URL\nasync function downloadFile(url: string, dest: string): Promise<void> {\n const response = await fetch(url, {\n signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to download: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(\"No response body\");\n }\n\n await writeFile(dest, Buffer.from(await response.arrayBuffer()));\n}\n\nfunction findBinaryRecursively(\n rootDir: string,\n binaryFileName: string,\n): string | null {\n const stack: string[] = [rootDir];\n\n while (stack.length > 0) {\n const currentDir = stack.pop();\n if (!currentDir) continue;\n\n const entries = readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isFile() && entry.name === binaryFileName) {\n return fullPath;\n }\n if (entry.isDirectory()) {\n stack.push(fullPath);\n }\n }\n }\n\n return null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n if (result.error?.message) {\n return result.error.message;\n }\n const stderr = result.stderr?.toString().trim();\n if (stderr) {\n return stderr;\n }\n const stdout = result.stdout?.toString().trim();\n if (stdout) {\n return stdout;\n }\n return `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n const result = spawnSync(command, args, { stdio: \"pipe\" });\n if (!result.error && result.status === 0) {\n return null;\n }\n return `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(\n archivePath: string,\n extractDir: string,\n assetName: string,\n): void {\n const failure = runExtractionCommand(\"tar\", [\n \"xzf\",\n archivePath,\n \"-C\",\n extractDir,\n ]);\n if (failure) {\n throw new Error(`Failed to extract ${assetName}: ${failure}`);\n }\n}\n\nfunction getWindowsTarCommand(): string {\n const systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n if (systemRoot) {\n const systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n if (existsSync(systemTar)) {\n return systemTar;\n }\n }\n return \"tar.exe\";\n}\n\nfunction extractZipArchive(\n archivePath: string,\n extractDir: string,\n assetName: string,\n): void {\n const failures: string[] = [];\n\n if (platform() === \"win32\") {\n // Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n // System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n const tarFailure = runExtractionCommand(getWindowsTarCommand(), [\n \"xf\",\n archivePath,\n \"-C\",\n extractDir,\n ]);\n if (!tarFailure) return;\n failures.push(tarFailure);\n\n const script =\n \"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n const powershellFailure = runExtractionCommand(\"powershell.exe\", [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-Command\",\n script,\n archivePath,\n extractDir,\n ]);\n if (!powershellFailure) return;\n failures.push(powershellFailure);\n } else {\n const unzipFailure = runExtractionCommand(\"unzip\", [\n \"-q\",\n archivePath,\n \"-d\",\n extractDir,\n ]);\n if (!unzipFailure) return;\n failures.push(unzipFailure);\n\n const tarFailure = runExtractionCommand(\"tar\", [\n \"xf\",\n archivePath,\n \"-C\",\n extractDir,\n ]);\n if (!tarFailure) return;\n failures.push(tarFailure);\n }\n\n throw new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\n// Download and install a tool\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n const config = TOOLS[tool];\n if (!config) throw new Error(`Unknown tool: ${tool}`);\n\n const plat = platform();\n const architecture = arch();\n\n // Get latest version\n const version = await getLatestVersion(config.repo);\n\n // Get asset name for this platform\n const assetName = config.getAssetName(version, plat, architecture);\n if (!assetName) {\n throw new Error(`Unsupported platform: ${plat}/${architecture}`);\n }\n\n // Create tools directory\n mkdirSync(TOOLS_DIR, { recursive: true });\n\n const downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n const archivePath = join(TOOLS_DIR, assetName);\n const binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n // Download\n await downloadFile(downloadUrl, archivePath);\n\n // Extract into a unique temp directory. fd and rg downloads can run concurrently\n // during startup, so sharing a fixed directory causes races.\n const extractDir = join(\n TOOLS_DIR,\n `extract_tmp_${config.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n );\n mkdirSync(extractDir, { recursive: true });\n\n try {\n if (assetName.endsWith(\".tar.gz\")) {\n extractTarGzArchive(archivePath, extractDir, assetName);\n } else if (assetName.endsWith(\".zip\")) {\n extractZipArchive(archivePath, extractDir, assetName);\n } else {\n throw new Error(`Unsupported archive format: ${assetName}`);\n }\n\n // Find the binary in extracted files. Some archives contain files directly\n // at root, others nest under a versioned subdirectory.\n const binaryFileName = config.binaryName + binaryExt;\n const extractedDir = join(\n extractDir,\n assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"),\n );\n const extractedBinaryCandidates = [\n join(extractedDir, binaryFileName),\n join(extractDir, binaryFileName),\n ];\n let extractedBinary = extractedBinaryCandidates.find((candidate) =>\n existsSync(candidate),\n );\n\n if (!extractedBinary) {\n extractedBinary =\n findBinaryRecursively(extractDir, binaryFileName) ?? undefined;\n }\n\n if (extractedBinary) {\n renameSync(extractedBinary, binaryPath);\n } else {\n throw new Error(\n `Binary not found in archive: expected ${binaryFileName} under ${extractDir}`,\n );\n }\n\n // Make executable (Unix only)\n if (plat !== \"win32\") {\n chmodSync(binaryPath, 0o755);\n }\n } finally {\n // Cleanup\n rmSync(archivePath, { force: true });\n rmSync(extractDir, { recursive: true, force: true });\n }\n\n return binaryPath;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n fd: \"fd\",\n rg: \"ripgrep\",\n};\n\n// Ensure a tool is available, downloading if necessary\n// Returns the path to the tool, or null if unavailable\nexport async function ensureTool(\n tool: \"fd\" | \"rg\",\n silent: boolean = false,\n): Promise<string | undefined> {\n const existingPath = getToolPath(tool);\n if (existingPath) {\n return existingPath;\n }\n\n const config = TOOLS[tool];\n if (!config) return undefined;\n\n if (isOfflineModeEnabled()) {\n if (!silent) {\n console.log(\n chalk.yellow(\n `${config.name} not found. Offline mode enabled, skipping download.`,\n ),\n );\n }\n return undefined;\n }\n\n // On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n // Users must install via pkg.\n if (platform() === \"android\") {\n const pkgName = TERMUX_PACKAGES[tool] ?? tool;\n if (!silent) {\n console.log(\n chalk.yellow(\n `${config.name} not found. Install with: pkg install ${pkgName}`,\n ),\n );\n }\n return undefined;\n }\n\n // Tool not found - download it\n if (!silent) {\n console.log(chalk.dim(`${config.name} not found. Downloading...`));\n }\n\n try {\n const path = await downloadTool(tool);\n if (!silent) {\n console.log(chalk.dim(`${config.name} installed to ${path}`));\n }\n return path;\n } catch (e) {\n if (!silent) {\n console.log(\n chalk.yellow(\n `Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n ),\n );\n }\n return undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"AAkGA,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAsB5D;AAuQD,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuD7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"AAeA,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpG;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAM/F;AAED,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiBtC;AAED,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB9F","sourcesContent":["import { compare, valid } from \"semver\";\nimport { ENV_OFFLINE, ENV_SKIP_VERSION_CHECK, PACKAGE_NAME, getEnvValue } from \"../config.ts\";\n\nconst LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\n/**\n * The versionless placeholder stamped on `main` and read from source-tree dev\n * runs (`bun packages/coding-agent/src/cli.ts`). Real releases never carry it —\n * `scripts/cut-release.ts` materializes the actual version on the tag commit —\n * so encountering it means this is a dev build that should not be compared\n * against the published registry version.\n */\nconst DEV_VERSION_PLACEHOLDER = \"0.0.0\";\n\nexport function isDevVersion(version: string): boolean {\n\treturn version.trim() === DEV_VERSION_PLACEHOLDER;\n}\n\nexport interface LatestPiRelease {\n\tversion: string;\n\tpackageName?: string;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestPiRelease(\n\toptions: { timeoutMs?: number } = {},\n): Promise<LatestPiRelease | undefined> {\n\tif (getEnvValue(ENV_SKIP_VERSION_CHECK) || getEnvValue(ENV_OFFLINE)) return undefined;\n\n\tconst response = await fetch(LATEST_VERSION_URL, {\n\t\theaders: {\n\t\t\taccept: \"application/json\",\n\t\t},\n\t\tsignal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS),\n\t});\n\tif (!response.ok) return undefined;\n\n\tconst data = (await response.json()) as { name?: unknown; version?: unknown };\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName = typeof data.name === \"string\" && data.name.trim() ? data.name.trim() : undefined;\n\treturn { version: data.version.trim(), packageName };\n}\n\nexport async function getLatestPiVersion(\n\toptions: { timeoutMs?: number } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestPiRelease(options))?.version;\n}\n\nexport async function checkForNewPiVersion(currentVersion: string): Promise<string | undefined> {\n\t// Dev builds always read the versionless `0.0.0` placeholder, which is older\n\t// than any published release, so the registry check would always nag. Skip it\n\t// (and the network call) for source-tree/dev runs.\n\tif (isDevVersion(currentVersion)) {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\tconst latestVersion = await getLatestPiVersion();\n\t\tif (latestVersion && isNewerPackageVersion(latestVersion, currentVersion)) {\n\t\t\treturn latestVersion;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"AAeA,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpG;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAM/F;AAED,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiBtC;AAED,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"windows-self-update.d.ts","sourceRoot":"","sources":["../../src/utils/windows-self-update.ts"],"names":[],"mappings":"AAkDA,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3E;AAED,wBAAgB,mCAAmC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAsB5E","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { copyFileSync, existsSync, mkdirSync, renameSync, rmSync } from \"node:fs\";\nimport { basename, dirname, join, relative, resolve, toNamespacedPath } from \"node:path\";\nimport { APP_NAME } from \"../config.ts\";\nimport { getCwdRelativePath } from \"./paths.ts\";\n\nconst QUARANTINE_DIR_NAME = `.${APP_NAME}-native-quarantine`;\n\nfunction normalizePath(path: string): string {\n\treturn toNamespacedPath(resolve(path));\n}\n\nfunction getQuarantineRoot(packageDir: string): string | undefined {\n\tlet current = resolve(packageDir);\n\twhile (true) {\n\t\tif (basename(current).toLowerCase() === \"node_modules\") {\n\t\t\treturn join(current, QUARANTINE_DIR_NAME);\n\t\t}\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) {\n\t\t\treturn undefined;\n\t\t}\n\t\tcurrent = parent;\n\t}\n}\n\nfunction getLoadedSharedObjectsInPackageDir(packageDir: string): string[] {\n\tconst sharedObjects = (process.report.getReport() as { sharedObjects?: unknown }).sharedObjects;\n\tif (!Array.isArray(sharedObjects)) {\n\t\treturn [];\n\t}\n\n\tconst root = normalizePath(packageDir).toLowerCase();\n\tconst seen = new Set<string>();\n\tconst loadedFiles: string[] = [];\n\tfor (const value of sharedObjects) {\n\t\tif (typeof value !== \"string\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst filePath = normalizePath(value);\n\t\tconst comparisonPath = filePath.toLowerCase();\n\t\tif (getCwdRelativePath(comparisonPath, root) === undefined || seen.has(comparisonPath)) {\n\t\t\tcontinue;\n\t\t}\n\t\tseen.add(comparisonPath);\n\t\tloadedFiles.push(filePath);\n\t}\n\treturn loadedFiles;\n}\n\nexport function cleanupWindowsSelfUpdateQuarantine(packageDir: string): void {\n\tconst quarantineRoot = getQuarantineRoot(packageDir);\n\tif (!quarantineRoot) {\n\t\treturn;\n\t}\n\ttry {\n\t\trmSync(quarantineRoot, { recursive: true, force: true });\n\t} catch {\n\t\t// A previous Atomic process may still be exiting and holding a native addon.\n\t}\n}\n\nexport function quarantineWindowsNativeDependencies(packageDir: string): void {\n\tconst resolvedPackageDir = normalizePath(packageDir);\n\tconst quarantineRoot = getQuarantineRoot(resolvedPackageDir);\n\tif (!quarantineRoot) {\n\t\treturn;\n\t}\n\n\tconst loadedFiles = getLoadedSharedObjectsInPackageDir(resolvedPackageDir);\n\tif (loadedFiles.length === 0) {\n\t\treturn;\n\t}\n\n\tconst quarantineRunDir = join(quarantineRoot, `${Date.now()}-${process.pid}-${randomUUID()}`);\n\tfor (const loadedFile of loadedFiles) {\n\t\tif (!existsSync(loadedFile)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst quarantinePath = join(quarantineRunDir, relative(resolvedPackageDir, loadedFile));\n\t\tmkdirSync(dirname(quarantinePath), { recursive: true });\n\t\trenameSync(loadedFile, quarantinePath);\n\t\tcopyFileSync(quarantinePath, loadedFile);\n\t}\n}\n"]}
1
+ {"version":3,"file":"windows-self-update.d.ts","sourceRoot":"","sources":["../../src/utils/windows-self-update.ts"],"names":[],"mappings":"AAkDA,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3E;AAED,wBAAgB,mCAAmC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAsB5E"}
@@ -643,7 +643,7 @@ interface ProviderModelConfig {
643
643
  /** Custom headers for this specific model. */
644
644
  headers?: Record<string, string>;
645
645
 
646
- /** OpenAI compatibility settings for openai-completions API. */
646
+ /** API-specific provider compatibility settings. */
647
647
  compat?: {
648
648
  supportsStore?: boolean;
649
649
  supportsDeveloperRole?: boolean;
@@ -657,6 +657,10 @@ interface ProviderModelConfig {
657
657
  thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
658
658
  chatTemplateKwargs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
659
659
  cacheControlFormat?: "anthropic";
660
+ sendSessionAffinityHeaders?: boolean;
661
+ sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter";
662
+ supportsLongCacheRetention?: boolean;
663
+ supportsToolSearch?: boolean;
660
664
  };
661
665
  }
662
666
  ```
@@ -665,3 +669,5 @@ The `cost` shape is equivalent to `Model<Api>["cost"]`. Base rates and every tie
665
669
 
666
670
  `openrouter` sends `reasoning: { effort }`. `deepseek` sends `thinking: { type: "enabled" | "disabled" }` and `reasoning_effort` when enabled. `together` sends `reasoning: { enabled }` and also `reasoning_effort` when `supportsReasoningEffort` is enabled. `qwen` is for DashScope-style top-level `enable_thinking`. Use `qwen-chat-template` for local Qwen-compatible servers that read `chat_template_kwargs.enable_thinking` and need `preserve_thinking`. Use `chat-template` for configurable `chat_template_kwargs`, for example DeepSeek V3.x behind vLLM with `chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } }`.
667
671
  `cacheControlFormat: "anthropic"` applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user/assistant text content.
672
+
673
+ For `openai-responses` providers, Pi v0.80.7 replaced `compat.sendSessionIdHeader` with `compat.sessionAffinityFormat`. Use `"openai"` for `session_id` plus `x-client-request-id`, `"openai-nosession"` to omit `session_id` while retaining `x-client-request-id`, or `"openrouter"` for `x-session-id`. Responses-compatible providers may also set `supportsToolSearch` when they support deferred tool loading.
@@ -1336,9 +1336,11 @@ Use `promptSnippet` to opt a custom tool into a one-line entry in `Available too
1336
1336
 
1337
1337
  See [dynamic-tools.ts](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/examples/extensions/dynamic-tools.ts) for a full example.
1338
1338
 
1339
+ Use Atomic's export rather than importing `StringEnum` directly from Pi. It preserves Pi's Google-compatible runtime schema while keeping the schema typed against Atomic's direct TypeBox version.
1340
+
1339
1341
  ```typescript
1340
1342
  import { Type } from "typebox";
1341
- import { StringEnum } from "@earendil-works/pi-ai";
1343
+ import { StringEnum } from "@bastani/atomic";
1342
1344
 
1343
1345
  pi.registerTool({
1344
1346
  name: "my_tool",
@@ -1815,7 +1817,7 @@ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
1815
1817
 
1816
1818
  ```typescript
1817
1819
  import { Type } from "typebox";
1818
- import { StringEnum } from "@earendil-works/pi-ai";
1820
+ import { StringEnum } from "@bastani/atomic";
1819
1821
  import { Text } from "@earendil-works/pi-tui";
1820
1822
 
1821
1823
  pi.registerTool({
@@ -1827,7 +1829,7 @@ pi.registerTool({
1827
1829
  "Use my_tool for todo planning instead of direct file edits when the user asks for a task list."
1828
1830
  ],
1829
1831
  parameters: Type.Object({
1830
- action: StringEnum(["list", "add"] as const), // Use StringEnum for Google compatibility
1832
+ action: StringEnum(["list", "add"] as const), // Atomic's Pi-compatible TypeBox helper
1831
1833
  text: Type.Optional(Type.String()),
1832
1834
  }),
1833
1835
  prepareArguments(args) {
@@ -1884,7 +1886,7 @@ async execute(toolCallId, params) {
1884
1886
  }
1885
1887
  ```
1886
1888
 
1887
- **Important:** Use `StringEnum` from `@earendil-works/pi-ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
1889
+ **Important:** Use `StringEnum` from `@bastani/atomic` for string enums. It retains Pi's Google-compatible schema and composes with Atomic's direct TypeBox types; `Type.Union`/`Type.Literal` doesn't work with Google's API.
1888
1890
 
1889
1891
  **Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it only when a custom tool must normalize arguments before validation. Return the object you want validated against `parameters`, keep the public schema strict, and avoid advertising deprecated fields.
1890
1892
 
@@ -181,8 +181,10 @@ export default function (pi: ExtensionAPI) {
181
181
  ## Key Patterns
182
182
 
183
183
  **Use StringEnum for string parameters** (required for Google API compatibility):
184
+ Atomic's export preserves Pi's Google-compatible schema while keeping it composable with Atomic's direct TypeBox version.
185
+
184
186
  ```typescript
185
- import { StringEnum } from "@earendil-works/pi-ai";
187
+ import { StringEnum } from "@bastani/atomic";
186
188
 
187
189
  // Good
188
190
  action: StringEnum(["list", "add"] as const)
@@ -1,4 +1,4 @@
1
- import { StringEnum } from "@earendil-works/pi-ai/compat";
1
+ import { StringEnum } from "@bastani/atomic";
2
2
  import { Type } from "typebox";
3
3
 
4
4
  export const MAX_PARALLEL_TASKS = 50;
@@ -17,9 +17,8 @@
17
17
  * separate variables. Only the agent cursor is ever exposed to the agent.
18
18
  */
19
19
 
20
- import { StringEnum } from "@earendil-works/pi-ai/compat";
20
+ import { StringEnum, type ExtensionAPI, type ExtensionContext, type ToolExecutionMode } from "@bastani/atomic";
21
21
  import { Text } from "@earendil-works/pi-tui";
22
- import type { ExtensionAPI, ExtensionContext, ToolExecutionMode } from "@bastani/atomic";
23
22
  import { Type } from "typebox";
24
23
  import { buildTicTacToeInstructions } from "./tic-tac-toe-instructions.js";
25
24
  import { BannerMessageComponent, GameOverMessageComponent, TicTacToeComponent } from "./tic-tac-toe-rendering.js";
@@ -10,8 +10,7 @@
10
10
  * correct for that point in history.
11
11
  */
12
12
 
13
- import { StringEnum } from "@earendil-works/pi-ai/compat";
14
- import type { ExtensionAPI, ExtensionContext, Theme } from "@bastani/atomic";
13
+ import { StringEnum, type ExtensionAPI, type ExtensionContext, type Theme } from "@bastani/atomic";
15
14
  import { matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
16
15
  import { Type } from "typebox";
17
16