rivetkit 2.0.9 → 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-APHV6WXU.js → chunk-346X2XU4.js} +2 -2
- package/dist/tsup/{chunk-DLPIL3VC.js → chunk-7E5K3375.js} +2 -2
- package/dist/tsup/{chunk-SOC4HWCG.cjs → chunk-CA3X5M6H.cjs} +92 -39
- package/dist/tsup/{chunk-VVCL5DXN.js.map → chunk-CA3X5M6H.cjs.map} +1 -1
- package/dist/tsup/{chunk-2MJYYF2Q.cjs → chunk-DVPXSB4B.cjs} +12 -12
- package/dist/tsup/{chunk-2MJYYF2Q.cjs.map → chunk-DVPXSB4B.cjs.map} +1 -1
- package/dist/tsup/{chunk-U2IXX6DY.cjs → chunk-GIFHYL7A.cjs} +5 -6
- package/dist/tsup/chunk-GIFHYL7A.cjs.map +1 -0
- package/dist/tsup/{chunk-KHZ2QSQ4.js → chunk-H7E2UU23.js} +32 -10
- package/dist/tsup/chunk-H7E2UU23.js.map +1 -0
- package/dist/tsup/{chunk-E63WU5PL.js → chunk-HI55LHM3.js} +5 -6
- package/dist/tsup/chunk-HI55LHM3.js.map +1 -0
- package/dist/tsup/{chunk-SDXTJDDR.cjs → chunk-I3FB346I.cjs} +58 -14
- package/dist/tsup/chunk-I3FB346I.cjs.map +1 -0
- package/dist/tsup/{chunk-WBSPHV5V.js → chunk-KGDZYQYE.js} +2 -2
- package/dist/tsup/{chunk-A44TWAS5.cjs → chunk-KH5WFDUK.cjs} +6 -6
- package/dist/tsup/{chunk-A44TWAS5.cjs.map → chunk-KH5WFDUK.cjs.map} +1 -1
- package/dist/tsup/{chunk-YR2VY4XS.js → chunk-KL4V2ULR.js} +5 -4
- package/dist/tsup/chunk-KL4V2ULR.js.map +1 -0
- package/dist/tsup/{chunk-R7OP5N25.js → chunk-MLQIYKAZ.js} +53 -9
- package/dist/tsup/chunk-MLQIYKAZ.js.map +1 -0
- package/dist/tsup/{chunk-F2YZNUPU.js → chunk-N3A5GYJU.js} +3 -3
- package/dist/tsup/{chunk-4YV6RDZL.cjs → chunk-PDFL7FBL.cjs} +698 -358
- package/dist/tsup/chunk-PDFL7FBL.cjs.map +1 -0
- package/dist/tsup/{chunk-DZZQG7VH.cjs → chunk-PPLR53PP.cjs} +3 -3
- package/dist/tsup/{chunk-DZZQG7VH.cjs.map → chunk-PPLR53PP.cjs.map} +1 -1
- package/dist/tsup/{chunk-7OMMIAWP.cjs → chunk-PSCDCEXM.cjs} +17 -12
- package/dist/tsup/chunk-PSCDCEXM.cjs.map +1 -0
- package/dist/tsup/{chunk-VVCL5DXN.js → chunk-QRFXXTLG.js} +96 -43
- package/dist/tsup/chunk-QRFXXTLG.js.map +1 -0
- package/dist/tsup/{chunk-WRSWUDFA.js → chunk-R2S45MO6.js} +14 -9
- package/dist/tsup/chunk-R2S45MO6.js.map +1 -0
- package/dist/tsup/{chunk-QGRYH6TU.cjs → chunk-SIWYIRXP.cjs} +7 -6
- package/dist/tsup/chunk-SIWYIRXP.cjs.map +1 -0
- package/dist/tsup/{chunk-FZP2IBIX.js → chunk-VJRXZPTT.js} +579 -239
- package/dist/tsup/chunk-VJRXZPTT.js.map +1 -0
- package/dist/tsup/{chunk-4PSLOAXR.cjs → chunk-VZMXAZKC.cjs} +226 -204
- package/dist/tsup/chunk-VZMXAZKC.cjs.map +1 -0
- package/dist/tsup/{chunk-DL7TPF63.cjs → chunk-YKVTF7MP.cjs} +7 -7
- package/dist/tsup/{chunk-DL7TPF63.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-CEh3WKbA.d.cts → conn-Cc9WHuN4.d.cts} +196 -191
- package/dist/tsup/{conn-Bt8rkUzm.d.ts → conn-DfPG71FA.d.ts} +196 -191
- package/dist/tsup/driver-helpers/mod.cjs +5 -5
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +1 -1
- package/dist/tsup/driver-helpers/mod.d.ts +1 -1
- package/dist/tsup/driver-helpers/mod.js +6 -6
- 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/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 +4 -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 +2 -2
- package/src/manager/gateway.ts +29 -11
- package/src/manager/router-schema.ts +20 -0
- package/src/manager/router.ts +105 -23
- package/src/registry/mod.ts +145 -119
- package/src/registry/run-config.ts +116 -47
- package/src/registry/serve.ts +3 -1
- package/src/serde.ts +3 -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-4PSLOAXR.cjs.map +0 -1
- package/dist/tsup/chunk-4YV6RDZL.cjs.map +0 -1
- package/dist/tsup/chunk-7OMMIAWP.cjs.map +0 -1
- package/dist/tsup/chunk-E63WU5PL.js.map +0 -1
- package/dist/tsup/chunk-FZP2IBIX.js.map +0 -1
- package/dist/tsup/chunk-KHZ2QSQ4.js.map +0 -1
- package/dist/tsup/chunk-QGRYH6TU.cjs.map +0 -1
- package/dist/tsup/chunk-R7OP5N25.js.map +0 -1
- package/dist/tsup/chunk-SDXTJDDR.cjs.map +0 -1
- package/dist/tsup/chunk-SOC4HWCG.cjs.map +0 -1
- package/dist/tsup/chunk-U2IXX6DY.cjs.map +0 -1
- package/dist/tsup/chunk-WRSWUDFA.js.map +0 -1
- package/dist/tsup/chunk-YR2VY4XS.js.map +0 -1
- /package/dist/tsup/{chunk-APHV6WXU.js.map → chunk-346X2XU4.js.map} +0 -0
- /package/dist/tsup/{chunk-DLPIL3VC.js.map → chunk-7E5K3375.js.map} +0 -0
- /package/dist/tsup/{chunk-WBSPHV5V.js.map → chunk-KGDZYQYE.js.map} +0 -0
- /package/dist/tsup/{chunk-F2YZNUPU.js.map → chunk-N3A5GYJU.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,
|
|
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 {
|
package/src/manager/gateway.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
-
?
|
|
113
|
-
:
|
|
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:
|
|
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${
|
|
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
|
+
});
|