@standardagents/builder 0.17.2 → 0.17.3

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.
@@ -63100,6 +63100,30 @@ function getTokenPrefix(token) {
63100
63100
  const match2 = token.match(/^([^_]+_)/);
63101
63101
  return match2 ? match2[1] : "";
63102
63102
  }
63103
+ var SESSION_COOKIE_NAME = "agtuser_session";
63104
+ function readSessionCookie(request) {
63105
+ const header = request.headers.get("Cookie");
63106
+ if (!header) return null;
63107
+ for (const part of header.split(";")) {
63108
+ const eq = part.indexOf("=");
63109
+ if (eq === -1) continue;
63110
+ if (part.slice(0, eq).trim() === SESSION_COOKIE_NAME) {
63111
+ return decodeURIComponent(part.slice(eq + 1).trim()) || null;
63112
+ }
63113
+ }
63114
+ return null;
63115
+ }
63116
+ function buildSessionCookie(request, token, maxAgeSeconds) {
63117
+ const attrs = ["Path=/", "HttpOnly", "SameSite=Lax"];
63118
+ try {
63119
+ if (new URL(request.url).protocol === "https:") attrs.push("Secure");
63120
+ } catch {
63121
+ }
63122
+ if (maxAgeSeconds <= 0) {
63123
+ return `${SESSION_COOKIE_NAME}=; ${attrs.join("; ")}; Max-Age=0`;
63124
+ }
63125
+ return `${SESSION_COOKIE_NAME}=${encodeURIComponent(token)}; ${attrs.join("; ")}; Max-Age=${maxAgeSeconds}`;
63126
+ }
63103
63127
  function isValidUserToken(token) {
63104
63128
  return token.startsWith("agtuser_") && token.length > 10;
63105
63129
  }
@@ -63202,6 +63226,10 @@ function extractBearerToken(request) {
63202
63226
  if (authHeader && authHeader.startsWith("Bearer ")) {
63203
63227
  return authHeader.substring(7);
63204
63228
  }
63229
+ const cookieToken = readSessionCookie(request);
63230
+ if (cookieToken) {
63231
+ return cookieToken;
63232
+ }
63205
63233
  const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
63206
63234
  if (isWebSocket) {
63207
63235
  try {
@@ -63399,6 +63427,10 @@ function platformApiKey(env2) {
63399
63427
  function hasPlatformApiKey(env2) {
63400
63428
  return platformApiKey(env2) !== null;
63401
63429
  }
63430
+ function isPlatformHosted(env2) {
63431
+ const value = env2.STANDARD_AGENTS_HOSTED;
63432
+ return typeof value === "string" ? value.trim() !== "" && value.trim() !== "0" && value.trim().toLowerCase() !== "false" : Boolean(value);
63433
+ }
63402
63434
  function platformEndpoint(env2) {
63403
63435
  const configured = env2.PLATFORM_ENDPOINT || env2.STANDARD_AGENTS_PLATFORM_URL || env2.PLATFORM_URL || env2.STANDARD_AGENTS_PUBLIC_URL;
63404
63436
  if (typeof configured === "string" && configured.trim()) {
@@ -63476,7 +63508,31 @@ async function getPlatformIdentity(env2) {
63476
63508
  platformEndpoint: endpoint
63477
63509
  };
63478
63510
  }
63511
+ async function mintLocalSessionForPlatformUser(env2, platformUserId) {
63512
+ const namespace = env2.AGENT_BUILDER;
63513
+ if (!namespace || typeof namespace.idFromName !== "function" || typeof namespace.get !== "function") {
63514
+ throw new Error("Server misconfigured: AGENT_BUILDER Durable Object binding is required for platform auth");
63515
+ }
63516
+ const agentBuilder = namespace.get(namespace.idFromName("singleton"));
63517
+ const username = sanitizeUsername(`sa-${platformUserId}`);
63518
+ let user = await agentBuilder.getUserByUsername(username);
63519
+ if (!user) {
63520
+ user = await agentBuilder.createUser({
63521
+ username,
63522
+ password_hash: await hashPassword(crypto.randomUUID()),
63523
+ role: "admin"
63524
+ });
63525
+ }
63526
+ const token = generateUserToken();
63527
+ const tokenHash = await hashToken(token);
63528
+ const expiresAt = Math.floor(Date.now() / 1e3) + SESSION_TTL_SECONDS;
63529
+ await agentBuilder.createSession({ user_id: user.id, token_hash: tokenHash, expires_at: expiresAt });
63530
+ return { token, user: { id: user.id, username: user.username, role: user.role } };
63531
+ }
63479
63532
  async function createPlatformLocalSession(env2) {
63533
+ if (isPlatformHosted(env2)) {
63534
+ return null;
63535
+ }
63480
63536
  const identity = await getPlatformIdentity(env2);
63481
63537
  if (!identity) {
63482
63538
  return null;
@@ -65734,11 +65790,15 @@ var config_get_default2 = defineController2(async ({ req, env: env2 }) => {
65734
65790
  const configuredPlatformEndpoint = resolvePlatformEndpoint(req, env2);
65735
65791
  const platformConnected = hasPlatformApiKey(env2) || !!findLocalBootstrapSession(void 0, env2);
65736
65792
  const localPassword = typeof env2.SUPER_ADMIN_PASSWORD === "string" && env2.SUPER_ADMIN_PASSWORD.length > 0;
65793
+ const hosted = isPlatformHosted(env2);
65794
+ const standardAgentsLogin = hosted && typeof env2.STANDARD_AGENTS_OAUTH_CLIENT_ID === "string" && env2.STANDARD_AGENTS_OAUTH_CLIENT_ID.trim() !== "";
65737
65795
  return {
65738
65796
  github: githubConfigured,
65739
65797
  google: googleConfigured,
65740
65798
  platformConnected,
65741
65799
  localPassword,
65800
+ hosted,
65801
+ standardAgentsLogin,
65742
65802
  platformEndpoint: hasPlatformApiKey(env2) ? platformEndpoint(env2) : configuredPlatformEndpoint
65743
65803
  };
65744
65804
  });
@@ -65826,28 +65886,28 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
65826
65886
 
65827
65887
  // src/api/auth/logout.post.ts
65828
65888
  var logout_post_default = defineController2(async ({ req, env: env2 }) => {
65889
+ const clearCookie = (body, status = 200) => new Response(JSON.stringify(body), {
65890
+ status,
65891
+ headers: {
65892
+ "Content-Type": "application/json",
65893
+ "Set-Cookie": buildSessionCookie(req, "", 0)
65894
+ }
65895
+ });
65829
65896
  try {
65830
65897
  const authHeader = req.headers.get("Authorization");
65831
- if (!authHeader || !authHeader.startsWith("Bearer ")) {
65832
- return Response.json({ error: "Unauthorized" }, { status: 401 });
65833
- }
65834
- const token = authHeader.substring(7);
65835
- if (token.includes(".")) {
65836
- return Response.json({ success: true });
65898
+ const token = authHeader && authHeader.startsWith("Bearer ") ? authHeader.substring(7) : readSessionCookie(req);
65899
+ if (!token) {
65900
+ return clearCookie({ success: true });
65837
65901
  }
65838
- if (isValidUserToken(token)) {
65902
+ if (!token.includes(".") && isValidUserToken(token)) {
65839
65903
  const tokenHash = await hashToken(token);
65840
- const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
65841
- const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
65904
+ const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
65842
65905
  await agentBuilder.deleteSession(tokenHash);
65843
65906
  }
65844
- return Response.json({ success: true });
65907
+ return clearCookie({ success: true });
65845
65908
  } catch (error) {
65846
65909
  console.error("Logout error:", error);
65847
- return Response.json(
65848
- { error: error.message || "Logout failed" },
65849
- { status: 500 }
65850
- );
65910
+ return clearCookie({ error: error.message || "Logout failed" }, 500);
65851
65911
  }
65852
65912
  });
65853
65913
 
@@ -68755,6 +68815,121 @@ var name_get_default7 = defineController2(async ({ params, url, prompts, agents,
68755
68815
  return Response.json({ variables });
68756
68816
  });
68757
68817
 
68818
+ // src/api/auth/sa/callback.get.ts
68819
+ var SESSION_TTL_SECONDS2 = 30 * 24 * 60 * 60;
68820
+ function readNamedCookie(req, name15) {
68821
+ const header = req.headers.get("Cookie");
68822
+ if (!header) return null;
68823
+ for (const part of header.split(";")) {
68824
+ const eq = part.indexOf("=");
68825
+ if (eq === -1) continue;
68826
+ if (part.slice(0, eq).trim() === name15) return decodeURIComponent(part.slice(eq + 1).trim()) || null;
68827
+ }
68828
+ return null;
68829
+ }
68830
+ function failRedirect(req, reason) {
68831
+ const origin = new URL(req.url).origin;
68832
+ const headers = new Headers({ Location: `${origin}/login?sa_error=${encodeURIComponent(reason)}` });
68833
+ headers.append("Set-Cookie", "sa_oauth=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0");
68834
+ return new Response(null, { status: 302, headers });
68835
+ }
68836
+ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
68837
+ const url = new URL(req.url);
68838
+ const code = url.searchParams.get("code");
68839
+ const state = url.searchParams.get("state");
68840
+ const stash = readNamedCookie(req, "sa_oauth");
68841
+ if (!code || !state || !stash) {
68842
+ return failRedirect(req, "invalid_response");
68843
+ }
68844
+ const sep = stash.indexOf(":");
68845
+ const verifier = sep === -1 ? "" : stash.slice(0, sep);
68846
+ const expectedState = sep === -1 ? "" : stash.slice(sep + 1);
68847
+ if (!verifier || expectedState !== state) {
68848
+ return failRedirect(req, "state_mismatch");
68849
+ }
68850
+ const clientId = typeof env2.STANDARD_AGENTS_OAUTH_CLIENT_ID === "string" ? env2.STANDARD_AGENTS_OAUTH_CLIENT_ID.trim() : "";
68851
+ if (!clientId) {
68852
+ return failRedirect(req, "not_configured");
68853
+ }
68854
+ const endpoint = platformEndpoint(env2);
68855
+ const redirectUri = `${url.origin}/api/auth/sa/callback`;
68856
+ let platformUserId;
68857
+ try {
68858
+ const tokenRes = await fetch(`${endpoint}/oauth/token`, {
68859
+ method: "POST",
68860
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
68861
+ body: new URLSearchParams({
68862
+ grant_type: "authorization_code",
68863
+ code,
68864
+ client_id: clientId,
68865
+ redirect_uri: redirectUri,
68866
+ code_verifier: verifier
68867
+ }).toString()
68868
+ });
68869
+ if (!tokenRes.ok) return failRedirect(req, "token_exchange_failed");
68870
+ const tokenBody = await tokenRes.json().catch(() => ({}));
68871
+ if (!tokenBody.access_token) return failRedirect(req, "token_exchange_failed");
68872
+ const userRes = await fetch(`${endpoint}/oauth/userinfo`, {
68873
+ headers: { Authorization: `Bearer ${tokenBody.access_token}` }
68874
+ });
68875
+ if (!userRes.ok) return failRedirect(req, "userinfo_failed");
68876
+ const userinfo = await userRes.json().catch(() => ({}));
68877
+ if (!userinfo.sub) return failRedirect(req, "userinfo_failed");
68878
+ platformUserId = userinfo.sub;
68879
+ } catch {
68880
+ return failRedirect(req, "platform_unreachable");
68881
+ }
68882
+ const session = await mintLocalSessionForPlatformUser(env2, platformUserId);
68883
+ const headers = new Headers({ Location: `${url.origin}/` });
68884
+ headers.append("Set-Cookie", buildSessionCookie(req, session.token, SESSION_TTL_SECONDS2));
68885
+ headers.append("Set-Cookie", "sa_oauth=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0");
68886
+ return new Response(null, { status: 302, headers });
68887
+ });
68888
+
68889
+ // src/api/auth/sa/start.get.ts
68890
+ function randomToken(bytesLength = 32) {
68891
+ const bytes = new Uint8Array(bytesLength);
68892
+ crypto.getRandomValues(bytes);
68893
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
68894
+ }
68895
+ function base64Url(bytes) {
68896
+ let binary = "";
68897
+ for (const byte of new Uint8Array(bytes)) binary += String.fromCharCode(byte);
68898
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
68899
+ }
68900
+ async function pkceChallenge(verifier) {
68901
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
68902
+ return base64Url(digest);
68903
+ }
68904
+ var start_get_default = defineController2(async ({ req, env: env2 }) => {
68905
+ const clientId = typeof env2.STANDARD_AGENTS_OAUTH_CLIENT_ID === "string" ? env2.STANDARD_AGENTS_OAUTH_CLIENT_ID.trim() : "";
68906
+ if (!clientId) {
68907
+ return Response.json({ error: "Standard Agents login is not configured for this instance." }, { status: 400 });
68908
+ }
68909
+ const verifier = randomToken(32);
68910
+ const challenge = await pkceChallenge(verifier);
68911
+ const state = randomToken(16);
68912
+ const reqUrl = new URL(req.url);
68913
+ const redirectUri = `${reqUrl.origin}/api/auth/sa/callback`;
68914
+ const authorize = new URL(`${platformEndpoint(env2)}/oauth/authorize`);
68915
+ authorize.searchParams.set("client_id", clientId);
68916
+ authorize.searchParams.set("redirect_uri", redirectUri);
68917
+ authorize.searchParams.set("code_challenge", challenge);
68918
+ authorize.searchParams.set("code_challenge_method", "S256");
68919
+ authorize.searchParams.set("state", state);
68920
+ authorize.searchParams.set("scope", "openid profile email");
68921
+ const attrs = ["Path=/", "HttpOnly", "SameSite=Lax", "Max-Age=600"];
68922
+ try {
68923
+ if (reqUrl.protocol === "https:") attrs.push("Secure");
68924
+ } catch {
68925
+ }
68926
+ const oauthCookie = `sa_oauth=${encodeURIComponent(`${verifier}:${state}`)}; ${attrs.join("; ")}`;
68927
+ return new Response(null, {
68928
+ status: 302,
68929
+ headers: { Location: authorize.toString(), "Set-Cookie": oauthCookie }
68930
+ });
68931
+ });
68932
+
68758
68933
  // src/api/users/me/env.get.ts
68759
68934
  var env_get_default2 = defineController2(async ({ req, env: env2 }) => {
68760
68935
  try {
@@ -69900,6 +70075,8 @@ var routeHandlers = {
69900
70075
  "GET:/variables/agents/:name": name_get_default5,
69901
70076
  "GET:/variables/prompts/:name": name_get_default6,
69902
70077
  "GET:/variables/tools/:name": name_get_default7,
70078
+ "GET:/auth/sa/callback": callback_get_default,
70079
+ "GET:/auth/sa/start": start_get_default,
69903
70080
  "GET:/users/me/env": env_get_default2,
69904
70081
  "PATCH:/users/me/env": env_patch_default2,
69905
70082
  "DELETE:/threads/:id/fs/**": path_default3,