From 9be612c72fa7dd34d6a558e28c3564985ab2e2c2 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 01:13:37 -0700 Subject: [PATCH 01/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): bump Cargo.toml workspace dep pins during release cut --- scripts/publish/src/lib/version.ts | 1 + scripts/publish/src/local/cut-release.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/scripts/publish/src/lib/version.ts b/scripts/publish/src/lib/version.ts index e98e3ac5e1..fd58d289b6 100644 --- a/scripts/publish/src/lib/version.ts +++ b/scripts/publish/src/lib/version.ts @@ -60,6 +60,7 @@ const PUBLISHED_RUST_WORKSPACE_DEPS = new Set([ "rivetkit-inspector-protocol", "rivetkit-client", "rivetkit-core", + "rivetkit-engine-process", ]); export interface BumpOptions { diff --git a/scripts/publish/src/local/cut-release.ts b/scripts/publish/src/local/cut-release.ts index b909cf5abb..1d95e18863 100644 --- a/scripts/publish/src/local/cut-release.ts +++ b/scripts/publish/src/local/cut-release.ts @@ -24,6 +24,7 @@ import { Command } from "commander"; import { $ } from "execa"; import { scoped } from "../lib/logger.js"; import { + bumpCargoVersions, bumpPackageJsons, getLatestGitVersion, listRecentVersions, @@ -137,6 +138,14 @@ async function main() { log.info("updating source files (Cargo.toml, examples)"); await updateSourceFiles(repoRoot, version); + // Bump the Cargo.toml workspace dependency pins (the `version = "=X"` + // exact pins on internal crates). updateSourceFiles only rewrites the + // [workspace.package] version, so without this the internal crate pins + // stay on the previous version and the Rust/wasm build fails to resolve. + // Always writes (like updateSourceFiles); dry-run still mutates source + // files and only skips the commit/push/trigger tail. + await bumpCargoVersions(repoRoot, version); + // 6. Rewrite package.json version fields via discovery. Uses versionOnly // mode so `workspace:*` dep specs are preserved — the lockfile depends on // them. CI runs the full publish-time bump (with dep rewriting + From 031e3a443a726341a162443890c6183f10c08cf3 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 10:49:40 -0700 Subject: [PATCH 02/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(rivetkit): resolve engine binary path on local-endpoint auto-spawn --- .../packages/rivetkit/src/registry/native.ts | 8 +++++++- .../packages/rivetkit/src/registry/runtime.ts | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts index a0a9e2af83..f5ea4bbfb1 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts @@ -31,6 +31,7 @@ import { } from "@/client/client"; import { convertRegistryConfigToClientConfig } from "@/client/config"; import { HEADER_CONN_PARAMS } from "@/common/actor-router-consts"; +import { isLocalEngineEndpoint } from "@/common/engine"; import type { AnyDatabaseProvider } from "@/common/database/config"; import { wrapJsNativeDatabase } from "@/common/database/native-database"; import { assertJsonCompatValue, type JsonCompatValue } from "@/common/encoding"; @@ -4756,7 +4757,12 @@ export async function buildServeConfig( serverlessMaxStartPayloadBytes: config.serverless.maxStartPayloadBytes, }; - if (config.startEngine) { + // Provide the engine binary path whenever the core will manage a local + // engine. The core auto-spawns the engine for any loopback endpoint (its + // EngineSpawnMode::Auto), not only when `startEngine` is set, so gating the + // binary path on `startEngine` alone leaves auto-spawn unable to locate the + // npm-installed engine binary and fail with engine.binary_unavailable. + if (config.startEngine || isLocalEngineEndpoint(config.endpoint)) { const { getEnginePath } = await loadEngineCli(); serveConfig.engineBinaryPath = getEnginePath(); serveConfig.engineHost = config.engineHost; diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts index 35f8e4748d..a67b90ff95 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts @@ -1,4 +1,5 @@ import type { SqliteNativeMetrics } from "@/common/database/config"; +import { isLocalEngineEndpoint } from "@/common/engine"; import type { RegistryConfig } from "./config"; declare const handleBrand: unique symbol; @@ -599,7 +600,12 @@ export async function buildServeConfig( serverlessMaxStartPayloadBytes: config.serverless.maxStartPayloadBytes, }; - if (config.startEngine) { + // Provide the engine binary path whenever the core will manage a local + // engine. The core auto-spawns the engine for any loopback endpoint (its + // EngineSpawnMode::Auto), not only when `startEngine` is set, so gating the + // binary path on `startEngine` alone leaves auto-spawn unable to locate the + // npm-installed engine binary and fail with engine.binary_unavailable. + if (config.startEngine || isLocalEngineEndpoint(config.endpoint)) { serveConfig.engineBinaryPath = await loadEnginePath(); serveConfig.engineHost = config.engineHost; serveConfig.enginePort = config.enginePort; From ebfd15119c98a7eb8851ee375e41422a008947f9 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 12:09:45 -0700 Subject: [PATCH 03/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): order envoy-protocol before depot-client; allow re-run when npm already published --- scripts/publish/src/ci/bin.ts | 5 ++++- scripts/publish/src/lib/npm.ts | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/publish/src/ci/bin.ts b/scripts/publish/src/ci/bin.ts index 9a46b8514d..59bfe1d3ad 100644 --- a/scripts/publish/src/ci/bin.ts +++ b/scripts/publish/src/ci/bin.ts @@ -47,9 +47,12 @@ const RUST_CRATES = [ "rivet-error", "rivet-metrics", "rivet-util-serde", + // rivet-envoy-protocol must precede rivet-depot-client, which pins it as an + // exact dependency. Publishing depot-client first makes cargo fail to + // resolve rivet-envoy-protocol because it is not yet on crates.io. + "rivet-envoy-protocol", "rivet-depot-client-types", "rivet-depot-client", - "rivet-envoy-protocol", "rivetkit-shared-types", "rivet-envoy-client", "rivetkit-actor-persist", diff --git a/scripts/publish/src/lib/npm.ts b/scripts/publish/src/lib/npm.ts index 3215ad65a9..9efcc1b0db 100644 --- a/scripts/publish/src/lib/npm.ts +++ b/scripts/publish/src/lib/npm.ts @@ -385,8 +385,12 @@ export async function publishAll( counts.failed === 0 && counts.alreadyExists === packages.length ) { - throw new Error( - `release mode: all ${packages.length} packages already published at this version. Did you forget to bump the version?`, + // This usually means a forgotten version bump, but it also happens on a + // legitimate re-run after npm fully published and a later pipeline step + // (crates.io, git tag, GitHub release) failed. Warn instead of failing so + // the re-run can reach those downstream steps; they are idempotent. + log.warn( + `release mode: all ${packages.length} packages already published at this version. Continuing (assuming a re-run); ensure the version was bumped.`, ); } From 870e9cb15bad73a69dda9d9f6c554f6158fcad7b Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 12:34:03 -0700 Subject: [PATCH 04/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): add rivetkit-engine-process to crates publish list --- scripts/publish/src/ci/bin.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/publish/src/ci/bin.ts b/scripts/publish/src/ci/bin.ts index 59bfe1d3ad..17fc5ea3c4 100644 --- a/scripts/publish/src/ci/bin.ts +++ b/scripts/publish/src/ci/bin.ts @@ -59,6 +59,9 @@ const RUST_CRATES = [ "rivetkit-client-protocol", "rivetkit-inspector-protocol", "rivetkit-client", + // rivetkit-core has an optional dependency on rivetkit-engine-process, which + // cargo still requires to be resolvable on crates.io at publish time. + "rivetkit-engine-process", "rivetkit-core", "rivetkit", ] as const; From e5bdda7ebd06c25c892f4b37e21e32e96e40a2c8 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 13:32:12 -0700 Subject: [PATCH 05/10] [SLOP(claude-opus-4-8[1m]-medium)] refactor(rivetkit): always best-effort pass engine binary path; core owns spawn decision --- .../packages/rivetkit/src/registry/native.ts | 22 +++++++++++-------- .../packages/rivetkit/src/registry/runtime.ts | 20 +++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts index f5ea4bbfb1..0307caf25f 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts @@ -31,7 +31,6 @@ import { } from "@/client/client"; import { convertRegistryConfigToClientConfig } from "@/client/config"; import { HEADER_CONN_PARAMS } from "@/common/actor-router-consts"; -import { isLocalEngineEndpoint } from "@/common/engine"; import type { AnyDatabaseProvider } from "@/common/database/config"; import { wrapJsNativeDatabase } from "@/common/database/native-database"; import { assertJsonCompatValue, type JsonCompatValue } from "@/common/encoding"; @@ -4757,17 +4756,22 @@ export async function buildServeConfig( serverlessMaxStartPayloadBytes: config.serverless.maxStartPayloadBytes, }; - // Provide the engine binary path whenever the core will manage a local - // engine. The core auto-spawns the engine for any loopback endpoint (its - // EngineSpawnMode::Auto), not only when `startEngine` is set, so gating the - // binary path on `startEngine` alone leaves auto-spawn unable to locate the - // npm-installed engine binary and fail with engine.binary_unavailable. - if (config.startEngine || isLocalEngineEndpoint(config.endpoint)) { + // Always best-effort resolve the npm-installed engine binary and hand its + // path to the core. The core alone decides whether to actually spawn a local + // engine (its `should_manage_engine`, based on the endpoint + spawn mode), so + // JS must not duplicate that decision here. Only JS knows the npm + // `node_modules` layout, so it resolves the path; if no binary is available + // (remote-only install, unsupported platform, optional deps skipped), leave + // it unset and let the core report `engine.binary_unavailable` if it actually + // needs one. + try { const { getEnginePath } = await loadEngineCli(); serveConfig.engineBinaryPath = getEnginePath(); - serveConfig.engineHost = config.engineHost; - serveConfig.enginePort = config.enginePort; + } catch { + // No local engine binary resolvable; the core decides whether it needs one. } + serveConfig.engineHost = config.engineHost; + serveConfig.enginePort = config.enginePort; if (config.test?.enabled) { serveConfig.inspectorTestToken = getEnvUniversal("_RIVET_TEST_INSPECTOR_TOKEN") ?? "token"; diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts index a67b90ff95..6e033ab72c 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts @@ -1,5 +1,4 @@ import type { SqliteNativeMetrics } from "@/common/database/config"; -import { isLocalEngineEndpoint } from "@/common/engine"; import type { RegistryConfig } from "./config"; declare const handleBrand: unique symbol; @@ -600,16 +599,19 @@ export async function buildServeConfig( serverlessMaxStartPayloadBytes: config.serverless.maxStartPayloadBytes, }; - // Provide the engine binary path whenever the core will manage a local - // engine. The core auto-spawns the engine for any loopback endpoint (its - // EngineSpawnMode::Auto), not only when `startEngine` is set, so gating the - // binary path on `startEngine` alone leaves auto-spawn unable to locate the - // npm-installed engine binary and fail with engine.binary_unavailable. - if (config.startEngine || isLocalEngineEndpoint(config.endpoint)) { + // Always best-effort resolve the engine binary path and hand it to the core. + // The core alone decides whether to actually spawn a local engine, so JS must + // not duplicate that decision here. `loadEnginePath` throws when no binary is + // available (remote-only install, unsupported platform, optional deps + // skipped); in that case leave it unset and let the core report + // `engine.binary_unavailable` only if it actually needs one. + try { serveConfig.engineBinaryPath = await loadEnginePath(); - serveConfig.engineHost = config.engineHost; - serveConfig.enginePort = config.enginePort; + } catch { + // No local engine binary resolvable; the core decides whether it needs one. } + serveConfig.engineHost = config.engineHost; + serveConfig.enginePort = config.enginePort; if (config.test?.enabled) { serveConfig.inspectorTestToken = process.env._RIVET_TEST_INSPECTOR_TOKEN ?? "token"; From 966c323f8254fd2d610a2498b06223201783861d Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 14:32:51 -0700 Subject: [PATCH 06/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): build inspector-ui before napi compile so it embeds in rivetkit-core --- docker/build/darwin-arm64.Dockerfile | 6 ++++++ docker/build/darwin-x64.Dockerfile | 6 ++++++ docker/build/linux-arm64-gnu.Dockerfile | 6 ++++++ docker/build/linux-arm64-musl.Dockerfile | 6 ++++++ docker/build/linux-x64-gnu.Dockerfile | 6 ++++++ docker/build/linux-x64-musl.Dockerfile | 6 ++++++ docker/build/windows-x64.Dockerfile | 6 ++++++ .../packages/rivetkit-napi/scripts/build.mjs | 14 ++++++++++++++ 8 files changed, 56 insertions(+) diff --git a/docker/build/darwin-arm64.Dockerfile b/docker/build/darwin-arm64.Dockerfile index 71c0ec23f7..3dcc91a4a3 100644 --- a/docker/build/darwin-arm64.Dockerfile +++ b/docker/build/darwin-arm64.Dockerfile @@ -43,6 +43,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-darwin-arm64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/darwin-x64.Dockerfile b/docker/build/darwin-x64.Dockerfile index 2d2433b434..485c94fd73 100644 --- a/docker/build/darwin-x64.Dockerfile +++ b/docker/build/darwin-x64.Dockerfile @@ -43,6 +43,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-darwin-x64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-arm64-gnu.Dockerfile b/docker/build/linux-arm64-gnu.Dockerfile index b4bf311e39..b36037633d 100644 --- a/docker/build/linux-arm64-gnu.Dockerfile +++ b/docker/build/linux-arm64-gnu.Dockerfile @@ -30,6 +30,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-linux-arm64-gnu,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-arm64-musl.Dockerfile b/docker/build/linux-arm64-musl.Dockerfile index eabcf6ee2b..15d71dd4cc 100644 --- a/docker/build/linux-arm64-musl.Dockerfile +++ b/docker/build/linux-arm64-musl.Dockerfile @@ -36,6 +36,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-linux-arm64-musl,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-x64-gnu.Dockerfile b/docker/build/linux-x64-gnu.Dockerfile index 32761c180a..3046e7ef8a 100644 --- a/docker/build/linux-x64-gnu.Dockerfile +++ b/docker/build/linux-x64-gnu.Dockerfile @@ -39,6 +39,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi # Build binary. diff --git a/docker/build/linux-x64-musl.Dockerfile b/docker/build/linux-x64-musl.Dockerfile index 19bcb67aea..d41a10fd3c 100644 --- a/docker/build/linux-x64-musl.Dockerfile +++ b/docker/build/linux-x64-musl.Dockerfile @@ -35,6 +35,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-linux-x64-musl,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/windows-x64.Dockerfile b/docker/build/windows-x64.Dockerfile index d55ecfe59e..0265456578 100644 --- a/docker/build/windows-x64.Dockerfile +++ b/docker/build/windows-x64.Dockerfile @@ -43,6 +43,12 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build:engine -F @rivetkit/engine-frontend; \ + elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \ + export NODE_OPTIONS="--max-old-space-size=8192" && \ + export SKIP_NAPI_BUILD=1 && \ + export SKIP_WASM_BUILD=1 && \ + pnpm install --ignore-scripts && \ + pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ fi RUN --mount=type=cache,id=cargo-registry-windows-x64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs index 749b402053..1ec066d378 100644 --- a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs +++ b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs @@ -16,6 +16,20 @@ if (process.env.SKIP_NAPI_BUILD === "1") { process.exit(0); } +// Build the per-actor inspector UI before compiling. rivetkit-core's build.rs +// embeds frontend/dist/inspector-ui at compile time (include_dir!); without it +// the napi ships an empty bundle and /inspector/ui/ returns +// inspector.ui_asset_not_found at runtime. Skip with SKIP_INSPECTOR_UI_BUILD=1 +// for fast iteration when the frontend is already built. +if (process.env.SKIP_INSPECTOR_UI_BUILD !== "1") { + console.log("[rivetkit-napi/build] building inspector UI frontend"); + execFileSync( + "pnpm", + ["--filter", "@rivetkit/engine-frontend", "run", "build:inspector-ui"], + { stdio: "inherit" }, + ); +} + const cmd = ["build", "--platform", ...extraFlags]; console.log(`[rivetkit-napi/build] running: napi ${cmd.join(" ")}`); execFileSync("napi", cmd, { stdio: "inherit" }); From 158652b9eadd679d34d6cc64124b423547adc6d9 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 14:32:51 -0700 Subject: [PATCH 07/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(frontend): treat 0.0.0 preview versions as latest in inspector version gate --- .../src/components/actors/actor-inspector-context.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/components/actors/actor-inspector-context.tsx b/frontend/src/components/actors/actor-inspector-context.tsx index a209b5227d..782565ce62 100644 --- a/frontend/src/components/actors/actor-inspector-context.tsx +++ b/frontend/src/components/actors/actor-inspector-context.tsx @@ -190,6 +190,14 @@ export function isVersionAtLeast( if (!parsed || !minParsed) { return false; } + // `0.0.0` is the dev/preview placeholder (e.g. `0.0.0-.` from + // pkg.pr.new or `0.0.0-main.` snapshots). These are built from the + // latest source, so treat them as newer than any release gate. Released + // prereleases like `2.3.0-rc.1` keep comparing by major.minor.patch + // (`parseSemver` already drops the prerelease suffix), so they pass too. + if (parsed.major === 0 && parsed.minor === 0 && parsed.patch === 0) { + return true; + } return compareSemver(parsed, minParsed) >= 0; } From 34894dab994a299b42cce773b7b08bebe45e8544 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 14:32:51 -0700 Subject: [PATCH 08/10] [SLOP(claude-opus-4-8[1m]-medium)] feat(rivetkit): warn when local engine binary cannot be resolved --- .../packages/rivetkit/src/registry/native.ts | 11 +++++++++-- .../packages/rivetkit/src/registry/runtime.ts | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts index 0307caf25f..a91ddca709 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/native.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/native.ts @@ -4767,8 +4767,15 @@ export async function buildServeConfig( try { const { getEnginePath } = await loadEngineCli(); serveConfig.engineBinaryPath = getEnginePath(); - } catch { - // No local engine binary resolvable; the core decides whether it needs one. + } catch (error) { + // The npm-installed engine binary could not be resolved. The core still + // decides whether it needs to spawn a local engine; if it does, it will + // fail with engine.binary_unavailable (auto-download is off in the napi + // runtime). Warn so the cause is actionable. + logger().warn({ + msg: "could not resolve a local engine binary; if a local engine must be spawned it will fail with engine.binary_unavailable — set RIVET_ENGINE_BINARY_PATH or install the @rivetkit/engine-cli platform package", + error: stringifyError(error), + }); } serveConfig.engineHost = config.engineHost; serveConfig.enginePort = config.enginePort; diff --git a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts index 6e033ab72c..606815ddba 100644 --- a/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts +++ b/rivetkit-typescript/packages/rivetkit/src/registry/runtime.ts @@ -1,5 +1,7 @@ +import { stringifyError } from "@/common/utils"; import type { SqliteNativeMetrics } from "@/common/database/config"; import type { RegistryConfig } from "./config"; +import { logger } from "./log"; declare const handleBrand: unique symbol; @@ -607,8 +609,14 @@ export async function buildServeConfig( // `engine.binary_unavailable` only if it actually needs one. try { serveConfig.engineBinaryPath = await loadEnginePath(); - } catch { - // No local engine binary resolvable; the core decides whether it needs one. + } catch (error) { + // The engine binary could not be resolved. The core still decides whether + // it needs to spawn a local engine; if it does, it will fail with + // engine.binary_unavailable (auto-download is off in the napi runtime). + logger().warn({ + msg: "could not resolve a local engine binary; if a local engine must be spawned it will fail with engine.binary_unavailable — set RIVET_ENGINE_BINARY_PATH or install the @rivetkit/engine-cli platform package", + error: stringifyError(error), + }); } serveConfig.engineHost = config.engineHost; serveConfig.enginePort = config.enginePort; From 8c2c69abaa0ee5eaa24b931cda69b71035122b92 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 14:46:16 -0700 Subject: [PATCH 09/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): build inspector-ui via turbo so rivetkit builds first in napi image --- docker/build/darwin-arm64.Dockerfile | 2 +- docker/build/darwin-x64.Dockerfile | 2 +- docker/build/linux-arm64-gnu.Dockerfile | 2 +- docker/build/linux-arm64-musl.Dockerfile | 2 +- docker/build/linux-x64-gnu.Dockerfile | 2 +- docker/build/linux-x64-musl.Dockerfile | 2 +- docker/build/windows-x64.Dockerfile | 2 +- turbo.json | 12 ++++++++++++ 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docker/build/darwin-arm64.Dockerfile b/docker/build/darwin-arm64.Dockerfile index 3dcc91a4a3..9b81734d3b 100644 --- a/docker/build/darwin-arm64.Dockerfile +++ b/docker/build/darwin-arm64.Dockerfile @@ -48,7 +48,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-darwin-arm64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/darwin-x64.Dockerfile b/docker/build/darwin-x64.Dockerfile index 485c94fd73..c6df3fdfa2 100644 --- a/docker/build/darwin-x64.Dockerfile +++ b/docker/build/darwin-x64.Dockerfile @@ -48,7 +48,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-darwin-x64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-arm64-gnu.Dockerfile b/docker/build/linux-arm64-gnu.Dockerfile index b36037633d..e762ae5d52 100644 --- a/docker/build/linux-arm64-gnu.Dockerfile +++ b/docker/build/linux-arm64-gnu.Dockerfile @@ -35,7 +35,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-linux-arm64-gnu,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-arm64-musl.Dockerfile b/docker/build/linux-arm64-musl.Dockerfile index 15d71dd4cc..06929dd39f 100644 --- a/docker/build/linux-arm64-musl.Dockerfile +++ b/docker/build/linux-arm64-musl.Dockerfile @@ -41,7 +41,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-linux-arm64-musl,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/linux-x64-gnu.Dockerfile b/docker/build/linux-x64-gnu.Dockerfile index 3046e7ef8a..c04fec715b 100644 --- a/docker/build/linux-x64-gnu.Dockerfile +++ b/docker/build/linux-x64-gnu.Dockerfile @@ -44,7 +44,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi # Build binary. diff --git a/docker/build/linux-x64-musl.Dockerfile b/docker/build/linux-x64-musl.Dockerfile index d41a10fd3c..6959b30757 100644 --- a/docker/build/linux-x64-musl.Dockerfile +++ b/docker/build/linux-x64-musl.Dockerfile @@ -40,7 +40,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-linux-x64-musl,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/docker/build/windows-x64.Dockerfile b/docker/build/windows-x64.Dockerfile index 0265456578..45f7f00eeb 100644 --- a/docker/build/windows-x64.Dockerfile +++ b/docker/build/windows-x64.Dockerfile @@ -48,7 +48,7 @@ RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export SKIP_NAPI_BUILD=1 && \ export SKIP_WASM_BUILD=1 && \ pnpm install --ignore-scripts && \ - pnpm --filter @rivetkit/engine-frontend run build:inspector-ui; \ + npx turbo build:inspector-ui -F @rivetkit/engine-frontend; \ fi RUN --mount=type=cache,id=cargo-registry-windows-x64,target=/usr/local/cargo/registry,sharing=locked \ diff --git a/turbo.json b/turbo.json index a4cf1ca17e..3cd0039cf6 100644 --- a/turbo.json +++ b/turbo.json @@ -25,6 +25,18 @@ "outputs": ["dist/**"], "env": ["BASE_URL", "VITE_APP_*", "VITE_FEATURE_FLAGS"] }, + "build:inspector-ui": { + "dependsOn": ["^build"], + "inputs": [ + "src/**", + "apps/inspector-ui/**", + "scripts/**", + "vite.inspector-ui.config.ts", + "tsconfig.json", + "package.json" + ], + "outputs": ["dist/inspector-ui/**", "dist/inspector-tab/**"] + }, "build:ladle": { "dependsOn": ["^build"], "inputs": [ From bd77b987f4c502df1f9f4248726eec67c833705c Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 14 Jun 2026 14:59:39 -0700 Subject: [PATCH 10/10] [SLOP(claude-opus-4-8[1m]-medium)] fix(publish): do not build inspector-ui from napi build.mjs (inverts dep order) --- .../packages/rivetkit-napi/scripts/build.mjs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs index 1ec066d378..dfa8539c3a 100644 --- a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs +++ b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs @@ -16,20 +16,14 @@ if (process.env.SKIP_NAPI_BUILD === "1") { process.exit(0); } -// Build the per-actor inspector UI before compiling. rivetkit-core's build.rs -// embeds frontend/dist/inspector-ui at compile time (include_dir!); without it -// the napi ships an empty bundle and /inspector/ui/ returns -// inspector.ui_asset_not_found at runtime. Skip with SKIP_INSPECTOR_UI_BUILD=1 -// for fast iteration when the frontend is already built. -if (process.env.SKIP_INSPECTOR_UI_BUILD !== "1") { - console.log("[rivetkit-napi/build] building inspector UI frontend"); - execFileSync( - "pnpm", - ["--filter", "@rivetkit/engine-frontend", "run", "build:inspector-ui"], - { stdio: "inherit" }, - ); -} - +// The per-actor inspector UI (frontend/dist/inspector-ui, embedded into +// rivetkit-core by its build.rs) must be built before this napi build runs. +// It is NOT built here: rivetkit-core's embed needs rivetkit/inspector-tab, +// which is downstream of this package in the build graph, so building it from +// the napi build would invert the dependency order. CI builds it via +// `turbo build:inspector-ui` in docker/build/*.Dockerfile before `napi build`; +// for local builds run `pnpm -F @rivetkit/engine-frontend build:inspector-ui` +// (or `turbo build:inspector-ui`) first. const cmd = ["build", "--platform", ...extraFlags]; console.log(`[rivetkit-napi/build] running: napi ${cmd.join(" ")}`); execFileSync("napi", cmd, { stdio: "inherit" });