rivetkit 2.0.37 → 2.0.39

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 (104) hide show
  1. package/dist/tsup/{chunk-G4N7FZMM.cjs → chunk-7E3RWMR6.cjs} +195 -93
  2. package/dist/tsup/chunk-7E3RWMR6.cjs.map +1 -0
  3. package/dist/tsup/{chunk-J6TX5EFW.js → chunk-BQ36VTSB.js} +108 -6
  4. package/dist/tsup/chunk-BQ36VTSB.js.map +1 -0
  5. package/dist/tsup/{chunk-4V7MS7SO.cjs → chunk-C64FV764.cjs} +3 -3
  6. package/dist/tsup/{chunk-4V7MS7SO.cjs.map → chunk-C64FV764.cjs.map} +1 -1
  7. package/dist/tsup/{chunk-XI335ZED.js → chunk-CDK6DRO2.js} +6 -4
  8. package/dist/tsup/chunk-CDK6DRO2.js.map +1 -0
  9. package/dist/tsup/{chunk-LYYTV7DN.cjs → chunk-DY4H3ASE.cjs} +50 -46
  10. package/dist/tsup/chunk-DY4H3ASE.cjs.map +1 -0
  11. package/dist/tsup/{chunk-B6BP74X3.cjs → chunk-KMYFL3OL.cjs} +318 -92
  12. package/dist/tsup/chunk-KMYFL3OL.cjs.map +1 -0
  13. package/dist/tsup/{chunk-22NKW7F5.cjs → chunk-MZPYVTVG.cjs} +9 -9
  14. package/dist/tsup/{chunk-22NKW7F5.cjs.map → chunk-MZPYVTVG.cjs.map} +1 -1
  15. package/dist/tsup/{chunk-RBA5AQTB.js → chunk-OJZRCEIA.js} +5 -5
  16. package/dist/tsup/{chunk-RXA3ZMCL.js → chunk-PHCD25XO.js} +2 -2
  17. package/dist/tsup/{chunk-5XGZXH74.js → chunk-PVKUXMOA.js} +264 -38
  18. package/dist/tsup/chunk-PVKUXMOA.js.map +1 -0
  19. package/dist/tsup/{chunk-FIUSIG6J.js → chunk-T7IPDBWH.js} +8 -4
  20. package/dist/tsup/{chunk-FIUSIG6J.js.map → chunk-T7IPDBWH.js.map} +1 -1
  21. package/dist/tsup/{chunk-5VVIFC6M.cjs → chunk-UAX5E3EU.cjs} +443 -369
  22. package/dist/tsup/chunk-UAX5E3EU.cjs.map +1 -0
  23. package/dist/tsup/{chunk-X5IX3YPO.cjs → chunk-X72X7I7T.cjs} +6 -4
  24. package/dist/tsup/chunk-X72X7I7T.cjs.map +1 -0
  25. package/dist/tsup/{chunk-ZQBSQ6H3.js → chunk-XU74APB4.js} +208 -134
  26. package/dist/tsup/chunk-XU74APB4.js.map +1 -0
  27. package/dist/tsup/client/mod.cjs +5 -5
  28. package/dist/tsup/client/mod.d.cts +3 -3
  29. package/dist/tsup/client/mod.d.ts +3 -3
  30. package/dist/tsup/client/mod.js +4 -4
  31. package/dist/tsup/common/log.cjs +2 -2
  32. package/dist/tsup/common/log.js +1 -1
  33. package/dist/tsup/common/websocket.cjs +3 -3
  34. package/dist/tsup/common/websocket.js +2 -2
  35. package/dist/tsup/{config--NjwiYlS.d.cts → config-BuBlMs6C.d.cts} +238 -60
  36. package/dist/tsup/{config-CRuzI6n4.d.ts → config-CBwo4ooA.d.ts} +238 -60
  37. package/dist/tsup/{driver-yKjYx9Yy.d.cts → driver-CPXmh8f8.d.cts} +1 -1
  38. package/dist/tsup/{driver-BcmckRaF.d.ts → driver-DxWa6HUO.d.ts} +1 -1
  39. package/dist/tsup/driver-helpers/mod.cjs +3 -3
  40. package/dist/tsup/driver-helpers/mod.d.cts +2 -2
  41. package/dist/tsup/driver-helpers/mod.d.ts +2 -2
  42. package/dist/tsup/driver-helpers/mod.js +2 -2
  43. package/dist/tsup/driver-test-suite/mod.cjs +81 -35
  44. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
  45. package/dist/tsup/driver-test-suite/mod.d.cts +2 -2
  46. package/dist/tsup/driver-test-suite/mod.d.ts +2 -2
  47. package/dist/tsup/driver-test-suite/mod.js +407 -361
  48. package/dist/tsup/driver-test-suite/mod.js.map +1 -1
  49. package/dist/tsup/{kv-CTM8sCvx.d.cts → keys-Chhy4ylv.d.cts} +1 -0
  50. package/dist/tsup/{kv-CTM8sCvx.d.ts → keys-Chhy4ylv.d.ts} +1 -0
  51. package/dist/tsup/mod.cjs +19 -7
  52. package/dist/tsup/mod.cjs.map +1 -1
  53. package/dist/tsup/mod.d.cts +5 -5
  54. package/dist/tsup/mod.d.ts +5 -5
  55. package/dist/tsup/mod.js +18 -6
  56. package/dist/tsup/test/mod.cjs +7 -7
  57. package/dist/tsup/test/mod.d.cts +1 -1
  58. package/dist/tsup/test/mod.d.ts +1 -1
  59. package/dist/tsup/test/mod.js +6 -6
  60. package/dist/tsup/utils.cjs +2 -2
  61. package/dist/tsup/utils.js +1 -1
  62. package/package.json +6 -4
  63. package/src/actor/config.ts +198 -2
  64. package/src/actor/contexts/base/actor.ts +12 -0
  65. package/src/actor/instance/connection-manager.ts +1 -1
  66. package/src/actor/instance/keys.ts +29 -0
  67. package/src/actor/instance/kv.ts +240 -14
  68. package/src/actor/instance/mod.ts +5 -4
  69. package/src/actor/instance/state-manager.ts +1 -1
  70. package/src/actor/mod.ts +2 -1
  71. package/src/actor/router-websocket-endpoints.ts +2 -1
  72. package/src/client/actor-conn.ts +70 -81
  73. package/src/client/actor-handle.ts +35 -15
  74. package/src/client/actor-query.ts +47 -0
  75. package/src/client/errors.ts +22 -58
  76. package/src/client/mod.ts +1 -1
  77. package/src/client/utils.ts +33 -0
  78. package/src/driver-helpers/utils.ts +1 -1
  79. package/src/driver-test-suite/mod.ts +3 -0
  80. package/src/driver-test-suite/test-inline-client-driver.ts +3 -0
  81. package/src/driver-test-suite/tests/actor-kv.ts +44 -0
  82. package/src/driver-test-suite/utils.ts +4 -0
  83. package/src/drivers/engine/actor-driver.ts +3 -3
  84. package/src/drivers/file-system/manager.ts +5 -0
  85. package/src/manager/driver.ts +8 -3
  86. package/src/manager-api/actors.ts +1 -20
  87. package/src/registry/config/index.ts +68 -0
  88. package/src/remote-manager-driver/actor-http-client.ts +5 -3
  89. package/src/remote-manager-driver/actor-websocket-client.ts +18 -7
  90. package/src/remote-manager-driver/mod.ts +21 -1
  91. package/src/serverless/router.test.ts +166 -0
  92. package/src/serverless/router.ts +58 -5
  93. package/src/utils/env-vars.ts +4 -1
  94. package/dist/tsup/chunk-5VVIFC6M.cjs.map +0 -1
  95. package/dist/tsup/chunk-5XGZXH74.js.map +0 -1
  96. package/dist/tsup/chunk-B6BP74X3.cjs.map +0 -1
  97. package/dist/tsup/chunk-G4N7FZMM.cjs.map +0 -1
  98. package/dist/tsup/chunk-J6TX5EFW.js.map +0 -1
  99. package/dist/tsup/chunk-LYYTV7DN.cjs.map +0 -1
  100. package/dist/tsup/chunk-X5IX3YPO.cjs.map +0 -1
  101. package/dist/tsup/chunk-XI335ZED.js.map +0 -1
  102. package/dist/tsup/chunk-ZQBSQ6H3.js.map +0 -1
  103. /package/dist/tsup/{chunk-RBA5AQTB.js.map → chunk-OJZRCEIA.js.map} +0 -0
  104. /package/dist/tsup/{chunk-RXA3ZMCL.js.map → chunk-PHCD25XO.js.map} +0 -0
@@ -1,5 +1,3 @@
1
- import { MAX_CONN_PARAMS_SIZE } from "@/common//network";
2
-
3
1
  export class ActorClientError extends Error {}
4
2
 
5
3
  export class InternalError extends ActorClientError {}
@@ -41,72 +39,38 @@ export class ActorConnDisposed extends ActorClientError {
41
39
  }
42
40
  }
43
41
 
44
- // === Actor Scheduling Error Types ===
45
-
46
- /**
47
- * Errors from serverless connection workflow.
48
- * Matches ServerlessConnectionError from API.
49
- */
50
- export type ServerlessConnectionError =
51
- | { http_error: { status_code: number; body: string } }
52
- | "stream_ended_early"
53
- | { connection_error: { message: string } }
54
- | "invalid_base64"
55
- | { invalid_payload: { message: string } }
56
- | "runner_config_not_found"
57
- | "runner_config_not_serverless"
58
- | "namespace_not_found";
59
-
60
42
  /**
61
- * Actor error details from API response.
62
- * Matches ActorError from API.
43
+ * Checks if an error code indicates a scheduling error that may have more details.
63
44
  */
64
- export type ActorErrorDetails =
65
- | { serverless_error: ServerlessConnectionError }
66
- | { no_capacity: { runner_name: string } }
67
- | { runner_no_response: { runner_id: string } };
45
+ export function isSchedulingError(group: string, code: string): boolean {
46
+ return (
47
+ group === "guard" &&
48
+ (code === "actor_ready_timeout" || code === "actor_runner_failed")
49
+ );
50
+ }
68
51
 
69
52
  /**
70
53
  * Error thrown when actor scheduling fails.
71
54
  * Provides detailed information about why the actor failed to start.
72
55
  */
73
- export class ActorSchedulingError extends ActorClientError {
56
+ export class ActorSchedulingError extends ActorError {
74
57
  public readonly actorId: string;
75
- public readonly errorType: string;
76
- public readonly details: ActorErrorDetails;
58
+ public readonly details: unknown;
77
59
 
78
- constructor(actorId: string, error: ActorErrorDetails) {
79
- const message = ActorSchedulingError.formatMessage(error);
80
- super(message);
60
+ constructor(
61
+ group: string,
62
+ code: string,
63
+ actorId: string,
64
+ details: unknown,
65
+ ) {
66
+ super(
67
+ group,
68
+ code,
69
+ `Actor failed to start (${actorId}): ${JSON.stringify(details)}`,
70
+ { actorId, details },
71
+ );
81
72
  this.name = "ActorSchedulingError";
82
73
  this.actorId = actorId;
83
- this.errorType = Object.keys(error)[0];
84
- this.details = error;
85
- }
86
-
87
- static formatMessage(error: ActorErrorDetails): string {
88
- if ("serverless_error" in error) {
89
- const se = error.serverless_error;
90
- if (typeof se === "string") {
91
- return `Serverless error: ${se.replace(/_/g, " ")}`;
92
- }
93
- if ("http_error" in se) {
94
- return `Serverless HTTP ${se.http_error.status_code}: ${se.http_error.body}`;
95
- }
96
- if ("connection_error" in se) {
97
- return `Serverless connection error: ${se.connection_error.message}`;
98
- }
99
- if ("invalid_payload" in se) {
100
- return `Invalid serverless payload: ${se.invalid_payload.message}`;
101
- }
102
- return "Unknown serverless error";
103
- }
104
- if ("no_capacity" in error) {
105
- return `No capacity available for runner: ${error.no_capacity.runner_name}`;
106
- }
107
- if ("runner_no_response" in error) {
108
- return `Runner ${error.runner_no_response.runner_id} did not respond`;
109
- }
110
- return "Unknown scheduling error";
74
+ this.details = details;
111
75
  }
112
76
  }
package/src/client/mod.ts CHANGED
@@ -22,7 +22,7 @@ export {
22
22
  ManagerError,
23
23
  } from "@/client/errors";
24
24
  export type { CreateRequest } from "@/manager/protocol/query";
25
- export { KEYS as KV_KEYS } from "../actor/instance/kv";
25
+ export { KEYS as KV_KEYS } from "../actor/instance/keys";
26
26
  export type { ActorActionFunction } from "./actor-common";
27
27
  export type {
28
28
  ActorConn,
@@ -20,6 +20,39 @@ import { httpUserAgent } from "@/utils";
20
20
  import { ActorError, HttpRequestError } from "./errors";
21
21
  import { logger } from "./log";
22
22
 
23
+ export interface ParsedCloseReason {
24
+ group: string;
25
+ code: string;
26
+ rayId?: string;
27
+ }
28
+
29
+ /**
30
+ * Parses WebSocket close reason string into structured data.
31
+ *
32
+ * Expected format examples:
33
+ * - "guard.actor_runner_failed#t1s80so6h3irenp8ymzltfoittcl00"
34
+ * - "ws.client_closed"
35
+ *
36
+ * Returns undefined if the format is invalid
37
+ */
38
+ export function parseWebSocketCloseReason(
39
+ reason: string,
40
+ ): ParsedCloseReason | undefined {
41
+ const [mainPart, rayId] = reason.split("#");
42
+ const [group, code] = mainPart.split(".");
43
+
44
+ if (!group || !code) {
45
+ logger().warn({ msg: "failed to parse close reason", reason });
46
+ return undefined;
47
+ }
48
+
49
+ return {
50
+ group,
51
+ code,
52
+ rayId,
53
+ };
54
+ }
55
+
23
56
  export type WebSocketMessage = string | Blob | ArrayBuffer | Uint8Array;
24
57
 
25
58
  export function messageLength(message: WebSocketMessage): number {
@@ -1,5 +1,5 @@
1
1
  import * as cbor from "cbor-x";
2
- import { KEYS } from "@/actor/instance/kv";
2
+ import { KEYS } from "@/actor/instance/keys";
3
3
  import type * as persistSchema from "@/schemas/actor-persist/mod";
4
4
  import {
5
5
  ACTOR_VERSIONED,
@@ -22,6 +22,7 @@ import { runActorErrorHandlingTests } from "./tests/actor-error-handling";
22
22
  import { runActorHandleTests } from "./tests/actor-handle";
23
23
  import { runActorInlineClientTests } from "./tests/actor-inline-client";
24
24
  import { runActorInspectorTests } from "./tests/actor-inspector";
25
+ import { runActorKvTests } from "./tests/actor-kv";
25
26
  import { runActorMetadataTests } from "./tests/actor-metadata";
26
27
  import { runActorOnStateChangeTests } from "./tests/actor-onstatechange";
27
28
  import { runActorVarsTests } from "./tests/actor-vars";
@@ -122,6 +123,8 @@ export function runDriverTests(
122
123
 
123
124
  runActorInlineClientTests(driverTestConfig);
124
125
 
126
+ runActorKvTests(driverTestConfig);
127
+
125
128
  runRawHttpTests(driverTestConfig);
126
129
 
127
130
  runRawHttpRequestPropertiesTests(driverTestConfig);
@@ -237,6 +237,9 @@ export function createTestInlineClientDriver(
237
237
  );
238
238
  return upgradeWebSocket(() => wsHandler)(c, noopNext());
239
239
  },
240
+ async buildGatewayUrl(actorId: string): Promise<string> {
241
+ return `${endpoint}/gateway/${actorId}`;
242
+ },
240
243
  displayInformation(): ManagerDisplayInformation {
241
244
  return { properties: {} };
242
245
  },
@@ -0,0 +1,44 @@
1
+ import type { DriverTestConfig } from "../mod";
2
+ import { setupDriverTest } from "../utils";
3
+ import { describe, expect, test, type TestContext } from "vitest";
4
+
5
+ export function runActorKvTests(driverTestConfig: DriverTestConfig) {
6
+ describe("Actor KV Tests", () => {
7
+ test("supports text encoding and decoding", async (c: TestContext) => {
8
+ const { client } = await setupDriverTest(c, driverTestConfig);
9
+ const kvHandle = client.kvActor.getOrCreate(["kv-text"]);
10
+
11
+ await kvHandle.putText("greeting", "hello");
12
+ const value = await kvHandle.getText("greeting");
13
+ expect(value).toBe("hello");
14
+
15
+ await kvHandle.putText("prefix-a", "alpha");
16
+ await kvHandle.putText("prefix-b", "beta");
17
+
18
+ const results = await kvHandle.listText("prefix-");
19
+ const sorted = results.sort((a, b) => a.key.localeCompare(b.key));
20
+ expect(sorted).toEqual([
21
+ { key: "prefix-a", value: "alpha" },
22
+ { key: "prefix-b", value: "beta" },
23
+ ]);
24
+ });
25
+
26
+ test(
27
+ "supports arrayBuffer encoding and decoding",
28
+ async (c: TestContext) => {
29
+ const { client } = await setupDriverTest(c, driverTestConfig);
30
+ const kvHandle = client.kvActor.getOrCreate(["kv-array-buffer"]);
31
+
32
+ const values = await kvHandle.roundtripArrayBuffer("bytes", [
33
+ 4,
34
+ 8,
35
+ 15,
36
+ 16,
37
+ 23,
38
+ 42,
39
+ ]);
40
+ expect(values).toEqual([4, 8, 15, 16, 23, 42]);
41
+ },
42
+ );
43
+ });
44
+ }
@@ -39,6 +39,10 @@ export async function setupDriverTest(
39
39
  namespace,
40
40
  runnerName,
41
41
  encoding: driverTestConfig.encoding,
42
+ // Disable metadata lookup to prevent redirect to the wrong port.
43
+ // Each test starts a new server on a dynamic port, but the
44
+ // registry's publicEndpoint defaults to port 6420.
45
+ disableMetadataLookup: true,
42
46
  });
43
47
  } else if (driverTestConfig.clientType === "inline") {
44
48
  // Use inline client from driver
@@ -11,7 +11,7 @@ import { WSContext, type WSContextInit } from "hono/ws";
11
11
  import invariant from "invariant";
12
12
  import { type AnyConn, CONN_STATE_MANAGER_SYMBOL } from "@/actor/conn/mod";
13
13
  import { lookupInRegistry } from "@/actor/definition";
14
- import { KEYS } from "@/actor/instance/kv";
14
+ import { KEYS } from "@/actor/instance/keys";
15
15
  import { deserializeActorKey } from "@/actor/keys";
16
16
  import { getValueLength } from "@/actor/protocol/old";
17
17
  import { type ActorRouter, createActorRouter } from "@/actor/router";
@@ -153,7 +153,7 @@ export class EngineActorDriver implements ActorDriver {
153
153
  onConnected: () => {
154
154
  this.#runnerStarted.resolve(undefined);
155
155
  },
156
- onDisconnected: (_code, _reason) => { },
156
+ onDisconnected: (_code, _reason) => {},
157
157
  onShutdown: () => {
158
158
  this.#runnerStopped.resolve(undefined);
159
159
  this.#isRunnerStopped = true;
@@ -358,7 +358,7 @@ export class EngineActorDriver implements ActorDriver {
358
358
  async serverlessHandleStart(c: HonoContext): Promise<Response> {
359
359
  return streamSSE(c, async (stream) => {
360
360
  // NOTE: onAbort does not work reliably
361
- stream.onAbort(() => { });
361
+ stream.onAbort(() => {});
362
362
  c.req.raw.signal.addEventListener("abort", () => {
363
363
  logger().debug("SSE aborted, shutting down runner");
364
364
 
@@ -147,6 +147,11 @@ export class FileSystemManagerDriver implements ManagerDriver {
147
147
  return upgradeWebSocket(() => wsHandler)(c, noopNext());
148
148
  }
149
149
 
150
+ async buildGatewayUrl(actorId: string): Promise<string> {
151
+ const port = this.#config.managerPort ?? 6420;
152
+ return `http://127.0.0.1:${port}/gateway/${encodeURIComponent(actorId)}`;
153
+ }
154
+
150
155
  async getForId({
151
156
  actorId,
152
157
  }: GetForIdInput): Promise<ActorOutput | undefined> {
@@ -1,7 +1,5 @@
1
1
  import type { Env, Hono, Context as HonoContext } from "hono";
2
2
  import type { ActorKey, Encoding, UniversalWebSocket } from "@/actor/mod";
3
-
4
- import type { ActorErrorDetails } from "@/client/errors";
5
3
  import type { RegistryConfig } from "@/registry/config";
6
4
  import type { GetUpgradeWebSocket } from "@/utils";
7
5
 
@@ -34,6 +32,13 @@ export interface ManagerDriver {
34
32
  params: unknown,
35
33
  ): Promise<Response>;
36
34
 
35
+ /**
36
+ * Build a public gateway URL for a specific actor.
37
+ *
38
+ * This lives on the driver because the base endpoint varies by runtime.
39
+ */
40
+ buildGatewayUrl(actorId: string): Promise<string>;
41
+
37
42
  displayInformation(): ManagerDisplayInformation;
38
43
 
39
44
  extraStartupLog?: () => Record<string, unknown>;
@@ -97,5 +102,5 @@ export interface ActorOutput {
97
102
  connectableTs?: number | null;
98
103
  sleepTs?: number | null;
99
104
  destroyTs?: number | null;
100
- error?: ActorErrorDetails | null;
105
+ error?: unknown;
101
106
  }
@@ -1,25 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { RivetIdSchema } from "./common";
3
3
 
4
- // Schema for serverless connection errors
5
- const ServerlessConnectionErrorSchema = z.union([
6
- z.object({ http_error: z.object({ status_code: z.number(), body: z.string() }) }),
7
- z.literal("stream_ended_early"),
8
- z.object({ connection_error: z.object({ message: z.string() }) }),
9
- z.literal("invalid_base64"),
10
- z.object({ invalid_payload: z.object({ message: z.string() }) }),
11
- z.literal("runner_config_not_found"),
12
- z.literal("runner_config_not_serverless"),
13
- z.literal("namespace_not_found"),
14
- ]);
15
-
16
- // Schema for actor error details from API
17
- const ActorErrorDetailsSchema = z.union([
18
- z.object({ serverless_error: ServerlessConnectionErrorSchema }),
19
- z.object({ no_capacity: z.object({ runner_name: z.string() }) }),
20
- z.object({ runner_no_response: z.object({ runner_id: z.string() }) }),
21
- ]);
22
-
23
4
  export const ActorSchema = z.object({
24
5
  actor_id: RivetIdSchema,
25
6
  name: z.string(),
@@ -31,7 +12,7 @@ export const ActorSchema = z.object({
31
12
  destroy_ts: z.number().nullable().optional(),
32
13
  sleep_ts: z.number().nullable().optional(),
33
14
  start_ts: z.number().nullable().optional(),
34
- error: ActorErrorDetailsSchema.nullable().optional(),
15
+ error: z.unknown().nullable().optional(),
35
16
  });
36
17
  export type Actor = z.infer<typeof ActorSchema>;
37
18
 
@@ -264,3 +264,71 @@ export function buildActorNames(
264
264
  Object.keys(config.use).map((name) => [name, { metadata: {} }]),
265
265
  );
266
266
  }
267
+
268
+ // MARK: Documentation Schemas
269
+ // These schemas are JSON-serializable versions used for documentation generation.
270
+ // They exclude runtime-only fields (transforms, custom types, Logger instances).
271
+
272
+ export const DocInspectorConfigSchema = z
273
+ .object({
274
+ enabled: z.boolean().optional().describe("Whether to enable the Rivet Inspector. Defaults to true in development mode."),
275
+ token: z.string().optional().describe("Token used to access the Inspector."),
276
+ defaultEndpoint: z.string().optional().describe("Default RivetKit server endpoint for Rivet Inspector to connect to."),
277
+ })
278
+ .optional()
279
+ .describe("Inspector configuration for debugging and development.");
280
+
281
+ export const DocConfigureRunnerPoolSchema = z
282
+ .object({
283
+ name: z.string().optional().describe("Name of the runner pool."),
284
+ url: z.string().describe("URL of the serverless platform to configure runners."),
285
+ headers: z.record(z.string(), z.string()).optional().describe("Headers to include in requests to the serverless platform."),
286
+ maxRunners: z.number().optional().describe("Maximum number of runners in the pool."),
287
+ minRunners: z.number().optional().describe("Minimum number of runners to keep warm."),
288
+ requestLifespan: z.number().optional().describe("Maximum lifespan of a request in milliseconds."),
289
+ runnersMargin: z.number().optional().describe("Buffer margin for scaling runners."),
290
+ slotsPerRunner: z.number().optional().describe("Number of actor slots per runner."),
291
+ metadata: z.record(z.string(), z.unknown()).optional().describe("Additional metadata to pass to the serverless platform."),
292
+ })
293
+ .optional();
294
+
295
+ export const DocServerlessConfigSchema = z.object({
296
+ spawnEngine: z.boolean().optional().describe("Downloads and starts the full Rust engine process. Auto-enabled in development mode when no endpoint is provided. Default: false"),
297
+ engineVersion: z.string().optional().describe("Version of the engine to download. Defaults to the current RivetKit version."),
298
+ configureRunnerPool: DocConfigureRunnerPoolSchema.describe("Automatically configure serverless runners in the engine."),
299
+ basePath: z.string().optional().describe("Base path for serverless API routes. Default: '/api/rivet'"),
300
+ publicEndpoint: z.string().optional().describe("The endpoint that clients should connect to. Supports URL auth syntax: https://namespace:token@api.rivet.dev"),
301
+ publicToken: z.string().optional().describe("Token that clients should use when connecting via the public endpoint."),
302
+ }).describe("Configuration for serverless deployment mode.");
303
+
304
+ export const DocRunnerConfigSchema = z.object({
305
+ totalSlots: z.number().optional().describe("Total number of actor slots available. Default: 100000"),
306
+ runnerName: z.string().optional().describe("Name of this runner. Default: 'default'"),
307
+ runnerKey: z.string().optional().describe("Authentication key for the runner."),
308
+ version: z.number().optional().describe("Version number of this runner. Default: 1"),
309
+ }).describe("Configuration for runner mode.");
310
+
311
+ export const DocRegistryConfigSchema = z
312
+ .object({
313
+ use: z.record(z.string(), z.unknown()).describe("Actor definitions. Keys are actor names, values are actor definitions."),
314
+ maxIncomingMessageSize: z.number().optional().describe("Maximum size of incoming WebSocket messages in bytes. Default: 65536"),
315
+ maxOutgoingMessageSize: z.number().optional().describe("Maximum size of outgoing WebSocket messages in bytes. Default: 1048576"),
316
+ noWelcome: z.boolean().optional().describe("Disable the welcome message on startup. Default: false"),
317
+ logging: z
318
+ .object({
319
+ level: LogLevelSchema.optional().describe("Log level for RivetKit. Default: 'warn'"),
320
+ })
321
+ .optional()
322
+ .describe("Logging configuration."),
323
+ endpoint: z.string().optional().describe("Endpoint URL to connect to Rivet Engine. Supports URL auth syntax: https://namespace:token@api.rivet.dev. Can also be set via RIVET_ENDPOINT environment variable."),
324
+ token: z.string().optional().describe("Authentication token for Rivet Engine. Can also be set via RIVET_TOKEN environment variable."),
325
+ namespace: z.string().optional().describe("Namespace to use. Default: 'default'. Can also be set via RIVET_NAMESPACE environment variable."),
326
+ headers: z.record(z.string(), z.string()).optional().describe("Additional headers to include in requests to Rivet Engine."),
327
+ serveManager: z.boolean().optional().describe("Whether to start the local manager server. Auto-determined based on endpoint and NODE_ENV if not specified."),
328
+ managerBasePath: z.string().optional().describe("Base path for the manager API. Default: '/'"),
329
+ managerPort: z.number().optional().describe("Port to run the manager on. Default: 6420"),
330
+ inspector: DocInspectorConfigSchema,
331
+ serverless: DocServerlessConfigSchema.optional(),
332
+ runner: DocRunnerConfigSchema.optional(),
333
+ })
334
+ .describe("RivetKit registry configuration.");
@@ -1,6 +1,6 @@
1
1
  import type { ClientConfig } from "@/client/config";
2
2
  import { HEADER_RIVET_TOKEN } from "@/common/actor-router-consts";
3
- import { combineUrlPath } from "@/utils";
3
+ import { buildActorGatewayUrl } from "./actor-websocket-client";
4
4
  import { getEndpoint } from "./api-utils";
5
5
 
6
6
  export async function sendHttpRequestToActor(
@@ -11,9 +11,11 @@ export async function sendHttpRequestToActor(
11
11
  // Route through guard port
12
12
  const url = new URL(actorRequest.url);
13
13
  const endpoint = getEndpoint(runConfig);
14
- const guardUrl = combineUrlPath(
14
+ const guardUrl = buildActorGatewayUrl(
15
15
  endpoint,
16
- `/gateway/${actorId}${url.pathname}${url.search}`,
16
+ actorId,
17
+ runConfig.token,
18
+ `${url.pathname}${url.search}`,
17
19
  );
18
20
 
19
21
  // Handle body properly based on method and presence
@@ -13,6 +13,18 @@ import { combineUrlPath } from "@/utils";
13
13
  import { getEndpoint } from "./api-utils";
14
14
  import { logger } from "./log";
15
15
 
16
+ export function buildActorGatewayUrl(
17
+ endpoint: string,
18
+ actorId: string,
19
+ token: string | undefined,
20
+ path = "",
21
+ ): string {
22
+ const tokenSegment =
23
+ token !== undefined ? `@${encodeURIComponent(token)}` : "";
24
+ const gatewayPath = `/gateway/${encodeURIComponent(actorId)}${tokenSegment}${path}`;
25
+ return combineUrlPath(endpoint, gatewayPath);
26
+ }
27
+
16
28
  export async function openWebSocketToActor(
17
29
  runConfig: ClientConfig,
18
30
  path: string,
@@ -24,13 +36,12 @@ export async function openWebSocketToActor(
24
36
 
25
37
  // WebSocket connections go through guard
26
38
  const endpoint = getEndpoint(runConfig);
27
- let gatewayPath;
28
- if (runConfig.token !== undefined) {
29
- gatewayPath = `/gateway/${encodeURIComponent(actorId)}@${encodeURIComponent(runConfig.token)}${path}`;
30
- } else {
31
- gatewayPath = `/gateway/${encodeURIComponent(actorId)}${path}`;
32
- }
33
- const guardUrl = combineUrlPath(endpoint, gatewayPath);
39
+ const guardUrl = buildActorGatewayUrl(
40
+ endpoint,
41
+ actorId,
42
+ runConfig.token,
43
+ path,
44
+ );
34
45
 
35
46
  logger().debug({
36
47
  msg: "opening websocket to actor via guard",
@@ -21,6 +21,7 @@ import { combineUrlPath, type GetUpgradeWebSocket } from "@/utils";
21
21
  import { getNextPhase } from "@/utils/env-vars";
22
22
  import { sendHttpRequestToActor } from "./actor-http-client";
23
23
  import {
24
+ buildActorGatewayUrl,
24
25
  buildWebSocketProtocols,
25
26
  openWebSocketToActor,
26
27
  } from "./actor-websocket-client";
@@ -81,9 +82,19 @@ export class RemoteManagerDriver implements ManagerDriver {
81
82
  // Override endpoint for all future requests
82
83
  if (metadataData.clientEndpoint) {
83
84
  this.#config.endpoint = metadataData.clientEndpoint;
85
+ if (metadataData.clientNamespace) {
86
+ this.#config.namespace =
87
+ metadataData.clientNamespace;
88
+ }
89
+ if (metadataData.clientToken) {
90
+ this.#config.token = metadataData.clientToken;
91
+ }
92
+
84
93
  logger().info({
85
- msg: "overriding cached client endpoint",
94
+ msg: "overriding client endpoint",
86
95
  endpoint: metadataData.clientEndpoint,
96
+ namespace: metadataData.clientNamespace,
97
+ token: metadataData.clientToken,
87
98
  });
88
99
  }
89
100
 
@@ -299,6 +310,15 @@ export class RemoteManagerDriver implements ManagerDriver {
299
310
  );
300
311
  }
301
312
 
313
+ async buildGatewayUrl(actorId: string): Promise<string> {
314
+ if (this.#metadataPromise) {
315
+ await this.#metadataPromise;
316
+ }
317
+
318
+ const endpoint = getEndpoint(this.#config);
319
+ return buildActorGatewayUrl(endpoint, actorId, this.#config.token);
320
+ }
321
+
302
322
  async proxyRequest(
303
323
  _c: HonoContext,
304
324
  actorRequest: Request,