mlx-bun 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +91 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  8. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  9. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  10. package/docs/design/batching.md +569 -0
  11. package/docs/design/decode-speed-program.md +3178 -169
  12. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  13. package/docs/design/generic-model-support.md +579 -334
  14. package/docs/design/kv-cache.md +680 -0
  15. package/docs/design/orpo-training.md +673 -1239
  16. package/docs/design/speculative-decoding.md +1627 -0
  17. package/docs/design/turboquant.md +2102 -0
  18. package/docs/design/unified-engine-frontier-plan.md +1831 -727
  19. package/docs/design/web-chat-redesign.md +666 -1083
  20. package/docs/planning/PRODUCT_ROADMAP.md +34 -1
  21. package/docs/planning/release-notes-v0.3.0.md +35 -0
  22. package/docs/planning/video-series.md +2 -2
  23. package/docs/reference/benchmarks.md +1162 -0
  24. package/docs/reference/cli.md +394 -159
  25. package/docs/reference/distribution.md +321 -182
  26. package/docs/reference/environment.md +348 -0
  27. package/docs/reference/glossary.md +39 -0
  28. package/docs/reference/library-api.md +566 -168
  29. package/docs/reference/memory.md +227 -64
  30. package/docs/reference/models.md +104 -6
  31. package/docs/reference/server-api.md +886 -425
  32. package/docs/reference/server-config.md +638 -330
  33. package/docs/reference/training.md +393 -254
  34. package/docs/reference/troubleshooting.md +166 -0
  35. package/package.json +9 -4
  36. package/src/audio/decode.ts +2 -2
  37. package/src/audio/features.ts +2 -2
  38. package/src/backends/mlx/autoregressive.ts +108 -0
  39. package/src/backends/mlx/batch-group.ts +1646 -0
  40. package/src/backends/mlx/diffusion.ts +39 -0
  41. package/src/backends/mlx/draft-target.ts +47 -0
  42. package/src/backends/mlx/evaluation.ts +105 -0
  43. package/src/backends/mlx/fill-append.ts +45 -0
  44. package/src/backends/mlx/gateway-binding.ts +71 -0
  45. package/src/backends/mlx/graph.ts +38 -0
  46. package/src/backends/mlx/memory-client.ts +189 -0
  47. package/src/backends/mlx/memory-guard.ts +40 -0
  48. package/src/backends/mlx/methods.ts +90 -0
  49. package/src/backends/mlx/model-prompt.ts +164 -0
  50. package/src/backends/mlx/model-serving.ts +79 -0
  51. package/src/backends/mlx/prefill.ts +33 -0
  52. package/src/backends/mlx/request-policy.ts +20 -0
  53. package/src/backends/mlx/rollback.ts +51 -0
  54. package/src/backends/mlx/serial-executor.ts +273 -0
  55. package/src/backends/mlx/speculative.ts +114 -0
  56. package/src/backends/mlx/state-views.ts +26 -0
  57. package/src/backends/mlx/text-engine.ts +73 -0
  58. package/src/cli.ts +58 -25
  59. package/src/client.ts +66 -0
  60. package/src/config.ts +21 -4
  61. package/src/contracts/completion.ts +16 -0
  62. package/src/contracts/execution.ts +49 -0
  63. package/src/contracts/generation.ts +103 -0
  64. package/src/contracts/host.ts +7 -0
  65. package/src/contracts/jobs.ts +66 -0
  66. package/src/contracts/pi-provider.ts +30 -0
  67. package/src/contracts/pi.ts +259 -0
  68. package/src/contracts/resources.ts +18 -0
  69. package/src/contracts/scheduling.ts +27 -0
  70. package/src/contracts/tsconfig.json +14 -0
  71. package/src/diffusion/diffusion-generate.ts +218 -153
  72. package/src/embed.ts +1 -1
  73. package/src/engine/admission.ts +59 -0
  74. package/src/engine/cancellation.ts +38 -0
  75. package/src/engine/engine.ts +56 -0
  76. package/src/engine/execution-coordinator.ts +70 -0
  77. package/src/engine/execution-plan.ts +51 -0
  78. package/src/engine/resources.ts +42 -0
  79. package/src/engine/scheduler.ts +67 -0
  80. package/src/engine/session.ts +247 -0
  81. package/src/eval/README.md +44 -1
  82. package/src/eval/kl.ts +236 -3
  83. package/src/eval/reference-logits.ts +207 -0
  84. package/src/eval/runner.ts +8 -96
  85. package/src/fill/echo-index.ts +188 -0
  86. package/src/fill/fill-session.ts +452 -0
  87. package/src/fill/proposal.ts +68 -0
  88. package/src/fill/schema-rows.ts +506 -0
  89. package/src/fill/tool-boundary.ts +103 -0
  90. package/src/fit.ts +1 -1
  91. package/src/generate.ts +656 -268
  92. package/src/grammar.ts +12 -7
  93. package/src/harness-pi.ts +14 -33
  94. package/src/hub-rest.ts +4 -4
  95. package/src/index.ts +7 -0
  96. package/src/inference/denoising.ts +17 -0
  97. package/src/inference/graph.ts +33 -0
  98. package/src/inference/prefill.ts +39 -0
  99. package/src/inference/rollback.ts +7 -0
  100. package/src/jobs/db.ts +4 -0
  101. package/src/jobs/index.ts +2 -0
  102. package/src/jobs/job-entry.ts +12 -12
  103. package/src/jobs/runner.ts +98 -13
  104. package/src/jobs/task-client.ts +19 -0
  105. package/src/jobs/types.ts +2 -58
  106. package/src/kv-store.ts +109 -49
  107. package/src/lab/README.md +18 -0
  108. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  109. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  110. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  111. package/src/{model → lab/paged-kv}/paged-kv.ts +9 -6
  112. package/src/library.ts +40 -0
  113. package/src/lora.ts +18 -0
  114. package/src/media-fetch.ts +1 -1
  115. package/src/memory/article.ts +1 -1
  116. package/src/memory/chunk.ts +1 -1
  117. package/src/memory/model.ts +30 -169
  118. package/src/memory/prompts.ts +1 -1
  119. package/src/memory/rest.ts +1 -1
  120. package/src/memory/schedule.ts +1 -1
  121. package/src/memory/stages.ts +4 -1
  122. package/src/memory/vault.ts +1 -3
  123. package/src/mlx/array.ts +9 -10
  124. package/src/mlx/compile.ts +12 -2
  125. package/src/mlx/ffi.ts +58 -11
  126. package/src/mlx/materialize.ts +23 -0
  127. package/src/mlx/metal-kernel.ts +10 -11
  128. package/src/mlx/ops.ts +33 -13
  129. package/src/mlx/quantized-rows.ts +36 -0
  130. package/src/mlx/token-bitmask.ts +52 -0
  131. package/src/mlx/turboquant-kv-decode.ts +82 -0
  132. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  133. package/src/mlx/turboquant-ops.ts +33 -3
  134. package/src/mlx/turboquant-tables.ts +2 -2
  135. package/src/model/batched-mask.ts +8 -2
  136. package/src/model/batched-quant.ts +1 -1
  137. package/src/model/batched-rotating-quant.ts +2 -2
  138. package/src/model/batched-rotating.ts +1 -1
  139. package/src/model/compiled-decode.ts +1 -1
  140. package/src/model/diffusion-gemma.ts +8 -8
  141. package/src/model/factory.ts +76 -33
  142. package/src/model/fingerprint.ts +1 -1
  143. package/src/model/flash-attention.ts +3 -3
  144. package/src/model/gemma4-base.ts +26 -9
  145. package/src/model/gemma4.ts +6 -6
  146. package/src/model/generated/gemma4-12b.ts +2 -2
  147. package/src/model/generated/gemma4-26b.ts +2 -2
  148. package/src/model/generated/gemma4-e4b.ts +2 -2
  149. package/src/model/glm52-dsa.ts +13 -9
  150. package/src/model/glm52-memory.ts +1 -1
  151. package/src/model/implementation.ts +69 -0
  152. package/src/model/minicpm5.ts +6 -8
  153. package/src/model/profile.ts +12 -2
  154. package/src/model/qwen-append.ts +12 -0
  155. package/src/model/qwen-conv.ts +52 -0
  156. package/src/model/qwen3_5-checkpoint.ts +164 -0
  157. package/src/model/qwen3_5.ts +140 -56
  158. package/src/model/support.ts +1 -1
  159. package/src/model/trellis-balanced-scatter.ts +51 -0
  160. package/src/model/trellis-linear.ts +672 -0
  161. package/src/model/trellis-shared-m.ts +151 -0
  162. package/src/model/trellis-shared-scatter.ts +144 -0
  163. package/src/model/trellis-splitk-prefill.ts +158 -0
  164. package/src/model/trellis-tiled-prefill.ts +136 -0
  165. package/src/model/trellis-vector-expand.ts +62 -0
  166. package/src/model/trellis-wide-prefill.ts +94 -0
  167. package/src/model/universal/dense.ts +1 -1
  168. package/src/model/universal/modules.ts +8 -1
  169. package/src/native-pack.ts +6 -5
  170. package/src/pi-provider.ts +6 -64
  171. package/src/pi-web.ts +8 -315
  172. package/src/prompt-cache.ts +96 -53
  173. package/src/quantize/allocator.ts +1 -1
  174. package/src/quantize/config-writer.ts +10 -3
  175. package/src/quantize/rotate.ts +4 -4
  176. package/src/quantize/trellis.ts +586 -0
  177. package/src/responses.ts +28 -1
  178. package/src/runtime-config.ts +18 -4
  179. package/src/sampler.ts +4 -4
  180. package/src/serve/batch-scheduler.ts +8 -1671
  181. package/src/serve/chat-request.ts +391 -0
  182. package/src/serve/chat-stage.ts +195 -0
  183. package/src/serve/checkpoint-identity.ts +64 -0
  184. package/src/serve/completion-executor.ts +15 -7
  185. package/src/serve/discovery-routes.ts +12 -13
  186. package/src/serve/generation-gateway.ts +154 -193
  187. package/src/serve/http.ts +159 -0
  188. package/src/serve/inference-request.ts +79 -0
  189. package/src/serve/isolate.ts +297 -105
  190. package/src/serve/lab-routes.ts +3 -0
  191. package/src/serve/lane-registry.ts +2 -1
  192. package/src/serve/model-admin-routes.ts +7 -5
  193. package/src/serve/model-binding.ts +43 -0
  194. package/src/serve/model-host.ts +512 -0
  195. package/src/serve/openai-wire.ts +103 -0
  196. package/src/serve/parent-application.ts +56 -0
  197. package/src/serve/pipeline.ts +33 -0
  198. package/src/serve/preparation.ts +33 -0
  199. package/src/serve/request-plan.ts +19 -15
  200. package/src/serve/request-prep.ts +346 -0
  201. package/src/serve/response-lease.ts +24 -0
  202. package/src/serve/responses-client.ts +69 -0
  203. package/src/serve/session-completion-engine.ts +110 -0
  204. package/src/serve/session-search.ts +1 -1
  205. package/src/serve/static-routes.ts +5 -5
  206. package/src/serve/text-completion-stage.ts +104 -0
  207. package/src/serve/token-streams.ts +519 -0
  208. package/src/server.ts +317 -2649
  209. package/src/spec/assistant-source.ts +10 -44
  210. package/src/spec/deepspec-source.ts +6 -6
  211. package/src/spec/dflash-source.ts +8 -8
  212. package/src/spec/dspark/ab-stats.ts +3 -3
  213. package/src/spec/dspark/calibration.ts +2 -2
  214. package/src/spec/dspark/deepspec-module.ts +2 -2
  215. package/src/spec/dspark/generate-dflash.ts +2 -2
  216. package/src/spec/dspark/generate.ts +2 -2
  217. package/src/spec/dspark/loader.ts +1 -1
  218. package/src/spec/dspark/module-dflash.ts +4 -3
  219. package/src/spec/dspark/module.ts +3 -3
  220. package/src/spec/dspark/quantize-drafter.ts +1 -1
  221. package/src/spec/generate.ts +1 -1
  222. package/src/spec/glm52-mtp-source.ts +1 -1
  223. package/src/spec/ngram-source.ts +1 -1
  224. package/src/spec/prefix-state.ts +45 -0
  225. package/src/spec/qwen-mtp-source.ts +166 -69
  226. package/src/spec/serve-loop.ts +154 -156
  227. package/src/spec/source.ts +42 -10
  228. package/src/spec/two-model.ts +1 -1
  229. package/src/ssd-cache.ts +80 -12
  230. package/src/ssd-durability.ts +25 -13
  231. package/src/tool-call.ts +51 -14
  232. package/src/train/flash-cce.ts +2 -2
  233. package/src/train/forward.ts +6 -0
  234. package/src/train/loss.ts +1 -1
  235. package/src/train/prefix-shared.ts +5 -1
  236. package/src/train/segmented.ts +5 -3
  237. package/src/train/trainer.ts +6 -6
  238. package/src/vision/prompt.ts +1 -1
  239. package/src/vision/qwen3vl-tower.ts +52 -10
  240. package/src/web/app.html +1 -1
  241. package/src/web/app.js +5 -3
  242. package/src/web/src/adapters-panel.ts +3 -3
  243. package/src/web/src/assistant.ts +2 -36
  244. package/src/web/src/chat.ts +4 -4
  245. package/src/web/src/composer.ts +4 -4
  246. package/src/web/src/hub.ts +1 -1
  247. package/src/web/src/markdown.ts +0 -0
  248. package/src/web/src/memory-panel.ts +1 -1
  249. package/src/web/src/model-picker.ts +1 -1
  250. package/src/web/src/palette.ts +1 -1
  251. package/src/web/src/protocol.ts +3 -39
  252. package/src/web/src/sessions.ts +1 -1
  253. package/src/web/src/ui-catalog.ts +4 -18
  254. package/src/web-assets.ts +47 -0
  255. package/src/weights.ts +55 -9
  256. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  257. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  258. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  259. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  260. package/docs/archive/investigations/hlg-ab.html +0 -34
  261. package/docs/archive/investigations/hlg-explorer.html +0 -238
  262. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  263. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  264. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  265. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  266. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  267. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  268. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  269. package/docs/archive/investigations/hlg-report.html +0 -262
  270. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  271. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  272. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  273. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  274. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  275. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  276. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  277. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  278. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  279. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  280. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  281. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  282. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  283. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  284. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  285. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  286. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  287. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  288. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  289. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  290. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  291. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  292. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  293. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  294. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  295. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  296. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  297. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  298. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  299. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  300. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  301. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  302. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  303. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  304. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  305. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  306. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  307. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  308. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  309. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  310. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  311. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  312. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  313. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  314. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  315. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  316. package/docs/archive/mlx-bun-lab-report.html +0 -275
  317. package/docs/archive/wwdc-mlx-bun.html +0 -848
  318. package/docs/design/adapters-end-to-end.md +0 -205
  319. package/docs/design/batching-perf-path.md +0 -255
  320. package/docs/design/batching-v2-plan.md +0 -529
  321. package/docs/design/bucketing-stage.md +0 -197
  322. package/docs/design/compat-cli-surface-design.md +0 -230
  323. package/docs/design/docs-reorg-plan.md +0 -165
  324. package/docs/design/dspark-serving-program.md +0 -371
  325. package/docs/design/dspark-speculative-decoding.md +0 -318
  326. package/docs/design/grammar-spec-batching-integration.md +0 -301
  327. package/docs/design/memory-inference-path.md +0 -422
  328. package/docs/design/memory-synthesis.md +0 -287
  329. package/docs/design/memory-system.md +0 -237
  330. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  331. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  332. package/docs/design/omlx-adoption-map.md +0 -98
  333. package/docs/design/orpo-dynamic-lambda.md +0 -220
  334. package/docs/design/orpo-future-enhancements.md +0 -340
  335. package/docs/design/paged-kv-cache.md +0 -214
  336. package/docs/design/parallel-slots.md +0 -567
  337. package/docs/design/parity-tier-dag.md +0 -161
  338. package/docs/design/repo-cleanup-plan.md +0 -231
  339. package/docs/design/runtime-isolation.md +0 -263
  340. package/docs/design/segmented-backward-training.md +0 -543
  341. package/docs/design/spec-decode-larger-targets.md +0 -229
  342. package/docs/design/ssd-kv-cold-tier.md +0 -415
  343. package/docs/design/structured-output.md +0 -484
  344. package/docs/design/tauri-desktop-app.md +0 -127
  345. package/docs/design/the-dreaming-master-plan.md +0 -445
  346. package/docs/design/turboquant-kv.md +0 -235
  347. package/docs/design/turboquant-weights.md +0 -467
  348. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  349. package/docs/design/web-chat-beat-matrix.md +0 -315
  350. package/docs/design/write-pipeline-entity.md +0 -468
  351. package/docs/planning/IDEAS.md +0 -69
  352. package/docs/reference/embedding.md +0 -197
  353. package/docs/reference/features-matrix.md +0 -101
  354. package/docs/reference/orpo-quickstart.md +0 -135
  355. package/src/flags.ts +0 -11
  356. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  357. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  358. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  359. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  360. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  361. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  362. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  363. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  364. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -1,209 +1,348 @@
1
- # Distributing mlx-bun via Homebrew (signed + notarized)
2
-
3
- The end-to-end runbook for shipping a `brew install`-able, Developer-ID
4
- signed and notarized mlx-bun. Companion to [embedding.md](./embedding.md)
5
- (which covers the Tauri/Electron *sidecar* layout); this doc is the
6
- *standalone CLI* distribution.
7
-
8
- ## What ships
9
-
10
- A single **self-contained** bundle — the whole `dist/` directory in one
11
- tarball (~73 MB compressed, ~228 MB installed):
12
-
13
- | file | what |
14
- |---|---|
15
- | `mlx-bun` | the CLI/server (`bun build --compile`), ~61 MB |
16
- | `libmlxc.dylib`, `libmlx.dylib`, `libjaccl.dylib` | MLX native runtime |
17
- | `libmlx_bun_expert_io.dylib` | bounded native expert-streaming helper required by direct Colibri GLM-5.2 |
18
- | `mlx-bun-frame-extract` | AVFoundation frame-extraction sidecar for video input (darwin-native codecs, the afconvert doctrine) |
19
- | `mlx.metallib` | Metal kernels (~150 MB — the bulk) |
20
- | pi assets | `photon_rs_bg.wasm`, `theme/`, `assets/`, `export-html/`, `native/`, `package.json`, `CHANGELOG.md` |
21
-
22
- We ship everything in the bottle (rather than the npm/sidecar approach of
23
- a tiny binary that downloads the native pack on first run) so that
24
- `brew install` is the *only* network step and the entire bundle is signed
25
- and notarized as one artifact.
26
-
27
- ## One-time Apple setup
28
-
29
- 1. **Developer ID Application certificate** — Xcode → Settings → Accounts →
1
+ # Distributing mlx-bun — build, sign, notarize, publish
2
+
3
+ The canonical ship doc. Everything here is what the scripts do, not a
4
+ recipe to type by hand: [scripts/build-binary.sh](../../scripts/build-binary.sh)
5
+ builds the bundle, [scripts/release-binary.sh](../../scripts/release-binary.sh)
6
+ signs and notarizes it, [scripts/publish-release.sh](../../scripts/publish-release.sh)
7
+ pushes it to every channel. If this doc and a script disagree, the script
8
+ is right and this doc has drifted.
9
+
10
+ Three install channels ship from one release:
11
+
12
+ | channel | what the user gets | network step |
13
+ |---|---|---|
14
+ | Homebrew (`brew install joshuarossi/tap/mlx-bun`) | the signed + notarized self-contained bundle | one tarball, via brew |
15
+ | Direct download (`curl -fsSL https://mlx-bun.dev/install.sh \| sh`) | the same bundle, into `~/.mlx-bun` | one tarball, via curl |
16
+ | npm / bunx (`bunx mlx-bun`, `bun install -g mlx-bun`) | a launcher + the TypeScript source, run under the user's Bun | the package, then the native runtime pack on first run |
17
+
18
+ [docs/reference/distribution.md](./distribution.md) covers the *sidecar* use of the same bundle
19
+ inside a Tauri/Electron app; the build and signing facts it relies on are
20
+ documented here.
21
+
22
+ ## What ships in the bundle
23
+
24
+ `scripts/build-binary.sh [outdir]` (default `dist/`) produces one relocatable
25
+ directory. The release tarball is the directory's *contents* (no parent
26
+ dir), ~80 MB compressed; `mlx.metallib` is the bulk of the installed size.
27
+
28
+ | file | what | built from |
29
+ |---|---|---|
30
+ | `mlx-bun` | the CLI/server, `bun build --compile src/cli.ts` | our code + Bun |
31
+ | `libmlxc.dylib` | mlx-c; load command rewritten to `@loader_path/libmlx.dylib` | `/opt/homebrew/lib/libmlxc.dylib` (override `MLXC_DYLIB`) |
32
+ | `libmlx.dylib`, `libjaccl.dylib`, `mlx.metallib` | MLX core, its distributed-comm dependency, the Metal kernels (libmlx loads the metallib from its own directory) | `/opt/homebrew/opt/mlx/lib` (override `MLX_LIB_DIR`) |
33
+ | `libmlx_bun_expert_io.dylib` | bounded streamed-expert I/O for GLM-5.2 MoE serving | [scripts/build-expert-io.sh](../../scripts/build-expert-io.sh) |
34
+ | `mlx-bun-frame-extract` | AVFoundation frame-extraction helper for video input (a Mach-O *executable*, not a dylib) | [scripts/build-frame-extract.sh](../../scripts/build-frame-extract.sh) → `swiftc -O src/native/frame_extract.swift` |
35
+ | `photon_rs_bg.wasm` | pi's image codec, resolved next to the executable by the web chat's `read`-on-image path (best-effort; pi degrades to a text note without it) | `node_modules/@silvia-odwyer/photon-node` (override `PHOTON_WASM`) |
36
+ | `theme/*.json`, `assets/*.png`, `export-html/`, `package.json`, `CHANGELOG.md`, `native/darwin/prebuilds/<arch>/darwin-modifiers.node` | assets the embedded pi **terminal** (`mlx-bun pi`) resolves by path: themes, startup art, `/export` template, version banner + changelog, native modifier-key helper (degrades if absent) | `node_modules/@earendil-works/pi-coding-agent/dist`, `pi-tui` |
37
+
38
+ pi's JavaScript itself is bundled into the executable by `bun build
39
+ --compile`; only what pi resolves *by path* rides along. For a Bun
40
+ single-file executable pi computes its package dir as
41
+ `dirname(process.execPath)`, so every asset sits flat next to `mlx-bun`.
42
+
43
+ After copying, the script runs `install_name_tool` (`libmlxc` →
44
+ `@loader_path/libmlx.dylib`; `@loader_path` rpath on `libmlx` for `libjaccl`)
45
+ and **ad-hoc signs** every Mach-O it touched — arm64 requires a valid
46
+ signature after any load-command rewrite. Ad-hoc is only for local runs; the
47
+ release script replaces it (below).
48
+
49
+ The C and Swift helpers target macOS 14.0 by default, matching the package's
50
+ minimum macOS version. `MACOSX_DEPLOYMENT_TARGET` can override that build
51
+ target. Verify the MLX libraries' deployment targets as well when replacing
52
+ the bundled runtime; compiling our helpers for 14.0 cannot lower a dependency's
53
+ minimum OS version.
54
+
55
+ **Build-time smoke.** The script then runs `mlx-bun --version`, `--help`, and
56
+ `ls`, and compiles [scripts/verify-binary-pi.ts](../../scripts/verify-binary-pi.ts)
57
+ into a *sibling* binary inside the same directory so `process.execPath` points
58
+ at the bundle exactly as it does for the real binary. That smoke builds the
59
+ same headless pi session `src/pi-web.ts` builds (against an unreachable
60
+ provider — a provider error is the success signal), exercises the photon
61
+ image path, calls `initTheme` (load-bearing for `mlx-bun pi`; a failure here
62
+ fails the smoke), and loads the native modifier helper. No model, no server.
63
+ A live streaming turn is the one thing it cannot cover.
64
+
65
+ **Runtime resolution.** The binary finds its native runtime in this order
66
+ (`src/mlx/ffi.ts`, mirrored by `nativeRuntimeDir()` in `src/native-pack.ts`):
67
+ `MLX_BUN_LIBMLXC` env → `libmlxc.dylib` beside the executable → the
68
+ native-pack cache (`~/Library/Caches/mlx-bun/native-v<ver>-<arch>/`) →
69
+ `/opt/homebrew/lib` → `/usr/local/lib`. The expert helper resolves
70
+ `MLX_BUN_EXPERT_IO_DYLIB` → beside the executable → the cache
71
+ (`src/expert-io.ts`); the frame extractor resolves `MLX_BUN_FRAME_EXTRACT` →
72
+ beside the executable → the cache → `dist-native/` → compile-on-demand from
73
+ `src/native/` via the Xcode CLT (`src/vision/video-frames.ts`). The whole
74
+ directory can be moved or renamed; nothing references an absolute path after
75
+ the fixups. Bun realpaths `execPath`, so a symlink to `mlx-bun` (Homebrew's
76
+ `bin/`, the installer's `~/.local/bin`) resolves back to the bundle.
77
+
78
+ ## One-time machine setup
79
+
80
+ Releases are inherently local: signing and notarization need the Developer
81
+ ID certificate and Apple credentials on this Mac, so the publish step is
82
+ local too (no cross-repo CI token, nothing for the tap to drift from). The
83
+ release machine needs:
84
+
85
+ 1. **Build inputs** — Homebrew `mlx` + `mlx-c` (the bundle copies the dylibs
86
+ and metallib from the brew prefixes above), the Xcode command-line tools
87
+ (`swiftc` for the frame extractor, the C toolchain for the expert helper),
88
+ and Bun at or above the floor in `package.json` `engines.bun`.
89
+ 2. **Developer ID Application certificate** — Xcode → Settings → Accounts →
30
90
  your Apple ID → *Manage Certificates…* → **+** → **Developer ID
31
- Application**. Verify:
32
- ```sh
33
- security find-identity -v -p codesigning # shows "Developer ID Application: NAME (TEAMID)"
34
- ```
35
- 2. **Notary credentials** — an App Store Connect **API key** (not the
36
- Developer-portal "Keys" page; not your Apple ID password). Create at
37
- **App Store Connect → Users and Access → Integrations → App Store
38
- Connect API → Team Keys → +** (role: Developer). Download the
39
- `AuthKey_<KEYID>.p8` (one-time download), note the **Key ID** and the
40
- **Issuer ID** (UUID at the top of the page), then:
91
+ Application**. The release script picks the first such identity from
92
+ `security find-identity -v -p codesigning`; override with
93
+ `MLX_BUN_SIGN_IDENTITY`.
94
+ 3. **Notary credentials** — an App Store Connect **API key** (not your Apple
95
+ ID password): App Store Connect → Users and Access → Integrations → App
96
+ Store Connect API → Team Keys → **+** (role: Developer). Download the
97
+ `AuthKey_<KEYID>.p8` (one-time download), note the Key ID and Issuer ID,
98
+ then store them under the profile name the script expects:
41
99
  ```sh
42
100
  xcrun notarytool store-credentials AC_PROFILE \
43
101
  --key ~/.private_keys/AuthKey_<KEYID>.p8 \
44
102
  --key-id "<KEYID>" --issuer "<ISSUER-UUID>"
45
103
  ```
46
- `store-credentials` validates against Apple before saving — success
47
- means notarization auth works.
48
-
49
- ## Release a build
50
-
51
- ```sh
52
- ./scripts/release-binary.sh # version from package.json
53
- # or: ./scripts/release-binary.sh 0.0.4
54
- ```
55
-
56
- This runs [build-binary.sh](../../scripts/build-binary.sh), then:
57
- - signs every nested Mach-O (dylibs, any `.node`) with the hardened
58
- runtime, then the executable with the JIT
59
- [entitlements](../../packaging/entitlements.plist) (JavaScriptCore JITs —
60
- without `allow-jit` the binary is killed on launch);
61
- - notarizes the zipped bundle via `AC_PROFILE` and waits;
62
- - emits `dist-release/mlx-bun-v<ver>-arm64.tar.gz` (+ `.sha256`) and
63
- prints the `version`/`url`/`sha256` for the formula.
64
-
65
- > A flat CLI bundle **cannot be stapled** (`stapler` only handles
66
- > `.app`/`.dmg`/`.pkg`); the notarization ticket lives on Apple's servers.
67
- > Gatekeeper checks it online only when a copy is *quarantined*. brew
68
- > formula installs are **not** quarantined (brew downloads via curl), so
69
- > the binary runs prompt-free regardless — notarization is load-bearing
70
- > only for a direct browser download or a Cask.
71
-
72
- Publish the artifact as a GitHub release whose tag matches the formula url:
73
- ```sh
74
- gh release create v0.0.4 dist-release/mlx-bun-v0.0.4-arm64.tar.gz \
75
- --title "mlx-bun v0.0.4" --notes "..."
76
- ```
77
-
78
- ## The tap
79
-
80
- Homebrew taps must be a repo named `homebrew-<name>`. The canonical tap is
81
- **`joshuarossi/homebrew-tap`** (Homebrew shorthand: `joshuarossi/tap`).
82
-
83
- 1. Create a public repo **`joshuarossi/homebrew-tap`** (one-time).
84
- 2. Copy [packaging/homebrew/mlx-bun.rb](../../packaging/homebrew/mlx-bun.rb)
85
- to `Formula/mlx-bun.rb` in it, updating `version`/`url`/`sha256` from
86
- the release-script output. Commit and push.
87
- 3. Install:
88
- ```sh
89
- brew install joshuarossi/tap/mlx-bun
90
- mlx-bun --version
91
- ```
92
-
93
- `brew audit --strict --new joshuarossi/tap/mlx-bun` before pushing
94
- catches most formula issues; `brew install --build-from-source` +
95
- `brew test` runs the formula's `test do` locally.
96
-
97
- ## Updating (one command)
98
-
99
- Releases must originate on this Mac (signing + notarization need the
100
- Developer ID cert + Apple creds), so the publish step is local too — no
101
- cross-repo CI token to manage, and the tap can't drift out of sync.
102
-
103
- Per release:
104
-
105
- 1. bump `version` in `package.json` and commit;
106
- 2. one shot:
107
- ```sh
108
- PUBLISH=1 ./scripts/release-binary.sh
109
- ```
110
- This builds → signs → notarizes → creates the `v<ver>` GitHub release →
111
- rewrites `version`/`url`/`sha256` in the tap's `Formula/mlx-bun.rb` and
112
- pushes it → mirrors the same fields into
113
- [packaging/homebrew/mlx-bun.rb](../../packaging/homebrew/mlx-bun.rb)
114
- (left staged — commit it).
115
-
116
- Prefer two steps? Run `./scripts/release-binary.sh` to build, eyeball the
117
- artifact, then `./scripts/publish-release.sh` to publish + sync the tap.
118
- Both are idempotent: re-running `publish-release.sh` clobbers the release
119
- asset and no-ops the tap push if nothing changed.
120
-
121
- Users then upgrade with:
122
- ```sh
123
- brew upgrade joshuarossi/tap/mlx-bun
124
- ```
125
-
126
- ## Direct download (stable URL)
104
+ `store-credentials` validates against Apple before saving. Override the
105
+ profile name with `NOTARY_PROFILE`.
106
+ 4. **GitHub** — `gh auth login` with push access to `joshuarossi/mlx-bun`
107
+ (releases) and `joshuarossi/homebrew-tap` (the tap is cloned over https
108
+ and pushed).
109
+ 5. **npm** — a publish token for `mlx-bun` (`.npmrc`; `npm whoami` must
110
+ answer).
111
+ 6. **The tap repo** — `joshuarossi/homebrew-tap` (Homebrew shorthand
112
+ `joshuarossi/tap`) must already contain `Formula/mlx-bun.rb`; the publish
113
+ script rewrites three fields in it and refuses to run if the file is
114
+ missing.
115
+
116
+ ## Per-release flow
117
+
118
+ Exactly what `bun run release` (= `PUBLISH=1 sh scripts/release-binary.sh`)
119
+ does, in order. Steps 1–3 are yours; the rest is the scripts.
120
+
121
+ 1. **Write the notes** at `docs/planning/release-notes-v<ver>.md`. The
122
+ publish script uses it as the GitHub release body (`--notes-file`); if it
123
+ is missing the release is created with a bare title and a warning. After
124
+ the release the file is archived to `docs/archive/planning/` (the
125
+ planning directory holds living docs only).
126
+ 2. **Bump `version` in `package.json`** and commit.
127
+ 3. **Push** so `HEAD == origin/main`. The publish preflight refuses a dirty
128
+ tree or a local HEAD that differs from `origin/main`: the site deploys from
129
+ the push while the binaries are built from the local tree, and the two
130
+ must tell the same story. `RELEASE_SKIP_GIT_CHECK=1` bypasses this for an
131
+ assets-only re-run.
132
+ 4. **Build** — `scripts/build-binary.sh dist` as described above
133
+ (`BUILD_DIR` overrides `dist`). Release builds are arm64-only; the script
134
+ exits on any other host arch.
135
+ 5. **Sign nested code first** — every `*.dylib`, `*.node`, and the
136
+ `mlx-bun-frame-extract` helper, matched by name under the build dir, gets
137
+ `codesign --force --timestamp --options runtime -s "<identity>"`.
138
+ Nested-before-container is required. The metallib, wasm, JSON, and PNG
139
+ assets are data, not code, and are skipped.
140
+ 6. **Sign the executable** with the hardened runtime and
141
+ [scripts/packaging/entitlements.plist](../../scripts/packaging/entitlements.plist):
142
+ - `allow-jit` + `allow-unsigned-executable-memory` — JavaScriptCore JITs;
143
+ under the hardened runtime the binary is killed on launch without them;
144
+ - `disable-library-validation` — required for the Homebrew install.
145
+ brew ad-hoc re-signs bundled dylibs during its relocation pass,
146
+ stripping our team ID off them while leaving the executable
147
+ Developer-ID signed; library validation would then reject the team-ID
148
+ mismatch at `dlopen`. Notarization still passes with it.
149
+ 7. **Verify + smoke** — `codesign --verify --strict`, then
150
+ `dist/mlx-bun --version`. A missing entitlement kills the binary here, not
151
+ in a user's `brew install`.
152
+ 8. **Notarize** — `ditto` the bundle into a zip, `xcrun notarytool submit
153
+ --wait` under the notary profile (Apple's automated scan, typically 1–5
154
+ min), delete the zip. The script greps the output for
155
+ `status: Accepted` and **exits non-zero on anything else**, printing the
156
+ `xcrun notarytool log <submission-id>` command to inspect the rejection.
157
+ `notarytool` itself exits 0 on `Invalid`, so without this gate a rejected
158
+ bundle would reach PUBLISH.
159
+ 9. **Package** — `tar -czf dist-release/mlx-bun-v<ver>-arm64.tar.gz` of the
160
+ bundle contents (only the files that exist from the fixed manifest:
161
+ binary, four dylibs, helper, metallib, wasm, pi assets), plus a `.sha256`
162
+ sidecar. The script prints the `version` / `url` / `sha256` triple for
163
+ the formula. (`OUT_DIR` overrides `dist-release`.) Without `PUBLISH=1` it
164
+ stops here and tells you to run `scripts/publish-release.sh`.
165
+ 10. **GitHub release** (`publish-release.sh`, step 1) — copies the tarball to
166
+ a versionless `mlx-bun-arm64.tar.gz`, then `gh release create v<ver>`
167
+ with both assets and the notes file; if the release already exists the
168
+ assets are re-uploaded with `--clobber` instead.
169
+ 11. **Tap push** (step 2) — clones `joshuarossi/homebrew-tap`, rewrites
170
+ `version` / `url` / `sha256` in `Formula/mlx-bun.rb` with `sed`, commits
171
+ and pushes only if the diff is non-empty.
172
+ 12. **Mirror the formula** (step 3) — the same three fields are rewritten in
173
+ the in-repo source of truth,
174
+ [scripts/packaging/homebrew/mlx-bun.rb](../../scripts/packaging/homebrew/mlx-bun.rb),
175
+ and left **uncommitted** for you.
176
+ 13. **npm publish** (step 4) — `npm view mlx-bun@<ver>`; if that version is
177
+ already live the step is skipped, otherwise `bun publish`. On failure the
178
+ script exits with the retry hint `bun run publish:npm`.
179
+ 14. **Commit the mirrored formula** — the script's closing line:
180
+ `git commit -am "chore(dist): mlx-bun <ver>"`.
181
+
182
+ Two-step variant: run `./scripts/release-binary.sh` alone, eyeball
183
+ `dist-release/`, then `./scripts/publish-release.sh`. Both are idempotent —
184
+ re-running publish clobbers the release assets, no-ops the tap push, and skips
185
+ npm if the version is live. Env overrides: `MLX_BUN_SIGN_IDENTITY`,
186
+ `NOTARY_PROFILE`, `BUILD_DIR`, `OUT_DIR` (release); `OUT_DIR`, `REPO`,
187
+ `TAP_REPO` (publish).
188
+
189
+ ### The notarization lesson (v0.2.0, 2026-08-24)
190
+
191
+ The first v0.2.0 submission came back **Invalid**. The release added
192
+ `mlx-bun-frame-extract`, a Swift executable, to the bundle; the signing step
193
+ matched only `*.dylib` and `*.node`, so the helper reached Apple with the
194
+ build script's ad-hoc signature — no Developer ID, no hardened runtime, no
195
+ timestamp — and notarization rejects any nested Mach-O signed that way. The
196
+ old script also treated `notarytool`'s exit 0 as success, which would have
197
+ let the rejected bundle go on to publish.
198
+
199
+ Two changes, both in `scripts/release-binary.sh`: helper executables are
200
+ matched **by name** in the nested-signing pass (add every new helper there
201
+ when it lands — the manifest in the packaging step is the checklist), and
202
+ the script fails on any status other than `Accepted`. The resubmission was
203
+ accepted and the release shipped the same day.
204
+
205
+ ### Stapling, quarantine, and where notarization matters
206
+
207
+ A flat CLI bundle **cannot be stapled** (`stapler` only handles `.app` /
208
+ `.dmg` / `.pkg`); the notarization ticket lives on Apple's servers, and
209
+ Gatekeeper consults it online only when a copy carries the
210
+ `com.apple.quarantine` xattr. Homebrew's download and the installer's
211
+ `curl` do not set that xattr, so both run prompt-free regardless;
212
+ notarization is load-bearing for a browser-downloaded copy (or a future
213
+ Cask). `xattr -d com.apple.quarantine <file>` is the manual escape hatch for
214
+ a quarantined copy on an offline machine.
215
+
216
+ ## Channel: Homebrew
217
+
218
+ `brew install joshuarossi/tap/mlx-bun`; upgrades with
219
+ `brew upgrade joshuarossi/tap/mlx-bun`. The formula
220
+ ([scripts/packaging/homebrew/mlx-bun.rb](../../scripts/packaging/homebrew/mlx-bun.rb),
221
+ mirrored to the tap by the publish script):
222
+
223
+ - `depends_on arch: :arm64`, `depends_on macos: :sonoma` — bump the macOS
224
+ floor if MLX needs newer;
225
+ - `libexec.install Dir["*"]` then `bin.install_symlink libexec/"mlx-bun"` —
226
+ the whole bundle lands in `libexec`, and the dylibs and pi assets resolve
227
+ next to the executable through Bun's realpath of `execPath`;
228
+ - `test do` asserts `mlx-bun --version` prints the formula version.
229
+
230
+ The formula deliberately ships everything in one tarball rather than the
231
+ npm approach of a small package plus a first-run download, so `brew install`
232
+ is the only network step and the entire bundle is one signed, notarized
233
+ artifact. Before pushing a formula change by hand, `brew audit --strict --new
234
+ joshuarossi/tap/mlx-bun` catches most issues and `brew install
235
+ --build-from-source` + `brew test` runs the `test do` block locally.
236
+
237
+ ## Channel: direct download
127
238
 
128
239
  `publish-release.sh` uploads two assets per release: the versioned tarball
129
- (`mlx-bun-v<ver>-arm64.tar.gz`) **and** a versionless stable copy
130
- (`mlx-bun-arm64.tar.gz`) pinned to `releases/latest`. This lets the
131
- install script target a URL that never changes:
240
+ and a versionless `mlx-bun-arm64.tar.gz`, so `releases/latest` has a URL
241
+ that never changes:
132
242
 
133
243
  ```
134
244
  https://github.com/joshuarossi/mlx-bun/releases/latest/download/mlx-bun-arm64.tar.gz
135
245
  ```
136
246
 
137
- The user-facing install path is the script at
138
- [`website/public/install.sh`](../../website/public/install.sh) (served at
139
- `mlx-bun.dev/install.sh`), which resolves that URL, extracts the bundle, and
140
- links `mlx-bun` onto `PATH`:
141
- ```sh
142
- curl -fsSL https://mlx-bun.dev/install.sh | sh
143
- ```
247
+ The user-facing path is [website/public/install.sh](../../website/public/install.sh)
248
+ (served at `mlx-bun.dev/install.sh`):
144
249
 
145
- Without the script, the same stable URL streams straight to `tar`:
146
250
  ```sh
147
- curl -fsSL https://github.com/joshuarossi/mlx-bun/releases/latest/download/mlx-bun-arm64.tar.gz \
148
- | tar -xz -C /usr/local/bin mlx-bun
251
+ curl -fsSL https://mlx-bun.dev/install.sh | sh
149
252
  ```
150
253
 
151
- Note: **browser downloads and `curl`-to-file** (e.g. `curl -O ...`) are
152
- quarantined by Gatekeeper — macOS sets the `com.apple.quarantine` xattr on
153
- the saved file. The piped one-liner above streams directly to `tar` and
154
- never writes a quarantined file, so it runs without any Gatekeeper prompt.
155
- For the browser/curl-to-file path, the notarization ticket resolves the
156
- Gatekeeper check online; `xattr -d com.apple.quarantine mlx-bun` is the
157
- manual escape hatch if offline.
254
+ It checks for Darwin/arm64, downloads that URL (or a pinned tag via
255
+ `MLX_BUN_VERSION=v<ver>`), extracts the **whole** bundle into
256
+ `MLX_BUN_INSTALL_DIR` (default `~/.mlx-bun`), and symlinks `mlx-bun` into the
257
+ first writable of `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin` —
258
+ or prints the `PATH` line to add. Extract into a directory, never just the
259
+ `mlx-bun` file: the executable alone would fall through to the native-pack
260
+ download on first run and lose the pi terminal's assets.
158
261
 
159
- ## npm / bunx
160
-
161
- mlx-bun is published to npm as `mlx-bun`. Install or run without a
162
- permanent install:
262
+ ## Channel: npm / bunx
163
263
 
164
264
  ```sh
165
- # one-off (no install)
166
- bunx mlx-bun
167
-
168
- # permanent global install
169
- bun install -g mlx-bun
265
+ bunx mlx-bun # one-off, no install
266
+ bun install -g mlx-bun # permanent
170
267
  mlx-bun --version
171
268
  ```
172
269
 
173
- The npm package is a thin launcher (no postinstall hook): it imports
174
- `src/cli.ts` under Bun, which fetches the small (~52 MB compressed,
175
- sha256-verified) native runtime pack — `libmlxc`/`libmlx`/`libjaccl`/
176
- `mlx.metallib` plus `libmlx_bun_expert_io.dylib` — into
177
- `~/Library/Caches/mlx-bun/` on first invocation if
178
- no copy is already resolvable (env / beside the binary / cache /
179
- Homebrew). This is the same npm/sidecar mechanism described above, not
180
- the signed/notarized Homebrew bundle. `bun publish` is already run
181
- automatically and idempotently as step 4 of `PUBLISH=1
182
- ./scripts/release-binary.sh`; run it standalone only as a manual
183
- fallback/retry:
184
-
185
- ```sh
186
- bun publish # manual retry; package.json version must already be bumped
187
- ```
188
-
189
- ### Publishing the first-run native pack
190
-
191
- The npm/bunx path has an independent, versioned release asset. Whenever its
192
- file manifest changes, build and publish it before shipping code that names the
193
- new `NATIVE_PACK_VERSION`:
270
+ The npm package is **source, not a binary**. `package.json` `files[]` ships
271
+ `bin/`, `src/`, `docs/`, `README.md`, `LICENSE`, `THIRD_PARTY_LICENSES.md`;
272
+ `exports["."]` points at `src/index.ts` (the library API), `bin.mlx-bun` at
273
+ [bin/mlx-bun.mjs](../../bin/mlx-bun.mjs). There is no postinstall hook and
274
+ no build step. `scripts/packaging/` is not in `files[]`, so the pi
275
+ adapter extension
276
+ ([scripts/packaging/pi-extensions/mlx-bun-adapter.ts](../../scripts/packaging/pi-extensions/mlx-bun-adapter.ts))
277
+ is installed by copying it from the repo into `~/.pi/agent/extensions/`.
278
+
279
+ **The launcher** is plain `.mjs` so any runtime can parse it before anything
280
+ touches `bun:ffi` / `bun:sqlite`. It exits with an install hint when `Bun` is
281
+ undefined (Node), when `Bun.version` is below `MIN_BUN` (kept equal to
282
+ `engines.bun`), or when the platform is not darwin/arm64; otherwise it
283
+ imports `src/cli.ts`.
284
+
285
+ **First run: the native runtime pack.** Before importing anything that
286
+ `dlopen`s, `src/cli.ts` calls `ensureNativeRuntime()` (`src/native-pack.ts`).
287
+ If no runtime resolves through the order above, it downloads
288
+ `mlx-bun-native-v<ver>-<arch>.tar.gz` from the GitHub release tagged
289
+ `native-v<ver>` (override the URL with `MLX_BUN_NATIVE_PACK_URL`) through the
290
+ same resumable, sha256- and size-verified `downloadOne` path as model
291
+ downloads, extracts it into a staging directory, checks every file in
292
+ `NATIVE_PACK_FILES` is present, and renames it into
293
+ `~/Library/Caches/mlx-bun/native-v<ver>-<arch>/` — a crashed extract never
294
+ leaves a half-populated cache. Dev trees and embedders resolve via
295
+ Homebrew/env or the sidecar and never hit this path.
296
+
297
+ The pack contains the six native files: `libmlxc.dylib`, `libmlx.dylib`,
298
+ `libjaccl.dylib`, `mlx.metallib`, `libmlx_bun_expert_io.dylib`,
299
+ `mlx-bun-frame-extract`. It is **versioned independently** of the package
300
+ (`NATIVE_PACK_VERSION` in `src/native-pack.ts`, not `package.json`), because
301
+ it changes only when MLX or the helper set changes.
302
+
303
+ ### Publishing a new native pack
304
+
305
+ Whenever the pack's file manifest or the MLX runtime changes, publish the pack
306
+ **before** the package release that depends on it — the download 404s until
307
+ the `native-v<ver>` release exists:
194
308
 
195
309
  ```sh
196
- sh scripts/build-native-pack.sh 0.2.0 dist-native
197
- gh release create native-v0.2.0 \
198
- dist-native/mlx-bun-native-v0.2.0-arm64.tar.gz \
199
- dist-native/mlx-bun-native-v0.2.0-arm64.tar.gz.sha256 \
200
- --title "mlx-bun native runtime v0.2.0" \
201
- --notes "Adds the bounded native expert-I/O helper required by direct Colibri GLM-5.2."
310
+ sh scripts/build-native-pack.sh <ver> dist-native
311
+ gh release create native-v<ver> \
312
+ dist-native/mlx-bun-native-v<ver>-arm64.tar.gz \
313
+ dist-native/mlx-bun-native-v<ver>-arm64.tar.gz.sha256 \
314
+ --title "mlx-bun native runtime v<ver>" --notes "<what changed>"
202
315
  ```
203
316
 
204
- For v0.2.0 the arm64 archive is 52,307,647 bytes with SHA-256
205
- `9bd3795c5ea8f52b18413501f2d68c32c264a20751302300a08dc04cd67df97c`.
206
- It contains `libmlxc.dylib`, `libmlx.dylib`, `libjaccl.dylib`,
207
- `mlx.metallib`, and `libmlx_bun_expert_io.dylib`. These values must match
208
- `src/native-pack.ts`; a fresh-cache extraction plus real MLX dlopen and native
209
- expert read is the pre-publish smoke.
317
+ `build-native-pack.sh` stages the same six files with the same
318
+ `install_name_tool` fixups and ad-hoc signatures as the bundle, tars them,
319
+ and prints the three constants to bake into `src/native-pack.ts`:
320
+ `NATIVE_PACK_VERSION`, the arm64 `SHA256`, and the arm64 `SIZE`. Paste them,
321
+ add any new file to `NATIVE_PACK_FILES`, and commit before bumping the
322
+ package. Pre-publish smoke: a fresh-cache extraction plus a real MLX
323
+ `dlopen` (and, when the helpers changed, an expert read or a video decode).
324
+ The pack is ad-hoc signed only — `build-native-pack.sh` never touches a
325
+ Developer ID and nothing notarizes it; it is `dlopen`ed by the user's own Bun
326
+ process, not by our signed executable. The most recent pack
327
+ (`native-v0.3.0`, published 2026-08-22) added `mlx-bun-frame-extract`.
328
+
329
+ The working tree targets native pack 0.4.0 with MLX 0.32.2 and MLX-C
330
+ `c74db5307cc8ce122f48d97ef951b30578674e7f`. Its six-file macOS 14 candidate
331
+ has been built and verified locally. It is not published. Release the native
332
+ asset before publishing any package that uses this manifest. Runtime bindings
333
+ match this one C API, and startup checks the linked core version. The old
334
+ runtime and source controls remain separate for benchmark comparisons.
335
+
336
+ CI builds that pinned C wrapper with
337
+ `sh scripts/packaging/build-native-runtime.sh <new-output-directory>` and sets
338
+ `MLX_BUN_LIBMLXC` to its `lib/libmlxc.dylib`. The script verifies the official
339
+ macOS 14 MLX-Metal archive hash and checks out the exact MLX-C commit. It
340
+ extracts native libraries and headers without running Python or compiling
341
+ Metal sources. This also reproduces local native-pack build inputs before
342
+ the candidate is published; Homebrew's C wrapper is not the CI runtime.
343
+
344
+ Managed jobs in the compiled binary re-exec that binary through a private job
345
+ entry, with the database/log paths passed in the environment. They do not depend
346
+ on a Bun executable or a TypeScript source tree. The build runs
347
+ `scripts/packaging/verify-jobs.ts` as a compiled smoke: subprocess completion,
348
+ progress persistence and the CLI entry must pass before the bundle is accepted.