rivetkit 2.0.9 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/tsup/{chunk-F2YZNUPU.js → chunk-3JUN3IEH.js} +3 -3
  2. package/dist/tsup/{chunk-DL7TPF63.cjs → chunk-5ABUOI3V.cjs} +7 -7
  3. package/dist/tsup/{chunk-DL7TPF63.cjs.map → chunk-5ABUOI3V.cjs.map} +1 -1
  4. package/dist/tsup/{chunk-SDXTJDDR.cjs → chunk-5JZPEJVJ.cjs} +58 -14
  5. package/dist/tsup/chunk-5JZPEJVJ.cjs.map +1 -0
  6. package/dist/tsup/{chunk-SOC4HWCG.cjs → chunk-6PORXHSQ.cjs} +92 -39
  7. package/dist/tsup/chunk-6PORXHSQ.cjs.map +1 -0
  8. package/dist/tsup/{chunk-QGRYH6TU.cjs → chunk-765F7ILI.cjs} +7 -6
  9. package/dist/tsup/chunk-765F7ILI.cjs.map +1 -0
  10. package/dist/tsup/{chunk-DLPIL3VC.js → chunk-7DCESQ4O.js} +2 -2
  11. package/dist/tsup/{chunk-R7OP5N25.js → chunk-AZI2T6UF.js} +53 -9
  12. package/dist/tsup/chunk-AZI2T6UF.js.map +1 -0
  13. package/dist/tsup/{chunk-A44TWAS5.cjs → chunk-GGIW54I2.cjs} +6 -6
  14. package/dist/tsup/{chunk-A44TWAS5.cjs.map → chunk-GGIW54I2.cjs.map} +1 -1
  15. package/dist/tsup/{chunk-7OMMIAWP.cjs → chunk-HDCLOVOE.cjs} +17 -12
  16. package/dist/tsup/chunk-HDCLOVOE.cjs.map +1 -0
  17. package/dist/tsup/{chunk-2MJYYF2Q.cjs → chunk-I6AVFIVY.cjs} +12 -12
  18. package/dist/tsup/{chunk-2MJYYF2Q.cjs.map → chunk-I6AVFIVY.cjs.map} +1 -1
  19. package/dist/tsup/{chunk-WBSPHV5V.js → chunk-JTWD6ZT2.js} +2 -2
  20. package/dist/tsup/{chunk-4YV6RDZL.cjs → chunk-LXFOO25H.cjs} +722 -359
  21. package/dist/tsup/chunk-LXFOO25H.cjs.map +1 -0
  22. package/dist/tsup/{chunk-YR2VY4XS.js → chunk-MIP4PYTD.js} +5 -4
  23. package/dist/tsup/chunk-MIP4PYTD.js.map +1 -0
  24. package/dist/tsup/{chunk-KHZ2QSQ4.js → chunk-MT5ES4XD.js} +32 -10
  25. package/dist/tsup/chunk-MT5ES4XD.js.map +1 -0
  26. package/dist/tsup/{chunk-U2IXX6DY.cjs → chunk-NLYAKGML.cjs} +5 -6
  27. package/dist/tsup/chunk-NLYAKGML.cjs.map +1 -0
  28. package/dist/tsup/{chunk-FZP2IBIX.js → chunk-NOXYAPM2.js} +603 -240
  29. package/dist/tsup/chunk-NOXYAPM2.js.map +1 -0
  30. package/dist/tsup/{chunk-E63WU5PL.js → chunk-NQFIZSTR.js} +5 -6
  31. package/dist/tsup/chunk-NQFIZSTR.js.map +1 -0
  32. package/dist/tsup/{chunk-4PSLOAXR.cjs → chunk-O7BIBANW.cjs} +226 -204
  33. package/dist/tsup/chunk-O7BIBANW.cjs.map +1 -0
  34. package/dist/tsup/{chunk-VVCL5DXN.js → chunk-OHSP4BUE.js} +97 -44
  35. package/dist/tsup/{chunk-VVCL5DXN.js.map → chunk-OHSP4BUE.js.map} +1 -1
  36. package/dist/tsup/{chunk-APHV6WXU.js → chunk-SBUJ3KIM.js} +2 -2
  37. package/dist/tsup/{chunk-DZZQG7VH.cjs → chunk-WYTLLIJM.cjs} +3 -3
  38. package/dist/tsup/{chunk-DZZQG7VH.cjs.map → chunk-WYTLLIJM.cjs.map} +1 -1
  39. package/dist/tsup/{chunk-WRSWUDFA.js → chunk-XO7VX4MN.js} +14 -9
  40. package/dist/tsup/chunk-XO7VX4MN.js.map +1 -0
  41. package/dist/tsup/client/mod.cjs +9 -9
  42. package/dist/tsup/client/mod.d.cts +2 -2
  43. package/dist/tsup/client/mod.d.ts +2 -2
  44. package/dist/tsup/client/mod.js +8 -8
  45. package/dist/tsup/common/log.cjs +3 -3
  46. package/dist/tsup/common/log.js +2 -2
  47. package/dist/tsup/common/websocket.cjs +4 -4
  48. package/dist/tsup/common/websocket.js +3 -3
  49. package/dist/tsup/{conn-CEh3WKbA.d.cts → conn-CT_8ZBT_.d.cts} +204 -199
  50. package/dist/tsup/{conn-Bt8rkUzm.d.ts → conn-d5F0M95s.d.ts} +204 -199
  51. package/dist/tsup/driver-helpers/mod.cjs +5 -5
  52. package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
  53. package/dist/tsup/driver-helpers/mod.d.cts +1 -1
  54. package/dist/tsup/driver-helpers/mod.d.ts +1 -1
  55. package/dist/tsup/driver-helpers/mod.js +6 -6
  56. package/dist/tsup/driver-test-suite/mod.cjs +117 -103
  57. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
  58. package/dist/tsup/driver-test-suite/mod.d.cts +3 -2
  59. package/dist/tsup/driver-test-suite/mod.d.ts +3 -2
  60. package/dist/tsup/driver-test-suite/mod.js +62 -48
  61. package/dist/tsup/driver-test-suite/mod.js.map +1 -1
  62. package/dist/tsup/inspector/mod.cjs +6 -6
  63. package/dist/tsup/inspector/mod.d.cts +6 -6
  64. package/dist/tsup/inspector/mod.d.ts +6 -6
  65. package/dist/tsup/inspector/mod.js +5 -5
  66. package/dist/tsup/mod.cjs +10 -10
  67. package/dist/tsup/mod.d.cts +8 -39
  68. package/dist/tsup/mod.d.ts +8 -39
  69. package/dist/tsup/mod.js +9 -9
  70. package/dist/tsup/test/mod.cjs +11 -11
  71. package/dist/tsup/test/mod.d.cts +1 -1
  72. package/dist/tsup/test/mod.d.ts +1 -1
  73. package/dist/tsup/test/mod.js +10 -10
  74. package/dist/tsup/utils.cjs +2 -2
  75. package/dist/tsup/utils.d.cts +2 -1
  76. package/dist/tsup/utils.d.ts +2 -1
  77. package/dist/tsup/utils.js +1 -1
  78. package/package.json +4 -5
  79. package/src/actor/driver.ts +2 -2
  80. package/src/actor/protocol/serde.ts +75 -3
  81. package/src/actor/router-endpoints.ts +6 -6
  82. package/src/actor/router.ts +2 -2
  83. package/src/client/actor-conn.ts +24 -3
  84. package/src/client/config.ts +19 -26
  85. package/src/driver-helpers/mod.ts +4 -1
  86. package/src/driver-test-suite/mod.ts +66 -44
  87. package/src/driver-test-suite/utils.ts +4 -1
  88. package/src/drivers/default.ts +11 -9
  89. package/src/drivers/engine/actor-driver.ts +42 -39
  90. package/src/drivers/engine/config.ts +9 -22
  91. package/src/drivers/engine/mod.ts +9 -8
  92. package/src/drivers/file-system/global-state.ts +4 -4
  93. package/src/engine-process/log.ts +5 -0
  94. package/src/engine-process/mod.ts +316 -0
  95. package/src/inspector/utils.ts +6 -4
  96. package/src/manager/driver.ts +2 -2
  97. package/src/manager/gateway.ts +29 -11
  98. package/src/manager/router-schema.ts +20 -0
  99. package/src/manager/router.ts +116 -29
  100. package/src/registry/mod.ts +168 -113
  101. package/src/registry/run-config.ts +116 -47
  102. package/src/registry/serve.ts +3 -1
  103. package/src/serde.ts +3 -3
  104. package/src/test/config.ts +2 -2
  105. package/src/test/mod.ts +6 -3
  106. package/src/utils.ts +2 -0
  107. package/dist/tsup/chunk-4PSLOAXR.cjs.map +0 -1
  108. package/dist/tsup/chunk-4YV6RDZL.cjs.map +0 -1
  109. package/dist/tsup/chunk-7OMMIAWP.cjs.map +0 -1
  110. package/dist/tsup/chunk-E63WU5PL.js.map +0 -1
  111. package/dist/tsup/chunk-FZP2IBIX.js.map +0 -1
  112. package/dist/tsup/chunk-KHZ2QSQ4.js.map +0 -1
  113. package/dist/tsup/chunk-QGRYH6TU.cjs.map +0 -1
  114. package/dist/tsup/chunk-R7OP5N25.js.map +0 -1
  115. package/dist/tsup/chunk-SDXTJDDR.cjs.map +0 -1
  116. package/dist/tsup/chunk-SOC4HWCG.cjs.map +0 -1
  117. package/dist/tsup/chunk-U2IXX6DY.cjs.map +0 -1
  118. package/dist/tsup/chunk-WRSWUDFA.js.map +0 -1
  119. package/dist/tsup/chunk-YR2VY4XS.js.map +0 -1
  120. /package/dist/tsup/{chunk-F2YZNUPU.js.map → chunk-3JUN3IEH.js.map} +0 -0
  121. /package/dist/tsup/{chunk-DLPIL3VC.js.map → chunk-7DCESQ4O.js.map} +0 -0
  122. /package/dist/tsup/{chunk-WBSPHV5V.js.map → chunk-JTWD6ZT2.js.map} +0 -0
  123. /package/dist/tsup/{chunk-APHV6WXU.js.map → chunk-SBUJ3KIM.js.map} +0 -0
@@ -1,17 +1,19 @@
1
1
  import type { Client } from "@/client/client";
2
2
  import type { ManagerDriver } from "@/manager/driver";
3
3
  import type { RegistryConfig } from "@/registry/config";
4
- import type { DriverConfig, RunConfig } from "@/registry/run-config";
4
+ import type { DriverConfig, RunnerConfig } from "@/registry/run-config";
5
5
  import { RemoteManagerDriver } from "@/remote-manager-driver/mod";
6
6
  import { EngineActorDriver } from "./actor-driver";
7
- import { ConfigSchema, type InputConfig } from "./config";
7
+ import { type EngineConfigInput, EngingConfigSchema } from "./config";
8
8
 
9
9
  export { EngineActorDriver } from "./actor-driver";
10
- export { type Config, ConfigSchema, type InputConfig } from "./config";
11
-
12
- export function createEngineDriver(inputConfig?: InputConfig): DriverConfig {
13
- const config = ConfigSchema.parse(inputConfig);
10
+ export {
11
+ type EngineConfig as Config,
12
+ type EngineConfigInput as InputConfig,
13
+ EngingConfigSchema as ConfigSchema,
14
+ } from "./config";
14
15
 
16
+ export function createEngineDriver(): DriverConfig {
15
17
  return {
16
18
  name: "engine",
17
19
  manager: (_registryConfig, runConfig) => {
@@ -19,7 +21,7 @@ export function createEngineDriver(inputConfig?: InputConfig): DriverConfig {
19
21
  },
20
22
  actor: (
21
23
  registryConfig: RegistryConfig,
22
- runConfig: RunConfig,
24
+ runConfig: RunnerConfig,
23
25
  managerDriver: ManagerDriver,
24
26
  inlineClient: Client<any>,
25
27
  ) => {
@@ -28,7 +30,6 @@ export function createEngineDriver(inputConfig?: InputConfig): DriverConfig {
28
30
  runConfig,
29
31
  managerDriver,
30
32
  inlineClient,
31
- config,
32
33
  );
33
34
  },
34
35
  };
@@ -14,7 +14,7 @@ import {
14
14
  serializeEmptyPersistData,
15
15
  } from "@/driver-helpers/mod";
16
16
  import type { RegistryConfig } from "@/registry/config";
17
- import type { RunConfig } from "@/registry/run-config";
17
+ import type { RunnerConfig } from "@/registry/run-config";
18
18
  import type * as schema from "@/schemas/file-system-driver/mod";
19
19
  import {
20
20
  ACTOR_ALARM_VERSIONED,
@@ -73,7 +73,7 @@ export class FileSystemGlobalState {
73
73
 
74
74
  #runnerParams?: {
75
75
  registryConfig: RegistryConfig;
76
- runConfig: RunConfig;
76
+ runConfig: RunnerConfig;
77
77
  inlineClient: AnyClient;
78
78
  actorDriver: ActorDriver;
79
79
  };
@@ -410,7 +410,7 @@ export class FileSystemGlobalState {
410
410
  */
411
411
  onRunnerStart(
412
412
  registryConfig: RegistryConfig,
413
- runConfig: RunConfig,
413
+ runConfig: RunnerConfig,
414
414
  inlineClient: AnyClient,
415
415
  actorDriver: ActorDriver,
416
416
  ) {
@@ -436,7 +436,7 @@ export class FileSystemGlobalState {
436
436
 
437
437
  async startActor(
438
438
  registryConfig: RegistryConfig,
439
- runConfig: RunConfig,
439
+ runConfig: RunnerConfig,
440
440
  inlineClient: AnyClient,
441
441
  actorDriver: ActorDriver,
442
442
  actorId: string,
@@ -0,0 +1,5 @@
1
+ import { getLogger } from "@/common/log";
2
+
3
+ export function logger() {
4
+ return getLogger("engine-process");
5
+ }
@@ -0,0 +1,316 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createWriteStream } from "node:fs";
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { pipeline } from "node:stream/promises";
6
+ import {
7
+ ensureDirectoryExists,
8
+ getStoragePath,
9
+ } from "@/drivers/file-system/utils";
10
+ import { logger } from "./log";
11
+
12
+ export const ENGINE_PORT = 6420;
13
+ export const ENGINE_ENDPOINT = `http://localhost:${ENGINE_PORT}`;
14
+
15
+ const ENGINE_BASE_URL = "https://releases.rivet.gg/engine";
16
+ const ENGINE_BINARY_NAME = "rivet-engine";
17
+
18
+ interface EnsureEngineProcessOptions {
19
+ version: string;
20
+ }
21
+
22
+ export async function ensureEngineProcess(
23
+ options: EnsureEngineProcessOptions,
24
+ ): Promise<void> {
25
+ logger().debug({ msg: "ensuring engine process", version: options.version });
26
+ const storageRoot = getStoragePath();
27
+ const binDir = path.join(storageRoot, "bin");
28
+ const varDir = path.join(storageRoot, "var");
29
+ const logsDir = path.join(varDir, "logs", "rivet-engine");
30
+ await ensureDirectoryExists(binDir);
31
+ await ensureDirectoryExists(varDir);
32
+ await ensureDirectoryExists(logsDir);
33
+
34
+ const executableName =
35
+ process.platform === "win32"
36
+ ? `${ENGINE_BINARY_NAME}-${options.version}.exe`
37
+ : `${ENGINE_BINARY_NAME}-${options.version}`;
38
+ const binaryPath = path.join(binDir, executableName);
39
+ await downloadEngineBinaryIfNeeded(binaryPath, options.version, varDir);
40
+
41
+ // Check if the engine is already running on the port
42
+ if (await isEngineRunning()) {
43
+ try {
44
+ await waitForEngineHealth();
45
+ logger().debug({
46
+ msg: "engine already running and healthy",
47
+ version: options.version,
48
+ });
49
+ return;
50
+ } catch (error) {
51
+ logger().warn({
52
+ msg: "existing engine process not healthy, cannot restart automatically",
53
+ error,
54
+ });
55
+ throw new Error(
56
+ "Engine process exists but is not healthy. Please manually stop the process on port 6420 and retry.",
57
+ );
58
+ }
59
+ }
60
+
61
+ // Create log file streams with timestamp in the filename
62
+ const timestamp = new Date()
63
+ .toISOString()
64
+ .replace(/:/g, "-")
65
+ .replace(/\./g, "-");
66
+ const stdoutLogPath = path.join(logsDir, `engine-${timestamp}-stdout.log`);
67
+ const stderrLogPath = path.join(logsDir, `engine-${timestamp}-stderr.log`);
68
+
69
+ const stdoutStream = createWriteStream(stdoutLogPath, { flags: "a" });
70
+ const stderrStream = createWriteStream(stderrLogPath, { flags: "a" });
71
+
72
+ logger().debug({
73
+ msg: "creating engine log files",
74
+ stdout: stdoutLogPath,
75
+ stderr: stderrLogPath,
76
+ });
77
+
78
+ const child = spawn(binaryPath, ["start"], {
79
+ cwd: path.dirname(binaryPath),
80
+ stdio: ["inherit", "pipe", "pipe"],
81
+ env: {
82
+ ...process.env,
83
+ },
84
+ });
85
+
86
+ if (!child.pid) {
87
+ throw new Error("failed to spawn rivet engine process");
88
+ }
89
+
90
+ // Pipe stdout and stderr to log files
91
+ if (child.stdout) {
92
+ child.stdout.pipe(stdoutStream);
93
+ }
94
+ if (child.stderr) {
95
+ child.stderr.pipe(stderrStream);
96
+ }
97
+
98
+ logger().debug({
99
+ msg: "spawned engine process",
100
+ pid: child.pid,
101
+ cwd: path.dirname(binaryPath),
102
+ });
103
+
104
+ child.once("exit", (code, signal) => {
105
+ logger().warn({
106
+ msg: "engine process exited",
107
+ code,
108
+ signal,
109
+ });
110
+ // Clean up log streams
111
+ stdoutStream.end();
112
+ stderrStream.end();
113
+ });
114
+
115
+ child.once("error", (error) => {
116
+ logger().error({
117
+ msg: "engine process failed",
118
+ error,
119
+ });
120
+ // Clean up log streams on error
121
+ stdoutStream.end();
122
+ stderrStream.end();
123
+ });
124
+
125
+ // Wait for engine to be ready
126
+ await waitForEngineHealth();
127
+
128
+ logger().info({
129
+ msg: "engine process started",
130
+ pid: child.pid,
131
+ version: options.version,
132
+ logs: {
133
+ stdout: stdoutLogPath,
134
+ stderr: stderrLogPath,
135
+ },
136
+ });
137
+ }
138
+
139
+ async function downloadEngineBinaryIfNeeded(
140
+ binaryPath: string,
141
+ version: string,
142
+ varDir: string,
143
+ ): Promise<void> {
144
+ const binaryExists = await fileExists(binaryPath);
145
+ if (binaryExists) {
146
+ logger().debug({
147
+ msg: "engine binary already cached",
148
+ version,
149
+ path: binaryPath,
150
+ });
151
+ return;
152
+ }
153
+
154
+ const { targetTriplet, extension } = resolveTargetTriplet();
155
+ const remoteFile = `${ENGINE_BINARY_NAME}-${targetTriplet}${extension}`;
156
+ const downloadUrl = `${ENGINE_BASE_URL}/${version}/${remoteFile}`;
157
+ logger().info({
158
+ msg: "downloading engine binary",
159
+ url: downloadUrl,
160
+ path: binaryPath,
161
+ version,
162
+ });
163
+
164
+ const response = await fetch(downloadUrl);
165
+ if (!response.ok || !response.body) {
166
+ throw new Error(
167
+ `failed to download rivet engine binary from ${downloadUrl}: ${response.status} ${response.statusText}`,
168
+ );
169
+ }
170
+
171
+ const tempPath = `${binaryPath}.${process.pid}.tmp`;
172
+ await pipeline(response.body, createWriteStream(tempPath));
173
+ if (process.platform !== "win32") {
174
+ await fs.chmod(tempPath, 0o755);
175
+ }
176
+ await fs.rename(tempPath, binaryPath);
177
+ logger().debug({
178
+ msg: "engine binary download complete",
179
+ version,
180
+ path: binaryPath,
181
+ });
182
+ logger().info({
183
+ msg: "engine binary downloaded",
184
+ version,
185
+ path: binaryPath,
186
+ });
187
+ }
188
+
189
+ function resolveTargetTriplet(): { targetTriplet: string; extension: string } {
190
+ return resolveTargetTripletFor(process.platform, process.arch);
191
+ }
192
+
193
+ export function resolveTargetTripletFor(
194
+ platform: NodeJS.Platform,
195
+ arch: typeof process.arch,
196
+ ): { targetTriplet: string; extension: string } {
197
+ switch (platform) {
198
+ case "darwin":
199
+ if (arch === "arm64") {
200
+ return { targetTriplet: "aarch64-apple-darwin", extension: "" };
201
+ }
202
+ if (arch === "x64") {
203
+ return { targetTriplet: "x86_64-apple-darwin", extension: "" };
204
+ }
205
+ break;
206
+ case "linux":
207
+ if (arch === "x64") {
208
+ return { targetTriplet: "x86_64-unknown-linux-musl", extension: "" };
209
+ }
210
+ break;
211
+ case "win32":
212
+ if (arch === "x64") {
213
+ return { targetTriplet: "x86_64-pc-windows-gnu", extension: ".exe" };
214
+ }
215
+ break;
216
+ }
217
+
218
+ throw new Error(
219
+ `unsupported platform for rivet engine binary: ${platform}/${arch}`,
220
+ );
221
+ }
222
+
223
+ async function isEngineRunning(): Promise<boolean> {
224
+ // Check if the engine is running on the port
225
+ return await checkIfEngineAlreadyRunningOnPort(ENGINE_PORT);
226
+ }
227
+
228
+ async function checkIfEngineAlreadyRunningOnPort(
229
+ port: number,
230
+ ): Promise<boolean> {
231
+ let response: Response;
232
+ try {
233
+ response = await fetch(`http://localhost:${port}/health`);
234
+ } catch (err) {
235
+ // Nothing is running on this port
236
+ return false;
237
+ }
238
+
239
+ if (response.ok) {
240
+ const health = (await response.json()) as {
241
+ status?: string;
242
+ runtime?: string;
243
+ version?: string;
244
+ };
245
+
246
+ // Check what's running on this port
247
+ if (health.runtime === "engine") {
248
+ logger().debug({
249
+ msg: "rivet engine already running on port",
250
+ port,
251
+ });
252
+ return true;
253
+ } else if (health.runtime === "rivetkit") {
254
+ logger().error({
255
+ msg: "another rivetkit process is already running on port",
256
+ port,
257
+ });
258
+ throw new Error(
259
+ "RivetKit process already running on port 6420, stop that process and restart this.",
260
+ );
261
+ } else {
262
+ throw new Error(
263
+ "Unknown process running on port 6420, cannot identify what it is.",
264
+ );
265
+ }
266
+ }
267
+
268
+ // Port responded but not with OK status
269
+ return false;
270
+ }
271
+
272
+ async function fileExists(filePath: string): Promise<boolean> {
273
+ try {
274
+ await fs.access(filePath);
275
+ return true;
276
+ } catch {
277
+ return false;
278
+ }
279
+ }
280
+
281
+ const HEALTH_MAX_WAIT = 10_000;
282
+ const HEALTH_INTERVAL = 100;
283
+
284
+ async function waitForEngineHealth(): Promise<void> {
285
+ const maxRetries = Math.ceil(HEALTH_MAX_WAIT / HEALTH_INTERVAL);
286
+
287
+ logger().debug({ msg: "waiting for engine health check" });
288
+
289
+ for (let i = 0; i < maxRetries; i++) {
290
+ try {
291
+ const response = await fetch(`${ENGINE_ENDPOINT}/health`);
292
+ if (response.ok) {
293
+ logger().debug({ msg: "engine health check passed" });
294
+ return;
295
+ }
296
+ } catch (error) {
297
+ // Expected to fail while engine is starting up
298
+ if (i === maxRetries - 1) {
299
+ throw new Error(
300
+ `engine health check failed after ${maxRetries} retries: ${error}`,
301
+ );
302
+ }
303
+ }
304
+
305
+ if (i < maxRetries - 1) {
306
+ logger().trace({
307
+ msg: "engine not ready, retrying",
308
+ attempt: i + 1,
309
+ maxRetries,
310
+ });
311
+ await new Promise((resolve) => setTimeout(resolve, HEALTH_INTERVAL));
312
+ }
313
+ }
314
+
315
+ throw new Error(`engine health check failed after ${maxRetries} retries`);
316
+ }
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
2
2
  import { createMiddleware } from "hono/factory";
3
3
  import type { ManagerDriver } from "@/driver-helpers/mod";
4
4
  import type { RunConfig } from "@/mod";
5
- import type { RunConfigInput } from "@/registry/run-config";
5
+ import type { RunnerConfigInput } from "@/registry/run-config";
6
6
  import { inspectorLogger } from "./log";
7
7
 
8
8
  export function compareSecrets(providedSecret: string, validSecret: string) {
@@ -47,7 +47,7 @@ export const secureInspector = (runConfig: RunConfig) =>
47
47
  await next();
48
48
  });
49
49
 
50
- export function getInspectorUrl(runConfig: RunConfigInput | undefined) {
50
+ export function getInspectorUrl(runConfig: RunnerConfigInput | undefined) {
51
51
  if (!runConfig?.inspector?.enabled) {
52
52
  return "disabled";
53
53
  }
@@ -65,8 +65,10 @@ export function getInspectorUrl(runConfig: RunConfigInput | undefined) {
65
65
 
66
66
  url.searchParams.set("t", accessToken);
67
67
 
68
- if (runConfig?.inspector?.defaultEndpoint) {
69
- url.searchParams.set("u", runConfig.inspector.defaultEndpoint);
68
+ const overrideDefaultEndpoint =
69
+ runConfig?.inspector?.defaultEndpoint ?? runConfig.overrideServerAddress;
70
+ if (overrideDefaultEndpoint) {
71
+ url.searchParams.set("u", overrideDefaultEndpoint);
70
72
  }
71
73
 
72
74
  return url.href;
@@ -2,11 +2,11 @@ import type { Env, Hono, Context as HonoContext } from "hono";
2
2
  import type { ActorKey, Encoding, UniversalWebSocket } from "@/actor/mod";
3
3
  import type { ManagerInspector } from "@/inspector/manager";
4
4
  import type { RegistryConfig } from "@/registry/config";
5
- import type { RunConfig } from "@/registry/run-config";
5
+ import type { RunnerConfig } from "@/registry/run-config";
6
6
 
7
7
  export type ManagerDriverBuilder = (
8
8
  registryConfig: RegistryConfig,
9
- runConfig: RunConfig,
9
+ runConfig: RunnerConfig,
10
10
  ) => ManagerDriver;
11
11
 
12
12
  export interface ManagerDriver {
@@ -14,7 +14,7 @@ import {
14
14
  } from "@/common/actor-router-consts";
15
15
  import { deconstructError, noopNext } from "@/common/utils";
16
16
  import type { UniversalWebSocket, UpgradeWebSocketArgs } from "@/mod";
17
- import type { RunConfig } from "@/registry/run-config";
17
+ import type { RunnerConfig } from "@/registry/run-config";
18
18
  import { promiseWithResolvers, stringifyError } from "@/utils";
19
19
  import type { ManagerDriver } from "./driver";
20
20
  import { logger } from "./log";
@@ -27,7 +27,7 @@ import { logger } from "./log";
27
27
  * - HTTP requests: Uses x-rivet-target and x-rivet-actor headers for routing
28
28
  */
29
29
  export async function actorGateway(
30
- runConfig: RunConfig,
30
+ runConfig: RunnerConfig,
31
31
  managerDriver: ManagerDriver,
32
32
  c: HonoContext,
33
33
  next: Next,
@@ -37,22 +37,38 @@ export async function actorGateway(
37
37
  return next();
38
38
  }
39
39
 
40
+ // Strip basePath from the request path
41
+ let strippedPath = c.req.path;
42
+ if (runConfig.basePath && strippedPath.startsWith(runConfig.basePath)) {
43
+ strippedPath = strippedPath.slice(runConfig.basePath.length);
44
+ // Ensure the path starts with /
45
+ if (!strippedPath.startsWith("/")) {
46
+ strippedPath = "/" + strippedPath;
47
+ }
48
+ }
49
+
40
50
  // Check if this is a WebSocket upgrade request
41
51
  if (c.req.header("upgrade") === "websocket") {
42
- return await handleWebSocketGateway(runConfig, managerDriver, c);
52
+ return await handleWebSocketGateway(
53
+ runConfig,
54
+ managerDriver,
55
+ c,
56
+ strippedPath,
57
+ );
43
58
  }
44
59
 
45
60
  // Handle regular HTTP requests
46
- return await handleHttpGateway(managerDriver, c, next);
61
+ return await handleHttpGateway(managerDriver, c, next, strippedPath);
47
62
  }
48
63
 
49
64
  /**
50
65
  * Handle WebSocket requests using sec-websocket-protocol for routing
51
66
  */
52
67
  async function handleWebSocketGateway(
53
- runConfig: RunConfig,
68
+ runConfig: RunnerConfig,
54
69
  managerDriver: ManagerDriver,
55
70
  c: HonoContext,
71
+ strippedPath: string,
56
72
  ) {
57
73
  const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
58
74
  if (!upgradeWebSocket) {
@@ -100,7 +116,7 @@ async function handleWebSocketGateway(
100
116
  logger().debug({
101
117
  msg: "proxying websocket to actor",
102
118
  actorId,
103
- path: c.req.path,
119
+ path: strippedPath,
104
120
  encoding: encodingRaw,
105
121
  });
106
122
 
@@ -109,8 +125,8 @@ async function handleWebSocketGateway(
109
125
 
110
126
  // Include query string if present
111
127
  const pathWithQuery = c.req.url.includes("?")
112
- ? c.req.path + c.req.url.substring(c.req.url.indexOf("?"))
113
- : c.req.path;
128
+ ? strippedPath + c.req.url.substring(c.req.url.indexOf("?"))
129
+ : strippedPath;
114
130
 
115
131
  return await managerDriver.proxyWebSocket(
116
132
  c,
@@ -130,6 +146,7 @@ async function handleHttpGateway(
130
146
  managerDriver: ManagerDriver,
131
147
  c: HonoContext,
132
148
  next: Next,
149
+ strippedPath: string,
133
150
  ) {
134
151
  const target = c.req.header(HEADER_RIVET_TARGET);
135
152
  const actorId = c.req.header(HEADER_RIVET_ACTOR);
@@ -145,7 +162,7 @@ async function handleHttpGateway(
145
162
  logger().debug({
146
163
  msg: "proxying request to actor",
147
164
  actorId,
148
- path: c.req.path,
165
+ path: strippedPath,
149
166
  method: c.req.method,
150
167
  });
151
168
 
@@ -156,14 +173,15 @@ async function handleHttpGateway(
156
173
 
157
174
  // Build the proxy request with the actor URL format
158
175
  const url = new URL(c.req.url);
159
- const proxyUrl = new URL(`http://actor${url.pathname}${url.search}`);
176
+ const proxyUrl = new URL(`http://actor${strippedPath}${url.search}`);
160
177
 
161
178
  const proxyRequest = new Request(proxyUrl, {
162
179
  method: c.req.raw.method,
163
180
  headers: proxyHeaders,
164
181
  body: c.req.raw.body,
165
182
  signal: c.req.raw.signal,
166
- });
183
+ duplex: "half",
184
+ } as RequestInit);
167
185
 
168
186
  return await managerDriver.proxyRequest(c, proxyRequest, actorId);
169
187
  }
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+
3
+ export const ServerlessStartHeadersSchema = z.object({
4
+ endpoint: z.string({ required_error: "x-rivet-endpoint header is required" }),
5
+ token: z
6
+ .string({ invalid_type_error: "x-rivet-token header must be a string" })
7
+ .optional(),
8
+ totalSlots: z.coerce
9
+ .number({
10
+ invalid_type_error: "x-rivet-total-slots header must be a number",
11
+ })
12
+ .int("x-rivet-total-slots header must be an integer")
13
+ .gte(1, "x-rivet-total-slots header must be positive"),
14
+ runnerName: z.string({
15
+ required_error: "x-rivet-runner-name header is required",
16
+ }),
17
+ namespace: z.string({
18
+ required_error: "x-rivet-namespace-id header is required",
19
+ }),
20
+ });