rivetkit 2.0.8 → 2.0.10

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 (125) hide show
  1. package/dist/tsup/{chunk-2FAWAPRT.js → chunk-346X2XU4.js} +2 -2
  2. package/dist/tsup/{chunk-SFRRXLRM.js → chunk-7E5K3375.js} +2 -2
  3. package/dist/tsup/{chunk-3WRAGTDC.cjs → chunk-CA3X5M6H.cjs} +92 -39
  4. package/dist/tsup/{chunk-PVKV2O2E.js.map → chunk-CA3X5M6H.cjs.map} +1 -1
  5. package/dist/tsup/{chunk-L5MHM6JJ.cjs → chunk-DVPXSB4B.cjs} +12 -12
  6. package/dist/tsup/{chunk-L5MHM6JJ.cjs.map → chunk-DVPXSB4B.cjs.map} +1 -1
  7. package/dist/tsup/{chunk-DQVVH5ZK.cjs → chunk-GIFHYL7A.cjs} +5 -6
  8. package/dist/tsup/chunk-GIFHYL7A.cjs.map +1 -0
  9. package/dist/tsup/{chunk-N7OVEOMU.js → chunk-H7E2UU23.js} +38 -15
  10. package/dist/tsup/chunk-H7E2UU23.js.map +1 -0
  11. package/dist/tsup/{chunk-A6TV3QU6.js → chunk-HI55LHM3.js} +5 -6
  12. package/dist/tsup/chunk-HI55LHM3.js.map +1 -0
  13. package/dist/tsup/{chunk-FGOZELKN.cjs → chunk-I3FB346I.cjs} +112 -58
  14. package/dist/tsup/chunk-I3FB346I.cjs.map +1 -0
  15. package/dist/tsup/{chunk-DOZBWJRI.js → chunk-KGDZYQYE.js} +2 -2
  16. package/dist/tsup/{chunk-KYEEAVJO.cjs → chunk-KH5WFDUK.cjs} +6 -6
  17. package/dist/tsup/{chunk-KYEEAVJO.cjs.map → chunk-KH5WFDUK.cjs.map} +1 -1
  18. package/dist/tsup/{chunk-WP7YG7S5.js → chunk-KL4V2ULR.js} +5 -4
  19. package/dist/tsup/chunk-KL4V2ULR.js.map +1 -0
  20. package/dist/tsup/{chunk-S6EAEZQA.js → chunk-MLQIYKAZ.js} +106 -52
  21. package/dist/tsup/chunk-MLQIYKAZ.js.map +1 -0
  22. package/dist/tsup/{chunk-3ZMJUIL3.js → chunk-N3A5GYJU.js} +3 -3
  23. package/dist/tsup/{chunk-CKSA7NOS.cjs → chunk-PDFL7FBL.cjs} +717 -380
  24. package/dist/tsup/chunk-PDFL7FBL.cjs.map +1 -0
  25. package/dist/tsup/{chunk-ESD2JX3L.cjs → chunk-PPLR53PP.cjs} +3 -3
  26. package/dist/tsup/{chunk-ESD2JX3L.cjs.map → chunk-PPLR53PP.cjs.map} +1 -1
  27. package/dist/tsup/{chunk-6INXQCH7.cjs → chunk-PSCDCEXM.cjs} +17 -12
  28. package/dist/tsup/chunk-PSCDCEXM.cjs.map +1 -0
  29. package/dist/tsup/{chunk-PVKV2O2E.js → chunk-QRFXXTLG.js} +96 -43
  30. package/dist/tsup/chunk-QRFXXTLG.js.map +1 -0
  31. package/dist/tsup/{chunk-RM2V2IRK.js → chunk-R2S45MO6.js} +14 -9
  32. package/dist/tsup/chunk-R2S45MO6.js.map +1 -0
  33. package/dist/tsup/{chunk-QGUQB3NC.cjs → chunk-SIWYIRXP.cjs} +7 -6
  34. package/dist/tsup/chunk-SIWYIRXP.cjs.map +1 -0
  35. package/dist/tsup/{chunk-E77RVI3P.js → chunk-VJRXZPTT.js} +601 -264
  36. package/dist/tsup/chunk-VJRXZPTT.js.map +1 -0
  37. package/dist/tsup/{chunk-KDNB2BQX.cjs → chunk-VZMXAZKC.cjs} +229 -206
  38. package/dist/tsup/chunk-VZMXAZKC.cjs.map +1 -0
  39. package/dist/tsup/{chunk-TPJNKVFB.cjs → chunk-YKVTF7MP.cjs} +7 -7
  40. package/dist/tsup/{chunk-TPJNKVFB.cjs.map → chunk-YKVTF7MP.cjs.map} +1 -1
  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-ChAuuTr0.d.cts → conn-Cc9WHuN4.d.cts} +196 -185
  50. package/dist/tsup/{conn-CjUkMEcm.d.ts → conn-DfPG71FA.d.ts} +196 -185
  51. package/dist/tsup/driver-helpers/mod.cjs +7 -5
  52. package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
  53. package/dist/tsup/driver-helpers/mod.d.cts +4 -2
  54. package/dist/tsup/driver-helpers/mod.d.ts +4 -2
  55. package/dist/tsup/driver-helpers/mod.js +9 -7
  56. package/dist/tsup/driver-test-suite/mod.cjs +116 -102
  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 +61 -47
  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/instance.ts +4 -4
  81. package/src/actor/protocol/serde.ts +75 -3
  82. package/src/actor/router-endpoints.ts +6 -6
  83. package/src/actor/router.ts +2 -2
  84. package/src/client/actor-conn.ts +24 -3
  85. package/src/client/config.ts +18 -25
  86. package/src/driver-helpers/mod.ts +5 -1
  87. package/src/driver-test-suite/mod.ts +65 -43
  88. package/src/driver-test-suite/utils.ts +4 -1
  89. package/src/drivers/default.ts +11 -9
  90. package/src/drivers/engine/actor-driver.ts +40 -39
  91. package/src/drivers/engine/config.ts +9 -22
  92. package/src/drivers/engine/mod.ts +9 -8
  93. package/src/drivers/file-system/global-state.ts +4 -4
  94. package/src/engine-process/log.ts +5 -0
  95. package/src/engine-process/mod.ts +316 -0
  96. package/src/inspector/utils.ts +6 -4
  97. package/src/manager/driver.ts +3 -3
  98. package/src/manager/gateway.ts +29 -11
  99. package/src/manager/router-schema.ts +20 -0
  100. package/src/manager/router.ts +139 -58
  101. package/src/registry/mod.ts +146 -120
  102. package/src/registry/run-config.ts +116 -47
  103. package/src/registry/serve.ts +3 -1
  104. package/src/remote-manager-driver/mod.ts +3 -2
  105. package/src/serde.ts +18 -3
  106. package/src/test/config.ts +2 -2
  107. package/src/test/mod.ts +6 -3
  108. package/src/utils.ts +2 -0
  109. package/dist/tsup/chunk-3WRAGTDC.cjs.map +0 -1
  110. package/dist/tsup/chunk-6INXQCH7.cjs.map +0 -1
  111. package/dist/tsup/chunk-A6TV3QU6.js.map +0 -1
  112. package/dist/tsup/chunk-CKSA7NOS.cjs.map +0 -1
  113. package/dist/tsup/chunk-DQVVH5ZK.cjs.map +0 -1
  114. package/dist/tsup/chunk-E77RVI3P.js.map +0 -1
  115. package/dist/tsup/chunk-FGOZELKN.cjs.map +0 -1
  116. package/dist/tsup/chunk-KDNB2BQX.cjs.map +0 -1
  117. package/dist/tsup/chunk-N7OVEOMU.js.map +0 -1
  118. package/dist/tsup/chunk-QGUQB3NC.cjs.map +0 -1
  119. package/dist/tsup/chunk-RM2V2IRK.js.map +0 -1
  120. package/dist/tsup/chunk-S6EAEZQA.js.map +0 -1
  121. package/dist/tsup/chunk-WP7YG7S5.js.map +0 -1
  122. /package/dist/tsup/{chunk-2FAWAPRT.js.map → chunk-346X2XU4.js.map} +0 -0
  123. /package/dist/tsup/{chunk-SFRRXLRM.js.map → chunk-7E5K3375.js.map} +0 -0
  124. /package/dist/tsup/{chunk-DOZBWJRI.js.map → chunk-KGDZYQYE.js.map} +0 -0
  125. /package/dist/tsup/{chunk-3ZMJUIL3.js.map → chunk-N3A5GYJU.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import type {
2
+ RunnerConfig as EngineRunnerConfig,
2
3
  ActorConfig as RunnerActorConfig,
3
- RunnerConfig,
4
4
  } from "@rivetkit/engine-runner";
5
5
  import { Runner } from "@rivetkit/engine-runner";
6
6
  import * as cbor from "cbor-x";
@@ -34,13 +34,13 @@ import {
34
34
  serializeEmptyPersistData,
35
35
  } from "@/driver-helpers/mod";
36
36
  import type { RegistryConfig } from "@/registry/config";
37
- import type { RunConfig } from "@/registry/run-config";
37
+ import type { RunnerConfig } from "@/registry/run-config";
38
+ import { getEndpoint } from "@/remote-manager-driver/api-utils";
38
39
  import {
39
40
  type LongTimeoutHandle,
40
41
  promiseWithResolvers,
41
42
  setLongTimeout,
42
43
  } from "@/utils";
43
- import type { Config } from "./config";
44
44
  import { KEYS } from "./kv";
45
45
  import { logger } from "./log";
46
46
 
@@ -54,10 +54,9 @@ export type DriverContext = {};
54
54
 
55
55
  export class EngineActorDriver implements ActorDriver {
56
56
  #registryConfig: RegistryConfig;
57
- #runConfig: RunConfig;
57
+ #runConfig: RunnerConfig;
58
58
  #managerDriver: ManagerDriver;
59
59
  #inlineClient: Client<any>;
60
- #config: Config;
61
60
  #runner: Runner;
62
61
  #actors: Map<string, ActorHandler> = new Map();
63
62
  #actorRouter: ActorRouter;
@@ -69,16 +68,22 @@ export class EngineActorDriver implements ActorDriver {
69
68
 
70
69
  constructor(
71
70
  registryConfig: RegistryConfig,
72
- runConfig: RunConfig,
71
+ runConfig: RunnerConfig,
73
72
  managerDriver: ManagerDriver,
74
73
  inlineClient: Client<any>,
75
- config: Config,
76
74
  ) {
77
75
  this.#registryConfig = registryConfig;
78
76
  this.#runConfig = runConfig;
79
77
  this.#managerDriver = managerDriver;
80
78
  this.#inlineClient = inlineClient;
81
- this.#config = config;
79
+
80
+ // HACK: Override inspector token (which are likely to be
81
+ // removed later on) with token from x-rivet-token header
82
+ const token = runConfig.token ?? runConfig.token;
83
+ if (token && runConfig.inspector && runConfig.inspector.enabled) {
84
+ runConfig.inspector.token = () => token;
85
+ }
86
+
82
87
  this.#actorRouter = createActorRouter(
83
88
  runConfig,
84
89
  this,
@@ -87,15 +92,14 @@ export class EngineActorDriver implements ActorDriver {
87
92
 
88
93
  // Create runner configuration
89
94
  let hasDisconnected = false;
90
- const runnerConfig: RunnerConfig = {
95
+ const engineRunnerConfig: EngineRunnerConfig = {
91
96
  version: this.#version,
92
- endpoint: config.endpoint,
93
- token: runConfig.token ?? config.token,
94
- pegboardEndpoint: config.pegboardEndpoint,
95
- namespace: config.namespace,
96
- totalSlots: runConfig.totalSlots ?? config.totalSlots,
97
- runnerName: config.runnerName,
98
- runnerKey: config.runnerKey,
97
+ endpoint: getEndpoint(runConfig),
98
+ token,
99
+ namespace: runConfig.namespace ?? runConfig.namespace,
100
+ totalSlots: runConfig.totalSlots ?? runConfig.totalSlots,
101
+ runnerName: runConfig.runnerName ?? runConfig.runnerName,
102
+ runnerKey: runConfig.runnerKey,
99
103
  metadata: {
100
104
  inspectorToken: this.#runConfig.inspector.token(),
101
105
  },
@@ -109,14 +113,14 @@ export class EngineActorDriver implements ActorDriver {
109
113
  if (hasDisconnected) {
110
114
  logger().info({
111
115
  msg: "runner reconnected",
112
- namespace: this.#config.namespace,
113
- runnerName: this.#config.runnerName,
116
+ namespace: this.#runConfig.namespace,
117
+ runnerName: this.#runConfig.runnerName,
114
118
  });
115
119
  } else {
116
120
  logger().debug({
117
121
  msg: "runner connected",
118
- namespace: this.#config.namespace,
119
- runnerName: this.#config.runnerName,
122
+ namespace: this.#runConfig.namespace,
123
+ runnerName: this.#runConfig.runnerName,
120
124
  });
121
125
  }
122
126
 
@@ -125,8 +129,8 @@ export class EngineActorDriver implements ActorDriver {
125
129
  onDisconnected: () => {
126
130
  logger().warn({
127
131
  msg: "runner disconnected",
128
- namespace: this.#config.namespace,
129
- runnerName: this.#config.runnerName,
132
+ namespace: this.#runConfig.namespace,
133
+ runnerName: this.#runConfig.runnerName,
130
134
  });
131
135
  hasDisconnected = true;
132
136
  },
@@ -141,13 +145,13 @@ export class EngineActorDriver implements ActorDriver {
141
145
  };
142
146
 
143
147
  // Create and start runner
144
- this.#runner = new Runner(runnerConfig);
148
+ this.#runner = new Runner(engineRunnerConfig);
145
149
  this.#runner.start();
146
150
  logger().debug({
147
151
  msg: "engine runner started",
148
- endpoint: config.endpoint,
149
- namespace: config.namespace,
150
- runnerName: config.runnerName,
152
+ endpoint: runConfig.endpoint,
153
+ namespace: runConfig.namespace,
154
+ runnerName: runConfig.runnerName,
151
155
  });
152
156
  }
153
157
 
@@ -228,20 +232,20 @@ export class EngineActorDriver implements ActorDriver {
228
232
  async #runnerOnActorStart(
229
233
  actorId: string,
230
234
  generation: number,
231
- config: RunnerActorConfig,
235
+ runConfig: RunnerActorConfig,
232
236
  ): Promise<void> {
233
237
  logger().debug({
234
238
  msg: "runner actor starting",
235
239
  actorId,
236
- name: config.name,
237
- key: config.key,
240
+ name: runConfig.name,
241
+ key: runConfig.key,
238
242
  generation,
239
243
  });
240
244
 
241
245
  // Deserialize input
242
246
  let input: any;
243
- if (config.input) {
244
- input = cbor.decode(config.input);
247
+ if (runConfig.input) {
248
+ input = cbor.decode(runConfig.input);
245
249
  }
246
250
 
247
251
  // Get or create handler
@@ -254,15 +258,12 @@ export class EngineActorDriver implements ActorDriver {
254
258
  this.#actors.set(actorId, handler);
255
259
  }
256
260
 
257
- const name = config.name as string;
258
- invariant(config.key, "actor should have a key");
259
- const key = deserializeActorKey(config.key);
261
+ const name = runConfig.name as string;
262
+ invariant(runConfig.key, "actor should have a key");
263
+ const key = deserializeActorKey(runConfig.key);
260
264
 
261
265
  // Create actor instance
262
- const definition = lookupInRegistry(
263
- this.#registryConfig,
264
- config.name as string, // TODO: Remove cast
265
- );
266
+ const definition = lookupInRegistry(this.#registryConfig, runConfig.name);
266
267
  handler.actor = definition.instantiate();
267
268
 
268
269
  // Start actor
@@ -413,7 +414,7 @@ export class EngineActorDriver implements ActorDriver {
413
414
  // Runner id should be set if the runner started
414
415
  const payload = this.#runner.getServerlessInitPacket();
415
416
  invariant(payload, "runnerId not set");
416
- stream.writeSSE({ data: payload });
417
+ await stream.writeSSE({ data: payload });
417
418
 
418
419
  return this.#runnerStopped.promise;
419
420
  });
@@ -1,35 +1,22 @@
1
- import type { Hono } from "hono";
2
1
  import { z } from "zod";
2
+ import { ClientConfigSchema } from "@/client/config";
3
3
  import { getEnvUniversal } from "@/utils";
4
4
 
5
- export const ConfigSchema = z
5
+ export const EngingConfigSchema = z
6
6
  .object({
7
- app: z.custom<Hono>().optional(),
8
- endpoint: z
9
- .string()
10
- .default(
11
- () => getEnvUniversal("RIVET_ENGINE") ?? "http://localhost:6420",
12
- ),
13
- token: z
14
- .string()
15
- .optional()
16
- .transform((val) => val ?? getEnvUniversal("RIVET_TOKEN")),
17
- pegboardEndpoint: z.string().optional(),
18
- namespace: z
19
- .string()
20
- .default(() => getEnvUniversal("RIVET_NAMESPACE") ?? "default"),
21
- runnerName: z
22
- .string()
23
- .default(() => getEnvUniversal("RIVET_RUNNER") ?? "rivetkit"),
24
- // TODO: Automatically attempt to determine key by common env vars (e.g. k8s pod name)
7
+ /** Unique key for this runner. Runners connecting a given key will replace any other runner connected with the same key. */
25
8
  runnerKey: z
26
9
  .string()
27
10
  .default(
28
11
  () => getEnvUniversal("RIVET_RUNNER_KEY") ?? crypto.randomUUID(),
29
12
  ),
13
+
14
+ /** How many actors this runner can run. */
30
15
  totalSlots: z.number().default(100_000),
31
16
  })
17
+ // We include the client config since this includes the common properties like endpoint, namespace, etc.
18
+ .merge(ClientConfigSchema)
32
19
  .default({});
33
20
 
34
- export type InputConfig = z.input<typeof ConfigSchema>;
35
- export type Config = z.infer<typeof ConfigSchema>;
21
+ export type EngineConfig = z.infer<typeof EngingConfigSchema>;
22
+ export type EngineConfigInput = z.input<typeof EngingConfigSchema>;
@@ -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 {
@@ -52,7 +52,7 @@ export interface ManagerDriver {
52
52
 
53
53
  /**
54
54
  * Get or create the inspector access token.
55
- * @internal
55
+ * @experimental
56
56
  * @returns creates or returns existing inspector access token
57
57
  */
58
58
  getOrCreateInspectorAccessToken: () => string;