rivetkit 2.3.20 → 2.3.22

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 (148) hide show
  1. package/dist/browser/client.d.ts +43 -5
  2. package/dist/browser/client.js +315 -63
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.js +2 -1
  5. package/dist/browser/inspector/client.js.map +1 -1
  6. package/dist/tsup/actor/errors.cjs +2 -2
  7. package/dist/tsup/actor/errors.cjs.map +1 -1
  8. package/dist/tsup/actor/errors.d.cts +1 -1
  9. package/dist/tsup/actor/errors.d.ts +1 -1
  10. package/dist/tsup/actor/errors.js +1 -1
  11. package/dist/tsup/agent-os/index.cjs +2 -1
  12. package/dist/tsup/agent-os/index.cjs.map +1 -1
  13. package/dist/tsup/agent-os/index.d.cts +32 -4
  14. package/dist/tsup/agent-os/index.d.ts +32 -4
  15. package/dist/tsup/agent-os/index.js +2 -1
  16. package/dist/tsup/agent-os/index.js.map +1 -1
  17. package/dist/tsup/{chunk-553CIQJY.js → chunk-47FSGXYN.js} +2 -2
  18. package/dist/tsup/{chunk-2NXLTFWI.js → chunk-4DTWJFIZ.js} +3 -3
  19. package/dist/tsup/chunk-4DTWJFIZ.js.map +1 -0
  20. package/dist/tsup/{chunk-2CE5UNWK.cjs → chunk-BDKVTNJF.cjs} +11 -10
  21. package/dist/tsup/chunk-BDKVTNJF.cjs.map +1 -0
  22. package/dist/tsup/{chunk-DHWZTUKA.js → chunk-DE2IFYJK.js} +4 -3
  23. package/dist/tsup/chunk-DE2IFYJK.js.map +1 -0
  24. package/dist/tsup/{chunk-DMQUERRN.js → chunk-GINS4O73.js} +2 -2
  25. package/dist/tsup/{chunk-3WS7Z64U.cjs → chunk-GZZNJGXY.cjs} +3 -3
  26. package/dist/tsup/chunk-GZZNJGXY.cjs.map +1 -0
  27. package/dist/tsup/{chunk-A36FMKOV.cjs → chunk-HEY27WZX.cjs} +479 -230
  28. package/dist/tsup/chunk-HEY27WZX.cjs.map +1 -0
  29. package/dist/tsup/{chunk-WAISW7KR.cjs → chunk-KEAUN5F5.cjs} +10 -10
  30. package/dist/tsup/chunk-KEAUN5F5.cjs.map +1 -0
  31. package/dist/tsup/chunk-KVFIONKQ.cjs.map +1 -1
  32. package/dist/tsup/{chunk-CVLNRT7O.cjs → chunk-LUMROGNM.cjs} +3 -3
  33. package/dist/tsup/chunk-LUMROGNM.cjs.map +1 -0
  34. package/dist/tsup/{chunk-JX2ITWAV.cjs → chunk-MGI2VVF6.cjs} +14 -4
  35. package/dist/tsup/chunk-MGI2VVF6.cjs.map +1 -0
  36. package/dist/tsup/chunk-MYWZCDML.cjs.map +1 -1
  37. package/dist/tsup/chunk-NWCAIWHL.cjs.map +1 -1
  38. package/dist/tsup/{chunk-RSIOGT2U.cjs → chunk-QOQA4JPN.cjs} +7 -7
  39. package/dist/tsup/chunk-QOQA4JPN.cjs.map +1 -0
  40. package/dist/tsup/{chunk-TZVAVPGJ.js → chunk-QXBNHJH5.js} +4 -4
  41. package/dist/tsup/{chunk-W2PXGWMR.js → chunk-RIPVNPR6.js} +2 -2
  42. package/dist/tsup/{chunk-5G2J26VM.js → chunk-VD47X7KQ.js} +318 -69
  43. package/dist/tsup/chunk-VD47X7KQ.js.map +1 -0
  44. package/dist/tsup/{chunk-76YTS32R.cjs → chunk-WLRGUAMW.cjs} +4 -4
  45. package/dist/tsup/chunk-WLRGUAMW.cjs.map +1 -0
  46. package/dist/tsup/{chunk-IHKDFKM4.cjs → chunk-WMNXGH25.cjs} +3 -3
  47. package/dist/tsup/chunk-WMNXGH25.cjs.map +1 -0
  48. package/dist/tsup/{chunk-SNNVRHLX.js → chunk-XZBDVEQN.js} +2 -2
  49. package/dist/tsup/{chunk-P2OFXH4G.js → chunk-ZOVJDAOW.js} +14 -4
  50. package/dist/tsup/chunk-ZOVJDAOW.js.map +1 -0
  51. package/dist/tsup/client/mod.cjs +6 -6
  52. package/dist/tsup/client/mod.cjs.map +1 -1
  53. package/dist/tsup/client/mod.d.cts +15 -6
  54. package/dist/tsup/client/mod.d.ts +15 -6
  55. package/dist/tsup/client/mod.js +5 -5
  56. package/dist/tsup/common/log.cjs +3 -3
  57. package/dist/tsup/common/log.cjs.map +1 -1
  58. package/dist/tsup/common/log.js +2 -2
  59. package/dist/tsup/common/websocket.cjs +4 -4
  60. package/dist/tsup/common/websocket.cjs.map +1 -1
  61. package/dist/tsup/common/websocket.js +3 -3
  62. package/dist/tsup/{config-Be-VOHaX.d.ts → config-Cv6pjCt-.d.ts} +37 -7
  63. package/dist/tsup/{config-Ct5uXxXk.d.cts → config-osdJqQwX.d.cts} +37 -7
  64. package/dist/tsup/{context-CXLa0BIM.d.ts → context-CoVbKBuk.d.ts} +1 -1
  65. package/dist/tsup/{context-Cw-11Q-B.d.cts → context-DpjrM7n7.d.cts} +1 -1
  66. package/dist/tsup/db/drizzle.cjs +3 -3
  67. package/dist/tsup/db/drizzle.cjs.map +1 -1
  68. package/dist/tsup/db/drizzle.js +2 -2
  69. package/dist/tsup/db/mod.cjs +4 -4
  70. package/dist/tsup/db/mod.cjs.map +1 -1
  71. package/dist/tsup/db/mod.js +3 -3
  72. package/dist/tsup/dynamic/mod.cjs.map +1 -1
  73. package/dist/tsup/dynamic/mod.d.cts +4 -3
  74. package/dist/tsup/dynamic/mod.d.ts +4 -3
  75. package/dist/tsup/inspector/mod.cjs +6 -6
  76. package/dist/tsup/inspector/mod.cjs.map +1 -1
  77. package/dist/tsup/inspector/mod.js +5 -5
  78. package/dist/tsup/inspector/workflow.cjs +5 -5
  79. package/dist/tsup/inspector/workflow.cjs.map +1 -1
  80. package/dist/tsup/inspector/workflow.js +4 -4
  81. package/dist/tsup/inspector-tab/mod.cjs.map +1 -1
  82. package/dist/tsup/inspector-tab/mod.d.cts +5 -4
  83. package/dist/tsup/inspector-tab/mod.d.ts +5 -4
  84. package/dist/tsup/mod.cjs +170 -158
  85. package/dist/tsup/mod.cjs.map +1 -1
  86. package/dist/tsup/mod.d.cts +6 -5
  87. package/dist/tsup/mod.d.ts +6 -5
  88. package/dist/tsup/mod.js +56 -44
  89. package/dist/tsup/mod.js.map +1 -1
  90. package/dist/tsup/test/mod.cjs +7 -7
  91. package/dist/tsup/test/mod.cjs.map +1 -1
  92. package/dist/tsup/test/mod.d.cts +4 -3
  93. package/dist/tsup/test/mod.d.ts +4 -3
  94. package/dist/tsup/test/mod.js +5 -5
  95. package/dist/tsup/unstable/migrations.cjs.map +1 -1
  96. package/dist/tsup/{utils-DsoDinPz.d.ts → utils-3rsE0JvI.d.cts} +12 -2
  97. package/dist/tsup/{utils-DsoDinPz.d.cts → utils-3rsE0JvI.d.ts} +12 -2
  98. package/dist/tsup/{utils-OQHBjfcM.d.ts → utils-C1O-SklO.d.ts} +1 -1
  99. package/dist/tsup/{utils-CNFeS-Dk.d.cts → utils-C6b0aYUM.d.cts} +1 -1
  100. package/dist/tsup/utils.cjs +3 -3
  101. package/dist/tsup/utils.cjs.map +1 -1
  102. package/dist/tsup/utils.d.cts +2 -2
  103. package/dist/tsup/utils.d.ts +2 -2
  104. package/dist/tsup/utils.js +2 -2
  105. package/dist/tsup/workflow/mod.cjs +11 -11
  106. package/dist/tsup/workflow/mod.cjs.map +1 -1
  107. package/dist/tsup/workflow/mod.d.cts +6 -5
  108. package/dist/tsup/workflow/mod.d.ts +6 -5
  109. package/dist/tsup/workflow/mod.js +5 -5
  110. package/package.json +10 -9
  111. package/src/actor/errors.ts +27 -4
  112. package/src/client/actor-conn.ts +34 -1
  113. package/src/client/actor-handle.ts +29 -1
  114. package/src/client/auth.ts +37 -0
  115. package/src/client/client.ts +10 -2
  116. package/src/client/config.ts +12 -1
  117. package/src/client/mod.ts +9 -0
  118. package/src/client/token-provider.test.ts +86 -0
  119. package/src/client/token-provider.ts +93 -0
  120. package/src/client/utils.ts +1 -0
  121. package/src/engine-client/actor-websocket-client.ts +2 -1
  122. package/src/engine-client/api-endpoints.ts +28 -0
  123. package/src/engine-client/driver.ts +4 -0
  124. package/src/engine-client/mod.ts +201 -64
  125. package/src/registry/config/envoy.ts +1 -17
  126. package/src/registry/config/index.test.ts +2 -2
  127. package/src/registry/config/index.ts +7 -2
  128. package/src/registry/index.ts +28 -0
  129. package/src/registry/native.ts +31 -3
  130. package/dist/tsup/chunk-2CE5UNWK.cjs.map +0 -1
  131. package/dist/tsup/chunk-2NXLTFWI.js.map +0 -1
  132. package/dist/tsup/chunk-3WS7Z64U.cjs.map +0 -1
  133. package/dist/tsup/chunk-5G2J26VM.js.map +0 -1
  134. package/dist/tsup/chunk-76YTS32R.cjs.map +0 -1
  135. package/dist/tsup/chunk-A36FMKOV.cjs.map +0 -1
  136. package/dist/tsup/chunk-CVLNRT7O.cjs.map +0 -1
  137. package/dist/tsup/chunk-DHWZTUKA.js.map +0 -1
  138. package/dist/tsup/chunk-IHKDFKM4.cjs.map +0 -1
  139. package/dist/tsup/chunk-JX2ITWAV.cjs.map +0 -1
  140. package/dist/tsup/chunk-P2OFXH4G.js.map +0 -1
  141. package/dist/tsup/chunk-RSIOGT2U.cjs.map +0 -1
  142. package/dist/tsup/chunk-WAISW7KR.cjs.map +0 -1
  143. package/src/registry/process-metrics.ts +0 -250
  144. /package/dist/tsup/{chunk-553CIQJY.js.map → chunk-47FSGXYN.js.map} +0 -0
  145. /package/dist/tsup/{chunk-DMQUERRN.js.map → chunk-GINS4O73.js.map} +0 -0
  146. /package/dist/tsup/{chunk-TZVAVPGJ.js.map → chunk-QXBNHJH5.js.map} +0 -0
  147. /package/dist/tsup/{chunk-W2PXGWMR.js.map → chunk-RIPVNPR6.js.map} +0 -0
  148. /package/dist/tsup/{chunk-SNNVRHLX.js.map → chunk-XZBDVEQN.js.map} +0 -0
@@ -66,7 +66,7 @@ var UserError = class extends RivetError {
66
66
  }
67
67
  };
68
68
  function encodeBridgeRivetError(error) {
69
- return `${BRIDGE_RIVET_ERROR_PREFIX}${JSON.stringify({
69
+ const payload = {
70
70
  group: error.group,
71
71
  code: error.code,
72
72
  message: error.message,
@@ -75,11 +75,21 @@ function encodeBridgeRivetError(error) {
75
75
  public: error.public,
76
76
  statusCode: error.statusCode,
77
77
  actor: error.actor
78
- })}`;
78
+ };
79
+ try {
80
+ return {
81
+ encoded: `${BRIDGE_RIVET_ERROR_PREFIX}${JSON.stringify(payload)}`
82
+ };
83
+ } catch (cause) {
84
+ return {
85
+ encoded: `${BRIDGE_RIVET_ERROR_PREFIX}${JSON.stringify({ ...payload, metadata: void 0 })}`,
86
+ droppedMetadataCause: cause
87
+ };
88
+ }
79
89
  }
80
90
  function encodeErrorForBridge(error) {
81
91
  if (isCanonicalStructuredRivetError(error)) {
82
- return encodeBridgeRivetError(error);
92
+ return encodeBridgeRivetError(error).encoded;
83
93
  }
84
94
  return String(error);
85
95
  }
@@ -220,7 +230,7 @@ function noopNext() {
220
230
  // package.json
221
231
  var package_default = {
222
232
  name: "rivetkit",
223
- version: "2.3.20",
233
+ version: "2.3.22",
224
234
  description: "Lightweight libraries for building stateful actors on edge platforms",
225
235
  license: "Apache-2.0",
226
236
  keywords: [
@@ -433,6 +443,7 @@ var package_default = {
433
443
  "@rivet-dev/agent-os-core": "^0.1.1",
434
444
  "@rivet-dev/services": "^0.1.5",
435
445
  "@rivetkit/bare-ts": "^0.6.2",
446
+ "@rivetkit/engine-api-full": "workspace:*",
436
447
  "@rivetkit/engine-cli": "workspace:*",
437
448
  "@rivetkit/engine-envoy-protocol": "workspace:*",
438
449
  "@rivetkit/on-change": "6.0.1",
@@ -1145,6 +1156,73 @@ function deserializeActorKey(keyString) {
1145
1156
  return parts;
1146
1157
  }
1147
1158
 
1159
+ // src/client/token-provider.ts
1160
+ function isInvalidToken(group, code) {
1161
+ return group === "auth" && (code === "invalid_token" || code === "token_expired");
1162
+ }
1163
+ function isInvalidTokenResponse(response) {
1164
+ if (response.status !== 401) return false;
1165
+ const error = response.headers.get("x-rivet-error");
1166
+ if (!error) return false;
1167
+ const separator = error.indexOf(".");
1168
+ return separator > 0 && isInvalidToken(error.slice(0, separator), error.slice(separator + 1));
1169
+ }
1170
+ var TokenProvider = class {
1171
+ constructor(getToken) {
1172
+ this.getToken = getToken;
1173
+ }
1174
+ #cached;
1175
+ #pending;
1176
+ #pendingForce = false;
1177
+ async current() {
1178
+ if (this.#cached && (this.#cached.expiresAt === void 0 || Date.now() < this.#cached.expiresAt - 5e3)) {
1179
+ return this.#cached.token;
1180
+ }
1181
+ return this.#load(false);
1182
+ }
1183
+ /** A late rejection for an older token must never erase a newer credential. */
1184
+ async refreshIfCurrent(failedToken) {
1185
+ if (this.#pending !== void 0) {
1186
+ const wasForced = this.#pendingForce;
1187
+ const refreshed = await this.#pending;
1188
+ if (wasForced || refreshed !== failedToken) return refreshed;
1189
+ }
1190
+ if (this.#cached?.token !== failedToken) {
1191
+ return this.current();
1192
+ }
1193
+ return this.#load(true);
1194
+ }
1195
+ #load(forceRefresh) {
1196
+ if (this.#pending !== void 0) return this.#pending;
1197
+ const pending = Promise.resolve().then(() => this.getToken({ forceRefresh })).then((token) => {
1198
+ if (typeof token !== "string" || token.length === 0) {
1199
+ throw new Error(
1200
+ "getToken must resolve to a nonempty string"
1201
+ );
1202
+ }
1203
+ this.#cached = { token, expiresAt: jwtExpiration(token) };
1204
+ return token;
1205
+ }).finally(() => {
1206
+ if (this.#pending === pending) this.#pending = void 0;
1207
+ });
1208
+ this.#pending = pending;
1209
+ this.#pendingForce = forceRefresh;
1210
+ return pending;
1211
+ }
1212
+ };
1213
+ function jwtExpiration(token) {
1214
+ try {
1215
+ const payload = token.split(".")[1];
1216
+ if (!payload) return void 0;
1217
+ const claims = JSON.parse(
1218
+ atob(payload.replace(/-/g, "+").replace(/_/g, "/"))
1219
+ );
1220
+ return typeof claims.exp === "number" && Number.isSafeInteger(claims.exp) ? claims.exp * 1e3 : void 0;
1221
+ } catch {
1222
+ return void 0;
1223
+ }
1224
+ }
1225
+
1148
1226
  // src/common/actor-router-consts.ts
1149
1227
  var PATH_CONNECT = "/connect";
1150
1228
  var PATH_WEBSOCKET_BASE = "/websocket";
@@ -1230,6 +1308,25 @@ function buildGuardHeaders(runConfig, actorRequest, options) {
1230
1308
  // src/client/config.ts
1231
1309
  import z3 from "zod/v4";
1232
1310
 
1311
+ // src/common/engine.ts
1312
+ var ENGINE_HOST = "127.0.0.1";
1313
+ var ENGINE_PORT = 6420;
1314
+ var ENGINE_ENDPOINT = buildEngineEndpoint(ENGINE_HOST, ENGINE_PORT);
1315
+ function buildEngineEndpoint(host, port) {
1316
+ const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
1317
+ return `http://${urlHost}:${port}`;
1318
+ }
1319
+ function isLocalEngineEndpoint(endpoint) {
1320
+ let url;
1321
+ try {
1322
+ url = new URL(endpoint);
1323
+ } catch {
1324
+ return false;
1325
+ }
1326
+ const hostname = url.hostname.toLowerCase();
1327
+ return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]" || hostname === "::1" || hostname === "[::1]" || /^127(?:\.\d{1,3}){0,3}$/.test(hostname);
1328
+ }
1329
+
1233
1330
  // src/utils/endpoint-parser.ts
1234
1331
  function tryParseEndpoint(ctx, options) {
1235
1332
  const {
@@ -1327,6 +1424,8 @@ var ClientConfigSchemaBase = z3.object({
1327
1424
  }),
1328
1425
  /** Token to use to authenticate with the API. */
1329
1426
  token: z3.string().optional().transform((val) => val ?? getRivetToken()),
1427
+ /** Issue and renew a client token on demand. Overrides the static token when provided. */
1428
+ getToken: z3.custom().optional(),
1330
1429
  /** Namespace to connect to. */
1331
1430
  namespace: z3.string().optional().transform((val) => val ?? getRivetNamespace()),
1332
1431
  /** Name of the envoy pool. This is used to group together envoys in to different pools. */
@@ -1368,7 +1467,7 @@ function transformClientConfig(config, ctx) {
1368
1467
  ...config,
1369
1468
  endpoint: parsedEndpoint?.endpoint,
1370
1469
  namespace: parsedEndpoint?.namespace ?? config.namespace ?? "default",
1371
- token: parsedEndpoint?.token ?? config.token
1470
+ token: parsedEndpoint?.token ?? config.token ?? (config.endpoint && isLocalEngineEndpoint(config.endpoint) ? "default" : void 0)
1372
1471
  };
1373
1472
  }
1374
1473
 
@@ -1679,7 +1778,8 @@ async function openWebSocketToGateway(runConfig, gatewayUrl, encoding, params, o
1679
1778
  const ackHookToken = typeof process !== "undefined" && process.env.VITEST ? crypto.randomUUID() : void 0;
1680
1779
  logger3().debug({
1681
1780
  msg: "opening websocket to actor via guard",
1682
- gatewayUrl
1781
+ // Gateway URLs can embed credentials; never include the path or query in logs.
1782
+ origin: new URL(gatewayUrl).origin
1683
1783
  });
1684
1784
  const ws = new WebSocket2(
1685
1785
  gatewayUrl,
@@ -3865,7 +3965,8 @@ async function sendHttpRequest(opts) {
3865
3965
  {
3866
3966
  metadata: responseData.metadata,
3867
3967
  rayId: rayId ?? void 0,
3868
- actor: responseData.actor
3968
+ actor: responseData.actor,
3969
+ statusCode: response.status
3869
3970
  }
3870
3971
  );
3871
3972
  } catch (error) {
@@ -3946,6 +4047,14 @@ async function apiCall(config, method, path, body) {
3946
4047
  }
3947
4048
 
3948
4049
  // src/engine-client/api-endpoints.ts
4050
+ async function issueToken(config, options) {
4051
+ return apiCall(config, "POST", "/auth/tokens", {
4052
+ namespace: config.namespace,
4053
+ subject: options.subject,
4054
+ duration: options.expiresIn,
4055
+ grants: options.grants
4056
+ });
4057
+ }
3949
4058
  async function getActor(config, _, actorId) {
3950
4059
  return apiCall(
3951
4060
  config,
@@ -4183,6 +4292,8 @@ function closeWebSocketIfOpen(ws, code, reason) {
4183
4292
  var RemoteEngineControlClient = class {
4184
4293
  #config;
4185
4294
  #metadataPromise;
4295
+ #tokenProvider;
4296
+ #webSocketTokens = /* @__PURE__ */ new WeakMap();
4186
4297
  constructor(runConfig) {
4187
4298
  if (getNextPhase() === "phase-production-build") {
4188
4299
  logger3().info(
@@ -4191,6 +4302,8 @@ var RemoteEngineControlClient = class {
4191
4302
  runConfig.disableMetadataLookup = true;
4192
4303
  }
4193
4304
  this.#config = { ...runConfig };
4305
+ if (runConfig.getToken)
4306
+ this.#tokenProvider = new TokenProvider(runConfig.getToken);
4194
4307
  if (!runConfig.disableMetadataLookup) {
4195
4308
  this.#metadataPromise = lookupMetadataCached(this.#config).then(
4196
4309
  (metadataData) => {
@@ -4199,14 +4312,13 @@ var RemoteEngineControlClient = class {
4199
4312
  if (metadataData.clientNamespace) {
4200
4313
  this.#config.namespace = metadataData.clientNamespace;
4201
4314
  }
4202
- if (metadataData.clientToken) {
4315
+ if (metadataData.clientToken && !this.#tokenProvider) {
4203
4316
  this.#config.token = metadataData.clientToken;
4204
4317
  }
4205
4318
  logger3().info({
4206
4319
  msg: "overriding client endpoint",
4207
4320
  endpoint: metadataData.clientEndpoint,
4208
- namespace: metadataData.clientNamespace,
4209
- token: metadataData.clientToken
4321
+ namespace: metadataData.clientNamespace
4210
4322
  });
4211
4323
  }
4212
4324
  logger3().info({
@@ -4219,12 +4331,47 @@ var RemoteEngineControlClient = class {
4219
4331
  );
4220
4332
  }
4221
4333
  }
4334
+ async #authorizedConfig() {
4335
+ if (!this.#tokenProvider) return this.#config;
4336
+ return { ...this.#config, token: await this.#tokenProvider.current() };
4337
+ }
4338
+ async #withCredential(request, readOnly = false) {
4339
+ const config = await this.#authorizedConfig();
4340
+ try {
4341
+ return await request(config);
4342
+ } catch (error) {
4343
+ if (this.#tokenProvider && config.token && error instanceof RivetError && error.statusCode === 401 && isInvalidToken(error.group, error.code)) {
4344
+ if (readOnly) {
4345
+ const token = await this.#tokenProvider.refreshIfCurrent(
4346
+ config.token
4347
+ );
4348
+ return request({ ...this.#config, token });
4349
+ }
4350
+ await this.#tokenProvider.refreshIfCurrent(config.token);
4351
+ }
4352
+ throw error;
4353
+ }
4354
+ }
4355
+ async issueToken(options) {
4356
+ await this.#metadataPromise;
4357
+ const response = await this.#withCredential(
4358
+ (config) => issueToken(config, options)
4359
+ );
4360
+ return {
4361
+ token: response.token,
4362
+ issuedAt: response.issued_ts,
4363
+ expiresAt: response.expires_ts
4364
+ };
4365
+ }
4222
4366
  async getForId({
4223
4367
  name,
4224
4368
  actorId
4225
4369
  }) {
4226
4370
  await this.#metadataPromise;
4227
- const response = await getActor(this.#config, name, actorId);
4371
+ const response = await this.#withCredential(
4372
+ (config) => getActor(config, name, actorId),
4373
+ true
4374
+ );
4228
4375
  const actor = response.actors[0];
4229
4376
  if (!actor) return void 0;
4230
4377
  if (actor.name !== name) {
@@ -4245,7 +4392,10 @@ var RemoteEngineControlClient = class {
4245
4392
  await this.#metadataPromise;
4246
4393
  logger3().debug({ msg: "getWithKey: searching for actor", name, key });
4247
4394
  try {
4248
- const response = await getActorByKey(this.#config, name, key);
4395
+ const response = await this.#withCredential(
4396
+ (config) => getActorByKey(config, name, key),
4397
+ true
4398
+ );
4249
4399
  const actor = response.actors[0];
4250
4400
  if (!actor) return void 0;
4251
4401
  logger3().debug({
@@ -4278,16 +4428,18 @@ var RemoteEngineControlClient = class {
4278
4428
  key
4279
4429
  });
4280
4430
  try {
4281
- const { actor, created } = await getOrCreateActor(this.#config, {
4282
- datacenter: region,
4283
- name,
4284
- key: serializeActorKey(key),
4285
- runner_name_selector: poolName ?? this.#config.poolName,
4286
- input: actorInput ? uint8ArrayToBase64(
4287
- encodeCborCompat(actorInput)
4288
- ) : void 0,
4289
- crash_policy: crashPolicy ?? "sleep"
4290
- });
4431
+ const { actor, created } = await this.#withCredential(
4432
+ (config) => getOrCreateActor(config, {
4433
+ datacenter: region,
4434
+ name,
4435
+ key: serializeActorKey(key),
4436
+ runner_name_selector: poolName ?? this.#config.poolName,
4437
+ input: actorInput ? uint8ArrayToBase64(
4438
+ encodeCborCompat(actorInput)
4439
+ ) : void 0,
4440
+ crash_policy: crashPolicy ?? "sleep"
4441
+ })
4442
+ );
4291
4443
  logger3().info({
4292
4444
  msg: "getOrCreateWithKey: actor ready",
4293
4445
  actorId: actor.actor_id,
@@ -4303,7 +4455,10 @@ var RemoteEngineControlClient = class {
4303
4455
  name,
4304
4456
  key
4305
4457
  });
4306
- const response = await getActorByKey(this.#config, name, key);
4458
+ const response = await this.#withCredential(
4459
+ (config) => getActorByKey(config, name, key),
4460
+ true
4461
+ );
4307
4462
  const existing = response.actors[0];
4308
4463
  if (!existing) throw error;
4309
4464
  logger3().info({
@@ -4327,14 +4482,18 @@ var RemoteEngineControlClient = class {
4327
4482
  }) {
4328
4483
  await this.#metadataPromise;
4329
4484
  logger3().info({ msg: "creating actor via engine api", name, key });
4330
- const result = await createActor(this.#config, {
4331
- datacenter: region,
4332
- name,
4333
- runner_name_selector: poolName ?? this.#config.poolName,
4334
- key: serializeActorKey(key),
4335
- input: input ? uint8ArrayToBase64(encodeCborCompat(input)) : void 0,
4336
- crash_policy: crashPolicy ?? "sleep"
4337
- });
4485
+ const result = await this.#withCredential(
4486
+ (config) => createActor(config, {
4487
+ datacenter: region,
4488
+ name,
4489
+ runner_name_selector: poolName ?? this.#config.poolName,
4490
+ key: serializeActorKey(key),
4491
+ input: input ? uint8ArrayToBase64(
4492
+ encodeCborCompat(input)
4493
+ ) : void 0,
4494
+ crash_policy: crashPolicy ?? "sleep"
4495
+ })
4496
+ );
4338
4497
  logger3().info({
4339
4498
  msg: "actor created",
4340
4499
  actorId: result.actor.actor_id,
@@ -4346,43 +4505,60 @@ var RemoteEngineControlClient = class {
4346
4505
  async listActors({ name }) {
4347
4506
  await this.#metadataPromise;
4348
4507
  logger3().debug({ msg: "listing actors via engine api", name });
4349
- const response = await listActorsByName(this.#config, name);
4508
+ const response = await this.#withCredential(
4509
+ (config) => listActorsByName(config, name),
4510
+ true
4511
+ );
4350
4512
  return response.actors.map(apiActorToOutput);
4351
4513
  }
4352
4514
  async destroyActor(actorId) {
4353
4515
  await this.#metadataPromise;
4354
4516
  logger3().info({ msg: "destroying actor via engine api", actorId });
4355
- await destroyActor(this.#config, actorId);
4517
+ await this.#withCredential((config) => destroyActor(config, actorId));
4356
4518
  logger3().info({ msg: "actor destroyed", actorId });
4357
4519
  }
4358
4520
  async sendRequest(target, actorRequest, options = {}) {
4359
4521
  await this.#metadataPromise;
4360
- const path = requestPath(actorRequest);
4361
- const gatewayUrl = this.#buildGatewayUrlForTarget(
4362
- target,
4363
- path,
4364
- options
4365
- );
4522
+ const config = await this.#authorizedConfig();
4366
4523
  const httpOptions = {
4367
4524
  ...options,
4368
4525
  directActorId: shouldSkipReadyWait(options) ? directActorIdFromTarget(target) : void 0
4369
4526
  };
4370
- return sendHttpRequestToGateway(
4371
- this.#config,
4372
- gatewayUrl,
4527
+ const send = (current) => sendHttpRequestToGateway(
4528
+ current,
4529
+ this.#buildGatewayUrlForTarget(
4530
+ current,
4531
+ target,
4532
+ requestPath(actorRequest),
4533
+ options
4534
+ ),
4373
4535
  actorRequest,
4374
4536
  httpOptions
4375
4537
  );
4538
+ const response = await send(config);
4539
+ if (!this.#tokenProvider || !config.token || !isInvalidTokenResponse(response))
4540
+ return response;
4541
+ if ((actorRequest.method === "GET" || actorRequest.method === "HEAD") && !("getOrCreateForKey" in target)) {
4542
+ const token = await this.#tokenProvider.refreshIfCurrent(
4543
+ config.token
4544
+ );
4545
+ await response.body?.cancel();
4546
+ return send({ ...this.#config, token });
4547
+ }
4548
+ await this.#tokenProvider.refreshIfCurrent(config.token);
4549
+ return response;
4376
4550
  }
4377
4551
  async openWebSocket(path, target, encoding, params, options = {}) {
4378
4552
  await this.#metadataPromise;
4553
+ const config = await this.#authorizedConfig();
4379
4554
  const gatewayUrl = this.#buildGatewayUrlForTarget(
4555
+ config,
4380
4556
  target,
4381
4557
  path,
4382
4558
  options
4383
4559
  );
4384
- return openWebSocketToGateway(
4385
- this.#config,
4560
+ const ws = await openWebSocketToGateway(
4561
+ config,
4386
4562
  gatewayUrl,
4387
4563
  encoding,
4388
4564
  params,
@@ -4391,24 +4567,58 @@ var RemoteEngineControlClient = class {
4391
4567
  directActorId: shouldSkipReadyWait(options) ? directActorIdFromTarget(target) : void 0
4392
4568
  }
4393
4569
  );
4570
+ this.#watchWebSocketAuth(ws, config.token);
4571
+ return ws;
4572
+ }
4573
+ async refreshAuthToken(ws) {
4574
+ const context2 = this.#webSocketTokens.get(ws);
4575
+ if (!context2?.refresh) return false;
4576
+ await context2.refresh;
4577
+ return true;
4578
+ }
4579
+ #watchWebSocketAuth(ws, token) {
4580
+ const tokenProvider = this.#tokenProvider;
4581
+ if (!tokenProvider || !token) return;
4582
+ const context2 = {};
4583
+ this.#webSocketTokens.set(ws, context2);
4584
+ ws.addEventListener(
4585
+ "close",
4586
+ (event) => {
4587
+ if (event.code !== 1008 || !event.reason) return;
4588
+ const separator = event.reason.indexOf(".");
4589
+ if (separator < 0) return;
4590
+ const code = event.reason.slice(separator + 1).split(/[\s:#]/, 1)[0];
4591
+ if (isInvalidToken(event.reason.slice(0, separator), code)) {
4592
+ context2.refresh = tokenProvider.refreshIfCurrent(token).catch((error) => {
4593
+ logger3().warn(
4594
+ "failed to renew authentication token"
4595
+ );
4596
+ throw error;
4597
+ });
4598
+ context2.refresh.catch(() => {
4599
+ });
4600
+ }
4601
+ }
4602
+ );
4394
4603
  }
4395
4604
  async buildGatewayUrl(target, options = {}) {
4396
4605
  await this.#metadataPromise;
4397
- return this.#buildGatewayUrlForTarget(target, "", options);
4606
+ return this.#buildGatewayUrlForTarget(
4607
+ await this.#authorizedConfig(),
4608
+ target,
4609
+ "",
4610
+ options
4611
+ );
4398
4612
  }
4399
4613
  async proxyRequest(_c, actorRequest, actorId) {
4400
- await this.#metadataPromise;
4401
- const gatewayUrl = this.#buildGatewayUrlForTarget(
4402
- { directId: actorId },
4403
- requestPath(actorRequest)
4404
- );
4405
- return sendHttpRequestToGateway(this.#config, gatewayUrl, actorRequest);
4614
+ return this.sendRequest({ directId: actorId }, actorRequest);
4406
4615
  }
4407
4616
  async proxyWebSocket(c, path, actorId, encoding, params) {
4408
4617
  await this.#metadataPromise;
4409
- const upgradeWebSocket = this.#config.getUpgradeWebSocket?.();
4618
+ const config = await this.#authorizedConfig();
4619
+ const upgradeWebSocket = config.getUpgradeWebSocket?.();
4410
4620
  invariant3(upgradeWebSocket, "missing getUpgradeWebSocket");
4411
- const endpoint = getEndpoint(this.#config);
4621
+ const endpoint = getEndpoint(config);
4412
4622
  const guardUrl = combineUrlPath(endpoint, path);
4413
4623
  const wsGuardUrl = guardUrl.replace("http://", "ws://");
4414
4624
  logger3().debug({
@@ -4418,7 +4628,7 @@ var RemoteEngineControlClient = class {
4418
4628
  guardUrl
4419
4629
  });
4420
4630
  const protocols = buildWebSocketProtocols(
4421
- this.#config,
4631
+ config,
4422
4632
  encoding,
4423
4633
  params,
4424
4634
  void 0,
@@ -4436,8 +4646,8 @@ var RemoteEngineControlClient = class {
4436
4646
  setGetUpgradeWebSocket(getUpgradeWebSocket) {
4437
4647
  this.#config.getUpgradeWebSocket = getUpgradeWebSocket;
4438
4648
  }
4439
- #buildGatewayUrlForTarget(target, path, options = {}) {
4440
- const endpoint = getEndpoint(this.#config);
4649
+ #buildGatewayUrlForTarget(config, target, path, options = {}) {
4650
+ const endpoint = getEndpoint(config);
4441
4651
  if (shouldSkipReadyWait(options) && directActorIdFromTarget(target) && canUseDirectSkipReadyWaitPath(path)) {
4442
4652
  return combineUrlPath(endpoint, path);
4443
4653
  }
@@ -4445,7 +4655,7 @@ var RemoteEngineControlClient = class {
4445
4655
  return buildActorGatewayUrl(
4446
4656
  endpoint,
4447
4657
  target.directId,
4448
- this.#config.token,
4658
+ config.token,
4449
4659
  path
4450
4660
  );
4451
4661
  }
@@ -4453,20 +4663,20 @@ var RemoteEngineControlClient = class {
4453
4663
  return buildActorGatewayUrl(
4454
4664
  endpoint,
4455
4665
  target.getForId.actorId,
4456
- this.#config.token,
4666
+ config.token,
4457
4667
  path
4458
4668
  );
4459
4669
  }
4460
4670
  if ("getForKey" in target || "getOrCreateForKey" in target) {
4461
4671
  return buildActorQueryGatewayUrl(
4462
4672
  endpoint,
4463
- this.#config.namespace,
4673
+ config.namespace,
4464
4674
  target,
4465
- this.#config.token,
4675
+ config.token,
4466
4676
  path,
4467
- this.#config.maxInputSize,
4677
+ config.maxInputSize,
4468
4678
  void 0,
4469
- "getOrCreateForKey" in target ? target.getOrCreateForKey.poolName ?? this.#config.poolName : void 0,
4679
+ "getOrCreateForKey" in target ? target.getOrCreateForKey.poolName ?? config.poolName : void 0,
4470
4680
  options
4471
4681
  );
4472
4682
  }
@@ -4949,6 +5159,7 @@ var ActorConnRaw = class {
4949
5159
  /** Promise used to indicate the socket has connected successfully. This will be rejected if the connection fails. */
4950
5160
  #onOpenPromise;
4951
5161
  #websocket;
5162
+ #authRetryAvailable = true;
4952
5163
  #client;
4953
5164
  #driver;
4954
5165
  #currentActorInvocation;
@@ -5191,6 +5402,10 @@ var ActorConnRaw = class {
5191
5402
  if (error instanceof ActorConnDisposed) {
5192
5403
  return false;
5193
5404
  }
5405
+ if (error instanceof RivetError && isInvalidToken(error.group, error.code) && this.#driver.refreshAuthToken && this.#authRetryAvailable) {
5406
+ this.#authRetryAvailable = false;
5407
+ return true;
5408
+ }
5194
5409
  if (error instanceof RivetError && this.#shouldReconnectForStaleActor(error.group, error.code)) {
5195
5410
  return true;
5196
5411
  }
@@ -5315,6 +5530,7 @@ var ActorConnRaw = class {
5315
5530
  messageQueueLength: this.#messageQueue.length,
5316
5531
  connId: this.#connId
5317
5532
  });
5533
+ this.#authRetryAvailable = true;
5318
5534
  this.#setConnStatus("connected");
5319
5535
  if (this.#onOpenPromise) {
5320
5536
  this.#onOpenPromise.resolve(void 0);
@@ -5456,6 +5672,7 @@ var ActorConnRaw = class {
5456
5672
  }
5457
5673
  /** Called by the onclose event from drivers. */
5458
5674
  async #handleOnClose(event) {
5675
+ const failedSocket = this.#websocket;
5459
5676
  const closeEvent = event;
5460
5677
  const wasClean = closeEvent.wasClean;
5461
5678
  const wasConnected = this.#connStatus === "connected";
@@ -5477,6 +5694,20 @@ var ActorConnRaw = class {
5477
5694
  const parsed = parseWebSocketCloseReason(reason);
5478
5695
  if (parsed) {
5479
5696
  const { group, code, rayId } = parsed;
5697
+ if (isInvalidToken(group, code) && closeEvent.code === 1008 && failedSocket) {
5698
+ try {
5699
+ if (!await this.#driver.refreshAuthToken?.(
5700
+ failedSocket
5701
+ )) {
5702
+ this.#authRetryAvailable = false;
5703
+ }
5704
+ } catch {
5705
+ logger2().warn("token renewal failed");
5706
+ this.#authRetryAvailable = false;
5707
+ }
5708
+ } else if (isInvalidToken(group, code)) {
5709
+ this.#authRetryAvailable = false;
5710
+ }
5480
5711
  if (this.#shouldReconnectForStaleActor(group, code)) {
5481
5712
  this.#clearResolvedActorIdentity();
5482
5713
  this.#onOpenPromise?.reject(
@@ -5526,7 +5757,7 @@ var ActorConnRaw = class {
5526
5757
  if (error instanceof RivetError) {
5527
5758
  this.#dispatchActorError(error);
5528
5759
  }
5529
- if (wasConnected) {
5760
+ if (wasConnected && this.#authRetryAvailable) {
5530
5761
  logger2().debug({
5531
5762
  msg: "triggering reconnect",
5532
5763
  connId: this.#connId
@@ -6832,6 +7063,22 @@ var ActorHandleRaw = class {
6832
7063
  "dynamic actor resolution did not produce a direct actor id"
6833
7064
  );
6834
7065
  }
7066
+ /** Issue a token scoped to this actor. Defaults to actor gateway read access. */
7067
+ async issueToken(options = {}) {
7068
+ const actorId = await this.resolve();
7069
+ const permissions = options.permissions ?? {
7070
+ actor_gateway: ["read"]
7071
+ };
7072
+ const grants = Object.keys(permissions).flatMap((resource) => {
7073
+ const operations = permissions[resource];
7074
+ return operations ? [{ resource, target: { id: actorId }, operations }] : [];
7075
+ });
7076
+ return this.#driver.issueToken({
7077
+ subject: options.subject,
7078
+ expiresIn: options.expiresIn,
7079
+ grants
7080
+ });
7081
+ }
6835
7082
  /**
6836
7083
  * Returns the raw URL for routing traffic to the actor.
6837
7084
  */
@@ -6866,6 +7113,8 @@ var ACTOR_CONNS_SYMBOL = /* @__PURE__ */ Symbol("actorConns");
6866
7113
  var CREATE_ACTOR_CONN_PROXY = /* @__PURE__ */ Symbol("createActorConnProxy");
6867
7114
  var ClientRaw = class {
6868
7115
  #disposed = false;
7116
+ /** Issue namespace-scoped tokens with an explicit grant list. */
7117
+ auth;
6869
7118
  [ACTOR_CONNS_SYMBOL] = /* @__PURE__ */ new Set();
6870
7119
  #driver;
6871
7120
  #encodingKind;
@@ -6877,6 +7126,9 @@ var ClientRaw = class {
6877
7126
  */
6878
7127
  constructor(driver, options = {}) {
6879
7128
  this.#driver = driver;
7129
+ this.auth = {
7130
+ issueToken: (issueOptions) => this.#driver.issueToken(issueOptions)
7131
+ };
6880
7132
  this.#encodingKind = options.encoding ?? "bare";
6881
7133
  this.#gatewayOptions = options.gateway ?? {};
6882
7134
  this.#currentActorInvocation = options.currentActorInvocation;