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.
- package/dist/tsup/{chunk-2FAWAPRT.js → chunk-346X2XU4.js} +2 -2
- package/dist/tsup/{chunk-SFRRXLRM.js → chunk-7E5K3375.js} +2 -2
- package/dist/tsup/{chunk-3WRAGTDC.cjs → chunk-CA3X5M6H.cjs} +92 -39
- package/dist/tsup/{chunk-PVKV2O2E.js.map → chunk-CA3X5M6H.cjs.map} +1 -1
- package/dist/tsup/{chunk-L5MHM6JJ.cjs → chunk-DVPXSB4B.cjs} +12 -12
- package/dist/tsup/{chunk-L5MHM6JJ.cjs.map → chunk-DVPXSB4B.cjs.map} +1 -1
- package/dist/tsup/{chunk-DQVVH5ZK.cjs → chunk-GIFHYL7A.cjs} +5 -6
- package/dist/tsup/chunk-GIFHYL7A.cjs.map +1 -0
- package/dist/tsup/{chunk-N7OVEOMU.js → chunk-H7E2UU23.js} +38 -15
- package/dist/tsup/chunk-H7E2UU23.js.map +1 -0
- package/dist/tsup/{chunk-A6TV3QU6.js → chunk-HI55LHM3.js} +5 -6
- package/dist/tsup/chunk-HI55LHM3.js.map +1 -0
- package/dist/tsup/{chunk-FGOZELKN.cjs → chunk-I3FB346I.cjs} +112 -58
- package/dist/tsup/chunk-I3FB346I.cjs.map +1 -0
- package/dist/tsup/{chunk-DOZBWJRI.js → chunk-KGDZYQYE.js} +2 -2
- package/dist/tsup/{chunk-KYEEAVJO.cjs → chunk-KH5WFDUK.cjs} +6 -6
- package/dist/tsup/{chunk-KYEEAVJO.cjs.map → chunk-KH5WFDUK.cjs.map} +1 -1
- package/dist/tsup/{chunk-WP7YG7S5.js → chunk-KL4V2ULR.js} +5 -4
- package/dist/tsup/chunk-KL4V2ULR.js.map +1 -0
- package/dist/tsup/{chunk-S6EAEZQA.js → chunk-MLQIYKAZ.js} +106 -52
- package/dist/tsup/chunk-MLQIYKAZ.js.map +1 -0
- package/dist/tsup/{chunk-3ZMJUIL3.js → chunk-N3A5GYJU.js} +3 -3
- package/dist/tsup/{chunk-CKSA7NOS.cjs → chunk-PDFL7FBL.cjs} +717 -380
- package/dist/tsup/chunk-PDFL7FBL.cjs.map +1 -0
- package/dist/tsup/{chunk-ESD2JX3L.cjs → chunk-PPLR53PP.cjs} +3 -3
- package/dist/tsup/{chunk-ESD2JX3L.cjs.map → chunk-PPLR53PP.cjs.map} +1 -1
- package/dist/tsup/{chunk-6INXQCH7.cjs → chunk-PSCDCEXM.cjs} +17 -12
- package/dist/tsup/chunk-PSCDCEXM.cjs.map +1 -0
- package/dist/tsup/{chunk-PVKV2O2E.js → chunk-QRFXXTLG.js} +96 -43
- package/dist/tsup/chunk-QRFXXTLG.js.map +1 -0
- package/dist/tsup/{chunk-RM2V2IRK.js → chunk-R2S45MO6.js} +14 -9
- package/dist/tsup/chunk-R2S45MO6.js.map +1 -0
- package/dist/tsup/{chunk-QGUQB3NC.cjs → chunk-SIWYIRXP.cjs} +7 -6
- package/dist/tsup/chunk-SIWYIRXP.cjs.map +1 -0
- package/dist/tsup/{chunk-E77RVI3P.js → chunk-VJRXZPTT.js} +601 -264
- package/dist/tsup/chunk-VJRXZPTT.js.map +1 -0
- package/dist/tsup/{chunk-KDNB2BQX.cjs → chunk-VZMXAZKC.cjs} +229 -206
- package/dist/tsup/chunk-VZMXAZKC.cjs.map +1 -0
- package/dist/tsup/{chunk-TPJNKVFB.cjs → chunk-YKVTF7MP.cjs} +7 -7
- package/dist/tsup/{chunk-TPJNKVFB.cjs.map → chunk-YKVTF7MP.cjs.map} +1 -1
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +2 -2
- package/dist/tsup/client/mod.d.ts +2 -2
- package/dist/tsup/client/mod.js +8 -8
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{conn-ChAuuTr0.d.cts → conn-Cc9WHuN4.d.cts} +196 -185
- package/dist/tsup/{conn-CjUkMEcm.d.ts → conn-DfPG71FA.d.ts} +196 -185
- package/dist/tsup/driver-helpers/mod.cjs +7 -5
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +4 -2
- package/dist/tsup/driver-helpers/mod.d.ts +4 -2
- package/dist/tsup/driver-helpers/mod.js +9 -7
- package/dist/tsup/driver-test-suite/mod.cjs +116 -102
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +3 -2
- package/dist/tsup/driver-test-suite/mod.d.ts +3 -2
- package/dist/tsup/driver-test-suite/mod.js +61 -47
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.d.cts +6 -6
- package/dist/tsup/inspector/mod.d.ts +6 -6
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -10
- package/dist/tsup/mod.d.cts +8 -39
- package/dist/tsup/mod.d.ts +8 -39
- package/dist/tsup/mod.js +9 -9
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.d.cts +2 -1
- package/dist/tsup/utils.d.ts +2 -1
- package/dist/tsup/utils.js +1 -1
- package/package.json +4 -5
- package/src/actor/driver.ts +2 -2
- package/src/actor/instance.ts +4 -4
- package/src/actor/protocol/serde.ts +75 -3
- package/src/actor/router-endpoints.ts +6 -6
- package/src/actor/router.ts +2 -2
- package/src/client/actor-conn.ts +24 -3
- package/src/client/config.ts +18 -25
- package/src/driver-helpers/mod.ts +5 -1
- package/src/driver-test-suite/mod.ts +65 -43
- package/src/driver-test-suite/utils.ts +4 -1
- package/src/drivers/default.ts +11 -9
- package/src/drivers/engine/actor-driver.ts +40 -39
- package/src/drivers/engine/config.ts +9 -22
- package/src/drivers/engine/mod.ts +9 -8
- package/src/drivers/file-system/global-state.ts +4 -4
- package/src/engine-process/log.ts +5 -0
- package/src/engine-process/mod.ts +316 -0
- package/src/inspector/utils.ts +6 -4
- package/src/manager/driver.ts +3 -3
- package/src/manager/gateway.ts +29 -11
- package/src/manager/router-schema.ts +20 -0
- package/src/manager/router.ts +139 -58
- package/src/registry/mod.ts +146 -120
- package/src/registry/run-config.ts +116 -47
- package/src/registry/serve.ts +3 -1
- package/src/remote-manager-driver/mod.ts +3 -2
- package/src/serde.ts +18 -3
- package/src/test/config.ts +2 -2
- package/src/test/mod.ts +6 -3
- package/src/utils.ts +2 -0
- package/dist/tsup/chunk-3WRAGTDC.cjs.map +0 -1
- package/dist/tsup/chunk-6INXQCH7.cjs.map +0 -1
- package/dist/tsup/chunk-A6TV3QU6.js.map +0 -1
- package/dist/tsup/chunk-CKSA7NOS.cjs.map +0 -1
- package/dist/tsup/chunk-DQVVH5ZK.cjs.map +0 -1
- package/dist/tsup/chunk-E77RVI3P.js.map +0 -1
- package/dist/tsup/chunk-FGOZELKN.cjs.map +0 -1
- package/dist/tsup/chunk-KDNB2BQX.cjs.map +0 -1
- package/dist/tsup/chunk-N7OVEOMU.js.map +0 -1
- package/dist/tsup/chunk-QGUQB3NC.cjs.map +0 -1
- package/dist/tsup/chunk-RM2V2IRK.js.map +0 -1
- package/dist/tsup/chunk-S6EAEZQA.js.map +0 -1
- package/dist/tsup/chunk-WP7YG7S5.js.map +0 -1
- /package/dist/tsup/{chunk-2FAWAPRT.js.map → chunk-346X2XU4.js.map} +0 -0
- /package/dist/tsup/{chunk-SFRRXLRM.js.map → chunk-7E5K3375.js.map} +0 -0
- /package/dist/tsup/{chunk-DOZBWJRI.js.map → chunk-KGDZYQYE.js.map} +0 -0
- /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 {
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
95
|
+
const engineRunnerConfig: EngineRunnerConfig = {
|
|
91
96
|
version: this.#version,
|
|
92
|
-
endpoint:
|
|
93
|
-
token
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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.#
|
|
113
|
-
runnerName: this.#
|
|
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.#
|
|
119
|
-
runnerName: this.#
|
|
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.#
|
|
129
|
-
runnerName: this.#
|
|
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(
|
|
148
|
+
this.#runner = new Runner(engineRunnerConfig);
|
|
145
149
|
this.#runner.start();
|
|
146
150
|
logger().debug({
|
|
147
151
|
msg: "engine runner started",
|
|
148
|
-
endpoint:
|
|
149
|
-
namespace:
|
|
150
|
-
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
|
-
|
|
235
|
+
runConfig: RunnerActorConfig,
|
|
232
236
|
): Promise<void> {
|
|
233
237
|
logger().debug({
|
|
234
238
|
msg: "runner actor starting",
|
|
235
239
|
actorId,
|
|
236
|
-
name:
|
|
237
|
-
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 (
|
|
244
|
-
input = cbor.decode(
|
|
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 =
|
|
258
|
-
invariant(
|
|
259
|
-
const key = deserializeActorKey(
|
|
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
|
|
5
|
+
export const EngingConfigSchema = z
|
|
6
6
|
.object({
|
|
7
|
-
|
|
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
|
|
35
|
-
export type
|
|
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,
|
|
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 {
|
|
7
|
+
import { type EngineConfigInput, EngingConfigSchema } from "./config";
|
|
8
8
|
|
|
9
9
|
export { EngineActorDriver } from "./actor-driver";
|
|
10
|
-
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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:
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
439
|
+
runConfig: RunnerConfig,
|
|
440
440
|
inlineClient: AnyClient,
|
|
441
441
|
actorDriver: ActorDriver,
|
|
442
442
|
actorId: string,
|
|
@@ -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
|
+
}
|
package/src/inspector/utils.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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
|
-
|
|
69
|
-
|
|
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;
|
package/src/manager/driver.ts
CHANGED
|
@@ -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 {
|
|
5
|
+
import type { RunnerConfig } from "@/registry/run-config";
|
|
6
6
|
|
|
7
7
|
export type ManagerDriverBuilder = (
|
|
8
8
|
registryConfig: RegistryConfig,
|
|
9
|
-
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
|
-
* @
|
|
55
|
+
* @experimental
|
|
56
56
|
* @returns creates or returns existing inspector access token
|
|
57
57
|
*/
|
|
58
58
|
getOrCreateInspectorAccessToken: () => string;
|