diff --git a/server/encryptid-sdk.d.ts b/server/encryptid-sdk.d.ts index e28fa40..d176c95 100644 --- a/server/encryptid-sdk.d.ts +++ b/server/encryptid-sdk.d.ts @@ -18,7 +18,7 @@ declare module '@encryptid/sdk/server' { options: { getSpaceConfig: (slug: string) => Promise }, ): Promise<{ allowed: boolean; readOnly: boolean; reason?: string; claims?: EncryptIDClaims }>; export function extractToken(headers: Headers): string | null; - export function authenticateWSUpgrade(req: Request): Promise; + export function authenticateWSUpgrade(req: Request, options?: VerifyOptions): Promise; export interface EncryptIDClaims { sub: string; diff --git a/server/index.ts b/server/index.ts index d794380..62dcc99 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1743,7 +1743,7 @@ app.post("/api/prompt", async (c) => { // Record tool calls and build function responses const fnResponseParts: any[] = []; for (const part of fnCalls) { - const fc = part.functionCall; + const fc = part.functionCall!; const tool = findTool(fc.name); const label = tool?.actionLabel(fc.args) || fc.name; toolCalls.push({ name: fc.name, args: fc.args, label }); diff --git a/shared/components/rstack-identity.ts b/shared/components/rstack-identity.ts index 3619c93..73b2616 100644 --- a/shared/components/rstack-identity.ts +++ b/shared/components/rstack-identity.ts @@ -223,6 +223,38 @@ function storeSession(token: string, username: string, did: string): void { localStorage.setItem(SESSION_KEY, JSON.stringify(session)); if (username) localStorage.setItem("rspace-username", username); _setSessionCookie(token); + if (username && did) addKnownPersona(username, did); +} + +// ── Persona helpers (client-side multi-account) ── + +const PERSONAS_KEY = "rspace-known-personas"; + +interface KnownPersona { + username: string; + did: string; +} + +function getKnownPersonas(): KnownPersona[] { + try { + return JSON.parse(localStorage.getItem(PERSONAS_KEY) || "[]"); + } catch { return []; } +} + +function addKnownPersona(username: string, did: string): void { + const personas = getKnownPersonas(); + const idx = personas.findIndex(p => p.did === did); + if (idx >= 0) { + personas[idx] = { username, did }; + } else { + personas.push({ username, did }); + } + localStorage.setItem(PERSONAS_KEY, JSON.stringify(personas)); +} + +function removeKnownPersona(did: string): void { + const personas = getKnownPersonas().filter(p => p.did !== did); + localStorage.setItem(PERSONAS_KEY, JSON.stringify(personas)); } // ── Auto-space resolution after auth ── @@ -311,11 +343,24 @@ export class RStackIdentity extends HTMLElement { if (session?.accessToken && session.claims.username) { autoProvisionSpace(session.accessToken); } + + // Propagate login/logout across tabs via storage events + window.addEventListener("storage", this.#onStorageChange); } disconnectedCallback() { + window.removeEventListener("storage", this.#onStorageChange); } + #onStorageChange = (e: StorageEvent) => { + if (e.key === "encryptid_session" || e.key === PERSONAS_KEY) { + this.#render(); + if (e.key === "encryptid_session") { + this.dispatchEvent(new CustomEvent("auth-change", { bubbles: true, composed: true })); + } + } + }; + async #refreshIfNeeded() { let session = getSession(); let token = session?.accessToken ?? null; @@ -369,6 +414,8 @@ export class RStackIdentity extends HTMLElement { const did = session.claims.did || session.claims.sub; const displayName = username || (did.length > 24 ? did.slice(0, 16) + "..." + did.slice(-6) : did); const initial = username ? username[0].toUpperCase() : did.slice(8, 10).toUpperCase(); + const currentDid = session.claims.did || ""; + const otherPersonas = getKnownPersonas().filter(p => p.did !== currentDid); this.#shadow.innerHTML = ` @@ -379,6 +426,21 @@ export class RStackIdentity extends HTMLElement { ${displayName}