@rivetkit/engine-runner 2.1.3 → 2.1.5
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/mod.cjs +49 -47
- package/dist/mod.cjs.map +1 -1
- package/dist/mod.d.cts +1 -0
- package/dist/mod.d.ts +1 -0
- package/dist/mod.js +49 -47
- package/dist/mod.js.map +1 -1
- package/package.json +2 -2
- package/src/mod.ts +36 -49
- package/src/tunnel.ts +7 -3
- package/src/utils.ts +3 -0
- package/src/websocket-tunnel-adapter.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivetkit/engine-runner",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"pino": "^9.9.5",
|
|
23
23
|
"ws": "^8.18.3",
|
|
24
24
|
"@rivetkit/virtual-websocket": "2.0.33",
|
|
25
|
-
"@rivetkit/engine-runner-protocol": "2.1.
|
|
25
|
+
"@rivetkit/engine-runner-protocol": "2.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.18.1",
|
package/src/mod.ts
CHANGED
|
@@ -18,7 +18,7 @@ export { RunnerActor, type ActorConfig };
|
|
|
18
18
|
export { idToStr } from "./utils";
|
|
19
19
|
|
|
20
20
|
const KV_EXPIRE: number = 30_000;
|
|
21
|
-
const PROTOCOL_VERSION: number =
|
|
21
|
+
const PROTOCOL_VERSION: number = 6;
|
|
22
22
|
|
|
23
23
|
/** Warn once the backlog significantly exceeds the server's ack batch size. */
|
|
24
24
|
const EVENT_BACKLOG_WARN_THRESHOLD = 10_000;
|
|
@@ -208,8 +208,10 @@ export class Runner {
|
|
|
208
208
|
#reconnectAttempt: number = 0;
|
|
209
209
|
#reconnectTimeout?: NodeJS.Timeout;
|
|
210
210
|
|
|
211
|
+
// Protocol metadata
|
|
212
|
+
#protocolMetadata?: protocol.ProtocolMetadata;
|
|
213
|
+
|
|
211
214
|
// Runner lost threshold management
|
|
212
|
-
#runnerLostThreshold?: number;
|
|
213
215
|
#runnerLostTimeout?: NodeJS.Timeout;
|
|
214
216
|
|
|
215
217
|
// Event storage for resending
|
|
@@ -839,14 +841,11 @@ export class Runner {
|
|
|
839
841
|
this.#stopAllActors();
|
|
840
842
|
}
|
|
841
843
|
|
|
842
|
-
|
|
843
|
-
this.#runnerLostThreshold = init.metadata?.runnerLostThreshold
|
|
844
|
-
? Number(init.metadata.runnerLostThreshold)
|
|
845
|
-
: undefined;
|
|
844
|
+
this.#protocolMetadata = init.metadata;
|
|
846
845
|
|
|
847
846
|
this.log?.info({
|
|
848
847
|
msg: "received init",
|
|
849
|
-
|
|
848
|
+
protocolMetadata: this.#protocolMetadata,
|
|
850
849
|
});
|
|
851
850
|
|
|
852
851
|
// Resend pending events
|
|
@@ -888,27 +887,7 @@ export class Runner {
|
|
|
888
887
|
});
|
|
889
888
|
|
|
890
889
|
if (!this.#shutdown) {
|
|
891
|
-
|
|
892
|
-
if (
|
|
893
|
-
!this.#runnerLostTimeout &&
|
|
894
|
-
this.#runnerLostThreshold &&
|
|
895
|
-
this.#runnerLostThreshold > 0
|
|
896
|
-
) {
|
|
897
|
-
this.log?.info({
|
|
898
|
-
msg: "starting runner lost timeout",
|
|
899
|
-
seconds: this.#runnerLostThreshold / 1000,
|
|
900
|
-
});
|
|
901
|
-
this.#runnerLostTimeout = setTimeout(() => {
|
|
902
|
-
try {
|
|
903
|
-
this.#handleLost();
|
|
904
|
-
} catch (err) {
|
|
905
|
-
this.log?.error({
|
|
906
|
-
msg: "error handling runner lost",
|
|
907
|
-
error: stringifyError(err),
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
}, this.#runnerLostThreshold);
|
|
911
|
-
}
|
|
890
|
+
this.#startRunnerLostTimeout();
|
|
912
891
|
|
|
913
892
|
// Attempt to reconnect if not stopped
|
|
914
893
|
this.#scheduleReconnect();
|
|
@@ -944,27 +923,7 @@ export class Runner {
|
|
|
944
923
|
this.#ackInterval = undefined;
|
|
945
924
|
}
|
|
946
925
|
|
|
947
|
-
|
|
948
|
-
if (
|
|
949
|
-
!this.#runnerLostTimeout &&
|
|
950
|
-
this.#runnerLostThreshold &&
|
|
951
|
-
this.#runnerLostThreshold > 0
|
|
952
|
-
) {
|
|
953
|
-
this.log?.info({
|
|
954
|
-
msg: "starting runner lost timeout",
|
|
955
|
-
seconds: this.#runnerLostThreshold / 1000,
|
|
956
|
-
});
|
|
957
|
-
this.#runnerLostTimeout = setTimeout(() => {
|
|
958
|
-
try {
|
|
959
|
-
this.#handleLost();
|
|
960
|
-
} catch (err) {
|
|
961
|
-
this.log?.error({
|
|
962
|
-
msg: "error handling runner lost",
|
|
963
|
-
error: stringifyError(err),
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
}, this.#runnerLostThreshold);
|
|
967
|
-
}
|
|
926
|
+
this.#startRunnerLostTimeout();
|
|
968
927
|
|
|
969
928
|
// Attempt to reconnect if not stopped
|
|
970
929
|
this.#scheduleReconnect();
|
|
@@ -976,6 +935,30 @@ export class Runner {
|
|
|
976
935
|
});
|
|
977
936
|
}
|
|
978
937
|
|
|
938
|
+
#startRunnerLostTimeout() {
|
|
939
|
+
// Start runner lost timeout if we have a threshold and are not shutting down
|
|
940
|
+
if (
|
|
941
|
+
!this.#runnerLostTimeout &&
|
|
942
|
+
this.#protocolMetadata &&
|
|
943
|
+
this.#protocolMetadata.runnerLostThreshold > 0
|
|
944
|
+
) {
|
|
945
|
+
this.log?.info({
|
|
946
|
+
msg: "starting runner lost timeout",
|
|
947
|
+
seconds: this.#protocolMetadata.runnerLostThreshold / 1000n,
|
|
948
|
+
});
|
|
949
|
+
this.#runnerLostTimeout = setTimeout(() => {
|
|
950
|
+
try {
|
|
951
|
+
this.#handleLost();
|
|
952
|
+
} catch (err) {
|
|
953
|
+
this.log?.error({
|
|
954
|
+
msg: "error handling runner lost",
|
|
955
|
+
error: stringifyError(err),
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
}, Number(this.#protocolMetadata.runnerLostThreshold));
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
979
962
|
#handleCommands(commands: protocol.ToClientCommands) {
|
|
980
963
|
this.log?.info({
|
|
981
964
|
msg: "received commands",
|
|
@@ -1859,4 +1842,8 @@ export class Runner {
|
|
|
1859
1842
|
//this.#log?.log(`Cleaned up ${toDelete.length} expired KV requests`);
|
|
1860
1843
|
}
|
|
1861
1844
|
}
|
|
1845
|
+
|
|
1846
|
+
getProtocolMetadata(): protocol.ProtocolMetadata | undefined {
|
|
1847
|
+
return this.#protocolMetadata;
|
|
1848
|
+
}
|
|
1862
1849
|
}
|
package/src/tunnel.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
stringifyToClientTunnelMessageKind,
|
|
16
16
|
stringifyToServerTunnelMessageKind,
|
|
17
17
|
} from "./stringify";
|
|
18
|
-
import { arraysEqual, idToStr, stringifyError, unreachable } from "./utils";
|
|
18
|
+
import { arraysEqual, idToStr, MAX_PAYLOAD_SIZE, stringifyError, unreachable } from "./utils";
|
|
19
19
|
import {
|
|
20
20
|
HIBERNATABLE_SYMBOL,
|
|
21
21
|
WebSocketTunnelAdapter,
|
|
@@ -855,6 +855,10 @@ export class Tunnel {
|
|
|
855
855
|
// Read the body first to get the actual content
|
|
856
856
|
const body = response.body ? await response.arrayBuffer() : null;
|
|
857
857
|
|
|
858
|
+
if (body && body.byteLength > MAX_PAYLOAD_SIZE) {
|
|
859
|
+
throw new Error("Response body too large");
|
|
860
|
+
}
|
|
861
|
+
|
|
858
862
|
// Convert headers to map and add Content-Length if not present
|
|
859
863
|
const headers = new Map<string, string>();
|
|
860
864
|
response.headers.forEach((value, key) => {
|
|
@@ -1079,7 +1083,7 @@ export class Tunnel {
|
|
|
1079
1083
|
});
|
|
1080
1084
|
|
|
1081
1085
|
if (clientMessageIndex < 0 || clientMessageIndex > 65535)
|
|
1082
|
-
throw new Error("
|
|
1086
|
+
throw new Error("Invalid websocket ack index");
|
|
1083
1087
|
|
|
1084
1088
|
// Get the actor to find the gatewayId
|
|
1085
1089
|
//
|
|
@@ -1157,7 +1161,7 @@ function buildRequestForWebSocket(
|
|
|
1157
1161
|
};
|
|
1158
1162
|
|
|
1159
1163
|
if (!path.startsWith("/")) {
|
|
1160
|
-
throw new Error("
|
|
1164
|
+
throw new Error("Path must start with leading slash");
|
|
1161
1165
|
}
|
|
1162
1166
|
|
|
1163
1167
|
const request = new Request(`http://actor${path}`, {
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { logger } from "./log";
|
|
2
2
|
|
|
3
|
+
// 20MiB. Keep in sync with runner_max_response_payload_body_size from engine/packages/config/src/config/pegboard.rs
|
|
4
|
+
export const MAX_PAYLOAD_SIZE = 20 * 1024 * 1024;
|
|
5
|
+
|
|
3
6
|
export function unreachable(x: never): never {
|
|
4
7
|
throw `Unreachable: ${x}`;
|
|
5
8
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "pino";
|
|
2
2
|
import { VirtualWebSocket, type UniversalWebSocket, type RivetMessageEvent } from "@rivetkit/virtual-websocket";
|
|
3
3
|
import type { Tunnel } from "./tunnel";
|
|
4
|
-
import { wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils";
|
|
4
|
+
import { MAX_PAYLOAD_SIZE, wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils";
|
|
5
5
|
|
|
6
6
|
export const HIBERNATABLE_SYMBOL = Symbol("hibernatable");
|
|
7
7
|
|
|
@@ -70,11 +70,20 @@ export class WebSocketTunnelAdapter {
|
|
|
70
70
|
let messageData: string | ArrayBuffer;
|
|
71
71
|
|
|
72
72
|
if (typeof data === "string") {
|
|
73
|
+
const encoder = new TextEncoder();
|
|
74
|
+
if (encoder.encode(data).byteLength > MAX_PAYLOAD_SIZE) {
|
|
75
|
+
throw new Error("WebSocket message too large");
|
|
76
|
+
}
|
|
77
|
+
|
|
73
78
|
messageData = data;
|
|
74
79
|
} else if (data instanceof ArrayBuffer) {
|
|
80
|
+
if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
|
|
81
|
+
|
|
75
82
|
isBinary = true;
|
|
76
83
|
messageData = data;
|
|
77
84
|
} else if (ArrayBuffer.isView(data)) {
|
|
85
|
+
if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
|
|
86
|
+
|
|
78
87
|
isBinary = true;
|
|
79
88
|
const view = data;
|
|
80
89
|
const buffer = view.buffer instanceof SharedArrayBuffer
|