rivetkit 2.3.19 → 2.3.21
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/browser/client.d.ts +82 -5
- package/dist/browser/client.js +302 -60
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.js +2 -1
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/agent-os/index.cjs +35 -1
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +71 -4
- package/dist/tsup/agent-os/index.d.ts +71 -4
- package/dist/tsup/agent-os/index.js +35 -1
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-X7RMJQGY.js → chunk-2CI2IEVN.js} +2 -2
- package/dist/tsup/{chunk-WME6KOXW.js → chunk-5EGLHQJJ.js} +2 -2
- package/dist/tsup/{chunk-464WE4RF.js → chunk-5IBUZFLP.js} +2 -2
- package/dist/tsup/{chunk-PPEIPCW4.cjs → chunk-6LRPYB4Q.cjs} +4 -4
- package/dist/tsup/chunk-6LRPYB4Q.cjs.map +1 -0
- package/dist/tsup/{chunk-7BRRGBRY.cjs → chunk-APOANSDG.cjs} +10 -10
- package/dist/tsup/chunk-APOANSDG.cjs.map +1 -0
- package/dist/tsup/{chunk-G4U5L6AX.cjs → chunk-ARGNQGYG.cjs} +3 -3
- package/dist/tsup/chunk-ARGNQGYG.cjs.map +1 -0
- package/dist/tsup/{chunk-IJUXBWH4.cjs → chunk-B5SNXPSS.cjs} +448 -199
- package/dist/tsup/chunk-B5SNXPSS.cjs.map +1 -0
- package/dist/tsup/{chunk-4SC2NSAV.js → chunk-CJXBGTVT.js} +2 -2
- package/dist/tsup/{chunk-EB77NZL4.js → chunk-CSOHAAZK.js} +33 -1
- package/dist/tsup/chunk-CSOHAAZK.js.map +1 -0
- package/dist/tsup/{chunk-Z4RAXFSD.cjs → chunk-GHKQC4PD.cjs} +3 -3
- package/dist/tsup/chunk-GHKQC4PD.cjs.map +1 -0
- package/dist/tsup/chunk-JX2ITWAV.cjs.map +1 -1
- package/dist/tsup/chunk-KVFIONKQ.cjs.map +1 -1
- package/dist/tsup/{chunk-RJC5O2SD.cjs → chunk-KWGN2ZBJ.cjs} +3 -2
- package/dist/tsup/chunk-KWGN2ZBJ.cjs.map +1 -0
- package/dist/tsup/{chunk-OGPFN6BT.cjs → chunk-LWSLKKZ5.cjs} +3 -3
- package/dist/tsup/chunk-LWSLKKZ5.cjs.map +1 -0
- package/dist/tsup/chunk-MYWZCDML.cjs.map +1 -1
- package/dist/tsup/{chunk-7A5EIXJJ.cjs → chunk-NL2EIKK6.cjs} +7 -7
- package/dist/tsup/chunk-NL2EIKK6.cjs.map +1 -0
- package/dist/tsup/{chunk-EO5UOXXY.cjs → chunk-NWCAIWHL.cjs} +34 -2
- package/dist/tsup/chunk-NWCAIWHL.cjs.map +1 -0
- package/dist/tsup/{chunk-BKX7UDTA.js → chunk-PYRI75DL.js} +314 -65
- package/dist/tsup/chunk-PYRI75DL.js.map +1 -0
- package/dist/tsup/{chunk-7HTST3NX.js → chunk-RFN3BG6D.js} +3 -3
- package/dist/tsup/chunk-RFN3BG6D.js.map +1 -0
- package/dist/tsup/{chunk-DN6MD3KK.js → chunk-RGIZNZYO.js} +3 -2
- package/dist/tsup/chunk-RGIZNZYO.js.map +1 -0
- package/dist/tsup/{chunk-DK3L22R6.js → chunk-VMMXXVDO.js} +4 -4
- package/dist/tsup/client/mod.cjs +5 -5
- package/dist/tsup/client/mod.cjs.map +1 -1
- package/dist/tsup/client/mod.d.cts +12 -3
- package/dist/tsup/client/mod.d.ts +12 -3
- package/dist/tsup/client/mod.js +4 -4
- package/dist/tsup/common/log.cjs +2 -2
- package/dist/tsup/common/log.cjs.map +1 -1
- package/dist/tsup/common/log.js +1 -1
- package/dist/tsup/common/websocket.cjs +3 -3
- package/dist/tsup/common/websocket.cjs.map +1 -1
- package/dist/tsup/common/websocket.js +2 -2
- package/dist/tsup/{config-DEm0yIVV.d.ts → config-Bc1nnvUl.d.ts} +76 -6
- package/dist/tsup/{config-8n2AOhXK.d.cts → config-Do_0guSS.d.cts} +76 -6
- package/dist/tsup/{context-DwjOVvlE.d.cts → context-CgNkhjdY.d.cts} +1 -1
- package/dist/tsup/{context-DGg2-IOm.d.ts → context-oGQxZDPz.d.ts} +1 -1
- package/dist/tsup/db/drizzle.cjs +2 -2
- package/dist/tsup/db/drizzle.cjs.map +1 -1
- package/dist/tsup/db/drizzle.js +1 -1
- package/dist/tsup/db/mod.cjs +3 -3
- package/dist/tsup/db/mod.cjs.map +1 -1
- package/dist/tsup/db/mod.js +2 -2
- package/dist/tsup/dynamic/mod.cjs +2 -2
- package/dist/tsup/dynamic/mod.cjs.map +1 -1
- package/dist/tsup/dynamic/mod.d.cts +2 -1
- package/dist/tsup/dynamic/mod.d.ts +2 -1
- package/dist/tsup/dynamic/mod.js +1 -1
- package/dist/tsup/inspector/mod.cjs +5 -5
- package/dist/tsup/inspector/mod.cjs.map +1 -1
- package/dist/tsup/inspector/mod.js +4 -4
- package/dist/tsup/inspector/workflow.cjs +4 -4
- package/dist/tsup/inspector/workflow.cjs.map +1 -1
- package/dist/tsup/inspector/workflow.js +3 -3
- package/dist/tsup/inspector-tab/mod.cjs +2 -2
- package/dist/tsup/inspector-tab/mod.cjs.map +1 -1
- package/dist/tsup/inspector-tab/mod.d.cts +3 -2
- package/dist/tsup/inspector-tab/mod.d.ts +3 -2
- package/dist/tsup/inspector-tab/mod.js +1 -1
- package/dist/tsup/mod.cjs +140 -136
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +4 -3
- package/dist/tsup/mod.d.ts +4 -3
- package/dist/tsup/mod.js +44 -40
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/test/mod.cjs +6 -6
- package/dist/tsup/test/mod.cjs.map +1 -1
- package/dist/tsup/test/mod.d.cts +2 -1
- package/dist/tsup/test/mod.d.ts +2 -1
- package/dist/tsup/test/mod.js +4 -4
- package/dist/tsup/unstable/migrations.cjs.map +1 -1
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.js +1 -1
- package/dist/tsup/workflow/mod.cjs +10 -10
- package/dist/tsup/workflow/mod.cjs.map +1 -1
- package/dist/tsup/workflow/mod.d.cts +4 -3
- package/dist/tsup/workflow/mod.d.ts +4 -3
- package/dist/tsup/workflow/mod.js +5 -5
- package/package.json +10 -9
- package/src/actor/actions.ts +36 -0
- package/src/actor/config.ts +27 -0
- package/src/actor/definition.ts +6 -1
- package/src/actor/mod.ts +5 -0
- package/src/actor/tracing.ts +28 -0
- package/src/client/actor-conn.ts +34 -1
- package/src/client/actor-handle.ts +29 -1
- package/src/client/auth.ts +37 -0
- package/src/client/client.ts +10 -2
- package/src/client/config.ts +12 -1
- package/src/client/mod.ts +9 -0
- package/src/client/token-provider.test.ts +86 -0
- package/src/client/token-provider.ts +93 -0
- package/src/client/utils.ts +1 -0
- package/src/engine-client/actor-websocket-client.ts +2 -1
- package/src/engine-client/api-endpoints.ts +28 -0
- package/src/engine-client/driver.ts +4 -0
- package/src/engine-client/mod.ts +201 -64
- package/src/registry/config/envoy.ts +1 -17
- package/src/registry/config/index.test.ts +2 -2
- package/src/registry/config/index.ts +7 -2
- package/src/registry/index.ts +28 -0
- package/src/registry/native.ts +18 -0
- package/src/registry/runtime.ts +8 -0
- package/dist/tsup/chunk-7A5EIXJJ.cjs.map +0 -1
- package/dist/tsup/chunk-7BRRGBRY.cjs.map +0 -1
- package/dist/tsup/chunk-7HTST3NX.js.map +0 -1
- package/dist/tsup/chunk-BKX7UDTA.js.map +0 -1
- package/dist/tsup/chunk-DN6MD3KK.js.map +0 -1
- package/dist/tsup/chunk-EB77NZL4.js.map +0 -1
- package/dist/tsup/chunk-EO5UOXXY.cjs.map +0 -1
- package/dist/tsup/chunk-G4U5L6AX.cjs.map +0 -1
- package/dist/tsup/chunk-IJUXBWH4.cjs.map +0 -1
- package/dist/tsup/chunk-OGPFN6BT.cjs.map +0 -1
- package/dist/tsup/chunk-PPEIPCW4.cjs.map +0 -1
- package/dist/tsup/chunk-RJC5O2SD.cjs.map +0 -1
- package/dist/tsup/chunk-Z4RAXFSD.cjs.map +0 -1
- /package/dist/tsup/{chunk-X7RMJQGY.js.map → chunk-2CI2IEVN.js.map} +0 -0
- /package/dist/tsup/{chunk-WME6KOXW.js.map → chunk-5EGLHQJJ.js.map} +0 -0
- /package/dist/tsup/{chunk-464WE4RF.js.map → chunk-5IBUZFLP.js.map} +0 -0
- /package/dist/tsup/{chunk-4SC2NSAV.js.map → chunk-CJXBGTVT.js.map} +0 -0
- /package/dist/tsup/{chunk-DK3L22R6.js.map → chunk-VMMXXVDO.js.map} +0 -0
|
@@ -336,7 +336,8 @@ export async function openWebSocketToGateway(
|
|
|
336
336
|
|
|
337
337
|
logger().debug({
|
|
338
338
|
msg: "opening websocket to actor via guard",
|
|
339
|
-
|
|
339
|
+
// Gateway URLs can embed credentials; never include the path or query in logs.
|
|
340
|
+
origin: new URL(gatewayUrl).origin,
|
|
340
341
|
});
|
|
341
342
|
|
|
342
343
|
// Create WebSocket connection
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { serializeActorKey } from "@/actor/keys";
|
|
2
2
|
import type { ClientConfig } from "@/client/client";
|
|
3
|
+
import type { IssuedToken, IssueTokenOptions } from "@/client/auth";
|
|
3
4
|
import type { MetadataResponse } from "@/common/router";
|
|
4
5
|
import type {
|
|
5
6
|
ActorsCreateRequest,
|
|
@@ -12,6 +13,33 @@ import type {
|
|
|
12
13
|
import type { RivetId } from "@/engine-api/common";
|
|
13
14
|
import { apiCall } from "./api-utils";
|
|
14
15
|
|
|
16
|
+
/** Engine's JSON response uses snake_case; the public result uses camelCase. */
|
|
17
|
+
export type IssueTokenResponse = {
|
|
18
|
+
token: IssuedToken["token"];
|
|
19
|
+
issued_ts: IssuedToken["issuedAt"];
|
|
20
|
+
expires_ts: IssuedToken["expiresAt"];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export async function issueToken(
|
|
24
|
+
config: ClientConfig,
|
|
25
|
+
options: IssueTokenOptions,
|
|
26
|
+
): Promise<IssueTokenResponse> {
|
|
27
|
+
return apiCall<
|
|
28
|
+
{
|
|
29
|
+
namespace: string;
|
|
30
|
+
subject?: IssueTokenOptions["subject"];
|
|
31
|
+
duration?: IssueTokenOptions["expiresIn"];
|
|
32
|
+
grants: IssueTokenOptions["grants"];
|
|
33
|
+
},
|
|
34
|
+
IssueTokenResponse
|
|
35
|
+
>(config, "POST", "/auth/tokens", {
|
|
36
|
+
namespace: config.namespace,
|
|
37
|
+
subject: options.subject,
|
|
38
|
+
duration: options.expiresIn,
|
|
39
|
+
grants: options.grants,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
15
43
|
// MARK: Get actor
|
|
16
44
|
export async function getActor(
|
|
17
45
|
config: ClientConfig,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Hono, Context as HonoContext } from "hono";
|
|
2
2
|
import type { ActorKey, Encoding, UniversalWebSocket } from "@/actor/mod";
|
|
3
3
|
import type { ActorQuery, CrashPolicy } from "@/client/query";
|
|
4
|
+
import type { IssuedToken, IssueTokenOptions } from "@/client/auth";
|
|
4
5
|
import type { RegistryConfig } from "@/registry/config";
|
|
5
6
|
import type { GetUpgradeWebSocket } from "@/utils";
|
|
6
7
|
|
|
@@ -17,6 +18,7 @@ export function shouldSkipReadyWait(
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface EngineControlClient {
|
|
21
|
+
issueToken(options: IssueTokenOptions): Promise<IssuedToken>;
|
|
20
22
|
getForId(input: GetForIdInput): Promise<ActorOutput | undefined>;
|
|
21
23
|
getWithKey(input: GetWithKeyInput): Promise<ActorOutput | undefined>;
|
|
22
24
|
getOrCreateWithKey(input: GetOrCreateWithKeyInput): Promise<ActorOutput>;
|
|
@@ -35,6 +37,8 @@ export interface EngineControlClient {
|
|
|
35
37
|
params: unknown,
|
|
36
38
|
options?: GatewayRequestOptions,
|
|
37
39
|
): Promise<UniversalWebSocket>;
|
|
40
|
+
/** Refresh a credential rejected during this WebSocket handshake, if supported. */
|
|
41
|
+
refreshAuthToken?(ws: UniversalWebSocket): Promise<boolean>;
|
|
38
42
|
proxyRequest(
|
|
39
43
|
c: HonoContext,
|
|
40
44
|
actorRequest: Request,
|
package/src/engine-client/mod.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { Context as HonoContext } from "hono";
|
|
|
2
2
|
import invariant from "invariant";
|
|
3
3
|
import { deserializeActorKey, serializeActorKey } from "@/actor/keys";
|
|
4
4
|
import type { ClientConfig } from "@/client/client";
|
|
5
|
+
import type { IssuedToken, IssueTokenOptions } from "@/client/auth";
|
|
6
|
+
import {
|
|
7
|
+
isInvalidToken,
|
|
8
|
+
isInvalidTokenResponse,
|
|
9
|
+
TokenProvider,
|
|
10
|
+
} from "@/client/token-provider";
|
|
5
11
|
import {
|
|
6
12
|
PATH_CONNECT,
|
|
7
13
|
PATH_WEBSOCKET_BASE,
|
|
@@ -40,6 +46,7 @@ import {
|
|
|
40
46
|
getActor,
|
|
41
47
|
getActorByKey,
|
|
42
48
|
getOrCreateActor,
|
|
49
|
+
issueToken as issueEngineToken,
|
|
43
50
|
listActorsByName,
|
|
44
51
|
} from "./api-endpoints";
|
|
45
52
|
import { EngineApiError, getEndpoint } from "./api-utils";
|
|
@@ -50,6 +57,13 @@ import { createWebSocketProxy } from "./ws-proxy";
|
|
|
50
57
|
export class RemoteEngineControlClient implements EngineControlClient {
|
|
51
58
|
#config: ClientConfig;
|
|
52
59
|
#metadataPromise: Promise<void> | undefined;
|
|
60
|
+
#tokenProvider?: TokenProvider;
|
|
61
|
+
#webSocketTokens = new WeakMap<
|
|
62
|
+
UniversalWebSocket,
|
|
63
|
+
{
|
|
64
|
+
refresh?: Promise<string>;
|
|
65
|
+
}
|
|
66
|
+
>();
|
|
53
67
|
|
|
54
68
|
constructor(runConfig: ClientConfig) {
|
|
55
69
|
// Disable health check if in Next.js build phase since there is no `/metadata` endpoint
|
|
@@ -65,6 +79,8 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
65
79
|
// Clone config so we can mutate the endpoint in #metadataPromise
|
|
66
80
|
// NOTE: This is a shallow clone, so mutating nested properties will not do anything
|
|
67
81
|
this.#config = { ...runConfig };
|
|
82
|
+
if (runConfig.getToken)
|
|
83
|
+
this.#tokenProvider = new TokenProvider(runConfig.getToken);
|
|
68
84
|
|
|
69
85
|
// Perform metadata check if enabled
|
|
70
86
|
if (!runConfig.disableMetadataLookup) {
|
|
@@ -80,7 +96,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
80
96
|
this.#config.namespace =
|
|
81
97
|
metadataData.clientNamespace;
|
|
82
98
|
}
|
|
83
|
-
if (metadataData.clientToken) {
|
|
99
|
+
if (metadataData.clientToken && !this.#tokenProvider) {
|
|
84
100
|
this.#config.token = metadataData.clientToken;
|
|
85
101
|
}
|
|
86
102
|
|
|
@@ -88,7 +104,6 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
88
104
|
msg: "overriding client endpoint",
|
|
89
105
|
endpoint: metadataData.clientEndpoint,
|
|
90
106
|
namespace: metadataData.clientNamespace,
|
|
91
|
-
token: metadataData.clientToken,
|
|
92
107
|
});
|
|
93
108
|
}
|
|
94
109
|
|
|
@@ -103,6 +118,51 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
async #authorizedConfig(): Promise<ClientConfig> {
|
|
122
|
+
if (!this.#tokenProvider) return this.#config;
|
|
123
|
+
return { ...this.#config, token: await this.#tokenProvider.current() };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async #withCredential<T>(
|
|
127
|
+
request: (config: ClientConfig) => Promise<T>,
|
|
128
|
+
readOnly = false,
|
|
129
|
+
): Promise<T> {
|
|
130
|
+
const config = await this.#authorizedConfig();
|
|
131
|
+
try {
|
|
132
|
+
return await request(config);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (
|
|
135
|
+
this.#tokenProvider &&
|
|
136
|
+
config.token &&
|
|
137
|
+
error instanceof EngineApiError &&
|
|
138
|
+
error.statusCode === 401 &&
|
|
139
|
+
isInvalidToken(error.group, error.code)
|
|
140
|
+
) {
|
|
141
|
+
if (readOnly) {
|
|
142
|
+
const token = await this.#tokenProvider.refreshIfCurrent(
|
|
143
|
+
config.token,
|
|
144
|
+
);
|
|
145
|
+
return request({ ...this.#config, token });
|
|
146
|
+
}
|
|
147
|
+
// Mutations may have executed; refresh the next call, not this one.
|
|
148
|
+
await this.#tokenProvider.refreshIfCurrent(config.token);
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async issueToken(options: IssueTokenOptions): Promise<IssuedToken> {
|
|
155
|
+
await this.#metadataPromise;
|
|
156
|
+
const response = await this.#withCredential((config) =>
|
|
157
|
+
issueEngineToken(config, options),
|
|
158
|
+
);
|
|
159
|
+
return {
|
|
160
|
+
token: response.token,
|
|
161
|
+
issuedAt: response.issued_ts,
|
|
162
|
+
expiresAt: response.expires_ts,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
106
166
|
async getForId({
|
|
107
167
|
name,
|
|
108
168
|
actorId,
|
|
@@ -110,7 +170,10 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
110
170
|
await this.#metadataPromise;
|
|
111
171
|
|
|
112
172
|
// Fetch from API if not in cache
|
|
113
|
-
const response = await
|
|
173
|
+
const response = await this.#withCredential(
|
|
174
|
+
(config) => getActor(config, name, actorId),
|
|
175
|
+
true,
|
|
176
|
+
);
|
|
114
177
|
const actor = response.actors[0];
|
|
115
178
|
if (!actor) return undefined;
|
|
116
179
|
|
|
@@ -138,7 +201,10 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
138
201
|
|
|
139
202
|
// If not in local cache, fetch by key from API
|
|
140
203
|
try {
|
|
141
|
-
const response = await
|
|
204
|
+
const response = await this.#withCredential(
|
|
205
|
+
(config) => getActorByKey(config, name, key),
|
|
206
|
+
true,
|
|
207
|
+
);
|
|
142
208
|
const actor = response.actors[0];
|
|
143
209
|
if (!actor) return undefined;
|
|
144
210
|
|
|
@@ -183,18 +249,20 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
183
249
|
});
|
|
184
250
|
|
|
185
251
|
try {
|
|
186
|
-
const { actor, created } = await
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
252
|
+
const { actor, created } = await this.#withCredential((config) =>
|
|
253
|
+
getOrCreateActor(config, {
|
|
254
|
+
datacenter: region,
|
|
255
|
+
name,
|
|
256
|
+
key: serializeActorKey(key),
|
|
257
|
+
runner_name_selector: poolName ?? this.#config.poolName,
|
|
258
|
+
input: actorInput
|
|
259
|
+
? uint8ArrayToBase64(
|
|
260
|
+
encodeCborCompat(actorInput as JsonCompatValue),
|
|
261
|
+
)
|
|
262
|
+
: undefined,
|
|
263
|
+
crash_policy: crashPolicy ?? "sleep",
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
198
266
|
|
|
199
267
|
logger().info({
|
|
200
268
|
msg: "getOrCreateWithKey: actor ready",
|
|
@@ -220,7 +288,10 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
220
288
|
key,
|
|
221
289
|
});
|
|
222
290
|
|
|
223
|
-
const response = await
|
|
291
|
+
const response = await this.#withCredential(
|
|
292
|
+
(config) => getActorByKey(config, name, key),
|
|
293
|
+
true,
|
|
294
|
+
);
|
|
224
295
|
const existing = response.actors[0];
|
|
225
296
|
if (!existing) throw error;
|
|
226
297
|
|
|
@@ -250,16 +321,20 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
250
321
|
logger().info({ msg: "creating actor via engine api", name, key });
|
|
251
322
|
|
|
252
323
|
// Create actor via engine API
|
|
253
|
-
const result = await
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
324
|
+
const result = await this.#withCredential((config) =>
|
|
325
|
+
createActor(config, {
|
|
326
|
+
datacenter: region,
|
|
327
|
+
name,
|
|
328
|
+
runner_name_selector: poolName ?? this.#config.poolName,
|
|
329
|
+
key: serializeActorKey(key),
|
|
330
|
+
input: input
|
|
331
|
+
? uint8ArrayToBase64(
|
|
332
|
+
encodeCborCompat(input as JsonCompatValue),
|
|
333
|
+
)
|
|
334
|
+
: undefined,
|
|
335
|
+
crash_policy: crashPolicy ?? "sleep",
|
|
336
|
+
}),
|
|
337
|
+
);
|
|
263
338
|
|
|
264
339
|
logger().info({
|
|
265
340
|
msg: "actor created",
|
|
@@ -276,7 +351,10 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
276
351
|
|
|
277
352
|
logger().debug({ msg: "listing actors via engine api", name });
|
|
278
353
|
|
|
279
|
-
const response = await
|
|
354
|
+
const response = await this.#withCredential(
|
|
355
|
+
(config) => listActorsByName(config, name),
|
|
356
|
+
true,
|
|
357
|
+
);
|
|
280
358
|
|
|
281
359
|
return response.actors.map(apiActorToOutput);
|
|
282
360
|
}
|
|
@@ -286,7 +364,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
286
364
|
|
|
287
365
|
logger().info({ msg: "destroying actor via engine api", actorId });
|
|
288
366
|
|
|
289
|
-
await
|
|
367
|
+
await this.#withCredential((config) => destroyActor(config, actorId));
|
|
290
368
|
|
|
291
369
|
logger().info({ msg: "actor destroyed", actorId });
|
|
292
370
|
}
|
|
@@ -297,13 +375,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
297
375
|
options: GatewayRequestOptions = {},
|
|
298
376
|
): Promise<Response> {
|
|
299
377
|
await this.#metadataPromise;
|
|
300
|
-
|
|
301
|
-
const path = requestPath(actorRequest);
|
|
302
|
-
const gatewayUrl = this.#buildGatewayUrlForTarget(
|
|
303
|
-
target,
|
|
304
|
-
path,
|
|
305
|
-
options,
|
|
306
|
-
);
|
|
378
|
+
const config = await this.#authorizedConfig();
|
|
307
379
|
const httpOptions = {
|
|
308
380
|
...options,
|
|
309
381
|
directActorId: shouldSkipReadyWait(options)
|
|
@@ -311,12 +383,37 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
311
383
|
: undefined,
|
|
312
384
|
};
|
|
313
385
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
386
|
+
const send = (current: ClientConfig) =>
|
|
387
|
+
sendHttpRequestToGateway(
|
|
388
|
+
current,
|
|
389
|
+
this.#buildGatewayUrlForTarget(
|
|
390
|
+
current,
|
|
391
|
+
target,
|
|
392
|
+
requestPath(actorRequest),
|
|
393
|
+
options,
|
|
394
|
+
),
|
|
395
|
+
actorRequest,
|
|
396
|
+
httpOptions,
|
|
397
|
+
);
|
|
398
|
+
const response = await send(config);
|
|
399
|
+
if (
|
|
400
|
+
!this.#tokenProvider ||
|
|
401
|
+
!config.token ||
|
|
402
|
+
!isInvalidTokenResponse(response)
|
|
403
|
+
)
|
|
404
|
+
return response;
|
|
405
|
+
if (
|
|
406
|
+
(actorRequest.method === "GET" || actorRequest.method === "HEAD") &&
|
|
407
|
+
!("getOrCreateForKey" in target)
|
|
408
|
+
) {
|
|
409
|
+
const token = await this.#tokenProvider.refreshIfCurrent(
|
|
410
|
+
config.token,
|
|
411
|
+
);
|
|
412
|
+
await response.body?.cancel();
|
|
413
|
+
return send({ ...this.#config, token });
|
|
414
|
+
}
|
|
415
|
+
await this.#tokenProvider.refreshIfCurrent(config.token);
|
|
416
|
+
return response;
|
|
320
417
|
}
|
|
321
418
|
|
|
322
419
|
async openWebSocket(
|
|
@@ -327,15 +424,17 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
327
424
|
options: GatewayRequestOptions = {},
|
|
328
425
|
): Promise<UniversalWebSocket> {
|
|
329
426
|
await this.#metadataPromise;
|
|
427
|
+
const config = await this.#authorizedConfig();
|
|
330
428
|
|
|
331
429
|
const gatewayUrl = this.#buildGatewayUrlForTarget(
|
|
430
|
+
config,
|
|
332
431
|
target,
|
|
333
432
|
path,
|
|
334
433
|
options,
|
|
335
434
|
);
|
|
336
435
|
|
|
337
|
-
|
|
338
|
-
|
|
436
|
+
const ws = await openWebSocketToGateway(
|
|
437
|
+
config,
|
|
339
438
|
gatewayUrl,
|
|
340
439
|
encoding,
|
|
341
440
|
params,
|
|
@@ -346,6 +445,45 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
346
445
|
: undefined,
|
|
347
446
|
},
|
|
348
447
|
);
|
|
448
|
+
this.#watchWebSocketAuth(ws, config.token);
|
|
449
|
+
return ws;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async refreshAuthToken(ws: UniversalWebSocket): Promise<boolean> {
|
|
453
|
+
const context = this.#webSocketTokens.get(ws);
|
|
454
|
+
if (!context?.refresh) return false;
|
|
455
|
+
await context.refresh;
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
#watchWebSocketAuth(ws: UniversalWebSocket, token?: string): void {
|
|
460
|
+
const tokenProvider = this.#tokenProvider;
|
|
461
|
+
if (!tokenProvider || !token) return;
|
|
462
|
+
const context: { refresh?: Promise<string> } = {};
|
|
463
|
+
this.#webSocketTokens.set(ws, context);
|
|
464
|
+
ws.addEventListener(
|
|
465
|
+
"close",
|
|
466
|
+
(event: { code?: number; reason?: string }) => {
|
|
467
|
+
if (event.code !== 1008 || !event.reason) return;
|
|
468
|
+
const separator = event.reason.indexOf(".");
|
|
469
|
+
if (separator < 0) return;
|
|
470
|
+
const code = event.reason
|
|
471
|
+
.slice(separator + 1)
|
|
472
|
+
.split(/[\s:#]/, 1)[0];
|
|
473
|
+
if (isInvalidToken(event.reason.slice(0, separator), code)) {
|
|
474
|
+
context.refresh = tokenProvider
|
|
475
|
+
.refreshIfCurrent(token)
|
|
476
|
+
.catch((error) => {
|
|
477
|
+
logger().warn(
|
|
478
|
+
"failed to renew authentication token",
|
|
479
|
+
);
|
|
480
|
+
throw error;
|
|
481
|
+
});
|
|
482
|
+
// Raw websockets have no built-in reconnection; preempt unhandled rejection.
|
|
483
|
+
context.refresh.catch(() => {});
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
);
|
|
349
487
|
}
|
|
350
488
|
|
|
351
489
|
async buildGatewayUrl(
|
|
@@ -353,7 +491,12 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
353
491
|
options: GatewayRequestOptions = {},
|
|
354
492
|
): Promise<string> {
|
|
355
493
|
await this.#metadataPromise;
|
|
356
|
-
return this.#buildGatewayUrlForTarget(
|
|
494
|
+
return this.#buildGatewayUrlForTarget(
|
|
495
|
+
await this.#authorizedConfig(),
|
|
496
|
+
target,
|
|
497
|
+
"",
|
|
498
|
+
options,
|
|
499
|
+
);
|
|
357
500
|
}
|
|
358
501
|
|
|
359
502
|
async proxyRequest(
|
|
@@ -361,14 +504,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
361
504
|
actorRequest: Request,
|
|
362
505
|
actorId: string,
|
|
363
506
|
): Promise<Response> {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const gatewayUrl = this.#buildGatewayUrlForTarget(
|
|
367
|
-
{ directId: actorId },
|
|
368
|
-
requestPath(actorRequest),
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
return sendHttpRequestToGateway(this.#config, gatewayUrl, actorRequest);
|
|
507
|
+
return this.sendRequest({ directId: actorId }, actorRequest);
|
|
372
508
|
}
|
|
373
509
|
|
|
374
510
|
async proxyWebSocket(
|
|
@@ -379,11 +515,12 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
379
515
|
params: unknown,
|
|
380
516
|
): Promise<Response> {
|
|
381
517
|
await this.#metadataPromise;
|
|
518
|
+
const config = await this.#authorizedConfig();
|
|
382
519
|
|
|
383
|
-
const upgradeWebSocket =
|
|
520
|
+
const upgradeWebSocket = config.getUpgradeWebSocket?.();
|
|
384
521
|
invariant(upgradeWebSocket, "missing getUpgradeWebSocket");
|
|
385
522
|
|
|
386
|
-
const endpoint = getEndpoint(
|
|
523
|
+
const endpoint = getEndpoint(config);
|
|
387
524
|
const guardUrl = combineUrlPath(endpoint, path);
|
|
388
525
|
const wsGuardUrl = guardUrl.replace("http://", "ws://");
|
|
389
526
|
|
|
@@ -396,7 +533,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
396
533
|
|
|
397
534
|
// Build protocols
|
|
398
535
|
const protocols = buildWebSocketProtocols(
|
|
399
|
-
|
|
536
|
+
config,
|
|
400
537
|
encoding,
|
|
401
538
|
params,
|
|
402
539
|
undefined,
|
|
@@ -419,11 +556,12 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
419
556
|
}
|
|
420
557
|
|
|
421
558
|
#buildGatewayUrlForTarget(
|
|
559
|
+
config: ClientConfig,
|
|
422
560
|
target: GatewayTarget,
|
|
423
561
|
path: string,
|
|
424
562
|
options: GatewayRequestOptions = {},
|
|
425
563
|
): string {
|
|
426
|
-
const endpoint = getEndpoint(
|
|
564
|
+
const endpoint = getEndpoint(config);
|
|
427
565
|
|
|
428
566
|
if (
|
|
429
567
|
shouldSkipReadyWait(options) &&
|
|
@@ -437,7 +575,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
437
575
|
return buildActorGatewayUrl(
|
|
438
576
|
endpoint,
|
|
439
577
|
target.directId,
|
|
440
|
-
|
|
578
|
+
config.token,
|
|
441
579
|
path,
|
|
442
580
|
);
|
|
443
581
|
}
|
|
@@ -446,7 +584,7 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
446
584
|
return buildActorGatewayUrl(
|
|
447
585
|
endpoint,
|
|
448
586
|
target.getForId.actorId,
|
|
449
|
-
|
|
587
|
+
config.token,
|
|
450
588
|
path,
|
|
451
589
|
);
|
|
452
590
|
}
|
|
@@ -454,15 +592,14 @@ export class RemoteEngineControlClient implements EngineControlClient {
|
|
|
454
592
|
if ("getForKey" in target || "getOrCreateForKey" in target) {
|
|
455
593
|
return buildActorQueryGatewayUrl(
|
|
456
594
|
endpoint,
|
|
457
|
-
|
|
595
|
+
config.namespace,
|
|
458
596
|
target,
|
|
459
|
-
|
|
597
|
+
config.token,
|
|
460
598
|
path,
|
|
461
|
-
|
|
599
|
+
config.maxInputSize,
|
|
462
600
|
undefined,
|
|
463
601
|
"getOrCreateForKey" in target
|
|
464
|
-
? (target.getOrCreateForKey.poolName ??
|
|
465
|
-
this.#config.poolName)
|
|
602
|
+
? (target.getOrCreateForKey.poolName ?? config.poolName)
|
|
466
603
|
: undefined,
|
|
467
604
|
options,
|
|
468
605
|
);
|
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
import { z } from "zod/v4";
|
|
2
|
-
import { getLogger } from "@/common/log";
|
|
3
2
|
import {
|
|
4
|
-
getNodeEnv,
|
|
5
3
|
getRivetEnvoyVersion,
|
|
6
4
|
getRivetPool,
|
|
7
5
|
getRivetTotalSlots,
|
|
8
6
|
} from "@/utils/env-vars";
|
|
9
7
|
|
|
10
|
-
let warnedMissingVersion = false;
|
|
11
|
-
|
|
12
8
|
export const EnvoyConfigSchema = z.object({
|
|
13
9
|
poolName: z.string().default(() => getRivetPool() ?? "default"),
|
|
14
|
-
version: z.number().default(() =>
|
|
15
|
-
const version = getRivetEnvoyVersion();
|
|
16
|
-
if (version !== undefined) return version;
|
|
17
|
-
|
|
18
|
-
if (getNodeEnv() === "production" && !warnedMissingVersion) {
|
|
19
|
-
warnedMissingVersion = true;
|
|
20
|
-
getLogger("rivetkit").error(
|
|
21
|
-
"RIVET_ENVOY_VERSION is not set. Actors will not be versioned, which means they won't be drained on deploy. This is only needed when self-hosting or using a custom envoy (not needed for Rivet Compute). Set this as a build arg in your Dockerfile. See https://rivet.dev/docs/actors/versions",
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return 1;
|
|
26
|
-
}),
|
|
10
|
+
version: z.number().default(() => getRivetEnvoyVersion() ?? 1),
|
|
27
11
|
|
|
28
12
|
// Deprecated.
|
|
29
13
|
totalSlots: z.number().default(() => getRivetTotalSlots() ?? 100000),
|
|
@@ -12,7 +12,7 @@ afterEach(() => {
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
describe("RegistryConfigSchema", () => {
|
|
15
|
-
test("defaults
|
|
15
|
+
test("defaults local Engine token", () => {
|
|
16
16
|
delete process.env.RIVET_TOKEN;
|
|
17
17
|
|
|
18
18
|
const config = RegistryConfigSchema.parse({
|
|
@@ -20,6 +20,6 @@ describe("RegistryConfigSchema", () => {
|
|
|
20
20
|
startEngine: true,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
expect(config.token).toBe("
|
|
23
|
+
expect(config.token).toBe("default");
|
|
24
24
|
});
|
|
25
25
|
});
|
|
@@ -379,8 +379,13 @@ export const RegistryConfigSchema = z
|
|
|
379
379
|
// Namespace priority: parsed from endpoint URL > config value (includes env var) > "default"
|
|
380
380
|
const namespace =
|
|
381
381
|
parsedEndpoint?.namespace ?? config.namespace ?? "default";
|
|
382
|
-
// Token priority: parsed from endpoint URL > config value (includes env var)
|
|
383
|
-
const token =
|
|
382
|
+
// Token priority: parsed from endpoint URL > config value (includes env var)
|
|
383
|
+
const token =
|
|
384
|
+
parsedEndpoint?.token ??
|
|
385
|
+
config.token ??
|
|
386
|
+
(endpoint && isLocalEngineEndpoint(endpoint)
|
|
387
|
+
? "default"
|
|
388
|
+
: undefined);
|
|
384
389
|
|
|
385
390
|
// Parse publicEndpoint string (env var fallback is applied via transform in serverless schema)
|
|
386
391
|
const parsedPublicEndpoint = config.serverless.publicEndpoint
|
package/src/registry/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { configureBaseLogger } from "@/common/log";
|
|
|
4
4
|
import { configureServerlessPool } from "@/serverless/configure";
|
|
5
5
|
import { VERSION } from "@/utils";
|
|
6
6
|
import {
|
|
7
|
+
getNodeEnv,
|
|
8
|
+
getRivetEnvoyVersion,
|
|
7
9
|
getRivetkitPublicDir,
|
|
8
10
|
getRivetkitRuntimeMode,
|
|
9
11
|
parsePortEnv,
|
|
@@ -624,6 +626,32 @@ export class Registry<A extends RegistryActors> {
|
|
|
624
626
|
*/
|
|
625
627
|
#startEnvoy(config: RegistryConfig, printWelcome: boolean) {
|
|
626
628
|
if (!this.#runtimeServePromise) {
|
|
629
|
+
const configuredEnvoyVersion = this.#config.envoy?.version;
|
|
630
|
+
const envoyVersionSource =
|
|
631
|
+
configuredEnvoyVersion !== undefined
|
|
632
|
+
? "config"
|
|
633
|
+
: getRivetEnvoyVersion() !== undefined
|
|
634
|
+
? "environment"
|
|
635
|
+
: "default";
|
|
636
|
+
|
|
637
|
+
logger().info({
|
|
638
|
+
msg: "starting rivetkit envoy",
|
|
639
|
+
rivetkitVersion: VERSION,
|
|
640
|
+
envoyVersion: config.envoy.version,
|
|
641
|
+
envoyVersionSource,
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
if (
|
|
645
|
+
envoyVersionSource === "default" &&
|
|
646
|
+
getNodeEnv() === "production"
|
|
647
|
+
) {
|
|
648
|
+
logger().error({
|
|
649
|
+
msg: "envoy version defaulted because neither config.envoy.version nor RIVET_ENVOY_VERSION is set; actors will not drain automatically across deploys. See https://rivet.dev/docs/actors/versions",
|
|
650
|
+
envoyVersion: config.envoy.version,
|
|
651
|
+
envoyVersionSource,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
627
655
|
const configuredRegistryPromise =
|
|
628
656
|
this.#buildConfiguredRegistry(config);
|
|
629
657
|
this.#runtimeServeConfiguredPromise = configuredRegistryPromise;
|
package/src/registry/native.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { VirtualWebSocket } from "@rivetkit/virtual-websocket";
|
|
|
2
2
|
import {
|
|
3
3
|
flattenActionHandlers,
|
|
4
4
|
flattenActionInputSchemas,
|
|
5
|
+
flattenActionTraceSamplers,
|
|
5
6
|
} from "@/actor/actions";
|
|
6
7
|
import {
|
|
7
8
|
ACTOR_CONTEXT_INTERNAL_SYMBOL,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
getQueueCanPublish,
|
|
42
43
|
hasSchemaConfigKey,
|
|
43
44
|
} from "@/actor/schema";
|
|
45
|
+
import type { ActionSampleRates } from "@/actor/tracing";
|
|
44
46
|
import {
|
|
45
47
|
type AnyClient,
|
|
46
48
|
type Client,
|
|
@@ -112,6 +114,7 @@ import type {
|
|
|
112
114
|
CoreRuntime,
|
|
113
115
|
RegistryHandle,
|
|
114
116
|
RuntimeActorConfig,
|
|
117
|
+
RuntimeActorTracingConfig,
|
|
115
118
|
RuntimeBytes,
|
|
116
119
|
RuntimeCronFire,
|
|
117
120
|
RuntimeCronJobInfo,
|
|
@@ -3854,6 +3857,17 @@ function withConnContext(
|
|
|
3854
3857
|
});
|
|
3855
3858
|
}
|
|
3856
3859
|
|
|
3860
|
+
function buildActorTracingConfig(
|
|
3861
|
+
actions: unknown,
|
|
3862
|
+
tracing: { sampler?: number; actions?: ActionSampleRates } | undefined,
|
|
3863
|
+
): RuntimeActorTracingConfig | undefined {
|
|
3864
|
+
if (tracing === undefined) return undefined;
|
|
3865
|
+
return {
|
|
3866
|
+
sampler: tracing.sampler,
|
|
3867
|
+
actions: flattenActionTraceSamplers(actions, tracing.actions),
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3857
3871
|
function buildActorConfig(
|
|
3858
3872
|
definition: AnyActorDefinition,
|
|
3859
3873
|
registryConfig: RegistryConfig,
|
|
@@ -3874,6 +3888,10 @@ function buildActorConfig(
|
|
|
3874
3888
|
hasDatabase: true,
|
|
3875
3889
|
remoteSqlite: usesRemoteSqlite,
|
|
3876
3890
|
sqliteProfiling,
|
|
3891
|
+
tracing: buildActorTracingConfig(
|
|
3892
|
+
definition.config.actions,
|
|
3893
|
+
definition.config.tracing,
|
|
3894
|
+
),
|
|
3877
3895
|
enableActorRuntimeSocket: options.enableActorRuntimeSocket === true,
|
|
3878
3896
|
hasState:
|
|
3879
3897
|
config.state !== undefined ||
|