diff --git a/.cargo/config.toml b/.cargo/config.toml index a439e96d4c..38c284958a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,22 @@ [build] rustflags = ["--cfg", "tokio_unstable"] +# Target-specific flags must repeat tokio_unstable because target rustflags +# override [build] rustflags. The link args are required on macOS for native +# Node.js addons (dynamic_lookup defers symbol resolution to runtime). +[target.aarch64-apple-darwin] +rustflags = [ + "--cfg", "tokio_unstable", + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] + +[target.x86_64-apple-darwin] +rustflags = [ + "--cfg", "tokio_unstable", + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] + [env] LIBSQLITE3_FLAGS = "SQLITE_ENABLE_BATCH_ATOMIC_WRITE" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 008228a8d7..e387775529 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -461,16 +461,6 @@ jobs: --version ${{ needs.context.outputs.version }} \ --latest ${{ needs.context.outputs.latest }} - - name: Upload @rivetkit/devtools to R2 - if: needs.context.outputs.trigger == 'release' - env: - R2_RELEASES_ACCESS_KEY_ID: ${{ secrets.R2_RELEASES_ACCESS_KEY_ID }} - R2_RELEASES_SECRET_ACCESS_KEY: ${{ secrets.R2_RELEASES_SECRET_ACCESS_KEY }} - run: | - pnpm --filter=publish exec tsx src/ci/bin.ts upload-devtools \ - --sha ${{ needs.context.outputs.sha }} \ - --version ${{ needs.context.outputs.version }} \ - --latest ${{ needs.context.outputs.latest }} - name: Retag Docker manifests to version if: needs.context.outputs.trigger == 'release' diff --git a/rivetkit-typescript/packages/devtools/src/mod.tsx b/rivetkit-typescript/packages/devtools/src/mod.tsx index 47462d4b19..5bb8344ec8 100644 --- a/rivetkit-typescript/packages/devtools/src/mod.tsx +++ b/rivetkit-typescript/packages/devtools/src/mod.tsx @@ -59,11 +59,11 @@ const openDevtools = () => { console.error("RivetKit Devtools: No client config found"); return; } - const url = new URL("http://localhost:6420/ui"); if (!config.endpoint) { console.error("RivetKit Devtools: No endpoint found in client config"); return; } + const url = new URL(`${config.endpoint.replace(/\/$/, "")}/ui`); url.searchParams.set("u", config.endpoint); if (config.token) { url.searchParams.set("t", config.token); diff --git a/rivetkit-typescript/packages/rivetkit/package.json b/rivetkit-typescript/packages/rivetkit/package.json index 311fe345e6..6ec8f2b0ff 100644 --- a/rivetkit-typescript/packages/rivetkit/package.json +++ b/rivetkit-typescript/packages/rivetkit/package.json @@ -317,7 +317,8 @@ "dump-asyncapi": "tsx scripts/dump-asyncapi.ts", "registry-config-schema-gen": "tsx scripts/registry-config-schema-gen.ts", "actor-config-schema-gen": "tsx scripts/actor-config-schema-gen.ts", - "build:pack-inspector": "tsx scripts/pack-inspector.ts" + "build:pack-inspector": "tsx scripts/pack-inspector.ts", + "build:pack-devtools": "tsx scripts/pack-devtools.ts" }, "dependencies": { "@rivet-dev/agent-os-core": "^0.1.1", diff --git a/rivetkit-typescript/packages/rivetkit/scripts/pack-devtools.ts b/rivetkit-typescript/packages/rivetkit/scripts/pack-devtools.ts new file mode 100644 index 0000000000..4fc4f81057 --- /dev/null +++ b/rivetkit-typescript/packages/rivetkit/scripts/pack-devtools.ts @@ -0,0 +1,19 @@ +import { existsSync } from "node:fs"; +import { copyFile, mkdir } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const src = join(__dirname, "../../devtools/dist/mod.js"); +const destDir = join(__dirname, "../dist/devtools"); +const dest = join(destDir, "mod.js"); + +if (!existsSync(src)) { + throw new Error( + `Devtools build not found at: ${src}. Run 'pnpm build -F @rivetkit/devtools' first.`, + ); +} + +await mkdir(destDir, { recursive: true }); +await copyFile(src, dest); +console.log(`Packed devtools into ${dest}`); diff --git a/rivetkit-typescript/packages/rivetkit/src/devtools-loader/index.ts b/rivetkit-typescript/packages/rivetkit/src/devtools-loader/index.ts index cccfca38ae..0eaadec9db 100644 --- a/rivetkit-typescript/packages/rivetkit/src/devtools-loader/index.ts +++ b/rivetkit-typescript/packages/rivetkit/src/devtools-loader/index.ts @@ -1,16 +1,11 @@ import type { ClientConfigInput } from "@/client/client"; -import { VERSION } from "@/utils"; import { logger } from "./log"; declare global { // Injected via tsup config - // biome-ignore lint/style/noVar: required for global declaration var CUSTOM_RIVETKIT_DEVTOOLS_URL: string | undefined; } -const DEFAULT_DEVTOOLS_URL = (version = VERSION) => - `https://releases.rivet.dev/rivet/latest/devtools/mod.js?v=${version}`; - const scriptId = "rivetkit-devtools-script"; export function injectDevtools(config: ClientConfigInput) { @@ -20,10 +15,12 @@ export function injectDevtools(config: ClientConfigInput) { } if (!document.getElementById(scriptId)) { + const src = + globalThis.CUSTOM_RIVETKIT_DEVTOOLS_URL || + `${config.endpoint?.replace(/\/$/, "")}/devtools/mod.js`; const script = document.createElement("script"); script.id = scriptId; - script.src = - globalThis.CUSTOM_RIVETKIT_DEVTOOLS_URL || DEFAULT_DEVTOOLS_URL(); + script.src = src; script.async = true; document.head.appendChild(script); } diff --git a/rivetkit-typescript/packages/rivetkit/src/devtools-loader/serve-devtools.ts b/rivetkit-typescript/packages/rivetkit/src/devtools-loader/serve-devtools.ts new file mode 100644 index 0000000000..aa52fd197b --- /dev/null +++ b/rivetkit-typescript/packages/rivetkit/src/devtools-loader/serve-devtools.ts @@ -0,0 +1,26 @@ +import { getNodeFs, getNodePath, getNodeUrl } from "@/utils/node"; + +export async function getDevtoolsPath(): Promise { + const url = getNodeUrl(); + const path = getNodePath(); + + const devtoolsPath = path.join( + path.dirname(url.fileURLToPath(import.meta.url)), + "../../dist/devtools/mod.js", + ); + + try { + await getNodeFs().access(devtoolsPath); + } catch { + throw new Error( + `Devtools bundle not found at ${devtoolsPath}. Run 'pnpm build:pack-devtools' first.`, + ); + } + + return devtoolsPath; +} + +export async function readDevtoolsBundle(): Promise { + const devtoolsPath = await getDevtoolsPath(); + return getNodeFs().readFile(devtoolsPath); +} diff --git a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts index e51de68a77..db417b4497 100644 --- a/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts +++ b/rivetkit-typescript/packages/rivetkit/src/drivers/engine/actor-driver.ts @@ -184,6 +184,7 @@ export class EngineActorDriver implements ActorDriver { token: config.token, namespace: config.namespace, poolName: config.envoy.poolName, + notGlobal: false, metadata: { rivetkit: { version: VERSION }, }, diff --git a/rivetkit-typescript/packages/rivetkit/src/engine-client/api-utils.ts b/rivetkit-typescript/packages/rivetkit/src/engine-client/api-utils.ts index cf68e3424f..f08fbdc2c5 100644 --- a/rivetkit-typescript/packages/rivetkit/src/engine-client/api-utils.ts +++ b/rivetkit-typescript/packages/rivetkit/src/engine-client/api-utils.ts @@ -21,7 +21,7 @@ export class EngineApiError extends Error { export function getEndpoint(config: ClientConfig | RegistryConfig) { // Endpoint is always defined for ClientConfig (has default in schema). // RegistryConfig may not have endpoint before the local runtime is prepared. - return config.endpoint ?? "http://127.0.0.1:6420"; + return config.endpoint ?? "http://127.0.0.1:6423"; } // Helper function for making API calls diff --git a/rivetkit-typescript/packages/rivetkit/src/engine-process/constants.ts b/rivetkit-typescript/packages/rivetkit/src/engine-process/constants.ts index 846cca5107..0c1f0e0fb2 100644 --- a/rivetkit-typescript/packages/rivetkit/src/engine-process/constants.ts +++ b/rivetkit-typescript/packages/rivetkit/src/engine-process/constants.ts @@ -1,2 +1,5 @@ -export const ENGINE_PORT = 6420; +// The engine guard (public HTTP API) runs on this internal port. +// The rivetkit manager runs on port 6420 (managerPort default) and proxies to +// the engine here, so clients always connect to the manager on 6420. +export const ENGINE_PORT = 6423; export const ENGINE_ENDPOINT = `http://127.0.0.1:${ENGINE_PORT}`; diff --git a/rivetkit-typescript/packages/rivetkit/src/engine-process/mod.ts b/rivetkit-typescript/packages/rivetkit/src/engine-process/mod.ts index 4cc850226a..46ad882ebd 100644 --- a/rivetkit-typescript/packages/rivetkit/src/engine-process/mod.ts +++ b/rivetkit-typescript/packages/rivetkit/src/engine-process/mod.ts @@ -58,7 +58,7 @@ export async function ensureEngineProcess( error, }); throw new Error( - "Engine process exists but is not healthy. Please manually stop the process on port 6420 and retry.", + `Engine process exists but is not healthy. Please manually stop the process on port ${ENGINE_PORT} and retry.`, ); } } @@ -129,6 +129,9 @@ export async function ensureEngineProcess( // // We reduce the timeouts for resetting a runner as healthy in // order to account for this. + // Run the engine guard on its internal port so the rivetkit manager + // can own the default client port (6420) and serve devtools/inspector. + RIVET__GUARD__PORT: String(ENGINE_PORT), RIVET__PEGBOARD__RETRY_RESET_DURATION: "100", RIVET__PEGBOARD__BASE_RETRY_TIMEOUT: "100", // Set max exponent to 1 to have a maximum of base_retry_timeout @@ -305,11 +308,11 @@ async function checkIfEngineAlreadyRunningOnPort( port, }); throw new Error( - "RivetKit process already running on port 6420, stop that process and restart this.", + `RivetKit process already running on port ${port}, stop that process and restart this.`, ); } else { throw new Error( - "Unknown process running on port 6420, cannot identify what it is.", + `Unknown process running on port ${port}, cannot identify what it is.`, ); } } diff --git a/rivetkit-typescript/packages/rivetkit/src/runtime-router/router.ts b/rivetkit-typescript/packages/rivetkit/src/runtime-router/router.ts index a4781174cb..39c56d81f9 100644 --- a/rivetkit-typescript/packages/rivetkit/src/runtime-router/router.ts +++ b/rivetkit-typescript/packages/rivetkit/src/runtime-router/router.ts @@ -1,26 +1,18 @@ import { createRoute } from "@hono/zod-openapi"; import * as cbor from "cbor-x"; import type { Hono } from "hono"; -import invariant from "invariant"; import { z } from "zod/v4"; import { Forbidden, RestrictedFeature } from "@/actor/errors"; import { deserializeActorKey, serializeActorKey } from "@/actor/keys"; import { actorGateway, - createTestWebSocketProxy, } from "@/actor-gateway/gateway"; -import type { Encoding } from "@/client/mod"; import { HEADER_RIVET_TOKEN, - WS_PROTOCOL_ACTOR, - WS_PROTOCOL_CONN_PARAMS, - WS_PROTOCOL_ENCODING, - WS_TEST_PROTOCOL_PATH, } from "@/common/actor-router-consts"; import { handleHealthRequest, handleMetadataRequest } from "@/common/router"; -import { deconstructError, noopNext, stringifyError } from "@/common/utils"; +import { stringifyError } from "@/common/utils"; -import { HEADER_ACTOR_ID } from "@/driver-helpers/mod"; import { ActorsCreateRequestSchema, type ActorsCreateResponse, @@ -469,6 +461,26 @@ export function buildRuntimeRouter( router.get("/ui", (c) => c.redirect("/ui/")); } + router.get("/devtools/mod.js", async (c) => { + let content: Buffer; + try { + content = await readDevtoolsBundle(); + } catch (error) { + logger().error({ + msg: "devtools bundle not found", + error: stringifyError(error), + }); + return c.text("Devtools bundle not found.", 404); + } + + return new Response(new Uint8Array(content), { + headers: { + "Content-Type": "application/javascript", + "Access-Control-Allow-Origin": "*", + }, + }); + }); + router.get("/health", (c) => handleHealthRequest(c)); router.get("/metadata", (c) => diff --git a/rivetkit-typescript/packages/rivetkit/turbo.json b/rivetkit-typescript/packages/rivetkit/turbo.json index 503a0f6211..c286c803c3 100644 --- a/rivetkit-typescript/packages/rivetkit/turbo.json +++ b/rivetkit-typescript/packages/rivetkit/turbo.json @@ -37,6 +37,11 @@ "inputs": ["scripts/pack-inspector.ts", "package.json"], "outputs": ["dist/inspector.tar.gz"] }, + "build:pack-devtools": { + "dependsOn": ["@rivetkit/devtools#build"], + "inputs": ["scripts/pack-devtools.ts", "package.json"], + "outputs": ["dist/devtools/mod.js"] + }, "build:dynamic-isolate-runtime": { "dependsOn": ["^build", "build:schema"], "inputs": [ @@ -98,7 +103,7 @@ ] }, "build:publish": { - "dependsOn": ["build", "build:pack-inspector"], + "dependsOn": ["build", "build:pack-inspector", "build:pack-devtools"], "outputs": [] } }