Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package opencode for openSUSE:Factory checked in at 2026-09-16 17:42:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/opencode (Old) and /work/SRC/openSUSE:Factory/.opencode.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "opencode" Wed Sep 16 17:42:05 2026 rev:13 rq:1378139 version:1.18.30 Changes: -------- --- /work/SRC/openSUSE:Factory/opencode/opencode.changes 2026-09-14 16:26:30.667953446 +0200 +++ /work/SRC/openSUSE:Factory/.opencode.new.383539/opencode.changes 2026-09-16 17:44:16.845872808 +0200 @@ -1,0 +2,41 @@ +Tue Sep 15 06:32:42 UTC 2026 - Martin Pluskal <[email protected]> + +- Highlight 31 of the languages the TUI used to download grammars + for from the distribution instead: their descriptors now point + at the tree-sitter-<lang>-wasm module and the -queries + highlights.scm (Recommends; cpp also takes c's, upstream's cpp + query is a delta on it), and %check loads and compiles every + pair in the bundled web-tree-sitter runtime. hcl, kotlin and vue + stay blocked: no packaged queries (hcl, kotlin), a query the + runtime rejects (vue) + +------------------------------------------------------------------- +Mon Sep 14 19:35:42 UTC 2026 - Martin Pluskal <[email protected]> + +- Take the highlighter's five grammar modules from the + tree-sitter-javascript-wasm, -typescript-wasm, -markdown-wasm and + -zig-wasm packages instead of building them from grammar tarballs + here, so the grammar versions follow the packaged releases + (javascript 0.23.1, markdown 0.5.3); drop those tarballs and the + clang, lld and wasi-libc build dependencies + +------------------------------------------------------------------- +Mon Sep 14 15:34:15 UTC 2026 - Martin Pluskal <[email protected]> + +- Build the native code the executable carries from source, or load + it from the distribution: + * Use the fff and bun-pty packages (required at the pinned + versions) for the file finder and the shell pty, loaded at + run time + * Take photon's WebAssembly module and glue from photon-node + * Compile @parcel/watcher's addon from its own sources + * Build the highlighter's five tree-sitter grammar modules from + the grammar sources with clang, lld and wasi-libc +- Add opencode-native-tree-sitter.patch: parse shell commands with + libtree-sitter and the tree-sitter-bash and + tree-sitter-powershell packages through bun:ffi, with a small C + shim (opencode-tsshim.c) installed under libdir; + web-tree-sitter's runtime stays only for the highlighter and is + declared bundled + +------------------------------------------------------------------- New: ---- node-addon-api-7.1.1.tgz opencode-native-tree-sitter.patch opencode-tsshim.c ----------(New B)---------- New: the grammar sources with clang, lld and wasi-libc - Add opencode-native-tree-sitter.patch: parse shell commands with libtree-sitter and the tree-sitter-bash and ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ opencode.spec ++++++ --- /var/tmp/diff_new_pack.JRFnoi/_old 2026-09-16 17:44:19.393979270 +0200 +++ /var/tmp/diff_new_pack.JRFnoi/_new 2026-09-16 17:44:19.398979480 +0200 @@ -26,6 +26,27 @@ # versioning of its own, so the runtime opentui package must be the # release this tree pins. %%prep checks that this still matches. %global opentui_version 0.4.5 +# The two native libraries loaded through bun:ffi at run time. Both ABIs are +# private to the TypeScript that ships in the vendor tree, so the packages +# are required at exactly the version that tree was generated from; %%prep +# checks the pins. +%global fff_version 0.9.4 +%global bun_pty_version 0.4.8 +%global photon_version 0.3.4 +# The tree-sitter grammars @opentui/core highlights with. The modules come +# from the tree-sitter-<lang>-wasm packages at these versions (its own +# parsers-config.ts pins javascript 0.25.0 and markdown 0.5.1; the +# highlight queries were checked against the packaged releases). +%global ts_javascript_version 0.23.1 +%global ts_typescript_version 0.23.2 +%global ts_markdown_version 0.5.3 +%global ts_zig_version 1.1.2 +%global parcel_watcher_version 2.5.1 +%global node_addon_api_version 7.1.1 +# The TUI highlighter's languages served from tree-sitter-<lang>-wasm and +# -queries packages (grammar names as in the upstream descriptor list; the +# c_sharp module lives in tree-sitter-c-sharp). +%global opencode_system_grammars python rust go cpp csharp bash c java ruby php scala html json yaml haskell css julia lua ocaml clojure swift toml nix diff elixir fsharp r make vim xml agda # Node's names for the platform, not RPM's: the build target is derived from # them, and @opentui/core resolves its native library by the same names. %ifarch x86_64 @@ -65,6 +86,10 @@ Source4: %{name}_vendor_trace Source5: %{name}-vendor-keep.txt Source6: README.SUSE-maint +# Header-only N-API wrapper @parcel/watcher's addon is compiled against. +Source11: https://registry.npmjs.org/node-addon-api/-/node-addon-api-%{node_addon_api_version}.tgz +# C side of the native shell parser (Patch5). +Source12: %{name}-tsshim.c # Upstream's build script fails unless bun satisfies a caret range around the # version it pins. It pins 1.3.14 and Factory has 1.4.2, so the check passes # and this patch is inert today; it stays because both versions float, and a @@ -79,22 +104,182 @@ Patch3: %{name}-no-grammar-download.patch # PATCH-FIX-UPSTREAM opencode-fix-filesystem-cycle.patch boo#1280159 gh#anomalyco/opencode#48397 Patch4: %{name}-fix-filesystem-cycle.patch +# Parses shell commands with the system libtree-sitter and grammar packages +# through bun:ffi; the wasm route needs a runtime module only emscripten can +# build. Placeholders are filled in below. +Patch5: %{name}-native-tree-sitter.patch # No floor. Which bun upstream wants changes with every release and Patch0 # turns a mismatch into a warning; a floor here would be a guess at which # older bun still works, and the package is a git snapshot anyway, so # 1.4.0~git sorts below 1.4.0. BuildRequires: bun +BuildRequires: gcc +BuildRequires: gcc-c++ +BuildRequires: nodejs26-devel +BuildRequires: photon-node = %{photon_version} # %%prep reads the @opentui/core pin out of package.json. BuildRequires: python3-base +# Only for %%check, which loads every system grammar module with its +# queries the way the highlighter will. +BuildRequires: tree-sitter-agda-queries +BuildRequires: tree-sitter-agda-wasm +BuildRequires: tree-sitter-bash +BuildRequires: tree-sitter-bash-queries +BuildRequires: tree-sitter-bash-wasm +BuildRequires: tree-sitter-c-queries +BuildRequires: tree-sitter-c-sharp-queries +BuildRequires: tree-sitter-c-sharp-wasm +BuildRequires: tree-sitter-c-wasm +BuildRequires: tree-sitter-clojure-queries +BuildRequires: tree-sitter-clojure-wasm +BuildRequires: tree-sitter-cpp-queries +BuildRequires: tree-sitter-cpp-wasm +BuildRequires: tree-sitter-css-queries +BuildRequires: tree-sitter-css-wasm +BuildRequires: tree-sitter-devel +BuildRequires: tree-sitter-diff-queries +BuildRequires: tree-sitter-diff-wasm +BuildRequires: tree-sitter-elixir-queries +BuildRequires: tree-sitter-elixir-wasm +BuildRequires: tree-sitter-fsharp-queries +BuildRequires: tree-sitter-fsharp-wasm +BuildRequires: tree-sitter-go-queries +BuildRequires: tree-sitter-go-wasm +BuildRequires: tree-sitter-haskell-queries +BuildRequires: tree-sitter-haskell-wasm +BuildRequires: tree-sitter-html-queries +BuildRequires: tree-sitter-html-wasm +BuildRequires: tree-sitter-java-queries +BuildRequires: tree-sitter-java-wasm +BuildRequires: tree-sitter-javascript-wasm = %{ts_javascript_version} +BuildRequires: tree-sitter-json-queries +BuildRequires: tree-sitter-json-wasm +BuildRequires: tree-sitter-julia-queries +BuildRequires: tree-sitter-julia-wasm +BuildRequires: tree-sitter-lua-queries +BuildRequires: tree-sitter-lua-wasm +BuildRequires: tree-sitter-make-queries +BuildRequires: tree-sitter-make-wasm +BuildRequires: tree-sitter-markdown-wasm = %{ts_markdown_version} +BuildRequires: tree-sitter-nix-queries +BuildRequires: tree-sitter-nix-wasm +BuildRequires: tree-sitter-ocaml-queries +BuildRequires: tree-sitter-ocaml-wasm +BuildRequires: tree-sitter-php-queries +BuildRequires: tree-sitter-php-wasm +BuildRequires: tree-sitter-powershell +BuildRequires: tree-sitter-python-queries +BuildRequires: tree-sitter-python-wasm +BuildRequires: tree-sitter-r-queries +BuildRequires: tree-sitter-r-wasm +BuildRequires: tree-sitter-ruby-queries +BuildRequires: tree-sitter-ruby-wasm +BuildRequires: tree-sitter-rust-queries +BuildRequires: tree-sitter-rust-wasm +BuildRequires: tree-sitter-scala-queries +BuildRequires: tree-sitter-scala-wasm +BuildRequires: tree-sitter-swift-queries +BuildRequires: tree-sitter-swift-wasm +BuildRequires: tree-sitter-toml-queries +BuildRequires: tree-sitter-toml-wasm +BuildRequires: tree-sitter-typescript-wasm = %{ts_typescript_version} +BuildRequires: tree-sitter-vim-queries +BuildRequires: tree-sitter-vim-wasm +BuildRequires: tree-sitter-xml-queries +BuildRequires: tree-sitter-xml-wasm +BuildRequires: tree-sitter-yaml-queries +BuildRequires: tree-sitter-yaml-wasm +BuildRequires: tree-sitter-zig-wasm = %{ts_zig_version} BuildRequires: zstd +Requires: bun-pty = %{bun_pty_version} +Requires: fff = %{fff_version} Requires: git-core # Native TUI library, loaded at runtime (not compiled into the binary). # Equality because the FFI ABI is private and unversioned. Requires: opentui = %{opentui_version} # Downloaded on first use otherwise; opencode shells out to it for grep. Requires: ripgrep -# bun only supports these two, and so do the prebuilt native modules left in -# the dependency tree. +Requires: tree-sitter-bash +Requires: tree-sitter-powershell +# The 31 languages the TUI highlighter can load from the distribution +# (see %%prep): a missing pair means plain text for that language. +Recommends: tree-sitter-agda-queries +Recommends: tree-sitter-agda-wasm +Recommends: tree-sitter-bash-queries +Recommends: tree-sitter-bash-wasm +Recommends: tree-sitter-c-queries +Recommends: tree-sitter-c-sharp-queries +Recommends: tree-sitter-c-sharp-wasm +Recommends: tree-sitter-c-wasm +Recommends: tree-sitter-clojure-queries +Recommends: tree-sitter-clojure-wasm +Recommends: tree-sitter-cpp-queries +Recommends: tree-sitter-cpp-wasm +Recommends: tree-sitter-css-queries +Recommends: tree-sitter-css-wasm +Recommends: tree-sitter-diff-queries +Recommends: tree-sitter-diff-wasm +Recommends: tree-sitter-elixir-queries +Recommends: tree-sitter-elixir-wasm +Recommends: tree-sitter-fsharp-queries +Recommends: tree-sitter-fsharp-wasm +Recommends: tree-sitter-go-queries +Recommends: tree-sitter-go-wasm +Recommends: tree-sitter-haskell-queries +Recommends: tree-sitter-haskell-wasm +Recommends: tree-sitter-html-queries +Recommends: tree-sitter-html-wasm +Recommends: tree-sitter-java-queries +Recommends: tree-sitter-java-wasm +Recommends: tree-sitter-json-queries +Recommends: tree-sitter-json-wasm +Recommends: tree-sitter-julia-queries +Recommends: tree-sitter-julia-wasm +Recommends: tree-sitter-lua-queries +Recommends: tree-sitter-lua-wasm +Recommends: tree-sitter-make-queries +Recommends: tree-sitter-make-wasm +Recommends: tree-sitter-nix-queries +Recommends: tree-sitter-nix-wasm +Recommends: tree-sitter-ocaml-queries +Recommends: tree-sitter-ocaml-wasm +Recommends: tree-sitter-php-queries +Recommends: tree-sitter-php-wasm +Recommends: tree-sitter-python-queries +Recommends: tree-sitter-python-wasm +Recommends: tree-sitter-r-queries +Recommends: tree-sitter-r-wasm +Recommends: tree-sitter-ruby-queries +Recommends: tree-sitter-ruby-wasm +Recommends: tree-sitter-rust-queries +Recommends: tree-sitter-rust-wasm +Recommends: tree-sitter-scala-queries +Recommends: tree-sitter-scala-wasm +Recommends: tree-sitter-swift-queries +Recommends: tree-sitter-swift-wasm +Recommends: tree-sitter-toml-queries +Recommends: tree-sitter-toml-wasm +Recommends: tree-sitter-vim-queries +Recommends: tree-sitter-vim-wasm +Recommends: tree-sitter-xml-queries +Recommends: tree-sitter-xml-wasm +Recommends: tree-sitter-yaml-queries +Recommends: tree-sitter-yaml-wasm +# Compiled into the executable from source during this build. +Provides: bundled(node-addon-api) = %{node_addon_api_version} +Provides: bundled(parcel-watcher) = %{parcel_watcher_version} +# Embedded from the distribution's own packages (the exact BuildRequires +# above keep these versions truthful; an update of one of those packages +# makes this one unresolvable until the pin follows). +Provides: bundled(photon-node) = %{photon_version} +Provides: bundled(tree-sitter-javascript) = %{ts_javascript_version} +Provides: bundled(tree-sitter-markdown) = %{ts_markdown_version} +Provides: bundled(tree-sitter-typescript) = %{ts_typescript_version} +Provides: bundled(tree-sitter-zig) = %{ts_zig_version} +# The one module still taken from npm: web-tree-sitter's runtime needs +# emscripten, which the distribution does not have. Used by the highlighter. +Provides: bundled(web-tree-sitter) = 0.25.10 +# bun only supports these two. ExclusiveArch: x86_64 aarch64 %description @@ -112,6 +297,8 @@ %prep %autosetup -p1 -a1 +# rpm honours one -a per %%autosetup, so the second tarball by hand. +tar -xzf %{SOURCE11} cp -a %{SOURCE6} . # Dependencies compiled into the executable whose licence asks for the text to @@ -144,7 +331,138 @@ export default "%{_libdir}/opentui/@opentui/core-linux-%{node_arch}/libopentui.so" EOF +# The two native libraries are loaded from their packages at run time, like +# libopentui.so above: the vendor tree's loaders get literal paths and the +# npm binaries are removed so nothing embeds them. Pins first. +for pin in "@ff-labs+fff-bun@%{fff_version}" "bun-pty@%{bun_pty_version}" \ + "@silvia-odwyer+photon-node@%{photon_version}" \ + "@parcel+watcher@%{parcel_watcher_version}"; do + test -d "node_modules/.bun/$pin" || { + echo "vendor tree has no $pin; the pinned package versions in this spec are stale" >&2 + exit 1 + } +done +fffbun="node_modules/.bun/@ff-labs+fff-bun@%{fff_version}/node_modules/@ff-labs/fff-bun/src" +cat > "$fffbun/embedded.ts" <<EOF +export const embeddedLibPath: string | null = "%{_libdir}/fff/libfff_c.so" +EOF +sed -i 's|const isEmbedded = embeddedLibPath?.includes("$bunfs") ?? false;|const isEmbedded = embeddedLibPath !== null;|' "$fffbun/ffi.ts" +grep -q 'const isEmbedded = embeddedLibPath !== null;' "$fffbun/ffi.ts" +rm -rf node_modules/.bun/@ff-labs+fff-bin-* +pty="node_modules/.bun/bun-pty@%{bun_pty_version}/node_modules/bun-pty/src/terminal.ts" +python3 - "$pty" <<'EOF' +import sys +p = sys.argv[1]; s = open(p).read() +a = s.index("\t// For bun compile:") +b = s.index("\t// Fallback: dynamic resolution") +s = s[:a] + '\treturn "@BUN_PTY_LIB@";\n\n' + s[b:] +open(p, "w").write(s) +EOF +sed -i 's|@BUN_PTY_LIB@|%{_libdir}/bun-pty/librust_pty.so|' "$pty" +grep -q 'return "%{_libdir}/bun-pty/librust_pty.so";' "$pty" +rm -rf node_modules/.bun/bun-pty@%{bun_pty_version}/node_modules/bun-pty/rust-pty + +# photon: the module and glue come from the photon-node package. Upstream +# patches the npm glue (patches/@silvia-odwyer%%2Fphoton-node@*.patch) so +# that it bundles under bun and finds the module at the path opencode +# embeds it under; the same two edits are applied to the package's glue, +# which a different wasm-bindgen release wrote, so hunks would not apply. +photon="node_modules/.bun/@silvia-odwyer+photon-node@%{photon_version}/node_modules/@silvia-odwyer/photon-node" +rm -f "$photon"/photon_rs_bg.wasm "$photon"/photon_rs.js "$photon"/photon_rs_bg.js "$photon"/*.d.ts +cp -p %{_datadir}/photon-node/* "$photon"/ +python3 - "$photon/photon_rs.js" <<'EOF' +import re, sys +p = sys.argv[1]; s = open(p).read() +s, n = re.subn(r"^imports\['__wbindgen_placeholder__'\] = module\.exports;$", + "const __wbindgen_placeholder__ = {};\nimports['__wbindgen_placeholder__'] = __wbindgen_placeholder__;", s, flags=re.M) +assert n == 1, "placeholder line not found" +s, n = re.subn(r"^module\.exports\.(__wb\w+) = function", r"__wbindgen_placeholder__.\1 = function", s, flags=re.M) +assert n > 10, "import shims not found" +old = "const path = require('path').join(__dirname, 'photon_rs_bg.wasm');" +assert old in s, "wasm path line not found" +s = s.replace(old, "const path = globalThis.__OPENCODE_PHOTON_WASM_PATH || require('path').join(__dirname, 'photon_rs_bg.wasm');") +open(p, "w").write(s) +EOF + +# Patch5's placeholders, and the npm modules the shell tool no longer loads. +sed -i 's|@OPENCODE_LIBDIR@|%{_libdir}/%{name}|; s|@TREE_SITTER_GRAMMARS@|%{_libdir}/tree-sitter|' \ + packages/opencode/src/tool/shell.ts packages/opencode/src/tool/shell-native.ts +grep -q '%{_libdir}/tree-sitter/libtree-sitter-bash.so' packages/opencode/src/tool/shell.ts + +# The TUI highlighter's other languages: upstream downloads the grammar +# module and a highlights.scm per language on first use (blocked by +# Patch3). Point the descriptors at the tree-sitter-<lang>-wasm and +# -queries packages instead; the loader takes a local path as it takes a +# URL. hcl and kotlin stay blocked: their packaged grammars ship no queries +# (Factory's kotlin is another grammar than the one upstream pins), and a +# module without queries highlights nothing. vue stays blocked too: its +# highlights.scm inherits html_tags and carries a #set! form the +# web-tree-sitter runtime opencode ships rejects, so it never compiles. +# Anything else upstream adds stays blocked until it is packaged and +# listed here. Each descriptor must carry exactly one highlights source +# and no injections, or the rewrite (and the download block) would not +# cover it. cpp's packaged query is upstream's delta on top of the C one, +# so its descriptor gets both files, C first. The locals entries (dead: +# nothing in @opentui/core reads them) are dropped so no web URL is left +# in a descriptor the download block no longer filters. +python3 - packages/tui/src/parsers-config.ts <<'EOF' +import re, sys +langs = "%{opencode_system_grammars}".split() +grammar = {"csharp": "c_sharp"} +before = {"cpp": ["c"]} +p = sys.argv[1]; s = open(p).read() +blocks = re.split(r'(?=\n \{\n filetype: ")', s) +out = []; done = [] +for b in blocks: + m = re.match(r'\n \{\n filetype: "([a-z_]+)"', b) + if m and m.group(1) in langs: + ft = m.group(1); g = grammar.get(ft, ft) + b = re.sub(r'\n\s*locals: \[.*?\],', '', b, count=1, flags=re.S) + live = re.sub(r'^\s*//.*$', '', b, flags=re.M) + assert "injections" not in live and "locals" not in live, ft + ": injections/locals not handled" + hl = re.search(r'highlights: \[(.*?)\],', live, flags=re.S) + assert hl and len(re.findall(r'"https://[^"]+"', hl.group(1))) == 1, ft + ": not exactly one highlights source" + b, n1 = re.subn(r'wasm: "https://[^"]+"', 'wasm: "%{_datadir}/tree-sitter/wasm/tree-sitter-' + g + '.wasm"', b, count=1) + srcs = ", ".join('"%{_datadir}/tree-sitter/queries/' + q + '/highlights.scm"' for q in before.get(ft, []) + [g]) + b, n2 = re.subn(r'highlights: \[.*?\],', 'highlights: [' + srcs + '],', b, count=1, flags=re.S) + assert n1 == 1 and n2 == 1, ft + done.append(ft) + out.append(b) +missing = sorted(set(langs) - set(done)) +assert not missing, "not in upstream parsers-config: " + " ".join(missing) +open(p, "w").write("".join(out)) +EOF +rm -rf node_modules/.bun/tree-sitter-bash@*/node_modules/tree-sitter-bash/prebuilds +rm -f node_modules/.bun/tree-sitter-bash@*/node_modules/tree-sitter-bash/tree-sitter-bash.wasm \ + node_modules/.bun/tree-sitter-powershell@*/node_modules/tree-sitter-powershell/tree-sitter-powershell.wasm \ + node_modules/.bun/web-tree-sitter@*/node_modules/web-tree-sitter/lib/tree-sitter.wasm \ + node_modules/.bun/web-tree-sitter@*/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map \ + node_modules/.bun/web-tree-sitter@*/node_modules/web-tree-sitter/tree-sitter.wasm.map + %build +# @parcel/watcher's addon, compiled from the source in its own npm package: +# the sources and defines are binding.gyp's Linux branch, the headers are +# Node's and node-addon-api's (Source11 unpacks as package/). +w="node_modules/.bun/@parcel+watcher@%{parcel_watcher_version}/node_modules/@parcel/watcher" +plat="node_modules/.bun/@parcel+watcher-linux-%{node_arch}-glibc@%{parcel_watcher_version}/node_modules/@parcel/watcher-linux-%{node_arch}-glibc" +test -d "$plat" +g++ %{optflags} -shared -fPIC -std=c++17 -DNAPI_DISABLE_CPP_EXCEPTIONS -DWATCHMAN -DINOTIFY -DBRUTE_FORCE \ + -I%{_includedir}/node26 -Ipackage -I"$w/src" \ + "$w"/src/binding.cc "$w"/src/Watcher.cc "$w"/src/Backend.cc "$w"/src/DirTree.cc \ + "$w"/src/Glob.cc "$w"/src/Debounce.cc "$w"/src/watchman/BSER.cc \ + "$w"/src/watchman/WatchmanBackend.cc "$w"/src/shared/BruteForceBackend.cc \ + "$w"/src/linux/InotifyBackend.cc "$w"/src/unix/legacy.cc -o watcher.node +# It goes into the payload, where no brp script reaches it. +strip --strip-debug watcher.node +install -pm 0644 watcher.node "$plat/watcher.node" + +# The highlighter's grammar modules, from the tree-sitter-<lang>-wasm +# packages (bun embeds what sits in the asset directories). +assets=$(ls -d node_modules/.bun/@opentui+core@%{opentui_version}+*/node_modules/@opentui/core/assets) +for lang in javascript typescript markdown markdown_inline zig; do + install -pm 0644 %{_datadir}/tree-sitter/wasm/tree-sitter-$lang.wasm "$assets/$lang/tree-sitter-$lang.wasm" +done + export HOME="$PWD/.home" export BUN_INSTALL_CACHE_DIR="$PWD/.bun-cache" mkdir -p "$HOME" "$BUN_INSTALL_CACHE_DIR" @@ -158,6 +476,10 @@ export OPENCODE_VERSION="%{version}" export OPENCODE_CHANNEL=latest +# No DWARF and stripped by hand: this package has no debuginfo subpackage +# (see the top of the file) and brp stripping is off for the payload's sake. +gcc %{optflags} -g0 -shared -fPIC -o libopencode-tsshim.so %{SOURCE12} -ltree-sitter +strip --strip-unneeded libopencode-tsshim.so cd packages/opencode # --single builds only the host platform. --skip-embed-web-ui leaves out the # browser interface, which needs another 101 npm packages and embeds two fonts @@ -173,6 +495,7 @@ export NO_BRP_STRIP_DEBUG=true install -Dpm 0755 packages/opencode/dist/%{name}-linux-%{node_arch}/bin/%{name} \ %{buildroot}%{_bindir}/%{name} +install -Dpm 0755 libopencode-tsshim.so %{buildroot}%{_libdir}/%{name}/libopencode-tsshim.so %check # The executable carries its own runtime and payload; if anything stripped or @@ -180,8 +503,77 @@ %{buildroot}%{_bindir}/%{name} --version test "$(%{buildroot}%{_bindir}/%{name} --version)" = "%{version}" +# What the payload carries: the distribution's modules and the addon built +# above, nothing from npm. +python3 - %{buildroot}%{_bindir}/%{name} %{_datadir}/tree-sitter/wasm %{_datadir}/photon-node/photon_rs_bg.wasm watcher.node <<'EOF' +import sys +exe = open(sys.argv[1], "rb").read() +for f in ["{}/tree-sitter-{}.wasm".format(sys.argv[2], l) for l in + ("javascript", "typescript", "markdown", "markdown_inline", "zig")] + sys.argv[3:]: + assert open(f, "rb").read() in exe, f + " is not embedded as built" +# bun's embedded-file table names every module the payload carries. +import re +found = sorted(set((m.group(1), m.group(2)) for m in + re.finditer(rb"\$bunfs/root/([A-Za-z0-9_.-]+?)-[a-z0-9]{8}\.(so|node|wasm)\b", exe))) +expected = sorted([(b"photon_rs_bg", b"wasm"), (b"tree-sitter", b"wasm"), (b"watcher", b"node")] + + [(b"tree-sitter-" + l.encode(), b"wasm") for l in + ("javascript", "typescript", "markdown", "markdown_inline", "zig")]) +assert found == expected, "embedded native/wasm files: {}".format(found) +EOF +# The native shell parser through the shim that ships and the grammar +# packages: a pipeline yields its two commands. +cat > check-parse.ts <<'EOF' +import { NativeParser, loadLanguage } from "./packages/opencode/src/tool/shell-native" +const bash = new NativeParser(loadLanguage("%{_libdir}/tree-sitter/libtree-sitter-bash.so", "tree_sitter_bash")) +const tree = bash.parse("cat foo | grep -i bar > out") +const cmds = tree.rootNode.descendantsOfType("command").map((n) => n.text) +if (cmds.length !== 2 || cmds[0] !== "cat foo") { console.log(cmds); process.exit(1) } +// Everything else shell.ts reads from a node or a tree. +const root = tree.rootNode +const first = root.child(0) +if (root.childCount < 1 || first === null || first.parent !== root || first.type === "" || cmds[1] !== "grep -i bar") { + console.log({ childCount: root.childCount, first: first?.type, parent: first?.parent?.type, cmds }) + process.exit(1) +} +tree.delete() +const ps = new NativeParser(loadLanguage("%{_libdir}/tree-sitter/libtree-sitter-powershell.so", "tree_sitter_powershell")) +if (ps.parse("Get-ChildItem -Recurse | Select-Object -First 5").rootNode.descendantsOfType("command").length < 1) process.exit(1) +EOF +OPENCODE_TSSHIM=%{buildroot}%{_libdir}/%{name}/libopencode-tsshim.so bun check-parse.ts +# Every language the descriptors now point at the distribution for: the +# module loads in the web-tree-sitter runtime opencode ships, its +# highlights.scm compiles against it, and a parse works. A query naming a +# node the packaged grammar lacks fails here instead of leaving that +# language unhighlighted at run time. +W=$(ls -d node_modules/.bun/web-tree-sitter@*/node_modules/web-tree-sitter | head -1) +test -d "$W" +cat > check-grammars.ts <<EOF +const { Parser, Language, Query } = await import("$PWD/$W/tree-sitter.js") +await Parser.init({ locateFile: () => "$PWD/$W/tree-sitter.wasm" }) +const grammar: Record<string, string> = { csharp: "c_sharp" } +const before: Record<string, string[]> = { cpp: ["c"] } +let bad = 0 +for (const ft of "%{opencode_system_grammars}".split(" ")) { + const g = grammar[ft] ?? ft + try { + const L = await Language.load("%{_datadir}/tree-sitter/wasm/tree-sitter-" + g + ".wasm") + const parts = [] + for (const q of [...(before[ft] ?? []), g]) parts.push(await Bun.file("%{_datadir}/tree-sitter/queries/" + q + "/highlights.scm").text()) + new Query(L, parts.join("\n")) + const p = new Parser(); p.setLanguage(L); if (!p.parse("x")) throw new Error("parse returned null") + } catch (e) { bad++; console.log(ft + ": " + String(e).slice(0, 200)) } +} +if (bad) process.exit(1) +EOF +bun check-grammars.ts +# What stays blocked (hcl, kotlin, vue, and anything upstream adds). +echo "descriptors still pointing at the web:" +grep -B1 -E 'wasm: "https://' packages/tui/src/parsers-config.ts | grep -oE 'filetype: "[a-z_]+"' || : + %files %license LICENSE licenses/LICENSE.caniuse-lite %doc README.md README.SUSE-maint %{_bindir}/%{name} +%dir %{_libdir}/%{name} +%{_libdir}/%{name}/libopencode-tsshim.so ++++++ README.SUSE-maint ++++++ --- /var/tmp/diff_new_pack.JRFnoi/_old 2026-09-16 17:44:19.492983407 +0200 +++ /var/tmp/diff_new_pack.JRFnoi/_new 2026-09-16 17:44:19.502983825 +0200 @@ -30,15 +30,30 @@ producing something subtly broken - see "OpenTUI" below. If it moved, bump the opentui package first and update opentui_version here. -2. Rebase the five patches. They are small and all of them sit in code +2. Rebase the six patches. They are small and all of them sit in code upstream changes rarely, but the grammar patch in particular is a single line in a large file. Drop opencode-fix-filesystem-cycle.patch once the release contains upstream PR 48397 (see "Runtime network access"). + opencode-native-tree-sitter.patch replaces the parser() block of + packages/opencode/src/tool/shell.ts; if upstream changes what shell.ts + reads from a node, shell-native.ts (added by the patch) must grow with it. + +3. Check the native pins. %prep fails if the vendor tree's @ff-labs/fff-bun, + bun-pty, @silvia-odwyer/photon-node or @parcel/watcher moved away from + fff_version, bun_pty_version, photon_version or parcel_watcher_version; + bump the fff, bun-pty and photon-node packages first (their ABIs are + private to the TypeScript in the tree, so the Requires are exact). The + grammar modules follow the tree-sitter-<lang>-wasm packages: the + BuildRequires are exact on ts_*_version, so a tree-sitter-<lang> + update in Factory makes this package unresolvable until the pin (and + the bundled() version it feeds) follows - by design. On an opentui + bump re-check its parsers-config.ts pins and highlight queries + against those versions. See "Native code" below. -3. Build locally, then read %check. It runs the compiled binary, which is the +4. Build locally, then read %check. It runs the compiled binary, which is the only thing that proves the payload survived packaging. -4. Re-run the licence check described under "Licensing". +5. Re-run the licence check described under "Licensing". Why the sources are generated @@ -156,16 +171,45 @@ OPENCODE_ALLOW_NPM_INSTALL=1 opencode-no-grammar-download.patch - 29 of the 34 syntax highlighting grammars in + All 34 syntax highlighting grammars in packages/tui/src/parsers-config.ts are https URLs; the .wasm comes from a GitHub release and the highlight queries from the master branch of nvim-treesitter, so what gets fetched changes over time. The five grammars @opentui/core ships as real assets - javascript, - typescript, markdown, markdown_inline, zig - still work; other - filetypes render unhighlighted, which is what upstream already does - when a download fails. + typescript, markdown, markdown_inline, zig - are not in that table + and still work; other filetypes render unhighlighted, which is what + upstream already does when a download fails. OPENCODE_ALLOW_GRAMMAR_DOWNLOAD=1 +31 of the 34 languages that paragraph is about now come from the +distribution instead: %prep rewrites packages/tui/src/parsers-config.ts so +that each descriptor's wasm and highlights point at +/usr/share/tree-sitter/wasm/tree-sitter-<grammar>.wasm and +/usr/share/tree-sitter/queries/<grammar>/highlights.scm (the loader takes a +local path exactly as it takes a URL, and a missing file means plain text +for that language, nothing worse), and the package Recommends the +tree-sitter-<lang>-wasm and -queries pair for each of them +(opencode_system_grammars in the spec; csharp's grammar is c_sharp in +tree-sitter-c-sharp; cpp's descriptor lists the C highlights before its +own, as upstream's tree-sitter-cpp query set is a delta on top of them). +The rewrite also drops the locals entries: nothing in @opentui/core reads +them, and they would otherwise keep web URLs in descriptors the download +block no longer filters. Three upstream entries stay blocked on purpose: +hcl, whose packaged grammar ships no queries; kotlin, where Factory +packages another grammar than the fwcd one upstream pins, also without +queries; and vue, whose highlights.scm inherits html_tags (upstream lists +two sources, the rewrite handles exactly one) and ends in a #set! with a +capture argument that web-tree-sitter 0.25 rejects, so it would never +compile. The rewrite asserts exactly one upstream highlights source and no +injections or locals left per descriptor; a descriptor that grows another +kind of entry needs the rewrite extended first. +%check loads every listed module with its queries in the web-tree-sitter +runtime opencode ships, so a query naming a node the packaged grammar lacks +fails the build rather than the highlighter. On a bump, diff upstream's +descriptor list against opencode_system_grammars: a new language needs its +grammar packaged (with -wasm and -queries) before it is added; until then +Patch3 keeps it blocked. + The other two patches are unrelated to the network. opencode-relax-bun-version.patch is, as of 1.18.30, inert. Upstream's build @@ -243,39 +287,61 @@ above must still hold - but a changed one only tells you where to look. -Prebuilt binaries ------------------ +Native code +----------- + +Everything compiled into the executable that is not JavaScript is built +from source, in three ways: -Some dependencies ship compiled code with no source in the tree, and it ends -up inside the executable. This is the part of the package a reviewer should -look at hardest. On aarch64 the embedded blobs are: - - @ff-labs/fff-bin-linux-arm64-gnu libfff_c.so 4.7 MB no source - @silvia-odwyer/photon-node photon_rs_bg.wasm 1.8 MB no source - bun-pty librust_pty.so 0.5 MB no source - tree-sitter-bash .node and .wasm 2.7 MB parser.c present - tree-sitter-powershell .wasm 0.9 MB parser.c present - @parcel/watcher-linux-arm64-glibc watcher.node 0.4 MB C++ in sibling - @opentui/core 5 grammar .wasm 3.1 MB no source - web-tree-sitter tree-sitter.wasm 0.2 MB no source - -What they do: fff is the fast file finder (OPENCODE_DISABLE_FFF falls back to -an internal walk), photon resizes images for vision models, bun-pty is the -pty behind the shell tool, and the tree-sitter pieces parse shell commands -for the permission system as well as highlighting. - -opencode_vendor removes everything of this kind that the two supported -architectures cannot run - foreign prebuilds/ directories, .dylib, .dll, -.exe, the Windows and x86_64 clipboardy fallbacks, web-tree-sitter's debug -build, and the .tgz copies some packages keep of themselves inside dist/. -That is 10.8 MB and it changes nothing about the result. What is listed above -is what remains after that, and it is genuinely used. - -Reducing it further means building those artefacts from source: the -tree-sitter grammars have their parser.c and could be compiled, though the -.wasm form needs emscripten; fff, bun-pty and photon are Rust with no source -in the tree at all and would each need their crate closure vendored -separately. + - Loaded from a package at run time (like libopentui.so): libfff_c.so + from the fff package (the file finder behind the @ file picker, + OPENCODE_DISABLE_FFF falls back to an internal walk) and librust_pty.so + from the bun-pty package (the pty behind the shell tool). %prep rewrites + the two npm loaders to those literal paths and removes the npm binaries, + so bun has nothing to embed. Both ABIs are unversioned and private to + the TypeScript shipped here, hence the exact Requires. + + - Embedded, built here from source: @parcel/watcher's watcher.node from the + C++ in its own npm package (binding.gyp's Linux branch, node-addon-api + headers from Source11). + + - Embedded, taken from packages: the five grammar modules @opentui/core + highlights with (javascript, typescript, markdown, markdown_inline, zig) + are the tree-sitter-<lang>-wasm subpackages' files, copied over the + asset directories before bun build. Those packages build every Factory + grammar to WebAssembly with the %treesitter_wasm_* macros (clang, lld, + wasi-libc through the tree-sitter CLI's own wasi-sdk invocation); the + modules parse identically to upstream's release modules under the + web-tree-sitter runtime opencode ships and accept opentui's highlight + queries (checked when this was introduced, against javascript 0.23.1 + and markdown 0.5.3 where opentui pins 0.25.0 and 0.5.1). + + - Embedded, taken from a package: photon_rs_bg.wasm and its glue from + photon-node (image resizing for vision models). The glue must be the one + the same wasm-bindgen wrote, so the whole npm package directory is + replaced by the package's files and upstream's two edits to the glue + (patches/@silvia-odwyer%2Fphoton-node@*.patch: bundle under bun, read + the module from where opencode embeds it) are re-applied by %prep. + +The shell tool's command parser no longer goes through wasm at all: +opencode-native-tree-sitter.patch loads libtree-sitter and the +tree-sitter-bash / tree-sitter-powershell packages' grammars through +bun:ffi, with libopencode-tsshim.so (Source12, installed under %_libdir) +walking the tree, because TSNode is returned by value and bun:ffi cannot +receive that. %check parses a pipeline through the shipped shim. + +One module is still the npm file: web-tree-sitter's tree-sitter.wasm +(205 KB, MIT), used by @opentui/core's highlighter. Its build needs +emscripten (MAIN_MODULE dynamic linking plus generated JavaScript glue), +which the distribution does not have. It is declared as +bundled(web-tree-sitter). Retiring it means either packaging emscripten or +giving the highlighter the same native treatment as the shell parser. + +%check reads bun's embedded-file table out of the executable and asserts it +lists exactly watcher.node, the five grammar modules, photon's module and +tree-sitter.wasm, and that the files built here are in the payload byte +for byte. (Counting ELF headers does not work: bun's own executable +contains the magic three times.) Things that will surprise you ++++++ opencode-native-tree-sitter.patch ++++++ From: Martin Pluskal <[email protected]> Subject: Parse shell commands with the system tree-sitter The permission system parses every shell command with tree-sitter. Upstream does that through web-tree-sitter, whose runtime module can only be built with emscripten, plus the grammar wasm files from the tree-sitter-bash and tree-sitter-powershell npm packages. The distribution build loads libtree-sitter and the grammar packages' shared objects through bun:ffi instead. TSNode is passed by value in the C API, which bun:ffi cannot receive, so opencode-tsshim.c walks the tree and hands over one JSON dump per parse; shell-native.ts turns that into the Node subset shell.ts reads. Placeholders @OPENCODE_LIBDIR@ and @TREE_SITTER_GRAMMARS@ are filled in by the spec. Distribution-specific, not for upstream. diff -Naur a/packages/opencode/src/tool/shell-native.ts b/packages/opencode/src/tool/shell-native.ts --- a/packages/opencode/src/tool/shell-native.ts 1970-01-01 01:00:00.000000000 +0100 +++ b/packages/opencode/src/tool/shell-native.ts 2026-09-14 17:51:24.493274369 +0200 @@ -0,0 +1,95 @@ +// Native tree-sitter for the shell tool: bun:ffi over the system libtree-sitter, +// with a C shim (opencode-tsshim.c) doing the tree walk because TSNode is +// returned by value, which bun:ffi cannot receive. Exposes the subset of the +// web-tree-sitter Node API that shell.ts reads: type, text, parent, children, +// descendantsOfType. + +import { CString, FFIType, dlopen, ptr } from "bun:ffi" + +const SHIM = process.env.OPENCODE_TSSHIM ?? "@OPENCODE_LIBDIR@/libopencode-tsshim.so" + +const shim = dlopen(SHIM, { + ocshim_parser_new: { args: [FFIType.ptr], returns: FFIType.ptr }, + ocshim_parser_delete: { args: [FFIType.ptr], returns: FFIType.void }, + ocshim_parse: { args: [FFIType.ptr, FFIType.ptr, FFIType.u32], returns: FFIType.ptr }, + ocshim_free: { args: [FFIType.ptr], returns: FFIType.void }, + ocshim_abi_version: { args: [FFIType.ptr], returns: FFIType.u32 }, +}) + +export function loadLanguage(path: string, symbol: string): number { + const lib = dlopen(path, { [symbol]: { args: [], returns: FFIType.ptr } }) + const lang = lib.symbols[symbol]() as number + if (!lang) throw new Error(`${symbol} returned NULL`) + return lang +} + +export class NativeNode { + children: NativeNode[] = [] + constructor( + readonly type: string, + readonly isNamed: boolean, + readonly startIndex: number, + readonly endIndex: number, + readonly parent: NativeNode | null, + private readonly buf: Buffer, + ) {} + get text(): string { + return this.buf.subarray(this.startIndex, this.endIndex).toString("utf8") + } + get namedChildren(): NativeNode[] { + return this.children.filter((c) => c.isNamed) + } + get childCount(): number { + return this.children.length + } + child(index: number): NativeNode | null { + return this.children[index] ?? null + } + descendantsOfType(types: string | string[]): NativeNode[] { + const set = new Set(Array.isArray(types) ? types : [types]) + const out: NativeNode[] = [] + const walk = (n: NativeNode) => { + if (set.has(n.type)) out.push(n) + for (const c of n.children) walk(c) + } + walk(this) + return out + } + toString(): string { + return this.isNamed ? `(${this.type}${this.children.filter((c) => c.isNamed).map((c) => " " + c.toString()).join("")})` : "" + } +} + +type Raw = [string, number, number, number, Raw[]] + +// The C side frees the tree before returning, so there is nothing to +// release here; delete() exists because shell.ts calls it. +export class NativeTree { + constructor(readonly rootNode: NativeNode) {} + delete(): void {} +} + +export class NativeParser { + private p: number + constructor(lang: number) { + const p = shim.symbols.ocshim_parser_new(lang) as number + if (!p) throw new Error(`ts_parser_set_language failed (grammar ABI ${shim.symbols.ocshim_abi_version(lang)})`) + this.p = p + } + parse(src: string): NativeTree { + const buf = Buffer.from(src, "utf8") + const out = shim.symbols.ocshim_parse(this.p, ptr(buf), buf.length) as number + if (!out) throw new Error("ts_parser_parse_string failed") + const json = new CString(out).toString() + shim.symbols.ocshim_free(out) + const build = (a: Raw, parent: NativeNode | null): NativeNode => { + const n = new NativeNode(a[0], a[1] === 1, a[2], a[3], parent, buf) + n.children = a[4].map((c) => build(c, n)) + return n + } + return new NativeTree(build(JSON.parse(json) as Raw, null)) + } + delete() { + shim.symbols.ocshim_parser_delete(this.p) + } +} diff -Naur a/packages/opencode/src/tool/shell.ts b/packages/opencode/src/tool/shell.ts --- a/packages/opencode/src/tool/shell.ts 2026-09-14 17:27:48.650152184 +0200 +++ b/packages/opencode/src/tool/shell.ts 2026-09-14 17:27:48.718159827 +0200 @@ -6,7 +6,7 @@ import { containsPath, type InstanceContext } from "../project/instance-context" import { InstanceState } from "@/effect/instance-state" import { lazy } from "@/util/lazy" -import { Language, type Node } from "web-tree-sitter" +import { NativeParser, loadLanguage, type NativeNode as Node } from "./shell-native" import { FSUtil } from "@opencode-ai/core/fs-util" import { fileURLToPath } from "url" @@ -309,29 +309,11 @@ }) } const parser = lazy(async () => { - const { Parser } = await import("web-tree-sitter") - const { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm" as string, { - with: { type: "wasm" }, - }) - const treePath = resolveWasm(treeWasm) - await Parser.init({ - locateFile() { - return treePath - }, - }) - const { default: bashWasm } = await import("tree-sitter-bash/tree-sitter-bash.wasm" as string, { - with: { type: "wasm" }, - }) - const { default: psWasm } = await import("tree-sitter-powershell/tree-sitter-powershell.wasm" as string, { - with: { type: "wasm" }, - }) - const bashPath = resolveWasm(bashWasm) - const psPath = resolveWasm(psWasm) - const [bashLanguage, psLanguage] = await Promise.all([Language.load(bashPath), Language.load(psPath)]) - const bash = new Parser() - bash.setLanguage(bashLanguage) - const ps = new Parser() - ps.setLanguage(psLanguage) + // The distribution build parses with the system libtree-sitter and the + // tree-sitter-bash / tree-sitter-powershell grammar packages instead of + // the wasm copies from npm; see shell-native.ts. + const bash = new NativeParser(loadLanguage("@TREE_SITTER_GRAMMARS@/libtree-sitter-bash.so", "tree_sitter_bash")) + const ps = new NativeParser(loadLanguage("@TREE_SITTER_GRAMMARS@/libtree-sitter-powershell.so", "tree_sitter_powershell")) return { bash, ps } }) ++++++ opencode-no-grammar-download.patch ++++++ --- /var/tmp/diff_new_pack.JRFnoi/_old 2026-09-16 17:44:19.623988881 +0200 +++ /var/tmp/diff_new_pack.JRFnoi/_new 2026-09-16 17:44:19.631989215 +0200 @@ -1,6 +1,6 @@ The syntax highlighting table in packages/tui/src/parsers-config.ts is 34 -entries, and 29 of them are plain https URLs: the tree-sitter grammar is a -.wasm fetched from a GitHub release and the highlight queries come from +entries, all of them plain https URLs: the tree-sitter grammar is a .wasm +fetched from a GitHub release and the highlight queries come from raw.githubusercontent.com. opentui downloads and caches them under ~/.local/share/opentui/tree-sitter the first time a file of that type is rendered, then loads the .wasm into the process. There is no env var and no @@ -11,7 +11,8 @@ nvim-treesitter rather than a tag, so what is fetched changes over time. The five grammars @opentui/core ships as real assets - javascript, -typescript, markdown, markdown_inline, zig - are local paths and are kept. +typescript, markdown, markdown_inline, zig - are not in that table and keep +working. Everything else renders as unhighlighted text, which is what loadFiletypeParser already does when a download fails, so this is a path upstream handles rather than a new one. ++++++ opencode-tsshim.c ++++++ /* Tree-sitter shim for the opencode shell tool (see the native-tree-sitter * patch). libtree-sitter passes TSNode by value, which bun:ffi cannot marshal, * so the walk happens here and one JSON string crosses the FFI boundary: * node := [type, isNamed, startByte, endByte, [node...]] */ #include <tree_sitter/api.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char *buf; size_t len, cap; } sb; static void sb_put(sb *b, const char *s, size_t n) { if (b->len + n + 1 > b->cap) { size_t c = b->cap ? b->cap * 2 : 4096; while (c < b->len + n + 1) c *= 2; b->buf = realloc(b->buf, c); b->cap = c; } memcpy(b->buf + b->len, s, n); b->len += n; b->buf[b->len] = 0; } static void sb_str(sb *b, const char *s) { sb_put(b, "\"", 1); for (; *s; s++) { unsigned char c = (unsigned char)*s; if (c == '"' || c == '\\') { char e[2] = {'\\', (char)c}; sb_put(b, e, 2); } else if (c < 0x20) { char e[8]; int n = snprintf(e, sizeof e, "\\u%04x", c); sb_put(b, e, (size_t)n); } else sb_put(b, s, 1); } sb_put(b, "\"", 1); } static void dump(sb *b, TSTreeCursor *c) { TSNode n = ts_tree_cursor_current_node(c); char tmp[64]; sb_put(b, "[", 1); sb_str(b, ts_node_type(n)); int len = snprintf(tmp, sizeof tmp, ",%d,%u,%u,[", ts_node_is_named(n) ? 1 : 0, ts_node_start_byte(n), ts_node_end_byte(n)); sb_put(b, tmp, (size_t)len); if (ts_tree_cursor_goto_first_child(c)) { int first = 1; do { if (!first) sb_put(b, ",", 1); first = 0; dump(b, c); } while (ts_tree_cursor_goto_next_sibling(c)); ts_tree_cursor_goto_parent(c); } sb_put(b, "]]", 2); } TSParser *ocshim_parser_new(const TSLanguage *lang) { TSParser *p = ts_parser_new(); if (!ts_parser_set_language(p, lang)) { ts_parser_delete(p); return NULL; } return p; } void ocshim_parser_delete(TSParser *p) { ts_parser_delete(p); } char *ocshim_parse(TSParser *p, const char *src, uint32_t len) { TSTree *t = ts_parser_parse_string(p, NULL, src, len); if (!t) return NULL; TSTreeCursor c = ts_tree_cursor_new(ts_tree_root_node(t)); sb b = {0}; dump(&b, &c); ts_tree_cursor_delete(&c); ts_tree_delete(t); return b.buf; } void ocshim_free(char *s) { free(s); } uint32_t ocshim_abi_version(const TSLanguage *lang) { return ts_language_abi_version(lang); } ++++++ opencode-vendor-1.18.30.tar.zst ++++++ /work/SRC/openSUSE:Factory/opencode/opencode-vendor-1.18.30.tar.zst /work/SRC/openSUSE:Factory/.opencode.new.383539/opencode-vendor-1.18.30.tar.zst differ: char 7, line 1 ++++++ opencode_vendor ++++++ --- /var/tmp/diff_new_pack.JRFnoi/_old 2026-09-16 17:44:19.800996276 +0200 +++ /var/tmp/diff_new_pack.JRFnoi/_new 2026-09-16 17:44:19.808996610 +0200 @@ -178,6 +178,18 @@ # from source. The shims next to it (index.js, package.json) stay, because # that is how @opentui/core locates the library. find "$store" -name 'libopentui.so' -delete -printf ' removed %P\n' +# The same goes for the rest of the native code, see README.SUSE-maint +# "Native code": libfff_c.so and librust_pty.so come from the fff and bun-pty +# packages at run time, photon's module and glue from photon-node, and +# watcher.node and the highlighter's grammar modules are compiled in %build. +# The shell tool parses with the system tree-sitter, so its two grammar +# packages' wasm and node prebuilds are unreferenced; web-tree-sitter's lib/ +# copy and the source maps are unreachable through its exports map. +find "$store" \( -name 'libfff_c.so' -o -name 'librust_pty*.so' -o -name 'watcher.node' \ + -o -name 'photon_rs_bg.wasm' -o -name 'photon_rs.js' -o -name 'photon_rs_bg.js' \ + -o -name 'tree-sitter-*.wasm' -o -name 'tree-sitter.wasm.map' \) -delete -printf ' removed %P\n' +find "$store" -path '*/tree-sitter-bash/prebuilds' -prune -exec rm -rf {} \; -printf ' removed %P\n' +find "$store" -path '*/web-tree-sitter/lib/tree-sitter.wasm*' -delete -printf ' removed %P\n' echo "==> packing the dependency tree" # The per-workspace node_modules are symlink farms into node_modules/.bun and
