@agent-native/core 0.13.0 → 0.13.1
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/cli/templates-meta.js +10 -10
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/components/AgentPresenceChip.d.ts +1 -1
- package/dist/client/components/AgentPresenceChip.js +1 -1
- package/dist/client/components/AgentPresenceChip.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +4 -4
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/components/PresenceBar.js +1 -1
- package/dist/client/components/PresenceBar.js.map +1 -1
- package/dist/client/composer/VoiceButton.js +3 -3
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +1 -1
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/settings/SecretsSection.js +3 -3
- package/dist/client/settings/SecretsSection.js.map +1 -1
- package/dist/collab/agent-identity.js +1 -1
- package/dist/collab/agent-identity.js.map +1 -1
- package/dist/collab/client.js +1 -1
- package/dist/collab/client.js.map +1 -1
- package/dist/server/google-auth-plugin.d.ts.map +1 -1
- package/dist/server/google-auth-plugin.js +28 -2
- package/dist/server/google-auth-plugin.js.map +1 -1
- package/dist/server/oauth-public-origin.d.ts +2 -0
- package/dist/server/oauth-public-origin.d.ts.map +1 -0
- package/dist/server/oauth-public-origin.js +28 -0
- package/dist/server/oauth-public-origin.js.map +1 -0
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +24 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms. Default: 2000 */\n pollInterval?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#a78bfa\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels)\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary);\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(update),\n requestSource,\n }),\n });\n };\n\n ydoc.on(\"update\", handler);\n return () => {\n ydoc.off(\"update\", handler);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, pollInterval);\n }\n\n async function poll() {\n if (stopped) return;\n try {\n // Poll for document updates\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n\n // Show agent presence indicator briefly\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n const awarenessRes = await fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n });\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n }\n }\n } catch {\n // Network error — retry next interval\n }\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n function handleVisibilityChange() {\n if (document.visibilityState === \"visible\") {\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms. Default: 2000 */\n pollInterval?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#14b8a6\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels)\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary);\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(update),\n requestSource,\n }),\n });\n };\n\n ydoc.on(\"update\", handler);\n return () => {\n ydoc.off(\"update\", handler);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, pollInterval);\n }\n\n async function poll() {\n if (stopped) return;\n try {\n // Poll for document updates\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n\n // Show agent presence indicator briefly\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n const awarenessRes = await fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n });\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n }\n }\n } catch {\n // Network error — retry next interval\n }\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n function handleVisibilityChange() {\n if (document.visibilityState === \"visible\") {\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-auth-plugin.d.ts","sourceRoot":"","sources":["../../src/server/google-auth-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"google-auth-plugin.d.ts","sourceRoot":"","sources":["../../src/server/google-auth-plugin.ts"],"names":[],"mappings":"AAGA,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAqQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,uBAAuB,GAChC,cAAc,CAUhB"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createAuthPlugin } from "./auth-plugin.js";
|
|
2
|
-
|
|
2
|
+
import { getPublicOAuthOrigin } from "./oauth-public-origin.js";
|
|
3
|
+
function getGoogleLoginHtml() {
|
|
4
|
+
const publicOAuthOrigin = getPublicOAuthOrigin();
|
|
5
|
+
return `<!DOCTYPE html>
|
|
3
6
|
<html lang="en">
|
|
4
7
|
<head>
|
|
5
8
|
<meta charset="UTF-8">
|
|
@@ -78,6 +81,20 @@ const GOOGLE_LOGIN_HTML = `<!DOCTYPE html>
|
|
|
78
81
|
function __anPath(path) {
|
|
79
82
|
return __anBasePath() + path;
|
|
80
83
|
}
|
|
84
|
+
var __AN_PUBLIC_OAUTH_ORIGIN = ${JSON.stringify(publicOAuthOrigin)};
|
|
85
|
+
function __anConfiguredOAuthOrigin() {
|
|
86
|
+
if (!__AN_PUBLIC_OAUTH_ORIGIN) return '';
|
|
87
|
+
try {
|
|
88
|
+
var origin = new URL(__AN_PUBLIC_OAUTH_ORIGIN).origin;
|
|
89
|
+
return origin && origin !== window.location.origin ? origin : '';
|
|
90
|
+
} catch(e) {
|
|
91
|
+
return '';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function __anAuthPath(path) {
|
|
95
|
+
var origin = __anIsBuilderPreview() ? __anConfiguredOAuthOrigin() : '';
|
|
96
|
+
return origin ? origin + path : __anPath(path);
|
|
97
|
+
}
|
|
81
98
|
function __anIsBuilderPreview() {
|
|
82
99
|
try {
|
|
83
100
|
var params = new URLSearchParams(window.location.search);
|
|
@@ -213,6 +230,14 @@ const GOOGLE_LOGIN_HTML = `<!DOCTYPE html>
|
|
|
213
230
|
__anStartBuilderOAuth(ret, btn, err);
|
|
214
231
|
return;
|
|
215
232
|
}
|
|
233
|
+
if (__anIsBuilderPreview()) {
|
|
234
|
+
var params = new URLSearchParams();
|
|
235
|
+
if (ret) params.set('return', ret);
|
|
236
|
+
params.set('redirect', '1');
|
|
237
|
+
__anSetOAuthDebug('Opening Google sign-in redirect');
|
|
238
|
+
__anOpenOAuthUrl(__anAuthPath('/_agent-native/google/auth-url') + '?' + params.toString());
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
216
241
|
try {
|
|
217
242
|
var res = await fetch(__anPath('/_agent-native/google/auth-url') + '?return=' + encodeURIComponent(ret));
|
|
218
243
|
var data = await res.json();
|
|
@@ -232,6 +257,7 @@ const GOOGLE_LOGIN_HTML = `<!DOCTYPE html>
|
|
|
232
257
|
</script>
|
|
233
258
|
</body>
|
|
234
259
|
</html>`;
|
|
260
|
+
}
|
|
235
261
|
/**
|
|
236
262
|
* Create an auth plugin that uses Google OAuth for authentication.
|
|
237
263
|
*
|
|
@@ -258,7 +284,7 @@ export function createGoogleAuthPlugin(options) {
|
|
|
258
284
|
"/_agent-native/auth/ba",
|
|
259
285
|
...(options?.publicPaths ?? []),
|
|
260
286
|
],
|
|
261
|
-
loginHtml:
|
|
287
|
+
loginHtml: getGoogleLoginHtml(),
|
|
262
288
|
});
|
|
263
289
|
}
|
|
264
290
|
//# sourceMappingURL=google-auth-plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-auth-plugin.js","sourceRoot":"","sources":["../../src/server/google-auth-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AASpD,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwOlB,CAAC;AAET;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAiC;IAEjC,OAAO,gBAAgB,CAAC;QACtB,WAAW,EAAE;YACX,gCAAgC;YAChC,gCAAgC;YAChC,wBAAwB;YACxB,GAAG,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC;SAChC;QACD,SAAS,EAAE,iBAAiB;KAC7B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { createAuthPlugin } from \"./auth-plugin.js\";\n\ntype NitroPluginDef = (nitroApp: any) => void | Promise<void>;\n\nexport interface GoogleAuthPluginOptions {\n /** Additional paths accessible without authentication */\n publicPaths?: string[];\n}\n\nconst GOOGLE_LOGIN_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<title>Sign in</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: #0a0a0a;\n color: #e5e5e5;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n width: 100%;\n max-width: 360px;\n padding: 2rem;\n background: #141414;\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 12px;\n text-align: center;\n }\n h1 { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: #fff; }\n .subtitle { font-size: 0.8125rem; color: #888; margin-bottom: 1.5rem; }\n button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n padding: 0.625rem;\n background: #fff;\n color: #000;\n border: none;\n border-radius: 8px;\n font-size: 0.9375rem;\n font-weight: 500;\n cursor: pointer;\n }\n button:hover { opacity: 0.85; }\n button:disabled { opacity: 0.5; cursor: wait; }\n .error { margin-top: 0.75rem; font-size: 0.8125rem; color: #f87171; display: none; }\n .error.show { display: block; }\n .debug {\n display: none;\n margin-top: 0.625rem;\n font-size: 0.6875rem;\n line-height: 1.45;\n color: #777;\n word-break: break-word;\n }\n .debug.show { display: block; }\n svg { width: 18px; height: 18px; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <h1>Sign in</h1>\n <p class=\"subtitle\">Continue with your Google account</p>\n <button id=\"btn\" onclick=\"signIn()\">\n <svg viewBox=\"0 0 24 24\"><path fill=\"#4285F4\" d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\"/><path fill=\"#34A853\" d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"/><path fill=\"#FBBC05\" d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"/><path fill=\"#EA4335\" d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"/></svg>\n Sign in with Google\n </button>\n <p class=\"error\" id=\"err\"></p>\n <p class=\"debug\" id=\"debug\"></p>\n</div>\n<script>\n function __anBasePath() {\n var marker = '/_agent-native';\n var idx = window.location.pathname.indexOf(marker);\n return idx > 0 ? window.location.pathname.slice(0, idx) : '';\n }\n function __anPath(path) {\n return __anBasePath() + path;\n }\n function __anIsBuilderPreview() {\n try {\n var params = new URLSearchParams(window.location.search);\n if (params.has('builder.preview') || params.has('builder.frameEditing') || params.has('__builder_editing__')) return true;\n } catch(e) {}\n try {\n var ref = document.referrer || '';\n return ref.indexOf('builder.io') !== -1 || ref.indexOf('builder.my') !== -1 || ref.indexOf('builderio.xyz') !== -1;\n } catch(e) {\n return false;\n }\n }\n function __anIsBuilderDesktop() {\n try {\n var ua = navigator.userAgent || '';\n return ua.indexOf('Electron') !== -1 && ua.indexOf('AgentNativeDesktop') === -1;\n } catch(e) {\n return false;\n }\n }\n var __anOAuthPollTimer = null;\n var __anOAuthPollCount = 0;\n function __anNewOAuthFlowId() {\n try {\n if (window.crypto && typeof window.crypto.randomUUID === 'function') {\n return window.crypto.randomUUID();\n }\n } catch(e) {}\n return 'builder-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);\n }\n function __anFlowDebugId(flowId) {\n return flowId ? String(flowId).slice(-10) : '';\n }\n function __anSetOAuthDebug(message, flowId) {\n var text = message + (flowId ? ' (flow ' + __anFlowDebugId(flowId) + ')' : '');\n try {\n console.info('[agent-native][google-oauth]', { message: message, flow: __anFlowDebugId(flowId) || undefined });\n } catch(e) {}\n var debug = document.getElementById('debug');\n if (debug) {\n debug.textContent = text;\n debug.classList.add('show');\n }\n }\n function __anShowOAuthError(err, btn, message) {\n if (__anOAuthPollTimer) {\n clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = null;\n }\n err.textContent = message;\n err.classList.add('show');\n btn.disabled = false;\n }\n function __anWaitForOAuthExchange(flowId, ret, btn, err) {\n var started = Date.now();\n var timeoutMs = 5 * 60 * 1000;\n __anOAuthPollCount = 0;\n async function check() {\n __anOAuthPollCount++;\n try {\n var res = await fetch(__anPath('/_agent-native/auth/desktop-exchange') + '?flow_id=' + encodeURIComponent(flowId), { credentials: 'include' });\n var data = await res.json().catch(function() { return {}; });\n if (data && (data.email || data.token)) {\n if (__anOAuthPollTimer) clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = null;\n __anSetOAuthDebug('OAuth exchange redeemed; returning to the app', flowId);\n window.location.href = ret || '/';\n return;\n }\n if (data && data.error) {\n __anSetOAuthDebug('OAuth exchange returned an error: ' + (data.message || data.error), flowId);\n __anShowOAuthError(err, btn, data.message || data.error);\n return;\n }\n if (data && data.pending && (__anOAuthPollCount === 1 || __anOAuthPollCount % 5 === 0)) {\n __anSetOAuthDebug('Waiting for the Google callback; polling attempt ' + __anOAuthPollCount, flowId);\n }\n } catch(e) {\n if (__anOAuthPollCount === 1 || __anOAuthPollCount % 5 === 0) {\n __anSetOAuthDebug('Could not reach the OAuth exchange endpoint: ' + (e && e.message ? e.message : 'network error'), flowId);\n }\n }\n if (Date.now() - started > timeoutMs) {\n __anShowOAuthError(err, btn, 'Google sign-in did not finish. Flow ' + __anFlowDebugId(flowId) + ' never redeemed; check server logs for [agent-native][google-oauth].');\n }\n }\n if (__anOAuthPollTimer) clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = setInterval(check, 1000);\n setTimeout(check, 500);\n }\n function __anStartBuilderOAuth(ret, btn, err) {\n var flowId = __anNewOAuthFlowId();\n var params = new URLSearchParams();\n if (ret) params.set('return', ret);\n params.set('desktop', '1');\n params.set('flow_id', flowId);\n params.set('redirect', '1');\n var url = __anPath('/_agent-native/google/auth-url') + '?' + params.toString();\n try { sessionStorage.setItem('__an_signin', '1'); } catch(e) {}\n __anSetOAuthDebug('Opening Google sign-in popup', flowId);\n try {\n var popup = window.open('', '_blank', 'width=640,height=760');\n if (!popup) {\n __anShowOAuthError(err, btn, 'Google popup was blocked. Allow popups for this site and try again (flow ' + __anFlowDebugId(flowId) + ').');\n return;\n }\n try { popup.opener = null; } catch(e) {}\n try {\n popup.location.href = url;\n } catch(e) {\n try { popup.close(); } catch(closeErr) {}\n __anShowOAuthError(err, btn, 'Could not navigate Google popup for flow ' + __anFlowDebugId(flowId) + ': ' + (e && e.message ? e.message : 'unknown error'));\n return;\n }\n __anSetOAuthDebug('Google popup opened; waiting for callback', flowId);\n } catch(e) {\n __anShowOAuthError(err, btn, 'Could not open Google popup for flow ' + __anFlowDebugId(flowId) + ': ' + (e && e.message ? e.message : 'unknown error'));\n return;\n }\n __anWaitForOAuthExchange(flowId, ret, btn, err);\n }\n function __anOpenOAuthUrl(url) {\n try { sessionStorage.setItem('__an_signin', '1'); } catch(e) {}\n window.location.href = url;\n }\n async function signIn() {\n var btn = document.getElementById('btn');\n var err = document.getElementById('err');\n var ret = window.location.pathname + window.location.search;\n btn.disabled = true;\n err.classList.remove('show');\n if (__anIsBuilderPreview() && !__anIsBuilderDesktop()) {\n __anStartBuilderOAuth(ret, btn, err);\n return;\n }\n try {\n var res = await fetch(__anPath('/_agent-native/google/auth-url') + '?return=' + encodeURIComponent(ret));\n var data = await res.json();\n if (data.url) {\n __anOpenOAuthUrl(data.url);\n } else {\n err.textContent = data.message || 'Google OAuth is not configured. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.';\n err.classList.add('show');\n btn.disabled = false;\n }\n } catch (e) {\n err.textContent = 'Failed to connect. Please try again.';\n err.classList.add('show');\n btn.disabled = false;\n }\n }\n</script>\n</body>\n</html>`;\n\n/**\n * Create an auth plugin that uses Google OAuth for authentication.\n *\n * When a user visits the app unauthenticated, they see a \"Sign in with Google\"\n * page. The Google OAuth callback (handled by the template) creates a session\n * tied to the user's Google email. `getSession()` then returns `{ email }` for\n * all subsequent requests.\n *\n * Better Auth handles Google OAuth internally when GOOGLE_CLIENT_ID and\n * GOOGLE_CLIENT_SECRET are set. The template's callback route at\n * /_agent-native/google/callback handles mobile deep linking.\n *\n * Usage in a template's `server/plugins/auth.ts`:\n * ```ts\n * import { createGoogleAuthPlugin } from \"@agent-native/core/server\";\n * export default createGoogleAuthPlugin();\n * ```\n */\nexport function createGoogleAuthPlugin(\n options?: GoogleAuthPluginOptions,\n): NitroPluginDef {\n return createAuthPlugin({\n publicPaths: [\n \"/_agent-native/google/callback\",\n \"/_agent-native/google/auth-url\",\n \"/_agent-native/auth/ba\",\n ...(options?.publicPaths ?? []),\n ],\n loginHtml: GOOGLE_LOGIN_HTML,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"google-auth-plugin.js","sourceRoot":"","sources":["../../src/server/google-auth-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAShE,SAAS,kBAAkB;IACzB,MAAM,iBAAiB,GAAG,oBAAoB,EAAE,CAAC;IACjD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCA+E0B,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA+K5D,CAAC;AACT,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAiC;IAEjC,OAAO,gBAAgB,CAAC;QACtB,WAAW,EAAE;YACX,gCAAgC;YAChC,gCAAgC;YAChC,wBAAwB;YACxB,GAAG,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC;SAChC;QACD,SAAS,EAAE,kBAAkB,EAAE;KAChC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { createAuthPlugin } from \"./auth-plugin.js\";\nimport { getPublicOAuthOrigin } from \"./oauth-public-origin.js\";\n\ntype NitroPluginDef = (nitroApp: any) => void | Promise<void>;\n\nexport interface GoogleAuthPluginOptions {\n /** Additional paths accessible without authentication */\n publicPaths?: string[];\n}\n\nfunction getGoogleLoginHtml(): string {\n const publicOAuthOrigin = getPublicOAuthOrigin();\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<title>Sign in</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: #0a0a0a;\n color: #e5e5e5;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n width: 100%;\n max-width: 360px;\n padding: 2rem;\n background: #141414;\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 12px;\n text-align: center;\n }\n h1 { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: #fff; }\n .subtitle { font-size: 0.8125rem; color: #888; margin-bottom: 1.5rem; }\n button {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n padding: 0.625rem;\n background: #fff;\n color: #000;\n border: none;\n border-radius: 8px;\n font-size: 0.9375rem;\n font-weight: 500;\n cursor: pointer;\n }\n button:hover { opacity: 0.85; }\n button:disabled { opacity: 0.5; cursor: wait; }\n .error { margin-top: 0.75rem; font-size: 0.8125rem; color: #f87171; display: none; }\n .error.show { display: block; }\n .debug {\n display: none;\n margin-top: 0.625rem;\n font-size: 0.6875rem;\n line-height: 1.45;\n color: #777;\n word-break: break-word;\n }\n .debug.show { display: block; }\n svg { width: 18px; height: 18px; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <h1>Sign in</h1>\n <p class=\"subtitle\">Continue with your Google account</p>\n <button id=\"btn\" onclick=\"signIn()\">\n <svg viewBox=\"0 0 24 24\"><path fill=\"#4285F4\" d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\"/><path fill=\"#34A853\" d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"/><path fill=\"#FBBC05\" d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"/><path fill=\"#EA4335\" d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"/></svg>\n Sign in with Google\n </button>\n <p class=\"error\" id=\"err\"></p>\n <p class=\"debug\" id=\"debug\"></p>\n</div>\n<script>\n function __anBasePath() {\n var marker = '/_agent-native';\n var idx = window.location.pathname.indexOf(marker);\n return idx > 0 ? window.location.pathname.slice(0, idx) : '';\n }\n function __anPath(path) {\n return __anBasePath() + path;\n }\n var __AN_PUBLIC_OAUTH_ORIGIN = ${JSON.stringify(publicOAuthOrigin)};\n function __anConfiguredOAuthOrigin() {\n if (!__AN_PUBLIC_OAUTH_ORIGIN) return '';\n try {\n var origin = new URL(__AN_PUBLIC_OAUTH_ORIGIN).origin;\n return origin && origin !== window.location.origin ? origin : '';\n } catch(e) {\n return '';\n }\n }\n function __anAuthPath(path) {\n var origin = __anIsBuilderPreview() ? __anConfiguredOAuthOrigin() : '';\n return origin ? origin + path : __anPath(path);\n }\n function __anIsBuilderPreview() {\n try {\n var params = new URLSearchParams(window.location.search);\n if (params.has('builder.preview') || params.has('builder.frameEditing') || params.has('__builder_editing__')) return true;\n } catch(e) {}\n try {\n var ref = document.referrer || '';\n return ref.indexOf('builder.io') !== -1 || ref.indexOf('builder.my') !== -1 || ref.indexOf('builderio.xyz') !== -1;\n } catch(e) {\n return false;\n }\n }\n function __anIsBuilderDesktop() {\n try {\n var ua = navigator.userAgent || '';\n return ua.indexOf('Electron') !== -1 && ua.indexOf('AgentNativeDesktop') === -1;\n } catch(e) {\n return false;\n }\n }\n var __anOAuthPollTimer = null;\n var __anOAuthPollCount = 0;\n function __anNewOAuthFlowId() {\n try {\n if (window.crypto && typeof window.crypto.randomUUID === 'function') {\n return window.crypto.randomUUID();\n }\n } catch(e) {}\n return 'builder-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);\n }\n function __anFlowDebugId(flowId) {\n return flowId ? String(flowId).slice(-10) : '';\n }\n function __anSetOAuthDebug(message, flowId) {\n var text = message + (flowId ? ' (flow ' + __anFlowDebugId(flowId) + ')' : '');\n try {\n console.info('[agent-native][google-oauth]', { message: message, flow: __anFlowDebugId(flowId) || undefined });\n } catch(e) {}\n var debug = document.getElementById('debug');\n if (debug) {\n debug.textContent = text;\n debug.classList.add('show');\n }\n }\n function __anShowOAuthError(err, btn, message) {\n if (__anOAuthPollTimer) {\n clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = null;\n }\n err.textContent = message;\n err.classList.add('show');\n btn.disabled = false;\n }\n function __anWaitForOAuthExchange(flowId, ret, btn, err) {\n var started = Date.now();\n var timeoutMs = 5 * 60 * 1000;\n __anOAuthPollCount = 0;\n async function check() {\n __anOAuthPollCount++;\n try {\n var res = await fetch(__anPath('/_agent-native/auth/desktop-exchange') + '?flow_id=' + encodeURIComponent(flowId), { credentials: 'include' });\n var data = await res.json().catch(function() { return {}; });\n if (data && (data.email || data.token)) {\n if (__anOAuthPollTimer) clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = null;\n __anSetOAuthDebug('OAuth exchange redeemed; returning to the app', flowId);\n window.location.href = ret || '/';\n return;\n }\n if (data && data.error) {\n __anSetOAuthDebug('OAuth exchange returned an error: ' + (data.message || data.error), flowId);\n __anShowOAuthError(err, btn, data.message || data.error);\n return;\n }\n if (data && data.pending && (__anOAuthPollCount === 1 || __anOAuthPollCount % 5 === 0)) {\n __anSetOAuthDebug('Waiting for the Google callback; polling attempt ' + __anOAuthPollCount, flowId);\n }\n } catch(e) {\n if (__anOAuthPollCount === 1 || __anOAuthPollCount % 5 === 0) {\n __anSetOAuthDebug('Could not reach the OAuth exchange endpoint: ' + (e && e.message ? e.message : 'network error'), flowId);\n }\n }\n if (Date.now() - started > timeoutMs) {\n __anShowOAuthError(err, btn, 'Google sign-in did not finish. Flow ' + __anFlowDebugId(flowId) + ' never redeemed; check server logs for [agent-native][google-oauth].');\n }\n }\n if (__anOAuthPollTimer) clearInterval(__anOAuthPollTimer);\n __anOAuthPollTimer = setInterval(check, 1000);\n setTimeout(check, 500);\n }\n function __anStartBuilderOAuth(ret, btn, err) {\n var flowId = __anNewOAuthFlowId();\n var params = new URLSearchParams();\n if (ret) params.set('return', ret);\n params.set('desktop', '1');\n params.set('flow_id', flowId);\n params.set('redirect', '1');\n var url = __anPath('/_agent-native/google/auth-url') + '?' + params.toString();\n try { sessionStorage.setItem('__an_signin', '1'); } catch(e) {}\n __anSetOAuthDebug('Opening Google sign-in popup', flowId);\n try {\n var popup = window.open('', '_blank', 'width=640,height=760');\n if (!popup) {\n __anShowOAuthError(err, btn, 'Google popup was blocked. Allow popups for this site and try again (flow ' + __anFlowDebugId(flowId) + ').');\n return;\n }\n try { popup.opener = null; } catch(e) {}\n try {\n popup.location.href = url;\n } catch(e) {\n try { popup.close(); } catch(closeErr) {}\n __anShowOAuthError(err, btn, 'Could not navigate Google popup for flow ' + __anFlowDebugId(flowId) + ': ' + (e && e.message ? e.message : 'unknown error'));\n return;\n }\n __anSetOAuthDebug('Google popup opened; waiting for callback', flowId);\n } catch(e) {\n __anShowOAuthError(err, btn, 'Could not open Google popup for flow ' + __anFlowDebugId(flowId) + ': ' + (e && e.message ? e.message : 'unknown error'));\n return;\n }\n __anWaitForOAuthExchange(flowId, ret, btn, err);\n }\n function __anOpenOAuthUrl(url) {\n try { sessionStorage.setItem('__an_signin', '1'); } catch(e) {}\n window.location.href = url;\n }\n async function signIn() {\n var btn = document.getElementById('btn');\n var err = document.getElementById('err');\n var ret = window.location.pathname + window.location.search;\n btn.disabled = true;\n err.classList.remove('show');\n if (__anIsBuilderPreview() && !__anIsBuilderDesktop()) {\n __anStartBuilderOAuth(ret, btn, err);\n return;\n }\n if (__anIsBuilderPreview()) {\n var params = new URLSearchParams();\n if (ret) params.set('return', ret);\n params.set('redirect', '1');\n __anSetOAuthDebug('Opening Google sign-in redirect');\n __anOpenOAuthUrl(__anAuthPath('/_agent-native/google/auth-url') + '?' + params.toString());\n return;\n }\n try {\n var res = await fetch(__anPath('/_agent-native/google/auth-url') + '?return=' + encodeURIComponent(ret));\n var data = await res.json();\n if (data.url) {\n __anOpenOAuthUrl(data.url);\n } else {\n err.textContent = data.message || 'Google OAuth is not configured. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.';\n err.classList.add('show');\n btn.disabled = false;\n }\n } catch (e) {\n err.textContent = 'Failed to connect. Please try again.';\n err.classList.add('show');\n btn.disabled = false;\n }\n }\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Create an auth plugin that uses Google OAuth for authentication.\n *\n * When a user visits the app unauthenticated, they see a \"Sign in with Google\"\n * page. The Google OAuth callback (handled by the template) creates a session\n * tied to the user's Google email. `getSession()` then returns `{ email }` for\n * all subsequent requests.\n *\n * Better Auth handles Google OAuth internally when GOOGLE_CLIENT_ID and\n * GOOGLE_CLIENT_SECRET are set. The template's callback route at\n * /_agent-native/google/callback handles mobile deep linking.\n *\n * Usage in a template's `server/plugins/auth.ts`:\n * ```ts\n * import { createGoogleAuthPlugin } from \"@agent-native/core/server\";\n * export default createGoogleAuthPlugin();\n * ```\n */\nexport function createGoogleAuthPlugin(\n options?: GoogleAuthPluginOptions,\n): NitroPluginDef {\n return createAuthPlugin({\n publicPaths: [\n \"/_agent-native/google/callback\",\n \"/_agent-native/google/auth-url\",\n \"/_agent-native/auth/ba\",\n ...(options?.publicPaths ?? []),\n ],\n loginHtml: getGoogleLoginHtml(),\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-public-origin.d.ts","sourceRoot":"","sources":["../../src/server/oauth-public-origin.ts"],"names":[],"mappings":"AASA,wBAAgB,oBAAoB,IAAI,MAAM,CAe7C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function normalizeOrigin(raw) {
|
|
2
|
+
if (!raw)
|
|
3
|
+
return "";
|
|
4
|
+
try {
|
|
5
|
+
return new URL(raw).origin;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function getPublicOAuthOrigin() {
|
|
12
|
+
for (const raw of [
|
|
13
|
+
process.env.WORKSPACE_OAUTH_ORIGIN,
|
|
14
|
+
process.env.VITE_WORKSPACE_OAUTH_ORIGIN,
|
|
15
|
+
process.env.APP_URL,
|
|
16
|
+
process.env.VITE_APP_URL,
|
|
17
|
+
process.env.BETTER_AUTH_URL,
|
|
18
|
+
process.env.VITE_BETTER_AUTH_URL,
|
|
19
|
+
process.env.WORKSPACE_GATEWAY_URL,
|
|
20
|
+
process.env.VITE_WORKSPACE_GATEWAY_URL,
|
|
21
|
+
]) {
|
|
22
|
+
const origin = normalizeOrigin(raw);
|
|
23
|
+
if (origin)
|
|
24
|
+
return origin;
|
|
25
|
+
}
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=oauth-public-origin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-public-origin.js","sourceRoot":"","sources":["../../src/server/oauth-public-origin.ts"],"names":[],"mappings":"AAAA,SAAS,eAAe,CAAC,GAAuB;IAC9C,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,KAAK,MAAM,GAAG,IAAI;QAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,OAAO,CAAC,GAAG,CAAC,2BAA2B;QACvC,OAAO,CAAC,GAAG,CAAC,OAAO;QACnB,OAAO,CAAC,GAAG,CAAC,YAAY;QACxB,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,oBAAoB;QAChC,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACjC,OAAO,CAAC,GAAG,CAAC,0BAA0B;KACvC,EAAE,CAAC;QACF,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC","sourcesContent":["function normalizeOrigin(raw: string | undefined): string {\n if (!raw) return \"\";\n try {\n return new URL(raw).origin;\n } catch {\n return \"\";\n }\n}\n\nexport function getPublicOAuthOrigin(): string {\n for (const raw of [\n process.env.WORKSPACE_OAUTH_ORIGIN,\n process.env.VITE_WORKSPACE_OAUTH_ORIGIN,\n process.env.APP_URL,\n process.env.VITE_APP_URL,\n process.env.BETTER_AUTH_URL,\n process.env.VITE_BETTER_AUTH_URL,\n process.env.WORKSPACE_GATEWAY_URL,\n process.env.VITE_WORKSPACE_GATEWAY_URL,\n ]) {\n const origin = normalizeOrigin(raw);\n if (origin) return origin;\n }\n return \"\";\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-html.d.ts","sourceRoot":"","sources":["../../src/server/onboarding-html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"onboarding-html.d.ts","sourceRoot":"","sources":["../../src/server/onboarding-html.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoCH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF;;;;OAIG;IACH,kBAAkB,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,qBAA0B,GAAG,MAAM,CA6/C1E;AAED,kDAAkD;AAClD,eAAO,MAAM,eAAe,QAAsB,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAwG7C"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* After first account exists, this page acts as a normal login page.
|
|
8
8
|
*/
|
|
9
|
+
import { getPublicOAuthOrigin } from "./oauth-public-origin.js";
|
|
9
10
|
function hasGoogleOAuth() {
|
|
10
11
|
return !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET);
|
|
11
12
|
}
|
|
@@ -43,6 +44,7 @@ export function getOnboardingHtml(opts = {}) {
|
|
|
43
44
|
const showGoogle = hasGoogleOAuth();
|
|
44
45
|
const googleOnly = !!opts.googleOnly;
|
|
45
46
|
const appBasePath = normalizeAppBasePath(process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH);
|
|
47
|
+
const publicOAuthOrigin = getPublicOAuthOrigin();
|
|
46
48
|
const marketing = opts.marketing;
|
|
47
49
|
const hasMarketing = !!marketing;
|
|
48
50
|
const runLocalCommand = marketing?.runLocalCommand?.trim();
|
|
@@ -832,6 +834,20 @@ ${googleOnly
|
|
|
832
834
|
function __anPath(path) {
|
|
833
835
|
return __anBasePath() + path;
|
|
834
836
|
}
|
|
837
|
+
var __AN_PUBLIC_OAUTH_ORIGIN = ${JSON.stringify(publicOAuthOrigin)};
|
|
838
|
+
function __anConfiguredOAuthOrigin() {
|
|
839
|
+
if (!__AN_PUBLIC_OAUTH_ORIGIN) return '';
|
|
840
|
+
try {
|
|
841
|
+
var origin = new URL(__AN_PUBLIC_OAUTH_ORIGIN).origin;
|
|
842
|
+
return origin && origin !== window.location.origin ? origin : '';
|
|
843
|
+
} catch(e) {
|
|
844
|
+
return '';
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function __anAuthPath(path) {
|
|
848
|
+
var origin = __anIsBuilderPreview() ? __anConfiguredOAuthOrigin() : '';
|
|
849
|
+
return origin ? origin + path : __anPath(path);
|
|
850
|
+
}
|
|
835
851
|
function __anGetReturnPath() {
|
|
836
852
|
try {
|
|
837
853
|
var inner = new URLSearchParams(window.location.search).get('return');
|
|
@@ -1439,6 +1455,14 @@ ${showGoogle
|
|
|
1439
1455
|
__anStartBuilderOAuth(ret, btn, err);
|
|
1440
1456
|
return;
|
|
1441
1457
|
}
|
|
1458
|
+
if (__anIsBuilderPreview()) {
|
|
1459
|
+
var params = new URLSearchParams();
|
|
1460
|
+
if (ret) params.set('return', ret);
|
|
1461
|
+
params.set('redirect', '1');
|
|
1462
|
+
__anSetOAuthDebug('Opening Google sign-in redirect');
|
|
1463
|
+
__anOpenOAuthUrl(__anAuthPath('/_agent-native/google/auth-url') + '?' + params.toString());
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1442
1466
|
try {
|
|
1443
1467
|
var authUrl = __anPath('/_agent-native/google/auth-url') + '?return=' + encodeURIComponent(ret);
|
|
1444
1468
|
var res = await fetch(authUrl);
|