git: 05e578be6139 - main - misc/opencode: update to 2.2.5
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 01 Nov 2026 17:11:51 UTC
The branch main has been updated by samm:
URL: https://cgit.FreeBSD.org/ports/commit/?id=05e578be6139f0a0f385993528923699ebf96474
commit 05e578be6139f0a0f385993528923699ebf96474
Author: Oleksii Samorukov <samm@FreeBSD.org>
AuthorDate: 2026-11-01 17:11:41 +0000
Commit: Oleksii Samorukov <samm@FreeBSD.org>
CommitDate: 2026-11-01 17:11:41 +0000
misc/opencode: update to 2.2.5
---
misc/opencode/Makefile | 53 ++++----
misc/opencode/Makefile.crates | 25 +---
misc/opencode/distinfo | 52 +-------
misc/opencode/files/patch-packages_app_vite.js | 19 +++
.../files/patch-packages_cli_script_build.ts | 32 ++---
.../patch-packages_cli_script_opencode-pty.ts | 2 +-
.../patch-packages_core_src_config_discovery.ts | 19 +++
...packages_core_src_filesystem_watcher-binding.ts | 24 ++++
.../patch-packages_core_src_filesystem_watcher.ts | 148 +++++++++++++++++++++
misc/opencode/files/setup-opentui-freebsd.py | 15 +--
misc/opencode/files/webui-wasm-package.json | 3 +-
11 files changed, 263 insertions(+), 129 deletions(-)
diff --git a/misc/opencode/Makefile b/misc/opencode/Makefile
index 260b1f4303bc..277178c23e76 100644
--- a/misc/opencode/Makefile
+++ b/misc/opencode/Makefile
@@ -1,7 +1,6 @@
PORTNAME= opencode
DISTVERSIONPREFIX= v
-DISTVERSION= 2.1.0
-PORTREVISION= 1
+DISTVERSION= 2.2.5
CATEGORIES= misc
# Upstream ships no FreeBSD builds of OpenTUI's Zig core, fff's Rust cdylib, the
@@ -13,15 +12,13 @@ CATEGORIES= misc
# OPENTUI_VERSION and FFF_VERSION must match the pinned @opentui/core and
# @ff-labs/fff-bun releases (the root package.json catalog and
# packages/core/package.json) or the native FFI ABI breaks.
-OPENTUI_VERSION= 0.6.6
+OPENTUI_VERSION= 0.8.0
FFF_VERSION= 0.10.5
#
-# opencode-pty is the persistent terminal daemon the client spawns. Its version
-# must match the pinned @opencode-ai/pty release and speak the same wire protocol
-# as the bundled client (packages/cli/package.json, packages/core/package.json),
-# and GHOSTTY_REVISION must match its ghostty-revision file, or the client
-# rejects the daemon as a protocol mismatch.
-OPENCODE_PTY_VERSION= 0.3.1
+# opencode-pty is the persistent terminal daemon the client spawns. It is built
+# from the same source tree as the client (packages/process-daemon), and
+# GHOSTTY_REVISION must match its ghostty-revision file, or the client rejects
+# the daemon as a protocol mismatch.
GHOSTTY_REVISION= ab0b9da9e88fcb4b0533a1854e84628f663930af
MASTER_SITES= https://registry.npmjs.org/@ff-labs/fff-bun/-/:fffbun
@@ -34,7 +31,6 @@ GH_PROJECT= opencode
GH_TAGNAME= v${DISTVERSION}
GH_TUPLE= anomalyco:opentui:v${OPENTUI_VERSION}:opentui \
dmtrKovalenko:fff:v${FFF_VERSION}:fff \
- anomalyco:opencode-pty:v${OPENCODE_PTY_VERSION}:opencodepty \
ghostty-org:ghostty:${GHOSTTY_REVISION}:ghostty
MAINTAINER= samm@FreeBSD.org
@@ -59,7 +55,9 @@ CARGO_TEST= no
# rolldown has no native binding and its WASI fallback SIGSEGVs under node26.
BUILD_DEPENDS= ${LOCALBASE}/bin/bun:lang/bun \
${LOCALBASE}/bin/zig:lang/zig \
- ${LOCALBASE}/bin/node:www/node24
+ ${LOCALBASE}/bin/node:www/node24 \
+ npm-node24>0:www/npm-node24 \
+ gmake:devel/gmake
# post-fetch warms the offline bun and Ghostty Zig caches, so both are needed.
# Zig 0.16 hardcodes /etc/ssl/cert.pem on FreeBSD; ca_root_nss provides it.
FETCH_DEPENDS= ${LOCALBASE}/bin/bun:lang/bun \
@@ -73,7 +71,7 @@ RUN_DEPENDS= rg:textproc/ripgrep
OPENTUI_WRKSRC= ${WRKDIR}/opentui-${OPENTUI_VERSION}
FFF_WRKSRC= ${WRKDIR}/fff-${FFF_VERSION}
-OPENCODE_PTY_WRKSRC= ${WRKDIR}/opencode-pty-${OPENCODE_PTY_VERSION}
+OPENCODE_PTY_WRKSRC= ${WRKSRC}/packages/process-daemon
GHOSTTY_WRKSRC= ${WRKDIR}/ghostty-${GHOSTTY_REVISION}
# Build only the fff C-ABI cdylib (not the nvim/python/mcp crates).
@@ -82,6 +80,8 @@ FFF_SO= ${CARGO_TARGET_DIR}/release/libfff_c.so
BUN= ${LOCALBASE}/bin/bun
ZIG= ${LOCALBASE}/bin/zig
+NODE= ${LOCALBASE}/bin/node
+NODE_GYP= ${LOCALBASE}/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js
BUN_INSTALL_CACHE_DIR= ${WRKDIR}/bun-cache
WEBUI_WASM_DIR= ${WRKDIR}/gen-webui
WEBUI_WASM_CACHE= ${DISTDIR}/${DIST_SUBDIR}/opencode-webui-wasm-${DISTVERSION}.tar.gz
@@ -89,7 +89,8 @@ BUN_CACHE_TARBALL= ${DISTDIR}/${DIST_SUBDIR}/opencode-bun-cache-${DISTVERSION}.t
ZIG_PTY_CACHE_DIR= ${WRKDIR}/zig-pty-cache
ZIG_PTY_CACHE= ${DISTDIR}/${DIST_SUBDIR}/opencode-pty-zig-cache-${DISTVERSION}.tar.gz
-PLIST_FILES= bin/${PORTNAME}
+PLIST_FILES= bin/${PORTNAME} \
+ lib/opencode/watcher.node
.include <bsd.port.pre.mk>
@@ -149,25 +150,23 @@ post-extract:
@${MKDIR} ${ZIG_PTY_CACHE_DIR}
@${TAR} xf ${ZIG_PTY_CACHE} -C ${ZIG_PTY_CACHE_DIR}
-# OpenTUI, opencode-pty and Ghostty are extra source trees, so their patches are
-# applied by hand.
+# OpenTUI and Ghostty are extra source trees, and opencode-pty lives in
+# packages/process-daemon, so their patches are applied by hand.
post-patch:
@${PATCH} -d ${OPENTUI_WRKSRC} -p0 --batch < ${FILESDIR}/opentui-build.zig.patch
@${PATCH} -d ${OPENTUI_WRKSRC} -p0 --batch < ${FILESDIR}/opentui-ghostty-vt-freebsd.patch
@${PATCH} -d ${OPENCODE_PTY_WRKSRC} -p1 --batch < ${FILESDIR}/opencode-pty-build.rs.patch
@${PATCH} -d ${GHOSTTY_WRKSRC} -p1 --batch < ${FILESDIR}/ghostty-zig-0.16-std.patch
-# Effect 4.0.1 rejects 64-bit st_dev values, which ZFS on FreeBSD reports.
-# Upstream fixed this in Effect 4.0.5, but OpenCode still pins 4.0.1.
do-build:
@${ECHO_MSG} "===> Installing OpenCode dependencies (offline)"
@cd ${WRKSRC} && ${SETENV} BUN_INSTALL_CACHE_DIR=${BUN_INSTALL_CACHE_DIR} \
${BUN} install --frozen-lockfile
- @${ECHO_MSG} "===> Allowing 64-bit ZFS device IDs in @effect/platform-node-shared"
- @for f in ${WRKSRC}/node_modules/.bun/@effect+platform-node-shared@*/node_modules/@effect/platform-node-shared/dist/NodeFileSystem.js \
- ${WRKSRC}/node_modules/@effect/platform-node-shared/dist/NodeFileSystem.js; do \
- [ -f "$${f}" ] || continue; \
- ${SED} -i '' 's/dev: bigintToNumber(stat.dev, "dev")/dev: Number(stat.dev)/' "$${f}"; \
+ @${ECHO_MSG} "===> Building the @parcel/watcher kqueue backend"
+ @for d in ${WRKSRC}/node_modules/.bun/@parcel+watcher@*/node_modules/@parcel/watcher; do \
+ [ -d "$${d}" ] || continue; \
+ (cd "$${d}" && ${SETENV} HOME=${WRKDIR} ${MAKE_ENV} \
+ ${NODE} ${NODE_GYP} rebuild --nodedir=${LOCALBASE} --python=${PYTHON_CMD}); \
done
@${ECHO_MSG} "===> Installing @ff-labs/fff-bun (upstream ships no FreeBSD package)"
@${MKDIR} ${WRKSRC}/node_modules/@ff-labs
@@ -212,10 +211,10 @@ do-build:
@cd ${WRKSRC}/packages/cli && ${SETENV} \
HOME=${WRKDIR} \
OPENCODE_VERSION=${DISTVERSION} \
+ OPENCODE_TARGET=freebsd-${OPENTUI_ARCH} \
BUN_INSTALL_CACHE_DIR=${BUN_INSTALL_CACHE_DIR} \
OPENCODE_PTY_BIN=${OPENCODE_PTY_BIN} \
${BUN} run script/build.ts \
- --target=opencode-freebsd-${OPENTUI_ARCH} \
--skip-install \
--outdir=dist
@test -x ${OPENCODE_BIN} || (${ECHO_MSG} "Missing ${OPENCODE_BIN}"; ${FALSE})
@@ -225,6 +224,11 @@ do-install:
# it (the default ${INSTALL_PROGRAM}) discards that payload, leaving a binary
# that runs as plain bun, so install it unstripped.
${INSTALL} -m 555 ${OPENCODE_BIN} ${STAGEDIR}${PREFIX}/bin/opencode
+ @${MKDIR} ${STAGEDIR}${PREFIX}/lib/opencode
+ @for f in ${WRKSRC}/node_modules/.bun/@parcel+watcher@*/node_modules/@parcel/watcher/build/Release/watcher.node; do \
+ [ -f "$${f}" ] || continue; \
+ ${INSTALL} -m 555 "$${f}" ${STAGEDIR}${PREFIX}/lib/opencode/watcher.node; \
+ done
do-test:
@${ECHO_MSG} "==> Running OpenCode smoke test..."
@@ -239,5 +243,8 @@ do-test:
${ECHO_MSG} "ERROR: the opencode-pty terminal daemon is not embedded in the binary"; \
${FALSE}; \
fi
+ @${ECHO_MSG} "==> Checking the @parcel/watcher native binding is installed..."
+ @test -f ${STAGEDIR}${PREFIX}/lib/opencode/watcher.node || \
+ (${ECHO_MSG} "ERROR: watcher.node is missing"; ${FALSE})
.include <bsd.port.post.mk>
diff --git a/misc/opencode/Makefile.crates b/misc/opencode/Makefile.crates
index 0055b2040d21..d151f41ddfb9 100644
--- a/misc/opencode/Makefile.crates
+++ b/misc/opencode/Makefile.crates
@@ -1,5 +1,6 @@
-# Crates.io dependencies of the fff and opencode-pty Cargo.locks (see Makefile).
-# Re-create after bumping FFF_VERSION or OPENCODE_PTY_VERSION with:
+# Crates.io dependencies of the fff and opencode-pty (packages/process-daemon)
+# Cargo.locks (see Makefile). Re-create after bumping FFF_VERSION or updating
+# packages/process-daemon with:
# make cargo-crates-merge # needs ports-mgmt/portfmt
# or:
# make cargo-crates > Makefile.crates && make makesum
@@ -12,7 +13,6 @@ CARGO_CRATES=ahash-0.8.12 \
anstyle-parse-1.0.0 \
anstyle-query-1.1.5 \
anstyle-wincon-3.0.11 \
- anyhow-1.0.103 \
anyhow-1.0.104 \
arrayref-0.3.9 \
arrayvec-0.7.6 \
@@ -22,7 +22,6 @@ CARGO_CRATES=ahash-0.8.12 \
bincode-1.3.3 \
bindgen-0.72.1 \
bitflags-1.3.2 \
- bitflags-2.11.0 \
bitflags-2.13.1 \
blake3-1.8.3 \
block-buffer-0.10.4 \
@@ -52,12 +51,10 @@ CARGO_CRATES=ahash-0.8.12 \
cpufeatures-0.2.17 \
criterion-0.5.1 \
criterion-plot-0.5.0 \
- crossbeam-channel-0.5.15 \
crossbeam-channel-0.5.16 \
crossbeam-deque-0.8.6 \
crossbeam-epoch-0.9.20 \
crossbeam-queue-0.3.12 \
- crossbeam-utils-0.8.21 \
crossbeam-utils-0.8.22 \
crunchy-0.2.4 \
crypto-common-0.1.7 \
@@ -136,7 +133,6 @@ CARGO_CRATES=ahash-0.8.12 \
is_terminal_polyfill-1.70.2 \
itertools-0.10.5 \
itertools-0.13.0 \
- itoa-1.0.17 \
itoa-1.0.18 \
jobserver-0.1.34 \
js-sys-0.3.91 \
@@ -144,7 +140,6 @@ CARGO_CRATES=ahash-0.8.12 \
kqueue-sys-1.0.4 \
lazy_static-1.5.0 \
leb128fmt-0.1.0 \
- libc-0.2.183 \
libc-0.2.189 \
libgit2-sys-0.18.7+1.9.6 \
libloading-0.8.9 \
@@ -155,10 +150,8 @@ CARGO_CRATES=ahash-0.8.12 \
litemap-0.8.1 \
lmdb-master-sys-0.2.5 \
lock_api-0.4.14 \
- log-0.4.29 \
log-0.4.33 \
matchers-0.2.0 \
- memchr-2.8.0 \
memchr-2.8.3 \
memmap2-0.9.11 \
memoffset-0.9.1 \
@@ -206,7 +199,6 @@ CARGO_CRATES=ahash-0.8.12 \
powerfmt-0.2.0 \
ppv-lite86-0.2.21 \
prettyplease-0.2.37 \
- proc-macro2-1.0.106 \
proc-macro2-1.0.107 \
proptest-1.11.0 \
pyo3-0.24.2 \
@@ -215,12 +207,10 @@ CARGO_CRATES=ahash-0.8.12 \
pyo3-macros-0.24.2 \
pyo3-macros-backend-0.24.2 \
quick-error-1.2.3 \
- quote-1.0.45 \
quote-1.0.47 \
r-efi-5.3.0 \
r-efi-6.0.0 \
rand-0.8.5 \
- rand-0.9.4 \
rand-0.9.5 \
rand_chacha-0.3.1 \
rand_chacha-0.9.0 \
@@ -247,14 +237,10 @@ CARGO_CRATES=ahash-0.8.12 \
schemars_derive-1.2.1 \
scopeguard-1.2.0 \
semver-1.0.27 \
- serde-1.0.228 \
serde-1.0.229 \
- serde_core-1.0.228 \
serde_core-1.0.229 \
- serde_derive-1.0.228 \
serde_derive-1.0.229 \
serde_derive_internals-0.29.1 \
- serde_json-1.0.149 \
serde_json-1.0.151 \
serial2-0.2.38 \
sha2-0.10.9 \
@@ -269,7 +255,6 @@ CARGO_CRATES=ahash-0.8.12 \
socket2-0.6.3 \
stable_deref_trait-1.2.1 \
strsim-0.11.1 \
- syn-2.0.117 \
syn-2.0.119 \
syn-3.0.3 \
synchronoise-1.0.1 \
@@ -308,7 +293,6 @@ CARGO_CRATES=ahash-0.8.12 \
version_check-0.9.5 \
walkdir-2.5.0 \
wasi-0.11.1+wasi-snapshot-preview1 \
- wasip2-1.0.2+wasi-0.2.9 \
wasip2-1.0.4+wasi-0.2.12 \
wasip3-0.4.0+wasi-0.3.0-rc-2026-01-06 \
wasm-bindgen-0.2.114 \
@@ -361,9 +345,7 @@ CARGO_CRATES=ahash-0.8.12 \
xxhash-rust-0.8.15 \
yoke-0.8.1 \
yoke-derive-0.8.1 \
- zerocopy-0.8.47 \
zerocopy-0.8.56 \
- zerocopy-derive-0.8.47 \
zerocopy-derive-0.8.56 \
zerofrom-0.1.6 \
zerofrom-derive-0.1.6 \
@@ -371,5 +353,4 @@ CARGO_CRATES=ahash-0.8.12 \
zerovec-0.11.5 \
zerovec-derive-0.11.2 \
zlob-1.6.3 \
- zmij-1.0.21 \
zmij-1.0.23
diff --git a/misc/opencode/distinfo b/misc/opencode/distinfo
index c049e364fe40..9ad5ba322651 100644
--- a/misc/opencode/distinfo
+++ b/misc/opencode/distinfo
@@ -1,4 +1,4 @@
-TIMESTAMP = 1792491194
+TIMESTAMP = 1793518348
SHA256 (opencode/fff-bun-0.10.5.tgz) = 16afa70bcb078c9b28378545396298f73a67fc10e7ff7bba4f9ff1d0462c27f0
SIZE (opencode/fff-bun-0.10.5.tgz) = 83121
SHA256 (opencode/rust/crates/ahash-0.8.12.crate) = 5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75
@@ -19,8 +19,6 @@ SHA256 (opencode/rust/crates/anstyle-query-1.1.5.crate) = 40c48f72fd53cd289104fc
SIZE (opencode/rust/crates/anstyle-query-1.1.5.crate) = 10264
SHA256 (opencode/rust/crates/anstyle-wincon-3.0.11.crate) = 291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d
SIZE (opencode/rust/crates/anstyle-wincon-3.0.11.crate) = 12638
-SHA256 (opencode/rust/crates/anyhow-1.0.103.crate) = 2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3
-SIZE (opencode/rust/crates/anyhow-1.0.103.crate) = 48746
SHA256 (opencode/rust/crates/anyhow-1.0.104.crate) = 330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470
SIZE (opencode/rust/crates/anyhow-1.0.104.crate) = 48819
SHA256 (opencode/rust/crates/arrayref-0.3.9.crate) = 76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb
@@ -39,8 +37,6 @@ SHA256 (opencode/rust/crates/bindgen-0.72.1.crate) = 993776b509cfb49c750f11b8f07
SIZE (opencode/rust/crates/bindgen-0.72.1.crate) = 246071
SHA256 (opencode/rust/crates/bitflags-1.3.2.crate) = bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a
SIZE (opencode/rust/crates/bitflags-1.3.2.crate) = 23021
-SHA256 (opencode/rust/crates/bitflags-2.11.0.crate) = 843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af
-SIZE (opencode/rust/crates/bitflags-2.11.0.crate) = 48906
SHA256 (opencode/rust/crates/bitflags-2.13.1.crate) = b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da
SIZE (opencode/rust/crates/bitflags-2.13.1.crate) = 51395
SHA256 (opencode/rust/crates/blake3-1.8.3.crate) = 2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d
@@ -99,8 +95,6 @@ SHA256 (opencode/rust/crates/criterion-0.5.1.crate) = f2b12d017a929603d80db1831c
SIZE (opencode/rust/crates/criterion-0.5.1.crate) = 110088
SHA256 (opencode/rust/crates/criterion-plot-0.5.0.crate) = 6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1
SIZE (opencode/rust/crates/criterion-plot-0.5.0.crate) = 22706
-SHA256 (opencode/rust/crates/crossbeam-channel-0.5.15.crate) = 82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2
-SIZE (opencode/rust/crates/crossbeam-channel-0.5.15.crate) = 92716
SHA256 (opencode/rust/crates/crossbeam-channel-0.5.16.crate) = d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e
SIZE (opencode/rust/crates/crossbeam-channel-0.5.16.crate) = 93189
SHA256 (opencode/rust/crates/crossbeam-deque-0.8.6.crate) = 9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51
@@ -109,8 +103,6 @@ SHA256 (opencode/rust/crates/crossbeam-epoch-0.9.20.crate) = 2d6914041f254d6e917
SIZE (opencode/rust/crates/crossbeam-epoch-0.9.20.crate) = 47545
SHA256 (opencode/rust/crates/crossbeam-queue-0.3.12.crate) = 0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115
SIZE (opencode/rust/crates/crossbeam-queue-0.3.12.crate) = 16270
-SHA256 (opencode/rust/crates/crossbeam-utils-0.8.21.crate) = d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28
-SIZE (opencode/rust/crates/crossbeam-utils-0.8.21.crate) = 42691
SHA256 (opencode/rust/crates/crossbeam-utils-0.8.22.crate) = 61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17
SIZE (opencode/rust/crates/crossbeam-utils-0.8.22.crate) = 46874
SHA256 (opencode/rust/crates/crunchy-0.2.4.crate) = 460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5
@@ -267,8 +259,6 @@ SHA256 (opencode/rust/crates/itertools-0.10.5.crate) = b0fd2260e829bddf4cb6ea802
SIZE (opencode/rust/crates/itertools-0.10.5.crate) = 115354
SHA256 (opencode/rust/crates/itertools-0.13.0.crate) = 413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186
SIZE (opencode/rust/crates/itertools-0.13.0.crate) = 146261
-SHA256 (opencode/rust/crates/itoa-1.0.17.crate) = 92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2
-SIZE (opencode/rust/crates/itoa-1.0.17.crate) = 15905
SHA256 (opencode/rust/crates/itoa-1.0.18.crate) = 8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682
SIZE (opencode/rust/crates/itoa-1.0.18.crate) = 15935
SHA256 (opencode/rust/crates/jobserver-0.1.34.crate) = 9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33
@@ -283,8 +273,6 @@ SHA256 (opencode/rust/crates/lazy_static-1.5.0.crate) = bbd2bcb4c963f2ddae06a2ef
SIZE (opencode/rust/crates/lazy_static-1.5.0.crate) = 14025
SHA256 (opencode/rust/crates/leb128fmt-0.1.0.crate) = 09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2
SIZE (opencode/rust/crates/leb128fmt-0.1.0.crate) = 14138
-SHA256 (opencode/rust/crates/libc-0.2.183.crate) = b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d
-SIZE (opencode/rust/crates/libc-0.2.183.crate) = 816918
SHA256 (opencode/rust/crates/libc-0.2.189.crate) = 3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2
SIZE (opencode/rust/crates/libc-0.2.189.crate) = 851502
SHA256 (opencode/rust/crates/libgit2-sys-0.18.7+1.9.6.crate) = 23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89
@@ -305,14 +293,10 @@ SHA256 (opencode/rust/crates/lmdb-master-sys-0.2.5.crate) = 864808e0b19fb6dd3b70
SIZE (opencode/rust/crates/lmdb-master-sys-0.2.5.crate) = 178812
SHA256 (opencode/rust/crates/lock_api-0.4.14.crate) = 224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965
SIZE (opencode/rust/crates/lock_api-0.4.14.crate) = 29249
-SHA256 (opencode/rust/crates/log-0.4.29.crate) = 5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897
-SIZE (opencode/rust/crates/log-0.4.29.crate) = 51515
SHA256 (opencode/rust/crates/log-0.4.33.crate) = 0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad
SIZE (opencode/rust/crates/log-0.4.33.crate) = 52997
SHA256 (opencode/rust/crates/matchers-0.2.0.crate) = d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9
SIZE (opencode/rust/crates/matchers-0.2.0.crate) = 7075
-SHA256 (opencode/rust/crates/memchr-2.8.0.crate) = f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79
-SIZE (opencode/rust/crates/memchr-2.8.0.crate) = 97780
SHA256 (opencode/rust/crates/memchr-2.8.3.crate) = cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98
SIZE (opencode/rust/crates/memchr-2.8.3.crate) = 99165
SHA256 (opencode/rust/crates/memmap2-0.9.11.crate) = d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0
@@ -407,8 +391,6 @@ SHA256 (opencode/rust/crates/ppv-lite86-0.2.21.crate) = 85eae3c4ed2f50dcfe72643d
SIZE (opencode/rust/crates/ppv-lite86-0.2.21.crate) = 22522
SHA256 (opencode/rust/crates/prettyplease-0.2.37.crate) = 479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b
SIZE (opencode/rust/crates/prettyplease-0.2.37.crate) = 72033
-SHA256 (opencode/rust/crates/proc-macro2-1.0.106.crate) = 8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934
-SIZE (opencode/rust/crates/proc-macro2-1.0.106.crate) = 59765
SHA256 (opencode/rust/crates/proc-macro2-1.0.107.crate) = 985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9
SIZE (opencode/rust/crates/proc-macro2-1.0.107.crate) = 59588
SHA256 (opencode/rust/crates/proptest-1.11.0.crate) = 4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744
@@ -425,8 +407,6 @@ SHA256 (opencode/rust/crates/pyo3-macros-backend-0.24.2.crate) = 822ece1c7e10127
SIZE (opencode/rust/crates/pyo3-macros-backend-0.24.2.crate) = 72654
SHA256 (opencode/rust/crates/quick-error-1.2.3.crate) = a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0
SIZE (opencode/rust/crates/quick-error-1.2.3.crate) = 15066
-SHA256 (opencode/rust/crates/quote-1.0.45.crate) = 41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924
-SIZE (opencode/rust/crates/quote-1.0.45.crate) = 31564
SHA256 (opencode/rust/crates/quote-1.0.47.crate) = 1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001
SIZE (opencode/rust/crates/quote-1.0.47.crate) = 31622
SHA256 (opencode/rust/crates/r-efi-5.3.0.crate) = 69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f
@@ -435,8 +415,6 @@ SHA256 (opencode/rust/crates/r-efi-6.0.0.crate) = f8dcc9c7d52a811697d2151c701e0d
SIZE (opencode/rust/crates/r-efi-6.0.0.crate) = 65303
SHA256 (opencode/rust/crates/rand-0.8.5.crate) = 34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404
SIZE (opencode/rust/crates/rand-0.8.5.crate) = 87113
-SHA256 (opencode/rust/crates/rand-0.9.4.crate) = 44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea
-SIZE (opencode/rust/crates/rand-0.9.4.crate) = 99786
SHA256 (opencode/rust/crates/rand-0.9.5.crate) = b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41
SIZE (opencode/rust/crates/rand-0.9.5.crate) = 100216
SHA256 (opencode/rust/crates/rand_chacha-0.3.1.crate) = e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88
@@ -489,22 +467,14 @@ SHA256 (opencode/rust/crates/scopeguard-1.2.0.crate) = 94143f37725109f92c262ed2c
SIZE (opencode/rust/crates/scopeguard-1.2.0.crate) = 11619
SHA256 (opencode/rust/crates/semver-1.0.27.crate) = d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2
SIZE (opencode/rust/crates/semver-1.0.27.crate) = 30081
-SHA256 (opencode/rust/crates/serde-1.0.228.crate) = 9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e
-SIZE (opencode/rust/crates/serde-1.0.228.crate) = 83652
SHA256 (opencode/rust/crates/serde-1.0.229.crate) = 4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba
SIZE (opencode/rust/crates/serde-1.0.229.crate) = 83669
-SHA256 (opencode/rust/crates/serde_core-1.0.228.crate) = 41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad
-SIZE (opencode/rust/crates/serde_core-1.0.228.crate) = 63111
SHA256 (opencode/rust/crates/serde_core-1.0.229.crate) = 67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48
SIZE (opencode/rust/crates/serde_core-1.0.229.crate) = 63100
-SHA256 (opencode/rust/crates/serde_derive-1.0.228.crate) = d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79
-SIZE (opencode/rust/crates/serde_derive-1.0.228.crate) = 59605
SHA256 (opencode/rust/crates/serde_derive-1.0.229.crate) = e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348
SIZE (opencode/rust/crates/serde_derive-1.0.229.crate) = 59864
SHA256 (opencode/rust/crates/serde_derive_internals-0.29.1.crate) = 18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711
SIZE (opencode/rust/crates/serde_derive_internals-0.29.1.crate) = 26189
-SHA256 (opencode/rust/crates/serde_json-1.0.149.crate) = 83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86
-SIZE (opencode/rust/crates/serde_json-1.0.149.crate) = 155994
SHA256 (opencode/rust/crates/serde_json-1.0.151.crate) = c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14
SIZE (opencode/rust/crates/serde_json-1.0.151.crate) = 156556
SHA256 (opencode/rust/crates/serial2-0.2.38.crate) = b16809bc35793b19ce4e0c53924bc0dce3937f15487997cfdaed936004180730
@@ -533,8 +503,6 @@ SHA256 (opencode/rust/crates/stable_deref_trait-1.2.1.crate) = 6ce2be8dc25455e1f
SIZE (opencode/rust/crates/stable_deref_trait-1.2.1.crate) = 8186
SHA256 (opencode/rust/crates/strsim-0.11.1.crate) = 7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f
SIZE (opencode/rust/crates/strsim-0.11.1.crate) = 14266
-SHA256 (opencode/rust/crates/syn-2.0.117.crate) = e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99
-SIZE (opencode/rust/crates/syn-2.0.117.crate) = 306011
SHA256 (opencode/rust/crates/syn-2.0.119.crate) = 872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297
SIZE (opencode/rust/crates/syn-2.0.119.crate) = 307407
SHA256 (opencode/rust/crates/syn-3.0.3.crate) = 53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3
@@ -611,8 +579,6 @@ SHA256 (opencode/rust/crates/walkdir-2.5.0.crate) = 29790946404f91d9c5d06f9874ef
SIZE (opencode/rust/crates/walkdir-2.5.0.crate) = 23951
SHA256 (opencode/rust/crates/wasi-0.11.1+wasi-snapshot-preview1.crate) = ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b
SIZE (opencode/rust/crates/wasi-0.11.1+wasi-snapshot-preview1.crate) = 28477
-SHA256 (opencode/rust/crates/wasip2-1.0.2+wasi-0.2.9.crate) = 9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5
-SIZE (opencode/rust/crates/wasip2-1.0.2+wasi-0.2.9.crate) = 129778
SHA256 (opencode/rust/crates/wasip2-1.0.4+wasi-0.2.12.crate) = b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487
SIZE (opencode/rust/crates/wasip2-1.0.4+wasi-0.2.12.crate) = 135311
SHA256 (opencode/rust/crates/wasip3-0.4.0+wasi-0.3.0-rc-2026-01-06.crate) = 5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5
@@ -717,12 +683,8 @@ SHA256 (opencode/rust/crates/yoke-0.8.1.crate) = 72d6e5c6afb84d73944e5cedb052c46
SIZE (opencode/rust/crates/yoke-0.8.1.crate) = 32016
SHA256 (opencode/rust/crates/yoke-derive-0.8.1.crate) = b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d
SIZE (opencode/rust/crates/yoke-derive-0.8.1.crate) = 7593
-SHA256 (opencode/rust/crates/zerocopy-0.8.47.crate) = efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87
-SIZE (opencode/rust/crates/zerocopy-0.8.47.crate) = 281496
SHA256 (opencode/rust/crates/zerocopy-0.8.56.crate) = 556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb
SIZE (opencode/rust/crates/zerocopy-0.8.56.crate) = 284907
-SHA256 (opencode/rust/crates/zerocopy-derive-0.8.47.crate) = 0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89
-SIZE (opencode/rust/crates/zerocopy-derive-0.8.47.crate) = 97648
SHA256 (opencode/rust/crates/zerocopy-derive-0.8.56.crate) = f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1
SIZE (opencode/rust/crates/zerocopy-derive-0.8.56.crate) = 105007
SHA256 (opencode/rust/crates/zerofrom-0.1.6.crate) = 50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5
@@ -737,17 +699,13 @@ SHA256 (opencode/rust/crates/zerovec-derive-0.11.2.crate) = eadce39539ca5cb39855
SIZE (opencode/rust/crates/zerovec-derive-0.11.2.crate) = 21421
SHA256 (opencode/rust/crates/zlob-1.6.3.crate) = f0f010d9b10495cc0037cb47cedc143c39c7fa2d59f752dca19036fd0234a210
SIZE (opencode/rust/crates/zlob-1.6.3.crate) = 172523
-SHA256 (opencode/rust/crates/zmij-1.0.21.crate) = b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa
-SIZE (opencode/rust/crates/zmij-1.0.21.crate) = 26665
SHA256 (opencode/rust/crates/zmij-1.0.23.crate) = 29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b
SIZE (opencode/rust/crates/zmij-1.0.23.crate) = 28612
-SHA256 (opencode/anomalyco-opencode-v2.1.0_GH0.tar.gz) = c2e3f3b1684c93e61efb5bf94ea3dec62f6cfaf9b4930e5103ab49b37d3b2a39
-SIZE (opencode/anomalyco-opencode-v2.1.0_GH0.tar.gz) = 79690631
-SHA256 (opencode/anomalyco-opentui-v0.6.6_GH0.tar.gz) = 9c7539a47c73f11022fcb81840fb7856342804a3d133a8e2a0a8cbe0c66a4fed
-SIZE (opencode/anomalyco-opentui-v0.6.6_GH0.tar.gz) = 31020853
+SHA256 (opencode/anomalyco-opencode-v2.2.5_GH0.tar.gz) = 04621edf6c47c2c7a36982ef39914d482c16977708fbd7b779f49f928bdc4fba
+SIZE (opencode/anomalyco-opencode-v2.2.5_GH0.tar.gz) = 83460564
+SHA256 (opencode/anomalyco-opentui-v0.8.0_GH0.tar.gz) = 20f55c5dea47b6db8d88166a767c9f3a588511662d909b567576b6d45aa37ef2
+SIZE (opencode/anomalyco-opentui-v0.8.0_GH0.tar.gz) = 31706403
SHA256 (opencode/dmtrKovalenko-fff-v0.10.5_GH0.tar.gz) = db50fa8aaabb77211f8dbeb4d77dc8852e0ee52070e40bfd9ef5af44ac50accc
SIZE (opencode/dmtrKovalenko-fff-v0.10.5_GH0.tar.gz) = 2798793
-SHA256 (opencode/anomalyco-opencode-pty-v0.3.1_GH0.tar.gz) = 101efcfde719cd9ead649d1fc1e1c31a31e89c854bf63b9cd2e977a7661f18a0
-SIZE (opencode/anomalyco-opencode-pty-v0.3.1_GH0.tar.gz) = 108956
SHA256 (opencode/ghostty-org-ghostty-ab0b9da9e88fcb4b0533a1854e84628f663930af_GH0.tar.gz) = 651ffa67e98060bc41aac04fcab45f71fb9998eb265957d85a89218ecf94d50b
SIZE (opencode/ghostty-org-ghostty-ab0b9da9e88fcb4b0533a1854e84628f663930af_GH0.tar.gz) = 38024504
diff --git a/misc/opencode/files/patch-packages_app_vite.js b/misc/opencode/files/patch-packages_app_vite.js
new file mode 100644
index 000000000000..5bb1af4e8e7f
--- /dev/null
+++ b/misc/opencode/files/patch-packages_app_vite.js
@@ -0,0 +1,19 @@
+Disable Solid's template validation in the production web UI build.
+
+babel-plugin-jsx-dom-expressions validates every generated template with
+parse5 when config.validate is enabled. On FreeBSD/aarch64 this
+intermittently gets stuck in postprocess/isInvalidMarkup and the web UI build
+spins indefinitely. The validation only warns about malformed template HTML,
+so disable it for the release build.
+
+--- packages/app/vite.js.orig
++++ packages/app/vite.js
+@@ -131,7 +131,7 @@
+ },
+ },
+ ...tailwind,
+- solidPlugin(),
++ solidPlugin({ solid: { validate: false } }),
+ ]
+
+ export function inlineThemePreload(html) {
diff --git a/misc/opencode/files/patch-packages_cli_script_build.ts b/misc/opencode/files/patch-packages_cli_script_build.ts
index e5bfa1373c37..c35100bfb8a7 100644
--- a/misc/opencode/files/patch-packages_cli_script_build.ts
+++ b/misc/opencode/files/patch-packages_cli_script_build.ts
@@ -1,16 +1,14 @@
Teach OpenCode's CLI build script about FreeBSD.
Upstream's target list only covers linux, darwin and win32, and its per-target
-plugins assume a native @parcel/watcher package and bytecode support, neither of
-which exists on FreeBSD:
+plugins assume a native @parcel/watcher package and bytecode support, neither
+of which exists on FreeBSD:
- * add the freebsd arm64/x64 targets so `--target=opencode-freebsd-<arch>`
+ * add the freebsd arm64/x64 targets so `OPENCODE_TARGET=freebsd-<arch>`
resolves and `Bun.build({ compile: { target: "bun-freebsd-<arch>" } })`
produces a FreeBSD executable;
- * degrade the parcel-watcher binding to a stub on FreeBSD (there is no
- @parcel/watcher-freebsd-* package); the watcher service already falls back
- to node:fs for file/entries watches and logs "backend not supported" for
- recursive directories;
+ * keep the real watcher binding on FreeBSD, since the port builds the native
+ kqueue addon and installs it next to the executable;
* disable bytecode on FreeBSD, where bun does not emit it.
--- packages/cli/script/build.ts.orig
@@ -22,24 +20,16 @@ which exists on FreeBSD:
+ { os: "freebsd", arch: "arm64" },
+ { os: "freebsd", arch: "x64" },
]
-
+
const targets =
-@@ -143,10 +145,11 @@
+@@ -143,6 +145,7 @@
const parcelWatcherPlugin: BunPlugin = {
name: "parcel-watcher-binding",
setup(build) {
-- build.onLoad({ filter: /filesystem[/\\]watcher-binding\.ts$/ }, () => ({
-- contents: `export default () => require(${JSON.stringify(parcelWatcherPackage)})`,
-- loader: "js",
-- }))
-+ build.onLoad({ filter: /filesystem[/\\]watcher-binding\.ts$/ }, () =>
-+ item.os === "freebsd"
-+ ? { contents: `export default () => { throw new Error("parcel watcher is unavailable on FreeBSD") }`, loader: "js" }
-+ : { contents: `export default () => require(${JSON.stringify(parcelWatcherPackage)})`, loader: "js" },
-+ )
- },
- }
- const target = targetName(item)
++ if (item.os === "freebsd") return
+ build.onLoad({ filter: /filesystem[/\\]watcher-binding\.ts$/ }, () => ({
+ contents: `export default () => require(${JSON.stringify(parcelWatcherPackage)})`,
+ loader: "js",
@@ -167,7 +170,7 @@
external: ["node-gyp"],
format: "esm",
diff --git a/misc/opencode/files/patch-packages_cli_script_opencode-pty.ts b/misc/opencode/files/patch-packages_cli_script_opencode-pty.ts
index a9446042c039..f2571fb7522b 100644
--- a/misc/opencode/files/patch-packages_cli_script_opencode-pty.ts
+++ b/misc/opencode/files/patch-packages_cli_script_opencode-pty.ts
@@ -8,7 +8,7 @@ binary passed via OPENCODE_PTY_BIN is embedded into the executable.
--- packages/cli/script/opencode-pty.ts.orig
+++ packages/cli/script/opencode-pty.ts
@@ -19,7 +19,13 @@
- const pty = createRequire(core.resolve("@opencode-ai/pty/package.json"))
+ const pty = createRequire(core.resolve("@opencode/pty/package.json"))
export async function resolveOpencodePty(target: Target): Promise<OpencodePtyAsset | undefined> {
- if (target.platform !== "darwin" && target.platform !== "linux" && target.platform !== "win32") return undefined
diff --git a/misc/opencode/files/patch-packages_core_src_config_discovery.ts b/misc/opencode/files/patch-packages_core_src_config_discovery.ts
new file mode 100644
index 000000000000..ed3a9e37ef90
--- /dev/null
+++ b/misc/opencode/files/patch-packages_core_src_config_discovery.ts
@@ -0,0 +1,19 @@
+Honor FreeBSD's hier(7) layout for the system-wide configuration directory.
+
+Upstream falls back to /etc/opencode on every platform that is neither Darwin
+nor Windows. FreeBSD ports install into LOCALBASE, so use
+/usr/local/etc/opencode there instead, alongside the rest of the package's
+configuration.
+
+Upstream PR: https://github.com/anomalyco/opencode/pull/58347
+
+--- packages/core/src/config/discovery.ts.orig
++++ packages/core/src/config/discovery.ts
+@@ -28,6 +28,7 @@
+ export function systemDirectory(platform: NodeJS.Platform = process.platform, env: NodeJS.ProcessEnv = process.env) {
+ if (platform === "darwin") return AbsolutePath.make("/Library/Application Support/opencode")
+ if (platform === "win32") return AbsolutePath.make(path.join(env.ProgramData || "C:\\ProgramData", "opencode"))
++ if (platform === "freebsd") return AbsolutePath.make("/usr/local/etc/opencode")
+ return AbsolutePath.make("/etc/opencode")
+ }
+
diff --git a/misc/opencode/files/patch-packages_core_src_filesystem_watcher-binding.ts b/misc/opencode/files/patch-packages_core_src_filesystem_watcher-binding.ts
new file mode 100644
index 000000000000..f1618791f35c
--- /dev/null
+++ b/misc/opencode/files/patch-packages_core_src_filesystem_watcher-binding.ts
@@ -0,0 +1,24 @@
+Load the FreeBSD @parcel/watcher binding from the installed addon.
+
+The FreeBSD port builds the native kqueue addon itself and installs it at
+PREFIX/lib/opencode/watcher.node; load that file instead of a non-existent
+@parcel/watcher-freebsd-* package.
+
+--- packages/core/src/filesystem/watcher-binding.ts.orig
++++ packages/core/src/filesystem/watcher-binding.ts
+@@ -1,4 +1,5 @@
+ import { createRequire } from "node:module"
++import path from "node:path"
+
+ declare const OPENCODE_LIBC: string | undefined
+
+@@ -9,6 +10,8 @@
+ const libc = typeof OPENCODE_LIBC === "undefined" ? undefined : OPENCODE_LIBC
+ return require(
+ process.env.OPENCODE_PARCEL_WATCHER_PATH ??
+- `@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${libc || "glibc"}` : ""}`,
++ (process.platform === "freebsd"
++ ? path.join(path.dirname(process.execPath), "..", "lib", "opencode", "watcher.node")
++ : `@parcel/watcher-${process.platform}-${process.arch}${process.platform === "linux" ? `-${libc || "glibc"}` : ""}`),
+ )
+ }
diff --git a/misc/opencode/files/patch-packages_core_src_filesystem_watcher.ts b/misc/opencode/files/patch-packages_core_src_filesystem_watcher.ts
new file mode 100644
index 000000000000..ab14e61e0372
--- /dev/null
+++ b/misc/opencode/files/patch-packages_core_src_filesystem_watcher.ts
@@ -0,0 +1,148 @@
+Fix the FreeBSD watcher without triggering Bun's N-API crash.
+
+Bun 1.4.x segfaults or hangs when @parcel/watcher's kqueue subscription
+delivers events from its callback thread through an N-API thread-safe
+function, and its fs.watch does not report missing entries on FreeBSD. Use
+the addon's native snapshot API for directory watches and a small JS poller
+for immediate file/entry watches instead. Await the initial snapshot before
+reporting the subscription ready so config reloads cannot miss the first write.
+
+--- packages/core/src/filesystem/watcher.ts.orig
++++ packages/core/src/filesystem/watcher.ts
+@@ -7,7 +7,8 @@
+ import { FSUtil } from "@opencode/util/fs-util"
+ import { Cause, Context, Effect, Layer, PubSub, RcMap, Stream } from "effect"
+ import { lazy } from "../util/lazy.js"
+-import { watch } from "node:fs"
++import { readdirSync, rmSync, statSync, watch } from "node:fs"
++import { tmpdir } from "node:os"
+ import path from "path"
+ import loadBinding from "./watcher-binding.js"
+
+@@ -198,13 +199,126 @@
+ }),
+ )
+
++function poll(directory: string, names: ReadonlySet<string>, publish: (update: Update) => void): Subscription {
++ const scan = () => {
++ const next = new Map<string, string>()
++ let entries
++ try {
++ entries = readdirSync(directory, { withFileTypes: true })
++ } catch {
++ return next
++ }
++ for (const entry of entries) {
++ if (!names.has(entry.name)) continue
++ const file = path.join(directory, entry.name)
++ if (entry.isDirectory()) {
++ next.set(file, "directory")
++ continue
++ }
++ try {
++ const stat = statSync(file)
++ next.set(file, `${stat.mtimeMs}:${stat.size}`)
++ } catch {
++ next.set(file, "file")
++ }
++ }
++ return next
++ }
++ let previous = scan()
++ const timer = setInterval(() => {
++ const next = scan()
++ for (const [file, signature] of next) {
++ const prior = previous.get(file)
++ if (prior === undefined) publish({ path: file, type: "create" })
++ else if (prior !== signature) publish({ path: file, type: "update" })
++ }
++ for (const file of previous.keys()) if (!next.has(file)) publish({ path: file, type: "delete" })
++ previous = next
++ }, 100)
++ return {
++ unsubscribe: () => {
++ clearInterval(timer)
++ return Promise.resolve()
++ },
++ backend: "poll",
++ }
++}
++
++function snapshotPoller(
++ native: typeof ParcelWatcher,
++ directory: string,
++ ignore: readonly string[],
++ publish: (update: Update) => void,
++): Effect.Effect<Subscription> {
++ return Effect.promise(async () => {
++ const snapshot = path.join(tmpdir(), `opencode-watch-${process.pid}-${Math.random().toString(36).slice(2)}`)
++ const options = { backend: "brute-force" as ParcelWatcher.BackendType, ignore: [...ignore] }
++ let active = true
++ let ready = false
++ let running = false
++ const tick = async () => {
++ if (!active || running) return
++ running = true
++ try {
++ if (!ready) {
++ await native.writeSnapshot(directory, snapshot, options)
++ ready = true
++ return
++ }
++ const updates = await native.getEventsSince(directory, snapshot, options)
++ await native.writeSnapshot(directory, snapshot, options)
++ for (const update of updates) publish(update)
++ } catch {
++ ready = false
++ } finally {
++ running = false
++ }
++ }
++ // Establish the baseline before reporting the subscription as ready, so
++ // files created by the consumer right after subscribe are observed.
++ try {
++ await native.writeSnapshot(directory, snapshot, options)
++ ready = true
++ } catch {}
++ const timer = setInterval(tick, 250)
++ return {
++ unsubscribe: () => {
++ active = false
++ clearInterval(timer)
++ rmSync(snapshot, { force: true })
++ return Promise.resolve()
++ },
++ backend: "brute-force",
++ }
++ })
++}
++
++
+ export const nativeLayer = Layer.succeed(
+ Native,
+ Native.of({
+ subscribe: (input) => {
++ // Bun's parcel subscription path crashes on FreeBSD (N-API thread-safe
++ // functions). Use the native snapshot API instead, which does not spawn
++ // a callback thread, and poll the immediate entries from JS.
++ if (process.platform === "freebsd" && input.type === "directory") {
++ const native = watcher()
++ if (!native) {
++ return Effect.logError("watcher backend not supported", {
++ directory: input.target,
++ platform: process.platform,
++ }).pipe(Effect.as(undefined))
++ }
++ return snapshotPoller(native, input.target, input.ignore, input.publish)
++ }
+ if (input.type === "file" || input.type === "entries") {
+ const directory = input.type === "file" ? path.dirname(input.target) : input.target
+ const names = new Set(input.type === "file" ? [path.basename(input.target)] : input.names)
++ // Bun's fs.watch does not deliver events for missing entries on FreeBSD.
++ // Poll the containing directory instead of using a native addon.
++ if (process.platform === "freebsd") {
++ return Effect.sync(() => poll(directory, names, input.publish))
++ }
+ return Effect.try(() =>
+ watch(directory, { recursive: false }, (_event, file) => {
+ if (file && !names.has(file)) return
diff --git a/misc/opencode/files/setup-opentui-freebsd.py b/misc/opencode/files/setup-opentui-freebsd.py
index 0ae4a29a705d..5a505ff1e2d6 100644
--- a/misc/opencode/files/setup-opentui-freebsd.py
+++ b/misc/opencode/files/setup-opentui-freebsd.py
@@ -15,7 +15,6 @@ Where <arch> is "x64" or "arm64" (the Node/Bun arch tag), and
containing node_modules/).
"""
import os
-import re
import shutil
import sys
@@ -44,13 +43,6 @@ BRANCH = """ if (process.platform === "freebsd") {
}
"""
-# Upstream disables the native renderer thread on Linux because it is
-# unreliable; on FreeBSD it deadlocks (the TUI freezes after the first render),
-# so force it off there too. The bundle keeps the guard and the assignment on
-# separate lines, so match across the whitespace between them.
-RENDERER_THREAD_RE = re.compile(r'(if \(process\.platform === "linux")\)(\s*config\.useThread = false)')
-RENDERER_THREAD_NEW = r'\1 || process.platform === "freebsd")\2'
-
INDEX_BUN = """const module = await import("./libopentui.so", { with: { type: "file" } })
export default module.default
@@ -111,11 +103,6 @@ def patch_bundles(core: str) -> None:
if 'process.platform === "freebsd"' not in src and THROW in src:
src = src.replace(THROW, BRANCH + THROW, 1)
changed = True
- # Disable the native renderer thread on FreeBSD (see RENDERER_THREAD_RE).
- out, n = RENDERER_THREAD_RE.subn(RENDERER_THREAD_NEW, src)
- if n:
- src = out
- changed = True
if changed:
open(path, "w", encoding="utf-8").write(src)
print(f" - patched {name}")
@@ -137,7 +124,7 @@ def create_platform_package(root: str, lib: str, version: str, arch: str) -> str
def main() -> int:
root = sys.argv[1]
lib = sys.argv[2]
- version = sys.argv[3] if len(sys.argv) > 3 else "0.6.6"
+ version = sys.argv[3] if len(sys.argv) > 3 else "0.8.0"
arch = sys.argv[4] if len(sys.argv) > 4 else "x64"
if arch not in ("x64", "arm64"):
raise SystemExit(f"unsupported arch tag: {arch}")
diff --git a/misc/opencode/files/webui-wasm-package.json b/misc/opencode/files/webui-wasm-package.json
index e83aa7dba2db..fdaaa0c62a9d 100644
--- a/misc/opencode/files/webui-wasm-package.json
+++ b/misc/opencode/files/webui-wasm-package.json
@@ -1,8 +1,9 @@
{
"name": "opencode-webui-wasm",
"private": true,
- "comment": "WASM fallbacks for the web UI's native Rust bindings, which have no FreeBSD/arm64 builds. Versions must match rolldown / lightningcss / @tailwindcss-oxide in the OpenCode tree.",
+ "comment": "WASM fallbacks for the web UI's native Rust bindings, which have no FreeBSD/arm64 builds. Versions must match rolldown / lightningcss / @tailwindcss-oxide in the OpenCode tree. @napi-rs/wasm-runtime is pinned because 1.2.6 requires an @emnapi version that does not match the one pinned by @rolldown/binding-wasm32-wasi.",
"dependencies": {
+ "@napi-rs/wasm-runtime": "1.2.5",
"@rolldown/binding-wasm32-wasi": "1.2.5",
"lightningcss-wasm": "1.32.0",
"@tailwindcss/oxide-wasm32-wasi": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz"