WebSocket native is now built for darwin-arm64 — source is at
github.com/SnowLuma/snowluma-websocket (cmake-js + node-addon-api,
clones to dev/snowluma-websocket and builds with `npm install`).
npm install in dev/snowluma-websocket → build/Release/snowluma_ws.node
cp → packages/runtime/native/websocket-darwin-arm64.node (102 KB)
Reverts the SNOWLUMA_BUILD_SKIP_NATIVES escape hatch in vite.config.ts
that 67f8bf3 added — no longer needed; the darwin native binary is
present. Keeps `packages/core/config/.gitignore` (independently useful
runtime-state hygiene). vite build now runs clean on darwin with no
env overrides; the runtime SNOWLUMA_DEV_NO_NATIVE=1 stub also becomes
optional (only needed if the operator wants to bypass WebSocket I/O
specifically).
Existing native binaries unchanged. CI invariant restored: every
release build must include every platform's native set.
Switch from open-source to a NapCat-style source-available, NON-commercial
license so the free software cannot be resold / paid-deployed — which an OSI
license (AGPL included) cannot prevent. Source stays public for study and
non-commercial self-hosting; commercial use and public release of modifications
require written permission; the native addon (snowluma-*.node) is carved out as
proprietary. Explicitly NOT OSI open source.
- LICENSE: SnowLuma Source-Available Non-Commercial License v1.0 (bilingual)
- all 14 package.json: "license": "SEE LICENSE IN LICENSE"
- README license section; CONTRIBUTING contribution-licensing note + a grant
letting the project relicense contributions under commercial terms
(contributors keep copyright)
Remove the in-tree @snowluma/sqlite wrapper and vendored better-sqlite3 prebuilts; migrate consumers to node:sqlite's synchronous API. Replaced imports and types (Database/Statement -> DatabaseSync/StatementSync), updated PRAGMA calls to use exec, and adjusted code in onebot (media/message/reaction stores) and protocol (identity service). Vite config no longer bundles sqlite native binaries and references to the sqlite workspace were removed. Also delete the packages/sqlite package and its native prebuild artifacts, and add a couple of unrelated AST improvements in proton: use a module-level shared wrapper cache and add an inheritance cache to avoid repeated scans. Minor import reordering/cleanup applied where needed.
Replaces "runtime npm dependency that gets `npm install`-ed into
dist/" with "TS wrapper that loads the native `.node` from
`dist/native/` like every other native binding in the project".
Why the change
--------------
The previous approach (better-sqlite3 declared in
packages/runtime/package.json, materialized by `npm install
--omit=dev` in dist/) worked but produced an artifact that needed
runtime npm install — a step the rest of SnowLuma's native bindings
don't need (websocket / snowluma-hook / ffmpeg are all standalone
`.node` files copied into `dist/native/` by vite-plugin-cp). The
asymmetry was friction: dev mode required `pnpm dist:install`,
build artifacts carried a `dist/node_modules/` tree the other
modules didn't need, and Docker images had to special-case the
glibc-vs-musl ABI lookup that prebuild-install runs.
The new shape:
- `@snowluma/sqlite` (packages/sqlite/) — TS wrapper. Has
`better-sqlite3` as a devDep (for the JS-layer types + the
actual JS wrapper code that vite inlines). At runtime, calls
`createRequire().require()` against
`<dist|repo>/native/better-sqlite3-v<abi>-<platform>-<arch>.node`
and passes the resolved addon to `new Database(path, {
nativeBinding })`.
- `packages/runtime/native/better-sqlite3-v<abi>-<triplet>.node`
— prebuilt binaries vendored into the repo. Two ABIs ship side-
by-side (v127 = Node 22 LTS used by CI, v137 = Node 24 used by
dev boxes) so `process.versions.modules` picks the matching one
at load time.
- `tools/fetch-sqlite-prebuilts.mjs` — fetches all 5 platforms ×
N ABIs from WiseLibs's GitHub releases. Idempotent: existing
`.node` files skip. Accepts extra ABI args:
`node tools/fetch-sqlite-prebuilts.mjs 12.10.0 v127 v137 v115`.
What was unwound
----------------
- packages/runtime/package.json — dependencies removed (runtime
artifact is now self-contained, no `npm install` step needed).
- root package.json — `dist:install` script removed (no node_modules
to install).
- .github/workflows/dev-build.yml — `Bundle runtime dependencies`
step removed.
- .github/workflows/release.yml — kept the `Bundle runtime
dependencies` step (was a no-op already, but harmless safety net
in case a future runtime dep shows up).
- packages/core/vite.config.ts — `better-sqlite3` no longer
external; vite inlines the JS wrapper (+~33 KB to bundle, ~2.84
MB → 2.87 MB total).
Naming
------
Mirrors the existing `websocket-${triplet}.node` convention (flat
dir under `runtime/native/`, dash-separated `<platform>-<arch>`).
Adds a `v<abi>` segment because better-sqlite3 is NOT N-API — one
binary per Node ABI. Naming:
better-sqlite3-v127-darwin-arm64.node Node 22 / macOS arm
better-sqlite3-v127-darwin-x64.node Node 22 / macOS intel
better-sqlite3-v127-linux-arm64.node Node 22 / Linux arm
better-sqlite3-v127-linux-x64.node Node 22 / Linux x64
better-sqlite3-v127-win32-x64.node Node 22 / Windows x64
better-sqlite3-v137-... (5) Node 24 / same 5 hosts
Total: 10 binaries × ~1.9 MB = ~19 MB committed. Aligned with the
existing native-vendoring footprint (websocket ~2 MB × 5, snowluma-
hook ~10 MB × Windows + Linux, ffmpeg ~10-30 MB × 4).
What changes for consumers
--------------------------
Source-side: nothing. `import Database, { type Database, type
Statement } from '@snowluma/sqlite'` is a drop-in for the previous
`import Database from 'better-sqlite3'`. The wrapper re-exports the
same types and the `Database` callable factory has the same
overload set.
Runtime: `node` finds the binding at
`<dist|repo>/native/better-sqlite3-v${process.versions.modules}-
${process.platform}-${process.arch}.node`. If the ABI doesn't
match a vendored binary, the wrapper throws a clear error pointing
at `tools/fetch-sqlite-prebuilts.mjs` with the missing ABI/triplet.
Bumped better-sqlite3 from 11.10.0 → 12.10.0 because v11 only had
prebuilt up to Node ABI v127 (no v137 = no Node 24). v12.10.0
ships both. Both versions are API-compat (the breaking changes
between v11 and v12 were either internal or in error-handling
paths SnowLuma doesn't exercise).
Verification
------------
- pnpm -r typecheck (12 projects): clean
- @snowluma/core test: 302/302
- @snowluma/onebot test: 250/250 (552/552 combined, unchanged from
the perf-series baseline)
- Bench numbers preserved — wrapper indirection is sub-μs:
storeMeta ~11 μs, storeEvent ~17 μs, findMeta hit ~1.1 μs (all
within noise of the post-RoadMap-#5 measurements).
- Vite build clean, bundle = 2.87 MB / 230 KB gzip
- `dist/index.mjs` no longer contains a `node:sqlite` import (it
did before — media-store hadn't migrated; now neither it nor
message-store reach into the Node builtin).
Bonus side-fix: `packages/onebot/src/media-store.ts` also moved
off `node:sqlite` (caught by the post-build bundle grep — the
file was using `DatabaseSync` + `StatementSync` from the builtin
even though it had already been written to cache its prepared
statements). Its hot paths now go through the same wrapper.
Followup to 0b6541c. That commit added `better-sqlite3` as a
workspace dependency and marked it as a vite external — meaning
the bundled `dist/index.mjs` does `require('better-sqlite3')` at
runtime instead of inlining its JS wrapper. Without the binding
+ wrapper present in the production archive, every shipped
SnowLuma would crash on first SQLite op.
The fix is in three small pieces:
1. `packages/runtime/package.json` — declare `better-sqlite3` as a
runtime dep. `vite-plugin-cp` copies this manifest into
`dist/package.json` during build (see `runtimeDistFiles` in
`packages/core/vite.config.ts`), so the release workflow's
existing `npm install --omit=dev` step inside `dist/` picks it
up and compiles the native binding for the matrix target via
prebuild-install (with node-gyp fallback). This is the same
mechanism release.yml has used since v1.x — it was just empty
until now because the previous vite config bundled everything.
2. `.github/workflows/dev-build.yml` — mirror the
`Bundle runtime dependencies` step from release.yml. Dev-build
was packaging `dist/` straight after vite, which would have
shipped a broken artifact (no `dist/node_modules/`). Added the
`npm install --omit=dev` step before the tar/zip packaging
steps so dev-build archives carry the binding too.
3. `package.json` root — add `dist:install` script (just
`cd dist && npm install --omit=dev`) so local prod-mimic flows
work: `pnpm build && pnpm dist:install && pnpm dev:mode`. Not
chained into `pnpm build` itself to keep the watch-cycle fast
for users who only need the vite build output; the install
step adds ~17s.
Note on dev mode: `pnpm dev` continues to work unchanged — it
runs via tsx against workspace `node_modules/`, which already has
`better-sqlite3` compiled (from the workspace-level pnpm install).
Only the dist/-based prod-mimic path needed the explicit install.
Verification
------------
- `pnpm build` (with stubbed native binary): clean, dist/ has the
updated `dist/package.json` with the dependency declared
- `pnpm dist:install`: installs 38 packages, compiles
`dist/node_modules/better-sqlite3/build/Release/better_sqlite3.node`
- Smoke-test from `dist/`: `node -e "import('better-sqlite3')..."`
prints `OK: [ { id: 42 } ]`, confirming the binding loads + a
basic INSERT/SELECT roundtrip works.
CI matrix coverage: win-x64, linux-x64, linux-arm64 (release.yml +
dev-build.yml share the same matrix). Each builds its own native
binding during the install step — prebuild-install hits
better-sqlite3's prebuilt-binary registry for these three targets
(no source compile needed in the common case, falls back to
node-gyp if a prebuilt isn't available).
- WebUI sidebar/login display and OneBot get_version_info now read the
root package.json version via Vite `define`, removing hardcoded
literals in webui/src/types.ts and core/onebot/actions/info.ts.
- tools/bump-version.mjs (exposed as `pnpm bump <ver>`) syncs the
version field across the root and all five workspace packages.
- Bump everything to 1.7.8.
Introduce NT SILK-based voice (PTT) upload pipeline and native FFmpeg addon wrapper.
- Add ffmpeg-addon wrapper (packages/core/src/bridge/highway/ffmpeg-addon.ts) to load platform/arch .node binaries, detect SILK files, and convert inputs to NT SILK via the native addon. Provides encodeSilk, isSilkFile and default temp dir helpers.
- Implement ptt-upload pipeline (packages/core/src/bridge/highway/ptt-upload.ts) to: resolve/fetch audio bytes, convert to NT SILK if needed, send OIDB upload request, perform highway HTTP upload when required, and produce Encodable MsgInfo bytes for commonElem.
- Wire record element support into element-builder: uploadPttMsgInfo is used to build a commonElem for 'record' sends; when SendContext is absent, record sends are skipped with a warning (graceful no-bridge path).
- Update message parsing to accept record segments from data.file, data.url or data.path and drop empty sources.
- Enable canSendRecord in OneBot instance context.
- Add tests covering record parsing and the graceful send path (packages/core/tests/message-parser.test.ts).
- Include prebuilt native ffmpeg addon binaries under packages/runtime/native/ffmpeg and update vite config to validate & copy the ffmpeg addon into the dist layout.
These changes enable sending voice messages by converting/encoding sources to the expected NT SILK format and uploading them through the existing highway/NTV2 flows.