@standardagents/builder 0.41.15 → 0.41.16

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.
@@ -79698,7 +79698,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
79698
79698
  });
79699
79699
 
79700
79700
  // src/version.ts
79701
- var BUILDER_VERSION = "0.41.15".length > 0 ? "0.41.15" : "dev";
79701
+ var BUILDER_VERSION = "0.41.16".length > 0 ? "0.41.16" : "dev";
79702
79702
 
79703
79703
  // src/api/diagnostics.get.ts
79704
79704
  init_LLMRequest();
@@ -82109,6 +82109,101 @@ function authRateLimitedResponse(result) {
82109
82109
  );
82110
82110
  }
82111
82111
 
82112
+ // src/utils/key-exchange.ts
82113
+ var HOST_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
82114
+ function validHost(host) {
82115
+ return host.length <= 253 && host.split(".").length >= 2 && host.split(".").every((label) => HOST_LABEL.test(label));
82116
+ }
82117
+ function canonicalSourceOrigin(value) {
82118
+ if (typeof value !== "string" || value.length > 300) return null;
82119
+ let parsed;
82120
+ try {
82121
+ parsed = new URL(value);
82122
+ } catch {
82123
+ return null;
82124
+ }
82125
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.port || parsed.search || parsed.hash || parsed.pathname !== "/" && parsed.pathname !== "" || !validHost(parsed.hostname) || value.replace(/\/$/, "") !== parsed.origin) {
82126
+ return null;
82127
+ }
82128
+ return parsed.origin;
82129
+ }
82130
+ function isTrustedOrigin(origin, trustedOrigins) {
82131
+ const host = origin.slice("https://".length);
82132
+ return trustedOrigins.some((entry) => {
82133
+ const trusted = entry.slice("https://".length);
82134
+ return trusted.startsWith("*.") ? host.endsWith(trusted.slice(1)) && host.length > trusted.length - 1 : host === trusted;
82135
+ });
82136
+ }
82137
+
82138
+ // src/api/auth/exchange.post.ts
82139
+ var ADDRESS_LIMIT = { limit: 30, window_seconds: 60 * 60 };
82140
+ var SOURCE_TIMEOUT_MS = 5e3;
82141
+ function refused(status, error, message) {
82142
+ return Response.json({ error, message }, { status, headers: { "Cache-Control": "no-store" } });
82143
+ }
82144
+ var exchange_post_default = defineController2(async ({ req, env: env2, config }) => {
82145
+ const trustedOrigins = config?.keyExchange?.trustedOrigins;
82146
+ if (!trustedOrigins?.length) {
82147
+ return refused(404, "key_exchange_unsupported", "This instance does not exchange keys.");
82148
+ }
82149
+ const body = await req.json().catch(() => null);
82150
+ const sourceOrigin = canonicalSourceOrigin(body?.source_origin);
82151
+ const sourceKey = typeof body?.source_key === "string" ? body.source_key.trim() : "";
82152
+ if (!sourceOrigin || !sourceKey || sourceKey.length > 512) {
82153
+ return refused(400, "invalid_request", "source_origin and source_key are required.");
82154
+ }
82155
+ if (sourceOrigin === new URL(req.url).origin || !isTrustedOrigin(sourceOrigin, trustedOrigins)) {
82156
+ return refused(403, "untrusted_origin", "This instance does not trust that origin.");
82157
+ }
82158
+ const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
82159
+ const addressLimit = await agentBuilder.consumeAuthRateLimit({
82160
+ key: await authRateLimitKey("key-exchange-address", authClientAddress(req)),
82161
+ ...ADDRESS_LIMIT
82162
+ });
82163
+ if (!addressLimit.allowed) return authRateLimitedResponse(addressLimit);
82164
+ let identity;
82165
+ try {
82166
+ const response = await fetch(`${sourceOrigin}/api/auth/me`, {
82167
+ headers: { Authorization: `Bearer ${sourceKey}`, Accept: "application/json" },
82168
+ redirect: "manual",
82169
+ signal: AbortSignal.timeout(SOURCE_TIMEOUT_MS)
82170
+ });
82171
+ if (response.status === 401 || response.status === 403) {
82172
+ return refused(401, "source_key_rejected", "The source deployment rejected the key.");
82173
+ }
82174
+ if (!response.ok) {
82175
+ return refused(502, "source_unavailable", `The source deployment answered ${response.status}.`);
82176
+ }
82177
+ identity = await response.json();
82178
+ } catch {
82179
+ return refused(502, "source_unavailable", "The source deployment could not be reached.");
82180
+ }
82181
+ if (identity?.authType !== "api_key") {
82182
+ return refused(401, "source_key_rejected", "Only API keys can be exchanged.");
82183
+ }
82184
+ const platformUserId = identity.user?.platform_user_id;
82185
+ if (typeof platformUserId !== "string" || !platformUserId) {
82186
+ return refused(403, "unknown_user", "The key does not belong to a platform user.");
82187
+ }
82188
+ const user = await agentBuilder.getUserByPlatformUserId(platformUserId);
82189
+ if (!user) {
82190
+ return refused(403, "unknown_user", "That user has not signed in to this instance.");
82191
+ }
82192
+ const token = generateApiKey();
82193
+ const sourceHost = new URL(sourceOrigin).hostname;
82194
+ await agentBuilder.createApiKey({
82195
+ name: `Key exchange from ${sourceHost}`.slice(0, 100),
82196
+ key_hash: await hashToken(token),
82197
+ key_prefix: getTokenPrefix(token),
82198
+ last_five: getLastChars(token, 5),
82199
+ user_id: user.id
82200
+ });
82201
+ return Response.json(
82202
+ { token, endpoint: new URL(req.url).origin, user: { id: user.id, username: user.username } },
82203
+ { status: 201, headers: { "Cache-Control": "no-store" } }
82204
+ );
82205
+ });
82206
+
82112
82207
  // src/api/auth/login.post.ts
82113
82208
  var LOGIN_ADDRESS_LIMIT = { limit: 10, window_seconds: 5 * 60 };
82114
82209
  var LOGIN_IDENTITY_LIMIT = { limit: 25, window_seconds: 15 * 60 };
@@ -88079,6 +88174,7 @@ var routeHandlers = {
88079
88174
  "POST:/auth/brolly-control": brolly_control_post_default,
88080
88175
  "GET:/auth/config": config_get_default2,
88081
88176
  "DELETE:/auth/current-key": current_key_delete_default,
88177
+ "POST:/auth/exchange": exchange_post_default,
88082
88178
  "POST:/auth/login": login_post_default,
88083
88179
  "POST:/auth/logout": logout_post_default,
88084
88180
  "GET:/auth/me": me_get_default,