@agent-native/core 0.66.1 → 0.66.2

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.
@@ -173,7 +173,7 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
173
173
  }
174
174
  else {
175
175
  title = "Connect Builder.io";
176
- subtitle = (_jsx(_Fragment, { children: "Connect Builder for managed LLM access, browser automation, and cloud code changes when they are enabled for this workspace." }));
176
+ subtitle = (_jsx(_Fragment, { children: "Builder.io's free tier includes AI credits, browser automation, and cloud code when enabled." }));
177
177
  }
178
178
  return (_jsx("div", { className: cn("my-2 rounded-lg border border-border overflow-hidden"), children: _jsxs("div", { className: "flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent", children: [_jsx("div", { className: cn("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", "bg-foreground text-background"), children: runResult ? (_jsx(IconLoader2, { className: "h-5 w-5 animate-spin" })) : (_jsx(BuilderBMark, { className: "h-5 w-5" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "text-sm font-semibold text-foreground", children: title }) }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground leading-relaxed", children: subtitle }), showWaitlist && (_jsxs("a", { href: DESKTOP_DOWNLOAD_URL, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground", children: ["Download desktop app", _jsx(IconExternalLink, { className: "h-3 w-3" })] })), err && _jsx("div", { className: "mt-2 text-xs text-destructive", children: err }), _jsx("div", { className: "mt-3", children: runResult ? (_jsxs("a", { href: runResult.url, target: "_blank", rel: "noopener noreferrer", className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: ["Open branch in Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) : canSend ? (_jsx("button", { type: "button", onClick: handleSend, disabled: sending, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", sending && "opacity-70 cursor-wait"), children: sending ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Sending to Builder\u2026"] })) : (_jsx(_Fragment, { children: "Send to Builder" })) })) : showWaitlist && !waitlistJoined ? (_jsx("button", { type: "button", onClick: handleJoinWaitlist, disabled: joiningWaitlist, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", joiningWaitlist && "opacity-70 cursor-wait"), children: joiningWaitlist ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Joining\u2026"] })) : (_jsx(_Fragment, { children: "Join the waitlist" })) })) : !configured ? (_jsx("button", { type: "button", onClick: () => flow.start(), disabled: connecting, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", connecting && "opacity-70 cursor-wait"), children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Waiting for Builder\u2026"] })) : (_jsxs(_Fragment, { children: ["Connect Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) })) : null })] })] }) }));
179
179
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectBuilderCard.js","sourceRoot":"","sources":["../../src/client/ConnectBuilderCard.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { IconExternalLink, IconLoader2 } from \"@tabler/icons-react\";\nimport { getCallbackOrigin } from \"./frame.js\";\nimport { useBuilderConnectFlow } from \"./settings/useBuilderStatus.js\";\nimport { BuilderBMark } from \"./builder-mark.js\";\nimport { cn } from \"./utils.js\";\nimport { agentNativePath } from \"./api-path.js\";\n\nconst DESKTOP_DOWNLOAD_URL = \"https://www.agent-native.com/download\";\n\nfunction isLocalBrowserOutsideDesktop() {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return false;\n }\n const hostname = window.location.hostname;\n const local =\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"::1\";\n return local && !/AgentNativeDesktop/i.test(navigator.userAgent || \"\");\n}\n\nexport interface ConnectBuilderCardProps {\n configured: boolean;\n /**\n * True when the server has a Builder branch project configured for this\n * request. When false, the card shows a waitlist CTA instead of a Send\n * button — the /builder/run endpoint would 403 anyway.\n */\n builderEnabled?: boolean;\n connectUrl: string;\n orgName?: string | null;\n /** The user's feature/change request, forwarded to Builder's cloud agent\n * when they click Send. Empty for generic \"connect Builder\" prompts. */\n prompt?: string;\n}\n\ninterface BuilderRunResult {\n branchName: string;\n projectId: string;\n url: string;\n status: string;\n}\n\n/**\n * Rich inline card rendered for the `connect-builder` tool call. Shows a\n * prominent Connect button that opens the Builder CLI auth flow and polls\n * /_agent-native/builder/status until credentials land.\n */\nexport function ConnectBuilderCard({\n configured: initialConfigured,\n builderEnabled: initialBuilderEnabled = true,\n connectUrl: initialConnectUrl,\n orgName: initialOrgName,\n prompt = \"\",\n}: ConnectBuilderCardProps) {\n // The connect-poll state machine is shared — the tool-call result is\n // frozen at render time, so the hook's mount-time fetch + focus refresh\n // is what catches a flow the user completed in another tab.\n const flow = useBuilderConnectFlow({\n popupUrl: initialConnectUrl,\n trackingSource: \"connect_builder_card\",\n });\n // Only use the server-rendered props until the hook's first status\n // fetch returns. After that, the hook is authoritative — including for\n // the disconnect case (where `flow.configured` flips back to `false`\n // even though `initialConfigured` was `true` at render time).\n const configured = flow.hasFetchedStatus\n ? flow.configured\n : initialConfigured;\n const builderEnabled = flow.hasFetchedStatus\n ? flow.builderEnabled\n : initialBuilderEnabled;\n const orgName = flow.hasFetchedStatus\n ? flow.orgName\n : (initialOrgName ?? null);\n const connecting = flow.connecting;\n\n const [waitlistJoined, setWaitlistJoined] = useState(false);\n const [joiningWaitlist, setJoiningWaitlist] = useState(false);\n const [waitlistErr, setWaitlistErr] = useState<string | null>(null);\n\n const [sending, setSending] = useState(false);\n const [runResult, setRunResult] = useState<BuilderRunResult | null>(null);\n const [sendErr, setSendErr] = useState<string | null>(null);\n const [localBrowser, setLocalBrowser] = useState(false);\n const mountedRef = useRef(true);\n // Tracks whether the user clicked \"Connect Builder\" *this session*. When\n // the connect-then-poll round-trip lands `configured=true`, we use this\n // flag to decide whether to retry the user's pending prompt automatically\n // — the alternative is making them click \"Send to Builder\" a second time\n // even though the agent had already captured their original ask. We do\n // NOT auto-send when the card mounts already-connected (e.g. user\n // revisits an old thread) — only when the connect just succeeded.\n const wasConnectingRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n setLocalBrowser(isLocalBrowserOutsideDesktop());\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const handleSend = useCallback(async () => {\n if (!prompt.trim()) return;\n setSending(true);\n setSendErr(null);\n try {\n const origin = getCallbackOrigin() || window.location.origin;\n const res = await fetch(\n new URL(agentNativePath(\"/_agent-native/builder/run\"), origin).href,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ prompt }),\n },\n );\n const data = await res.json().catch(() => ({}));\n if (!res.ok) {\n throw new Error(\n typeof data?.error === \"string\"\n ? data.error\n : `Request failed (${res.status})`,\n );\n }\n if (!mountedRef.current) return;\n setRunResult(data as BuilderRunResult);\n setSending(false);\n } catch (e) {\n if (!mountedRef.current) return;\n setSendErr(e instanceof Error ? e.message : \"Send failed\");\n setSending(false);\n }\n }, [prompt]);\n\n const handleJoinWaitlist = useCallback(async () => {\n setJoiningWaitlist(true);\n setWaitlistErr(null);\n try {\n const origin = getCallbackOrigin() || window.location.origin;\n const res = await fetch(\n new URL(\n agentNativePath(\"/_agent-native/builder/branch-waitlist\"),\n origin,\n ).href,\n { method: \"POST\" },\n );\n if (!res.ok) {\n const data = await res.json().catch(() => ({}));\n throw new Error(\n typeof data?.error === \"string\"\n ? data.error\n : `Request failed (${res.status})`,\n );\n }\n if (!mountedRef.current) return;\n setWaitlistJoined(true);\n setJoiningWaitlist(false);\n } catch (e) {\n if (!mountedRef.current) return;\n setWaitlistErr(e instanceof Error ? e.message : \"Couldn't join waitlist\");\n setJoiningWaitlist(false);\n }\n }, []);\n\n // Combine connect-flow errors, send errors, and waitlist errors.\n const err = sendErr ?? waitlistErr ?? flow.error;\n\n const hasPrompt = prompt.trim().length > 0;\n const canSend = configured && builderEnabled && hasPrompt;\n\n // Auto-send the user's pending prompt the moment connecting finishes\n // successfully. Without this, the connect popup closing leaves the user\n // staring at a \"Send to Builder\" button — feels like they have to\n // re-submit even though the prompt is right there in the card.\n useEffect(() => {\n if (flow.connecting) {\n wasConnectingRef.current = true;\n return;\n }\n if (!wasConnectingRef.current) return;\n if (canSend && !sending && !runResult && !sendErr) {\n wasConnectingRef.current = false;\n void handleSend();\n }\n }, [flow.connecting, canSend, sending, runResult, sendErr, handleSend]);\n // Branch creation is gated by a server-side project id, which may come\n // from deployment config or org-scoped secrets.\n const showWaitlist = !builderEnabled && hasPrompt;\n\n // Title + subtitle depend on which mode we're in. We compute them up front\n // so the render tree below stays flat.\n const connectedCapabilityText = builderEnabled\n ? \"LLM access, browser automation, and cloud code changes are ready to use.\"\n : \"LLM access and browser automation are ready to use. Builder Cloud Agents for code changes are not available for this workspace yet.\";\n let title: string;\n let subtitle: React.ReactNode;\n if (runResult) {\n title = \"Builder is working on it\";\n subtitle = (\n <>\n Working on branch{\" \"}\n <span className=\"font-mono text-foreground\">\n {runResult.branchName}\n </span>\n . Click through to watch progress in the Visual Editor.\n </>\n );\n } else if (showWaitlist) {\n title = waitlistJoined\n ? \"You're on the waitlist\"\n : \"Builder Cloud Agents coming soon\";\n subtitle = waitlistJoined ? (\n <>\n We'll let you know when Builder Cloud Agents are available for this\n workspace.{\" \"}\n {localBrowser\n ? \"Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone.\"\n : \"You can still clone the project locally and use the desktop app for code changes.\"}\n </>\n ) : (\n <>\n You don't have access to Builder Cloud Agents for this workspace yet.{\" \"}\n {localBrowser\n ? \"Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone.\"\n : \"You can still clone the project locally and use the desktop app for code changes.\"}\n </>\n );\n } else if (canSend) {\n title = \"Send this to Builder\";\n subtitle = (\n <>\n Builder's cloud coding agent will make this code change on a fresh\n branch.\n </>\n );\n } else if (configured) {\n title = \"Builder.io connected\";\n subtitle = flow.envManaged ? (\n <>\n Managed by this deployment — every user of this app uses the same\n Builder identity. {connectedCapabilityText}\n </>\n ) : orgName ? (\n <>\n Connected to{\" \"}\n <span className=\"font-medium text-foreground\">{orgName}</span>.{\" \"}\n {connectedCapabilityText}\n </>\n ) : (\n <>{connectedCapabilityText}</>\n );\n } else {\n title = \"Connect Builder.io\";\n subtitle = (\n <>\n Connect Builder for managed LLM access, browser automation, and cloud\n code changes when they are enabled for this workspace.\n </>\n );\n }\n\n return (\n <div className={cn(\"my-2 rounded-lg border border-border overflow-hidden\")}>\n <div className=\"flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent\">\n <div\n className={cn(\n \"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg\",\n \"bg-foreground text-background\",\n )}\n >\n {runResult ? (\n <IconLoader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <BuilderBMark className=\"h-5 w-5\" />\n )}\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-foreground\">\n {title}\n </span>\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground leading-relaxed\">\n {subtitle}\n </div>\n\n {showWaitlist && (\n <a\n href={DESKTOP_DOWNLOAD_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground\"\n >\n Download desktop app\n <IconExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n\n {err && <div className=\"mt-2 text-xs text-destructive\">{err}</div>}\n\n <div className=\"mt-3\">\n {runResult ? (\n <a\n href={runResult.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n )}\n >\n Open branch in Builder\n <IconExternalLink className=\"h-3.5 w-3.5\" />\n </a>\n ) : canSend ? (\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={sending}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n sending && \"opacity-70 cursor-wait\",\n )}\n >\n {sending ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Sending to Builder…\n </>\n ) : (\n <>Send to Builder</>\n )}\n </button>\n ) : showWaitlist && !waitlistJoined ? (\n <button\n type=\"button\"\n onClick={handleJoinWaitlist}\n disabled={joiningWaitlist}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n joiningWaitlist && \"opacity-70 cursor-wait\",\n )}\n >\n {joiningWaitlist ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Joining…\n </>\n ) : (\n <>Join the waitlist</>\n )}\n </button>\n ) : !configured ? (\n <button\n type=\"button\"\n onClick={() => flow.start()}\n disabled={connecting}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n connecting && \"opacity-70 cursor-wait\",\n )}\n >\n {connecting ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Waiting for Builder…\n </>\n ) : (\n <>\n Connect Builder\n <IconExternalLink className=\"h-3.5 w-3.5\" />\n </>\n )}\n </button>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"ConnectBuilderCard.js","sourceRoot":"","sources":["../../src/client/ConnectBuilderCard.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { IconExternalLink, IconLoader2 } from \"@tabler/icons-react\";\nimport { getCallbackOrigin } from \"./frame.js\";\nimport { useBuilderConnectFlow } from \"./settings/useBuilderStatus.js\";\nimport { BuilderBMark } from \"./builder-mark.js\";\nimport { cn } from \"./utils.js\";\nimport { agentNativePath } from \"./api-path.js\";\n\nconst DESKTOP_DOWNLOAD_URL = \"https://www.agent-native.com/download\";\n\nfunction isLocalBrowserOutsideDesktop() {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return false;\n }\n const hostname = window.location.hostname;\n const local =\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"::1\";\n return local && !/AgentNativeDesktop/i.test(navigator.userAgent || \"\");\n}\n\nexport interface ConnectBuilderCardProps {\n configured: boolean;\n /**\n * True when the server has a Builder branch project configured for this\n * request. When false, the card shows a waitlist CTA instead of a Send\n * button — the /builder/run endpoint would 403 anyway.\n */\n builderEnabled?: boolean;\n connectUrl: string;\n orgName?: string | null;\n /** The user's feature/change request, forwarded to Builder's cloud agent\n * when they click Send. Empty for generic \"connect Builder\" prompts. */\n prompt?: string;\n}\n\ninterface BuilderRunResult {\n branchName: string;\n projectId: string;\n url: string;\n status: string;\n}\n\n/**\n * Rich inline card rendered for the `connect-builder` tool call. Shows a\n * prominent Connect button that opens the Builder CLI auth flow and polls\n * /_agent-native/builder/status until credentials land.\n */\nexport function ConnectBuilderCard({\n configured: initialConfigured,\n builderEnabled: initialBuilderEnabled = true,\n connectUrl: initialConnectUrl,\n orgName: initialOrgName,\n prompt = \"\",\n}: ConnectBuilderCardProps) {\n // The connect-poll state machine is shared — the tool-call result is\n // frozen at render time, so the hook's mount-time fetch + focus refresh\n // is what catches a flow the user completed in another tab.\n const flow = useBuilderConnectFlow({\n popupUrl: initialConnectUrl,\n trackingSource: \"connect_builder_card\",\n });\n // Only use the server-rendered props until the hook's first status\n // fetch returns. After that, the hook is authoritative — including for\n // the disconnect case (where `flow.configured` flips back to `false`\n // even though `initialConfigured` was `true` at render time).\n const configured = flow.hasFetchedStatus\n ? flow.configured\n : initialConfigured;\n const builderEnabled = flow.hasFetchedStatus\n ? flow.builderEnabled\n : initialBuilderEnabled;\n const orgName = flow.hasFetchedStatus\n ? flow.orgName\n : (initialOrgName ?? null);\n const connecting = flow.connecting;\n\n const [waitlistJoined, setWaitlistJoined] = useState(false);\n const [joiningWaitlist, setJoiningWaitlist] = useState(false);\n const [waitlistErr, setWaitlistErr] = useState<string | null>(null);\n\n const [sending, setSending] = useState(false);\n const [runResult, setRunResult] = useState<BuilderRunResult | null>(null);\n const [sendErr, setSendErr] = useState<string | null>(null);\n const [localBrowser, setLocalBrowser] = useState(false);\n const mountedRef = useRef(true);\n // Tracks whether the user clicked \"Connect Builder\" *this session*. When\n // the connect-then-poll round-trip lands `configured=true`, we use this\n // flag to decide whether to retry the user's pending prompt automatically\n // — the alternative is making them click \"Send to Builder\" a second time\n // even though the agent had already captured their original ask. We do\n // NOT auto-send when the card mounts already-connected (e.g. user\n // revisits an old thread) — only when the connect just succeeded.\n const wasConnectingRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n setLocalBrowser(isLocalBrowserOutsideDesktop());\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const handleSend = useCallback(async () => {\n if (!prompt.trim()) return;\n setSending(true);\n setSendErr(null);\n try {\n const origin = getCallbackOrigin() || window.location.origin;\n const res = await fetch(\n new URL(agentNativePath(\"/_agent-native/builder/run\"), origin).href,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ prompt }),\n },\n );\n const data = await res.json().catch(() => ({}));\n if (!res.ok) {\n throw new Error(\n typeof data?.error === \"string\"\n ? data.error\n : `Request failed (${res.status})`,\n );\n }\n if (!mountedRef.current) return;\n setRunResult(data as BuilderRunResult);\n setSending(false);\n } catch (e) {\n if (!mountedRef.current) return;\n setSendErr(e instanceof Error ? e.message : \"Send failed\");\n setSending(false);\n }\n }, [prompt]);\n\n const handleJoinWaitlist = useCallback(async () => {\n setJoiningWaitlist(true);\n setWaitlistErr(null);\n try {\n const origin = getCallbackOrigin() || window.location.origin;\n const res = await fetch(\n new URL(\n agentNativePath(\"/_agent-native/builder/branch-waitlist\"),\n origin,\n ).href,\n { method: \"POST\" },\n );\n if (!res.ok) {\n const data = await res.json().catch(() => ({}));\n throw new Error(\n typeof data?.error === \"string\"\n ? data.error\n : `Request failed (${res.status})`,\n );\n }\n if (!mountedRef.current) return;\n setWaitlistJoined(true);\n setJoiningWaitlist(false);\n } catch (e) {\n if (!mountedRef.current) return;\n setWaitlistErr(e instanceof Error ? e.message : \"Couldn't join waitlist\");\n setJoiningWaitlist(false);\n }\n }, []);\n\n // Combine connect-flow errors, send errors, and waitlist errors.\n const err = sendErr ?? waitlistErr ?? flow.error;\n\n const hasPrompt = prompt.trim().length > 0;\n const canSend = configured && builderEnabled && hasPrompt;\n\n // Auto-send the user's pending prompt the moment connecting finishes\n // successfully. Without this, the connect popup closing leaves the user\n // staring at a \"Send to Builder\" button — feels like they have to\n // re-submit even though the prompt is right there in the card.\n useEffect(() => {\n if (flow.connecting) {\n wasConnectingRef.current = true;\n return;\n }\n if (!wasConnectingRef.current) return;\n if (canSend && !sending && !runResult && !sendErr) {\n wasConnectingRef.current = false;\n void handleSend();\n }\n }, [flow.connecting, canSend, sending, runResult, sendErr, handleSend]);\n // Branch creation is gated by a server-side project id, which may come\n // from deployment config or org-scoped secrets.\n const showWaitlist = !builderEnabled && hasPrompt;\n\n // Title + subtitle depend on which mode we're in. We compute them up front\n // so the render tree below stays flat.\n const connectedCapabilityText = builderEnabled\n ? \"LLM access, browser automation, and cloud code changes are ready to use.\"\n : \"LLM access and browser automation are ready to use. Builder Cloud Agents for code changes are not available for this workspace yet.\";\n let title: string;\n let subtitle: React.ReactNode;\n if (runResult) {\n title = \"Builder is working on it\";\n subtitle = (\n <>\n Working on branch{\" \"}\n <span className=\"font-mono text-foreground\">\n {runResult.branchName}\n </span>\n . Click through to watch progress in the Visual Editor.\n </>\n );\n } else if (showWaitlist) {\n title = waitlistJoined\n ? \"You're on the waitlist\"\n : \"Builder Cloud Agents coming soon\";\n subtitle = waitlistJoined ? (\n <>\n We'll let you know when Builder Cloud Agents are available for this\n workspace.{\" \"}\n {localBrowser\n ? \"Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone.\"\n : \"You can still clone the project locally and use the desktop app for code changes.\"}\n </>\n ) : (\n <>\n You don't have access to Builder Cloud Agents for this workspace yet.{\" \"}\n {localBrowser\n ? \"Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone.\"\n : \"You can still clone the project locally and use the desktop app for code changes.\"}\n </>\n );\n } else if (canSend) {\n title = \"Send this to Builder\";\n subtitle = (\n <>\n Builder's cloud coding agent will make this code change on a fresh\n branch.\n </>\n );\n } else if (configured) {\n title = \"Builder.io connected\";\n subtitle = flow.envManaged ? (\n <>\n Managed by this deployment — every user of this app uses the same\n Builder identity. {connectedCapabilityText}\n </>\n ) : orgName ? (\n <>\n Connected to{\" \"}\n <span className=\"font-medium text-foreground\">{orgName}</span>.{\" \"}\n {connectedCapabilityText}\n </>\n ) : (\n <>{connectedCapabilityText}</>\n );\n } else {\n title = \"Connect Builder.io\";\n subtitle = (\n <>\n Builder.io's free tier includes AI credits, browser automation, and\n cloud code when enabled.\n </>\n );\n }\n\n return (\n <div className={cn(\"my-2 rounded-lg border border-border overflow-hidden\")}>\n <div className=\"flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent\">\n <div\n className={cn(\n \"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg\",\n \"bg-foreground text-background\",\n )}\n >\n {runResult ? (\n <IconLoader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <BuilderBMark className=\"h-5 w-5\" />\n )}\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-foreground\">\n {title}\n </span>\n </div>\n <div className=\"mt-0.5 text-xs text-muted-foreground leading-relaxed\">\n {subtitle}\n </div>\n\n {showWaitlist && (\n <a\n href={DESKTOP_DOWNLOAD_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground\"\n >\n Download desktop app\n <IconExternalLink className=\"h-3 w-3\" />\n </a>\n )}\n\n {err && <div className=\"mt-2 text-xs text-destructive\">{err}</div>}\n\n <div className=\"mt-3\">\n {runResult ? (\n <a\n href={runResult.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n )}\n >\n Open branch in Builder\n <IconExternalLink className=\"h-3.5 w-3.5\" />\n </a>\n ) : canSend ? (\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={sending}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n sending && \"opacity-70 cursor-wait\",\n )}\n >\n {sending ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Sending to Builder…\n </>\n ) : (\n <>Send to Builder</>\n )}\n </button>\n ) : showWaitlist && !waitlistJoined ? (\n <button\n type=\"button\"\n onClick={handleJoinWaitlist}\n disabled={joiningWaitlist}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n joiningWaitlist && \"opacity-70 cursor-wait\",\n )}\n >\n {joiningWaitlist ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Joining…\n </>\n ) : (\n <>Join the waitlist</>\n )}\n </button>\n ) : !configured ? (\n <button\n type=\"button\"\n onClick={() => flow.start()}\n disabled={connecting}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors\",\n \"bg-foreground text-background hover:bg-foreground/90\",\n connecting && \"opacity-70 cursor-wait\",\n )}\n >\n {connecting ? (\n <>\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n Waiting for Builder…\n </>\n ) : (\n <>\n Connect Builder\n <IconExternalLink className=\"h-3.5 w-3.5\" />\n </>\n )}\n </button>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"default-steps.d.ts","sourceRoot":"","sources":["../../src/onboarding/default-steps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqZH,6DAA6D;AAC7D,wBAAgB,8BAA8B,IAAI,IAAI,CAQrD"}
1
+ {"version":3,"file":"default-steps.d.ts","sourceRoot":"","sources":["../../src/onboarding/default-steps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsZH,6DAA6D;AAC7D,wBAAgB,8BAA8B,IAAI,IAAI,CAQrD"}
@@ -65,8 +65,9 @@ const llmStep = {
65
65
  id: "builder",
66
66
  kind: "builder-cli-auth",
67
67
  label: "Connect Builder",
68
- description: "Connect the Builder space where this app should run. This unlocks managed LLM credits, web search, browser automation, and file uploads. Cloud code changes appear when Builder Cloud Agents are available for the workspace.",
68
+ description: "Builder.io's free tier includes AI credits, web search, browser automation, and file uploads.",
69
69
  primary: true,
70
+ badge: "free",
70
71
  payload: {
71
72
  scope: "llm",
72
73
  },
@@ -1 +1 @@
1
- {"version":3,"file":"default-steps.js","sourceRoot":"","sources":["../../src/onboarding/default-steps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Default framework-level onboarding steps.\n *\n * Registered when `createOnboardingPlugin()` mounts (auto-mount or explicit).\n * Templates can override any step by registering another step with the same\n * `id` after these have been registered.\n */\n\nimport { registerOnboardingStep } from \"./registry.js\";\nimport type { OnboardingStep } from \"./types.js\";\nimport {\n PROVIDER_ENV_META,\n PROVIDER_ENV_VARS,\n} from \"../agent/engine/provider-env-vars.js\";\nimport {\n detectEngineFromUserSecrets,\n isAgentEngineSettingConfigured,\n} from \"../agent/engine/registry.js\";\nimport {\n canUseDeployCredentialFallbackForRequest,\n readDeployCredentialEnv,\n} from \"../server/credential-provider.js\";\nimport { getSetting } from \"../settings/store.js\";\n\ntype LlmKeyMethod = {\n provider: keyof typeof PROVIDER_ENV_META;\n id: string;\n label: string;\n description: string;\n primary?: boolean;\n};\n\nconst LLM_KEY_METHODS: LlmKeyMethod[] = [\n {\n provider: \"anthropic\",\n id: \"anthropic-key\",\n label: \"Anthropic\",\n description: \"Claude models with your own Anthropic key.\",\n },\n {\n provider: \"openai\",\n id: \"openai-key\",\n label: \"OpenAI\",\n description: \"GPT models with your own OpenAI key.\",\n },\n {\n provider: \"google\",\n id: \"google-key\",\n label: \"Google Gemini\",\n description: \"Gemini models with your own Google AI key.\",\n },\n {\n provider: \"openrouter\",\n id: \"openrouter-key\",\n label: \"OpenRouter\",\n description: \"OpenRouter models with your own OpenRouter key.\",\n },\n {\n provider: \"groq\",\n id: \"groq-key\",\n label: \"Groq\",\n description: \"Groq-hosted models with your own Groq key.\",\n },\n {\n provider: \"mistral\",\n id: \"mistral-key\",\n label: \"Mistral\",\n description: \"Mistral models with your own Mistral key.\",\n },\n {\n provider: \"cohere\",\n id: \"cohere-key\",\n label: \"Cohere\",\n description: \"Cohere models with your own Cohere key.\",\n },\n];\n\nconst llmStep: OnboardingStep = {\n id: \"llm\",\n order: 10,\n required: true,\n title: \"Connect an AI engine\",\n description: \"Use Builder's managed gateway, or bring your own provider key.\",\n methods: [\n {\n id: \"builder\",\n kind: \"builder-cli-auth\",\n label: \"Connect Builder\",\n description:\n \"Connect the Builder space where this app should run. This unlocks managed LLM credits, web search, browser automation, and file uploads. Cloud code changes appear when Builder Cloud Agents are available for the workspace.\",\n primary: true,\n payload: {\n scope: \"llm\",\n },\n },\n ...LLM_KEY_METHODS.map(({ provider, id, label, description, primary }) => {\n const meta = PROVIDER_ENV_META[provider];\n return {\n id,\n kind: \"form\" as const,\n label,\n description,\n ...(primary ? { primary: true } : {}),\n payload: {\n writeScope: \"workspace\" as const,\n fields: [\n {\n key: meta.envVar,\n label: meta.envVar,\n placeholder: meta.placeholder,\n secret: true,\n },\n ],\n },\n };\n }),\n ],\n isComplete: async () => {\n try {\n const { resolveHasCompleteBuilderConnection } =\n await import(\"../server/credential-provider.js\");\n if (await resolveHasCompleteBuilderConnection()) return true;\n } catch {\n if (process.env.BUILDER_PRIVATE_KEY && process.env.BUILDER_PUBLIC_KEY) {\n return true;\n }\n }\n try {\n if (await detectEngineFromUserSecrets()) return true;\n } catch {\n // Fall through to legacy/env detection.\n }\n if (\n canUseDeployCredentialFallbackForRequest() &&\n PROVIDER_ENV_VARS.some((k) => !!readDeployCredentialEnv(k))\n ) {\n return true;\n }\n try {\n return isAgentEngineSettingConfigured(await getSetting(\"agent-engine\"));\n } catch {\n return false;\n }\n },\n};\n\n/** Step 2 — where application data lives. The default DB is non-blocking. */\nconst databaseStep: OnboardingStep = {\n id: \"database\",\n order: 20,\n required: false,\n title: \"Database\",\n description:\n \"Agent-native stores app data in SQL. Set DATABASE_URL when you want to point this app at a specific database.\",\n methods: [\n {\n id: \"database-url\",\n kind: \"form\",\n label: \"Set DATABASE_URL\",\n description: \"Paste the SQL connection string this app should use.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"DATABASE_URL\",\n label: \"DATABASE_URL\",\n placeholder: \"postgres://..., libsql://..., file:./data/app.db\",\n },\n {\n key: \"DATABASE_AUTH_TOKEN\",\n label: \"DATABASE_AUTH_TOKEN (if needed)\",\n placeholder: \"Token for providers such as Turso/libSQL\",\n secret: true,\n },\n ],\n },\n },\n ],\n // The default local database means this step is always satisfied.\n isComplete: () => true,\n};\n\n/** Step 3 — how users sign in. Built-in account auth is non-blocking. */\nconst authStep: OnboardingStep = {\n id: \"auth\",\n order: 30,\n required: false,\n title: \"Authentication\",\n description:\n \"Built-in email/password accounts work by default. Add OAuth or access tokens only if you want another sign-in path.\",\n methods: [\n {\n id: \"google-oauth\",\n kind: \"form\",\n label: \"Google OAuth\",\n description: \"Add Google as an optional sign-in provider.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n { key: \"GOOGLE_CLIENT_ID\", label: \"GOOGLE_CLIENT_ID\" },\n {\n key: \"GOOGLE_CLIENT_SECRET\",\n label: \"GOOGLE_CLIENT_SECRET\",\n secret: true,\n },\n ],\n },\n },\n {\n id: \"github-oauth\",\n kind: \"form\",\n label: \"GitHub OAuth\",\n description: \"Add GitHub as an optional sign-in provider.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n { key: \"GITHUB_CLIENT_ID\", label: \"GITHUB_CLIENT_ID\" },\n {\n key: \"GITHUB_CLIENT_SECRET\",\n label: \"GITHUB_CLIENT_SECRET\",\n secret: true,\n },\n ],\n },\n },\n ],\n isComplete: () => true,\n};\n\n/** Step 4 — transactional email (password resets, invitations). Optional. */\nconst emailStep: OnboardingStep = {\n id: \"email\",\n order: 40,\n required: false,\n title: \"Email delivery\",\n description:\n \"Optional for local work. Before deploying with password resets, invitations, or share notifications, connect an email provider.\",\n methods: [\n {\n id: \"resend\",\n kind: \"form\",\n label: \"Resend\",\n description: \"Use Resend for transactional email.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"RESEND_API_KEY\",\n label: \"RESEND_API_KEY\",\n placeholder: \"re_...\",\n secret: true,\n },\n {\n key: \"EMAIL_FROM\",\n label: \"EMAIL_FROM (from address)\",\n placeholder: \"Agent Native <noreply@yourdomain.com>\",\n },\n {\n key: \"APP_NAME\",\n label: \"APP_NAME (shown in invite emails)\",\n placeholder: \"Acme Forms\",\n },\n ],\n },\n },\n {\n id: \"sendgrid\",\n kind: \"form\",\n label: \"SendGrid\",\n description: \"Use SendGrid for transactional email.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"SENDGRID_API_KEY\",\n label: \"SENDGRID_API_KEY\",\n placeholder: \"SG....\",\n secret: true,\n },\n {\n key: \"EMAIL_FROM\",\n label: \"EMAIL_FROM (from address)\",\n placeholder: \"Agent Native <noreply@yourdomain.com>\",\n },\n ],\n },\n },\n ],\n isComplete: () => {\n if (process.env.RESEND_API_KEY) return true;\n // SendGrid rejects Resend's sandbox sender, so EMAIL_FROM must also be\n // set — otherwise sendEmail() throws at runtime even though the API key\n // is configured.\n if (process.env.SENDGRID_API_KEY) return !!process.env.EMAIL_FROM;\n return false;\n },\n};\n\nconst githubRepositoryStep: OnboardingStep = {\n id: \"github-repository\",\n order: 50,\n required: false,\n title: \"Connect a GitHub repository\",\n description:\n \"Optional for cloud/headless repo work. Grants connector-scoped file read and write access without cloning a repo or running code.\",\n methods: [\n {\n id: \"settings\",\n kind: \"link\",\n primary: true,\n label: \"Open GitHub token settings\",\n description:\n \"Save a fine-grained token scoped to the repositories this workspace may access.\",\n payload: {\n url: \"#secrets:GITHUB_TOKEN\",\n external: false,\n },\n },\n {\n id: \"local-env\",\n kind: \"form\",\n label: \"Use local .env\",\n description:\n \"For local/single-tenant work, save a token and optional default owner/repo.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"GITHUB_TOKEN\",\n label: \"GITHUB_TOKEN\",\n placeholder: \"github_pat_...\",\n secret: true,\n },\n {\n key: \"GITHUB_REPOSITORY\",\n label: \"GITHUB_REPOSITORY\",\n placeholder: \"owner/repo\",\n },\n ],\n },\n },\n ],\n isComplete: async (context) => {\n const userEmail = context?.userEmail;\n const orgId = context?.orgId ?? null;\n if (userEmail) {\n try {\n const { resolveWorkspaceConnectionCredentialForApp } =\n await import(\"../workspace-connections/index.js\");\n const result = await resolveWorkspaceConnectionCredentialForApp({\n appId:\n process.env.AGENT_NATIVE_APP_ID ||\n process.env.APP_ID ||\n process.env.npm_package_name ||\n \"app\",\n provider: \"github\",\n key: \"GITHUB_TOKEN\",\n userEmail,\n orgId,\n });\n if (result.available) return true;\n } catch {\n // Fall through to local credential stores.\n }\n\n try {\n const { resolveCredential } = await import(\"../credentials/index.js\");\n if (await resolveCredential(\"GITHUB_TOKEN\", { userEmail, orgId })) {\n return true;\n }\n } catch {\n // Fall through to app_secrets.\n }\n\n try {\n const { readAppSecretMeta } = await import(\"../secrets/storage.js\");\n const refs: Array<{\n scope: \"user\" | \"org\" | \"workspace\";\n scopeId: string;\n }> = [{ scope: \"user\", scopeId: userEmail }];\n if (orgId) {\n refs.push(\n { scope: \"org\", scopeId: orgId },\n { scope: \"workspace\", scopeId: orgId },\n );\n } else {\n refs.push({ scope: \"workspace\", scopeId: `solo:${userEmail}` });\n }\n for (const ref of refs) {\n const meta = await readAppSecretMeta({\n key: \"GITHUB_TOKEN\",\n scope: ref.scope,\n scopeId: ref.scopeId,\n });\n if (meta) return true;\n }\n } catch {\n // Fall through to local/single-tenant env.\n }\n }\n\n if (!canUseDeployCredentialFallbackForRequest()) return false;\n return !!(\n readDeployCredentialEnv(\"GITHUB_TOKEN\") ||\n readDeployCredentialEnv(\"GH_TOKEN\")\n );\n },\n};\n\nlet registered = false;\n\n/** Idempotent. Safe to call from every plugin-mount call. */\nexport function registerDefaultOnboardingSteps(): void {\n if (registered) return;\n registered = true;\n registerOnboardingStep(llmStep);\n registerOnboardingStep(databaseStep);\n registerOnboardingStep(authStep);\n registerOnboardingStep(emailStep);\n registerOnboardingStep(githubRepositoryStep);\n}\n"]}
1
+ {"version":3,"file":"default-steps.js","sourceRoot":"","sources":["../../src/onboarding/default-steps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Default framework-level onboarding steps.\n *\n * Registered when `createOnboardingPlugin()` mounts (auto-mount or explicit).\n * Templates can override any step by registering another step with the same\n * `id` after these have been registered.\n */\n\nimport { registerOnboardingStep } from \"./registry.js\";\nimport type { OnboardingStep } from \"./types.js\";\nimport {\n PROVIDER_ENV_META,\n PROVIDER_ENV_VARS,\n} from \"../agent/engine/provider-env-vars.js\";\nimport {\n detectEngineFromUserSecrets,\n isAgentEngineSettingConfigured,\n} from \"../agent/engine/registry.js\";\nimport {\n canUseDeployCredentialFallbackForRequest,\n readDeployCredentialEnv,\n} from \"../server/credential-provider.js\";\nimport { getSetting } from \"../settings/store.js\";\n\ntype LlmKeyMethod = {\n provider: keyof typeof PROVIDER_ENV_META;\n id: string;\n label: string;\n description: string;\n primary?: boolean;\n};\n\nconst LLM_KEY_METHODS: LlmKeyMethod[] = [\n {\n provider: \"anthropic\",\n id: \"anthropic-key\",\n label: \"Anthropic\",\n description: \"Claude models with your own Anthropic key.\",\n },\n {\n provider: \"openai\",\n id: \"openai-key\",\n label: \"OpenAI\",\n description: \"GPT models with your own OpenAI key.\",\n },\n {\n provider: \"google\",\n id: \"google-key\",\n label: \"Google Gemini\",\n description: \"Gemini models with your own Google AI key.\",\n },\n {\n provider: \"openrouter\",\n id: \"openrouter-key\",\n label: \"OpenRouter\",\n description: \"OpenRouter models with your own OpenRouter key.\",\n },\n {\n provider: \"groq\",\n id: \"groq-key\",\n label: \"Groq\",\n description: \"Groq-hosted models with your own Groq key.\",\n },\n {\n provider: \"mistral\",\n id: \"mistral-key\",\n label: \"Mistral\",\n description: \"Mistral models with your own Mistral key.\",\n },\n {\n provider: \"cohere\",\n id: \"cohere-key\",\n label: \"Cohere\",\n description: \"Cohere models with your own Cohere key.\",\n },\n];\n\nconst llmStep: OnboardingStep = {\n id: \"llm\",\n order: 10,\n required: true,\n title: \"Connect an AI engine\",\n description: \"Use Builder's managed gateway, or bring your own provider key.\",\n methods: [\n {\n id: \"builder\",\n kind: \"builder-cli-auth\",\n label: \"Connect Builder\",\n description:\n \"Builder.io's free tier includes AI credits, web search, browser automation, and file uploads.\",\n primary: true,\n badge: \"free\",\n payload: {\n scope: \"llm\",\n },\n },\n ...LLM_KEY_METHODS.map(({ provider, id, label, description, primary }) => {\n const meta = PROVIDER_ENV_META[provider];\n return {\n id,\n kind: \"form\" as const,\n label,\n description,\n ...(primary ? { primary: true } : {}),\n payload: {\n writeScope: \"workspace\" as const,\n fields: [\n {\n key: meta.envVar,\n label: meta.envVar,\n placeholder: meta.placeholder,\n secret: true,\n },\n ],\n },\n };\n }),\n ],\n isComplete: async () => {\n try {\n const { resolveHasCompleteBuilderConnection } =\n await import(\"../server/credential-provider.js\");\n if (await resolveHasCompleteBuilderConnection()) return true;\n } catch {\n if (process.env.BUILDER_PRIVATE_KEY && process.env.BUILDER_PUBLIC_KEY) {\n return true;\n }\n }\n try {\n if (await detectEngineFromUserSecrets()) return true;\n } catch {\n // Fall through to legacy/env detection.\n }\n if (\n canUseDeployCredentialFallbackForRequest() &&\n PROVIDER_ENV_VARS.some((k) => !!readDeployCredentialEnv(k))\n ) {\n return true;\n }\n try {\n return isAgentEngineSettingConfigured(await getSetting(\"agent-engine\"));\n } catch {\n return false;\n }\n },\n};\n\n/** Step 2 — where application data lives. The default DB is non-blocking. */\nconst databaseStep: OnboardingStep = {\n id: \"database\",\n order: 20,\n required: false,\n title: \"Database\",\n description:\n \"Agent-native stores app data in SQL. Set DATABASE_URL when you want to point this app at a specific database.\",\n methods: [\n {\n id: \"database-url\",\n kind: \"form\",\n label: \"Set DATABASE_URL\",\n description: \"Paste the SQL connection string this app should use.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"DATABASE_URL\",\n label: \"DATABASE_URL\",\n placeholder: \"postgres://..., libsql://..., file:./data/app.db\",\n },\n {\n key: \"DATABASE_AUTH_TOKEN\",\n label: \"DATABASE_AUTH_TOKEN (if needed)\",\n placeholder: \"Token for providers such as Turso/libSQL\",\n secret: true,\n },\n ],\n },\n },\n ],\n // The default local database means this step is always satisfied.\n isComplete: () => true,\n};\n\n/** Step 3 — how users sign in. Built-in account auth is non-blocking. */\nconst authStep: OnboardingStep = {\n id: \"auth\",\n order: 30,\n required: false,\n title: \"Authentication\",\n description:\n \"Built-in email/password accounts work by default. Add OAuth or access tokens only if you want another sign-in path.\",\n methods: [\n {\n id: \"google-oauth\",\n kind: \"form\",\n label: \"Google OAuth\",\n description: \"Add Google as an optional sign-in provider.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n { key: \"GOOGLE_CLIENT_ID\", label: \"GOOGLE_CLIENT_ID\" },\n {\n key: \"GOOGLE_CLIENT_SECRET\",\n label: \"GOOGLE_CLIENT_SECRET\",\n secret: true,\n },\n ],\n },\n },\n {\n id: \"github-oauth\",\n kind: \"form\",\n label: \"GitHub OAuth\",\n description: \"Add GitHub as an optional sign-in provider.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n { key: \"GITHUB_CLIENT_ID\", label: \"GITHUB_CLIENT_ID\" },\n {\n key: \"GITHUB_CLIENT_SECRET\",\n label: \"GITHUB_CLIENT_SECRET\",\n secret: true,\n },\n ],\n },\n },\n ],\n isComplete: () => true,\n};\n\n/** Step 4 — transactional email (password resets, invitations). Optional. */\nconst emailStep: OnboardingStep = {\n id: \"email\",\n order: 40,\n required: false,\n title: \"Email delivery\",\n description:\n \"Optional for local work. Before deploying with password resets, invitations, or share notifications, connect an email provider.\",\n methods: [\n {\n id: \"resend\",\n kind: \"form\",\n label: \"Resend\",\n description: \"Use Resend for transactional email.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"RESEND_API_KEY\",\n label: \"RESEND_API_KEY\",\n placeholder: \"re_...\",\n secret: true,\n },\n {\n key: \"EMAIL_FROM\",\n label: \"EMAIL_FROM (from address)\",\n placeholder: \"Agent Native <noreply@yourdomain.com>\",\n },\n {\n key: \"APP_NAME\",\n label: \"APP_NAME (shown in invite emails)\",\n placeholder: \"Acme Forms\",\n },\n ],\n },\n },\n {\n id: \"sendgrid\",\n kind: \"form\",\n label: \"SendGrid\",\n description: \"Use SendGrid for transactional email.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"SENDGRID_API_KEY\",\n label: \"SENDGRID_API_KEY\",\n placeholder: \"SG....\",\n secret: true,\n },\n {\n key: \"EMAIL_FROM\",\n label: \"EMAIL_FROM (from address)\",\n placeholder: \"Agent Native <noreply@yourdomain.com>\",\n },\n ],\n },\n },\n ],\n isComplete: () => {\n if (process.env.RESEND_API_KEY) return true;\n // SendGrid rejects Resend's sandbox sender, so EMAIL_FROM must also be\n // set — otherwise sendEmail() throws at runtime even though the API key\n // is configured.\n if (process.env.SENDGRID_API_KEY) return !!process.env.EMAIL_FROM;\n return false;\n },\n};\n\nconst githubRepositoryStep: OnboardingStep = {\n id: \"github-repository\",\n order: 50,\n required: false,\n title: \"Connect a GitHub repository\",\n description:\n \"Optional for cloud/headless repo work. Grants connector-scoped file read and write access without cloning a repo or running code.\",\n methods: [\n {\n id: \"settings\",\n kind: \"link\",\n primary: true,\n label: \"Open GitHub token settings\",\n description:\n \"Save a fine-grained token scoped to the repositories this workspace may access.\",\n payload: {\n url: \"#secrets:GITHUB_TOKEN\",\n external: false,\n },\n },\n {\n id: \"local-env\",\n kind: \"form\",\n label: \"Use local .env\",\n description:\n \"For local/single-tenant work, save a token and optional default owner/repo.\",\n payload: {\n writeScope: \"workspace\",\n fields: [\n {\n key: \"GITHUB_TOKEN\",\n label: \"GITHUB_TOKEN\",\n placeholder: \"github_pat_...\",\n secret: true,\n },\n {\n key: \"GITHUB_REPOSITORY\",\n label: \"GITHUB_REPOSITORY\",\n placeholder: \"owner/repo\",\n },\n ],\n },\n },\n ],\n isComplete: async (context) => {\n const userEmail = context?.userEmail;\n const orgId = context?.orgId ?? null;\n if (userEmail) {\n try {\n const { resolveWorkspaceConnectionCredentialForApp } =\n await import(\"../workspace-connections/index.js\");\n const result = await resolveWorkspaceConnectionCredentialForApp({\n appId:\n process.env.AGENT_NATIVE_APP_ID ||\n process.env.APP_ID ||\n process.env.npm_package_name ||\n \"app\",\n provider: \"github\",\n key: \"GITHUB_TOKEN\",\n userEmail,\n orgId,\n });\n if (result.available) return true;\n } catch {\n // Fall through to local credential stores.\n }\n\n try {\n const { resolveCredential } = await import(\"../credentials/index.js\");\n if (await resolveCredential(\"GITHUB_TOKEN\", { userEmail, orgId })) {\n return true;\n }\n } catch {\n // Fall through to app_secrets.\n }\n\n try {\n const { readAppSecretMeta } = await import(\"../secrets/storage.js\");\n const refs: Array<{\n scope: \"user\" | \"org\" | \"workspace\";\n scopeId: string;\n }> = [{ scope: \"user\", scopeId: userEmail }];\n if (orgId) {\n refs.push(\n { scope: \"org\", scopeId: orgId },\n { scope: \"workspace\", scopeId: orgId },\n );\n } else {\n refs.push({ scope: \"workspace\", scopeId: `solo:${userEmail}` });\n }\n for (const ref of refs) {\n const meta = await readAppSecretMeta({\n key: \"GITHUB_TOKEN\",\n scope: ref.scope,\n scopeId: ref.scopeId,\n });\n if (meta) return true;\n }\n } catch {\n // Fall through to local/single-tenant env.\n }\n }\n\n if (!canUseDeployCredentialFallbackForRequest()) return false;\n return !!(\n readDeployCredentialEnv(\"GITHUB_TOKEN\") ||\n readDeployCredentialEnv(\"GH_TOKEN\")\n );\n },\n};\n\nlet registered = false;\n\n/** Idempotent. Safe to call from every plugin-mount call. */\nexport function registerDefaultOnboardingSteps(): void {\n if (registered) return;\n registered = true;\n registerOnboardingStep(llmStep);\n registerOnboardingStep(databaseStep);\n registerOnboardingStep(authStep);\n registerOnboardingStep(emailStep);\n registerOnboardingStep(githubRepositoryStep);\n}\n"]}
@@ -5,12 +5,9 @@ import type { OrgContext, OrgRole } from "./types.js";
5
5
  *
6
6
  * - For users in multiple orgs, honors their `active-org-id` user setting.
7
7
  * - Falls back to the user's first membership.
8
- * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has
9
- * zero memberships, provisions a default org named after the user
10
- * ({name}'s workspace, falling back to the email local-part). Opt-in
11
- * per deployment so templates that don't use orgs don't accrue phantom
12
- * default orgs in their DB. The <RequireActiveOrg> client guard remains
13
- * the safety net for pre-existing accounts or provisioning failures.
8
+ * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has zero
9
+ * memberships, provisions a default org named after the user ({name}'s
10
+ * workspace, falling back to the email local-part).
14
11
  *
15
12
  * Per-request memoized on `event.context` — mirrors the `getSession`
16
13
  * pattern so multiple callers in the same request (e.g. ssr-handler +
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/org/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAMlC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA2BtD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AA6ID;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,OAAiB,GACtB,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAqBD;AAqLD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa3E;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAepD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/org/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAMlC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiCtD;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AA6ID;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,OAAiB,GACtB,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAqBD;AAqLD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa3E;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAepD"}
@@ -17,6 +17,12 @@ function normalizeOrgRole(value) {
17
17
  function isLikelyPersonalWorkspace(membership, email, session) {
18
18
  return membership.orgName.trim() === defaultOrgName(email, session);
19
19
  }
20
+ function autoCreateDefaultOrgEnabled() {
21
+ const raw = process.env.AUTO_CREATE_DEFAULT_ORG;
22
+ if (raw === undefined || raw.trim() === "")
23
+ return false;
24
+ return !["0", "false", "no", "off"].includes(raw.trim().toLowerCase());
25
+ }
20
26
  const nanoid = () => globalThis.crypto?.randomUUID?.().replace(/-/g, "") ??
21
27
  Math.random().toString(36).slice(2) + Date.now().toString(36);
22
28
  /**
@@ -24,12 +30,9 @@ const nanoid = () => globalThis.crypto?.randomUUID?.().replace(/-/g, "") ??
24
30
  *
25
31
  * - For users in multiple orgs, honors their `active-org-id` user setting.
26
32
  * - Falls back to the user's first membership.
27
- * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has
28
- * zero memberships, provisions a default org named after the user
29
- * ({name}'s workspace, falling back to the email local-part). Opt-in
30
- * per deployment so templates that don't use orgs don't accrue phantom
31
- * default orgs in their DB. The <RequireActiveOrg> client guard remains
32
- * the safety net for pre-existing accounts or provisioning failures.
33
+ * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has zero
34
+ * memberships, provisions a default org named after the user ({name}'s
35
+ * workspace, falling back to the email local-part).
33
36
  *
34
37
  * Per-request memoized on `event.context` — mirrors the `getSession`
35
38
  * pattern so multiple callers in the same request (e.g. ssr-handler +
@@ -121,12 +124,12 @@ async function resolveOrgContextUncached(event) {
121
124
  role: sessionOrgRole,
122
125
  };
123
126
  }
124
- if (memberships.length === 0 && process.env.AUTO_CREATE_DEFAULT_ORG) {
127
+ if (memberships.length === 0 && autoCreateDefaultOrgEnabled()) {
125
128
  const created = await tryCreateDefaultOrg(exec, email, session);
126
129
  if (created)
127
130
  return created;
128
- // Creation failed (race / DB error); fall through and let the
129
- // RequireActiveOrg client guard prompt the user.
131
+ // Creation failed (race / DB error); fall through with an empty org context
132
+ // so non-blocking invite/domain UI can still surface recovery options.
130
133
  }
131
134
  if (memberships.length === 0) {
132
135
  return { email, orgId: null, orgName: null, role: null };
@@ -229,8 +232,8 @@ function defaultOrgName(email, session) {
229
232
  /**
230
233
  * Check whether the user has a pending invitation. If so, auto-create
231
234
  * MUST be skipped — otherwise we'd provision a personal org for them
232
- * before they ever see the inviter's org in the RequireActiveOrg
233
- * accept-invite pane, and they'd never join the team that invited them.
235
+ * before they ever see the inviter's org in the invitation banner, and they'd
236
+ * never join the team that invited them.
234
237
  */
235
238
  async function hasPendingInvitation(exec, email) {
236
239
  try {
@@ -242,7 +245,7 @@ async function hasPendingInvitation(exec, email) {
242
245
  }
243
246
  catch {
244
247
  // If we can't tell, err on the side of NOT auto-creating — the
245
- // RequireActiveOrg client guard will surface the situation.
248
+ // invitation banner or team UI can surface the situation.
246
249
  return true;
247
250
  }
248
251
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/org/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getSession } from \"../server/auth.js\";\nimport { getUserSetting, putUserSetting } from \"../settings/user-settings.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport { getSetting } from \"../settings/store.js\";\nimport { autoJoinDomainMatchingOrgs } from \"./auto-join-domain.js\";\nimport type { OrgContext, OrgRole } from \"./types.js\";\n\nconst EMPTY_CONTEXT: OrgContext = {\n email: \"\",\n orgId: null,\n orgName: null,\n role: null,\n};\n\nfunction normalizeOrgRole(value: unknown): OrgRole | null {\n return value === \"owner\" || value === \"admin\" || value === \"member\"\n ? value\n : null;\n}\n\nfunction isLikelyPersonalWorkspace(\n membership: { orgName: string },\n email: string,\n session: { name?: string } | null,\n): boolean {\n return membership.orgName.trim() === defaultOrgName(email, session);\n}\n\nconst nanoid = (): string =>\n globalThis.crypto?.randomUUID?.().replace(/-/g, \"\") ??\n Math.random().toString(36).slice(2) + Date.now().toString(36);\n\n/**\n * Resolve the current user's organization context from their session.\n *\n * - For users in multiple orgs, honors their `active-org-id` user setting.\n * - Falls back to the user's first membership.\n * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has\n * zero memberships, provisions a default org named after the user\n * ({name}'s workspace, falling back to the email local-part). Opt-in\n * per deployment so templates that don't use orgs don't accrue phantom\n * default orgs in their DB. The <RequireActiveOrg> client guard remains\n * the safety net for pre-existing accounts or provisioning failures.\n *\n * Per-request memoized on `event.context` — mirrors the `getSession`\n * pattern so multiple callers in the same request (e.g. ssr-handler +\n * a loader) share a single org_members round trip.\n */\nexport async function getOrgContext(event: H3Event): Promise<OrgContext> {\n // Per-request memoization. Multiple call sites per request (action wrappers,\n // SSR handler, loaders) must not each pay a separate org_members query.\n const ctx = event.context as {\n __anOrgContextCache?: Promise<OrgContext>;\n };\n return (ctx.__anOrgContextCache ??= resolveOrgContextUncached(event));\n}\n\nasync function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {\n const session = await getSession(event);\n const email = session?.email;\n if (!email) return EMPTY_CONTEXT;\n const sessionOrgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const sessionOrgRole = normalizeOrgRole(session.orgRole);\n\n const exec = getDbExec();\n\n let memberships = await loadMemberships(exec, email);\n if (memberships === null) {\n if (sessionOrgId) {\n return {\n email,\n orgId: sessionOrgId,\n orgName: null,\n role: sessionOrgRole,\n };\n }\n return { email, orgId: null, orgName: null, role: null };\n }\n\n if (memberships.length > 1) {\n const activeOrgSetting = (await getUserSetting(email, \"active-org-id\")) as {\n orgId: string;\n } | null;\n if (activeOrgSetting?.orgId) {\n const active = memberships.find(\n (m) => m.orgId === activeOrgSetting.orgId,\n );\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n }\n }\n\n const sessionMembership = sessionOrgId\n ? memberships.find((m) => m.orgId === sessionOrgId)\n : null;\n const shouldTryDomainAutoJoin =\n memberships.length === 0 ||\n (memberships.length === 1 &&\n isLikelyPersonalWorkspace(memberships[0], email, session));\n\n if (shouldTryDomainAutoJoin) {\n const joined = await autoJoinDomainMatchingOrgs(email, {\n activateJoinedOrg: \"always\",\n });\n if (joined.joined.length > 0) {\n const refreshed = await loadMemberships(exec, email);\n if (refreshed !== null) memberships = refreshed;\n }\n\n if (joined.activeOrgId) {\n const active = memberships.find((m) => m.orgId === joined.activeOrgId);\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n }\n }\n\n if (sessionOrgId) {\n const active =\n sessionMembership ?? memberships.find((m) => m.orgId === sessionOrgId);\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n return {\n email,\n orgId: sessionOrgId,\n orgName: null,\n role: sessionOrgRole,\n };\n }\n\n if (memberships.length === 0 && process.env.AUTO_CREATE_DEFAULT_ORG) {\n const created = await tryCreateDefaultOrg(exec, email, session);\n if (created) return created;\n // Creation failed (race / DB error); fall through and let the\n // RequireActiveOrg client guard prompt the user.\n }\n\n if (memberships.length === 0) {\n return { email, orgId: null, orgName: null, role: null };\n }\n\n return {\n email,\n orgId: memberships[0].orgId,\n orgName: memberships[0].orgName,\n role: memberships[0].role,\n };\n}\n\nasync function loadMemberships(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<Array<{\n orgId: string;\n role: OrgRole;\n orgName: string;\n}> | null> {\n try {\n const { rows } = await exec.execute({\n sql: `SELECT m.org_id AS \"orgId\", m.role AS role, o.name AS \"orgName\"\n FROM org_members m\n INNER JOIN organizations o ON m.org_id = o.id\n WHERE LOWER(m.email) = ?`,\n args: [email.toLowerCase()],\n });\n return rows.map((r: any) => ({\n orgId: String(r.orgId ?? r.org_id),\n role: String(r.role) as OrgRole,\n orgName: String(r.orgName ?? r.org_name),\n }));\n } catch {\n // Tables may not exist yet on first boot before migrations finish.\n return null;\n }\n}\n\n/**\n * Resolve the active org ID for a given email — for non-HTTP contexts like\n * the integration webhook handler where we have an email but no event/session.\n * Picks the user's active-org-id setting if set, otherwise the first membership.\n * Returns null if the user has no memberships.\n */\nexport async function resolveOrgIdForEmail(\n email: string,\n): Promise<string | null> {\n const exec = getDbExec();\n if (!exec) return null;\n try {\n const { rows } = await exec.execute({\n sql: `SELECT org_id FROM org_members WHERE LOWER(email) = ?`,\n args: [email.toLowerCase()],\n });\n if (rows.length === 0) return null;\n const ids = rows.map((r: any) => String(r.org_id));\n if (ids.length === 1) return ids[0];\n const activeOrgSetting = (await getUserSetting(email, \"active-org-id\")) as {\n orgId: string;\n } | null;\n if (activeOrgSetting?.orgId && ids.includes(activeOrgSetting.orgId)) {\n return activeOrgSetting.orgId;\n }\n return ids[0];\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new organization and add the caller as a member with the given\n * role. Generates a per-org A2A secret for cross-app delegation and writes\n * the caller's `active-org-id` user-setting so the new org is immediately\n * active.\n *\n */\nexport async function createOrganization(\n name: string,\n email: string,\n role: OrgRole = \"owner\",\n): Promise<{\n id: string;\n name: string;\n role: OrgRole;\n a2aSecret: string;\n createdAt: number;\n}> {\n const trimmedName = name.trim();\n const exec = getDbExec();\n const id = nanoid();\n const createdAt = Date.now();\n const { randomBytes } = await import(\"node:crypto\");\n const a2aSecret = randomBytes(32).toString(\"base64url\");\n\n await exec.execute({\n sql: `INSERT INTO organizations (id, name, created_by, created_at, a2a_secret) VALUES (?, ?, ?, ?, ?)`,\n args: [id, trimmedName, email, createdAt, a2aSecret],\n });\n\n await exec.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,\n args: [nanoid(), id, email, role, createdAt],\n });\n\n await putUserSetting(email, \"active-org-id\", { orgId: id });\n\n return { id, name: trimmedName, role, a2aSecret, createdAt };\n}\n\nfunction defaultOrgName(\n email: string,\n session: { name?: string } | null,\n): string {\n const full = session?.name?.trim();\n if (full) return `${full}'s workspace`;\n const local = email.split(\"@\")[0] ?? email;\n const cleaned = local.replace(/[._-]+/g, \" \").trim();\n const titled =\n cleaned\n .split(\" \")\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \") || \"My\";\n return `${titled}'s workspace`;\n}\n\n/**\n * Check whether the user has a pending invitation. If so, auto-create\n * MUST be skipped — otherwise we'd provision a personal org for them\n * before they ever see the inviter's org in the RequireActiveOrg\n * accept-invite pane, and they'd never join the team that invited them.\n */\nasync function hasPendingInvitation(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<boolean> {\n try {\n const { rows } = await exec.execute({\n sql: `SELECT 1 FROM org_invitations WHERE LOWER(email) = ? AND status = 'pending' LIMIT 1`,\n args: [email.toLowerCase()],\n });\n return rows.length > 0;\n } catch {\n // If we can't tell, err on the side of NOT auto-creating — the\n // RequireActiveOrg client guard will surface the situation.\n return true;\n }\n}\n\nasync function hasDomainMatch(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<boolean> {\n try {\n const domain = email.split(\"@\")[1]?.toLowerCase();\n if (!domain) return false;\n const { rows } = await exec.execute({\n sql: `SELECT 1 FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/** Stale-claim threshold. A claim row this old is treated as abandoned\n * (process crashed, DELETE failed, etc.) and a new caller may take it\n * over. Long enough that two genuine concurrent first-loads don't\n * trample each other (those settle in milliseconds), short enough that\n * a stuck user recovers on their next navigation. */\nconst CLAIM_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Attempt to provision a default org + owner membership for a user with\n * zero memberships.\n *\n * Race protection: claims the user's auto-create slot via an atomic\n * INSERT into the framework `settings` table (PRIMARY KEY (key) — so\n * concurrent inserts for the same key throw uniqueness violations on\n * both SQLite and Postgres). Only the request that wins the claim\n * proceeds to create the org; losers bail. By the time a losing\n * request retries on a subsequent navigation, the winner's org is in\n * `org_members` and the auto-create branch is skipped entirely.\n *\n * Stuck-state recovery: a stale claim (held longer than CLAIM_TTL_MS)\n * is reclaimed automatically. So even if the DELETE on the failure\n * path fails (network blip, DB error), the user isn't stranded — the\n * next request after the TTL elapses retries cleanly.\n *\n * Returns null on any failure so the caller can fall back to the\n * empty-context / client-guard path.\n */\nasync function tryCreateDefaultOrg(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n session: { name?: string } | null,\n): Promise<OrgContext | null> {\n // Make sure the framework `settings` table exists before we use it as\n // a claim primitive. getSetting() ensures the table on first call.\n await getSetting(\"__init\").catch(() => null);\n\n const claimKey = `u:${email.toLowerCase()}:auto-create-claim`;\n\n if (!(await acquireClaim(exec, claimKey))) return null;\n\n // Pending-invite check happens INSIDE the claim so the window where a\n // newly-arrived invitation can be missed is narrowed to a single SQL\n // round-trip. (A still-narrower window would require a transaction\n // spanning org_invitations and settings — out of scope.)\n if (await hasPendingInvitation(exec, email)) {\n await releaseClaim(exec, claimKey);\n return null;\n }\n\n if (await hasDomainMatch(exec, email)) {\n await releaseClaim(exec, claimKey);\n return null;\n }\n\n try {\n const orgId = nanoid();\n const orgName = defaultOrgName(email, session);\n const now = Date.now();\n\n await exec.execute({\n sql: `INSERT INTO organizations (id, name, created_by, created_at) VALUES (?, ?, ?, ?)`,\n args: [orgId, orgName, email, now],\n });\n await exec.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,\n args: [nanoid(), orgId, email, \"owner\", now],\n });\n\n await putUserSetting(email, \"active-org-id\", { orgId });\n\n return { email, orgId, orgName, role: \"owner\" };\n } catch {\n await releaseClaim(exec, claimKey);\n return null;\n }\n}\n\nasync function acquireClaim(\n exec: ReturnType<typeof getDbExec>,\n claimKey: string,\n): Promise<boolean> {\n const now = Date.now();\n try {\n await exec.execute({\n sql: `INSERT INTO settings (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [claimKey, JSON.stringify({ at: now }), now],\n });\n return true;\n } catch {\n // Conflict — someone else's claim is already in the row. If it's\n // stale (older than CLAIM_TTL_MS) we take it over.\n //\n // CRITICAL: this MUST be a single atomic UPDATE guarded on\n // `updated_at <= staleThreshold`. A read-then-DELETE-then-INSERT\n // sequence lets two concurrent reclaimers each observe the stale\n // timestamp, delete each other's fresh claim, and both think they\n // won — duplicating org creation. The conditional UPDATE matches\n // each stale row at most once: only the first writer sees\n // rowsAffected === 1; the row's updated_at is now `now`, so any\n // subsequent UPDATE no longer satisfies `updated_at <= staleThreshold`\n // and matches zero rows.\n const staleThreshold = now - CLAIM_TTL_MS;\n const result = (await exec.execute({\n sql: `UPDATE settings SET value = ?, updated_at = ? WHERE key = ? AND updated_at <= ?`,\n args: [JSON.stringify({ at: now }), now, claimKey, staleThreshold],\n })) as { rowsAffected?: number };\n return (result.rowsAffected ?? 0) > 0;\n }\n}\n\nasync function releaseClaim(\n exec: ReturnType<typeof getDbExec>,\n claimKey: string,\n): Promise<void> {\n // Best-effort. If this fails (transient network/DB error), the\n // CLAIM_TTL_MS-based takeover in acquireClaim recovers automatically\n // on a future request — no permanent stuck state.\n await exec\n .execute({ sql: `DELETE FROM settings WHERE key = ?`, args: [claimKey] })\n .catch(() => {});\n}\n\n/**\n * Look up the `allowed_domain` for an org by its ID.\n * Used when making outbound A2A calls so the JWT includes the\n * caller's org domain for cross-app org resolution.\n */\nexport async function getOrgDomain(orgId: string): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT allowed_domain FROM organizations WHERE id = ? LIMIT 1`,\n args: [orgId],\n });\n if (!rows[0]) return null;\n const domain = String((rows[0] as any).allowed_domain || \"\");\n return domain || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Look up the org's A2A secret by org ID.\n * Used when making outbound A2A calls so the JWT is signed with the\n * org-specific secret rather than the global A2A_SECRET env var.\n */\nexport async function getOrgA2ASecret(orgId: string): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT a2a_secret FROM organizations WHERE id = ? LIMIT 1`,\n args: [orgId],\n });\n if (!rows[0]) return null;\n const secret = String((rows[0] as any).a2a_secret || \"\");\n return secret || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Look up an org's A2A secret by its `allowed_domain`.\n * Used on the A2A receiving side: the caller's JWT includes `org_domain`,\n * and the receiver looks up which local org matches that domain to find\n * the secret used to verify the JWT signature.\n */\nexport async function getA2ASecretByDomain(\n domain: string,\n): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT a2a_secret FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain.toLowerCase()],\n });\n if (!rows[0]) return null;\n const secret = String((rows[0] as any).a2a_secret || \"\");\n return secret || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve a local org by its `allowed_domain`.\n * Used on the A2A receiving side: the caller sends `org_domain` in the JWT,\n * and the receiver looks up which local org matches that domain.\n */\nexport async function resolveOrgByDomain(\n domain: string,\n): Promise<{ orgId: string; orgName: string } | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT id, name FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain.toLowerCase()],\n });\n if (!rows[0]) return null;\n return {\n orgId: String((rows[0] as any).id),\n orgName: String((rows[0] as any).name),\n };\n } catch {\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/org/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { H3Event } from \"h3\";\nimport { getSession } from \"../server/auth.js\";\nimport { getUserSetting, putUserSetting } from \"../settings/user-settings.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport { getSetting } from \"../settings/store.js\";\nimport { autoJoinDomainMatchingOrgs } from \"./auto-join-domain.js\";\nimport type { OrgContext, OrgRole } from \"./types.js\";\n\nconst EMPTY_CONTEXT: OrgContext = {\n email: \"\",\n orgId: null,\n orgName: null,\n role: null,\n};\n\nfunction normalizeOrgRole(value: unknown): OrgRole | null {\n return value === \"owner\" || value === \"admin\" || value === \"member\"\n ? value\n : null;\n}\n\nfunction isLikelyPersonalWorkspace(\n membership: { orgName: string },\n email: string,\n session: { name?: string } | null,\n): boolean {\n return membership.orgName.trim() === defaultOrgName(email, session);\n}\n\nfunction autoCreateDefaultOrgEnabled(): boolean {\n const raw = process.env.AUTO_CREATE_DEFAULT_ORG;\n if (raw === undefined || raw.trim() === \"\") return false;\n return ![\"0\", \"false\", \"no\", \"off\"].includes(raw.trim().toLowerCase());\n}\n\nconst nanoid = (): string =>\n globalThis.crypto?.randomUUID?.().replace(/-/g, \"\") ??\n Math.random().toString(36).slice(2) + Date.now().toString(36);\n\n/**\n * Resolve the current user's organization context from their session.\n *\n * - For users in multiple orgs, honors their `active-org-id` user setting.\n * - Falls back to the user's first membership.\n * - When `AUTO_CREATE_DEFAULT_ORG` is set and the authenticated user has zero\n * memberships, provisions a default org named after the user ({name}'s\n * workspace, falling back to the email local-part).\n *\n * Per-request memoized on `event.context` — mirrors the `getSession`\n * pattern so multiple callers in the same request (e.g. ssr-handler +\n * a loader) share a single org_members round trip.\n */\nexport async function getOrgContext(event: H3Event): Promise<OrgContext> {\n // Per-request memoization. Multiple call sites per request (action wrappers,\n // SSR handler, loaders) must not each pay a separate org_members query.\n const ctx = event.context as {\n __anOrgContextCache?: Promise<OrgContext>;\n };\n return (ctx.__anOrgContextCache ??= resolveOrgContextUncached(event));\n}\n\nasync function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {\n const session = await getSession(event);\n const email = session?.email;\n if (!email) return EMPTY_CONTEXT;\n const sessionOrgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const sessionOrgRole = normalizeOrgRole(session.orgRole);\n\n const exec = getDbExec();\n\n let memberships = await loadMemberships(exec, email);\n if (memberships === null) {\n if (sessionOrgId) {\n return {\n email,\n orgId: sessionOrgId,\n orgName: null,\n role: sessionOrgRole,\n };\n }\n return { email, orgId: null, orgName: null, role: null };\n }\n\n if (memberships.length > 1) {\n const activeOrgSetting = (await getUserSetting(email, \"active-org-id\")) as {\n orgId: string;\n } | null;\n if (activeOrgSetting?.orgId) {\n const active = memberships.find(\n (m) => m.orgId === activeOrgSetting.orgId,\n );\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n }\n }\n\n const sessionMembership = sessionOrgId\n ? memberships.find((m) => m.orgId === sessionOrgId)\n : null;\n const shouldTryDomainAutoJoin =\n memberships.length === 0 ||\n (memberships.length === 1 &&\n isLikelyPersonalWorkspace(memberships[0], email, session));\n\n if (shouldTryDomainAutoJoin) {\n const joined = await autoJoinDomainMatchingOrgs(email, {\n activateJoinedOrg: \"always\",\n });\n if (joined.joined.length > 0) {\n const refreshed = await loadMemberships(exec, email);\n if (refreshed !== null) memberships = refreshed;\n }\n\n if (joined.activeOrgId) {\n const active = memberships.find((m) => m.orgId === joined.activeOrgId);\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n }\n }\n\n if (sessionOrgId) {\n const active =\n sessionMembership ?? memberships.find((m) => m.orgId === sessionOrgId);\n if (active) {\n return {\n email,\n orgId: active.orgId,\n orgName: active.orgName,\n role: active.role,\n };\n }\n return {\n email,\n orgId: sessionOrgId,\n orgName: null,\n role: sessionOrgRole,\n };\n }\n\n if (memberships.length === 0 && autoCreateDefaultOrgEnabled()) {\n const created = await tryCreateDefaultOrg(exec, email, session);\n if (created) return created;\n // Creation failed (race / DB error); fall through with an empty org context\n // so non-blocking invite/domain UI can still surface recovery options.\n }\n\n if (memberships.length === 0) {\n return { email, orgId: null, orgName: null, role: null };\n }\n\n return {\n email,\n orgId: memberships[0].orgId,\n orgName: memberships[0].orgName,\n role: memberships[0].role,\n };\n}\n\nasync function loadMemberships(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<Array<{\n orgId: string;\n role: OrgRole;\n orgName: string;\n}> | null> {\n try {\n const { rows } = await exec.execute({\n sql: `SELECT m.org_id AS \"orgId\", m.role AS role, o.name AS \"orgName\"\n FROM org_members m\n INNER JOIN organizations o ON m.org_id = o.id\n WHERE LOWER(m.email) = ?`,\n args: [email.toLowerCase()],\n });\n return rows.map((r: any) => ({\n orgId: String(r.orgId ?? r.org_id),\n role: String(r.role) as OrgRole,\n orgName: String(r.orgName ?? r.org_name),\n }));\n } catch {\n // Tables may not exist yet on first boot before migrations finish.\n return null;\n }\n}\n\n/**\n * Resolve the active org ID for a given email — for non-HTTP contexts like\n * the integration webhook handler where we have an email but no event/session.\n * Picks the user's active-org-id setting if set, otherwise the first membership.\n * Returns null if the user has no memberships.\n */\nexport async function resolveOrgIdForEmail(\n email: string,\n): Promise<string | null> {\n const exec = getDbExec();\n if (!exec) return null;\n try {\n const { rows } = await exec.execute({\n sql: `SELECT org_id FROM org_members WHERE LOWER(email) = ?`,\n args: [email.toLowerCase()],\n });\n if (rows.length === 0) return null;\n const ids = rows.map((r: any) => String(r.org_id));\n if (ids.length === 1) return ids[0];\n const activeOrgSetting = (await getUserSetting(email, \"active-org-id\")) as {\n orgId: string;\n } | null;\n if (activeOrgSetting?.orgId && ids.includes(activeOrgSetting.orgId)) {\n return activeOrgSetting.orgId;\n }\n return ids[0];\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new organization and add the caller as a member with the given\n * role. Generates a per-org A2A secret for cross-app delegation and writes\n * the caller's `active-org-id` user-setting so the new org is immediately\n * active.\n *\n */\nexport async function createOrganization(\n name: string,\n email: string,\n role: OrgRole = \"owner\",\n): Promise<{\n id: string;\n name: string;\n role: OrgRole;\n a2aSecret: string;\n createdAt: number;\n}> {\n const trimmedName = name.trim();\n const exec = getDbExec();\n const id = nanoid();\n const createdAt = Date.now();\n const { randomBytes } = await import(\"node:crypto\");\n const a2aSecret = randomBytes(32).toString(\"base64url\");\n\n await exec.execute({\n sql: `INSERT INTO organizations (id, name, created_by, created_at, a2a_secret) VALUES (?, ?, ?, ?, ?)`,\n args: [id, trimmedName, email, createdAt, a2aSecret],\n });\n\n await exec.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,\n args: [nanoid(), id, email, role, createdAt],\n });\n\n await putUserSetting(email, \"active-org-id\", { orgId: id });\n\n return { id, name: trimmedName, role, a2aSecret, createdAt };\n}\n\nfunction defaultOrgName(\n email: string,\n session: { name?: string } | null,\n): string {\n const full = session?.name?.trim();\n if (full) return `${full}'s workspace`;\n const local = email.split(\"@\")[0] ?? email;\n const cleaned = local.replace(/[._-]+/g, \" \").trim();\n const titled =\n cleaned\n .split(\" \")\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \") || \"My\";\n return `${titled}'s workspace`;\n}\n\n/**\n * Check whether the user has a pending invitation. If so, auto-create\n * MUST be skipped — otherwise we'd provision a personal org for them\n * before they ever see the inviter's org in the invitation banner, and they'd\n * never join the team that invited them.\n */\nasync function hasPendingInvitation(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<boolean> {\n try {\n const { rows } = await exec.execute({\n sql: `SELECT 1 FROM org_invitations WHERE LOWER(email) = ? AND status = 'pending' LIMIT 1`,\n args: [email.toLowerCase()],\n });\n return rows.length > 0;\n } catch {\n // If we can't tell, err on the side of NOT auto-creating — the\n // invitation banner or team UI can surface the situation.\n return true;\n }\n}\n\nasync function hasDomainMatch(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n): Promise<boolean> {\n try {\n const domain = email.split(\"@\")[1]?.toLowerCase();\n if (!domain) return false;\n const { rows } = await exec.execute({\n sql: `SELECT 1 FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain],\n });\n return rows.length > 0;\n } catch {\n return false;\n }\n}\n\n/** Stale-claim threshold. A claim row this old is treated as abandoned\n * (process crashed, DELETE failed, etc.) and a new caller may take it\n * over. Long enough that two genuine concurrent first-loads don't\n * trample each other (those settle in milliseconds), short enough that\n * a stuck user recovers on their next navigation. */\nconst CLAIM_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Attempt to provision a default org + owner membership for a user with\n * zero memberships.\n *\n * Race protection: claims the user's auto-create slot via an atomic\n * INSERT into the framework `settings` table (PRIMARY KEY (key) — so\n * concurrent inserts for the same key throw uniqueness violations on\n * both SQLite and Postgres). Only the request that wins the claim\n * proceeds to create the org; losers bail. By the time a losing\n * request retries on a subsequent navigation, the winner's org is in\n * `org_members` and the auto-create branch is skipped entirely.\n *\n * Stuck-state recovery: a stale claim (held longer than CLAIM_TTL_MS)\n * is reclaimed automatically. So even if the DELETE on the failure\n * path fails (network blip, DB error), the user isn't stranded — the\n * next request after the TTL elapses retries cleanly.\n *\n * Returns null on any failure so the caller can fall back to the\n * empty-context / client-guard path.\n */\nasync function tryCreateDefaultOrg(\n exec: ReturnType<typeof getDbExec>,\n email: string,\n session: { name?: string } | null,\n): Promise<OrgContext | null> {\n // Make sure the framework `settings` table exists before we use it as\n // a claim primitive. getSetting() ensures the table on first call.\n await getSetting(\"__init\").catch(() => null);\n\n const claimKey = `u:${email.toLowerCase()}:auto-create-claim`;\n\n if (!(await acquireClaim(exec, claimKey))) return null;\n\n // Pending-invite check happens INSIDE the claim so the window where a\n // newly-arrived invitation can be missed is narrowed to a single SQL\n // round-trip. (A still-narrower window would require a transaction\n // spanning org_invitations and settings — out of scope.)\n if (await hasPendingInvitation(exec, email)) {\n await releaseClaim(exec, claimKey);\n return null;\n }\n\n if (await hasDomainMatch(exec, email)) {\n await releaseClaim(exec, claimKey);\n return null;\n }\n\n try {\n const orgId = nanoid();\n const orgName = defaultOrgName(email, session);\n const now = Date.now();\n\n await exec.execute({\n sql: `INSERT INTO organizations (id, name, created_by, created_at) VALUES (?, ?, ?, ?)`,\n args: [orgId, orgName, email, now],\n });\n await exec.execute({\n sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)`,\n args: [nanoid(), orgId, email, \"owner\", now],\n });\n\n await putUserSetting(email, \"active-org-id\", { orgId });\n\n return { email, orgId, orgName, role: \"owner\" };\n } catch {\n await releaseClaim(exec, claimKey);\n return null;\n }\n}\n\nasync function acquireClaim(\n exec: ReturnType<typeof getDbExec>,\n claimKey: string,\n): Promise<boolean> {\n const now = Date.now();\n try {\n await exec.execute({\n sql: `INSERT INTO settings (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [claimKey, JSON.stringify({ at: now }), now],\n });\n return true;\n } catch {\n // Conflict — someone else's claim is already in the row. If it's\n // stale (older than CLAIM_TTL_MS) we take it over.\n //\n // CRITICAL: this MUST be a single atomic UPDATE guarded on\n // `updated_at <= staleThreshold`. A read-then-DELETE-then-INSERT\n // sequence lets two concurrent reclaimers each observe the stale\n // timestamp, delete each other's fresh claim, and both think they\n // won — duplicating org creation. The conditional UPDATE matches\n // each stale row at most once: only the first writer sees\n // rowsAffected === 1; the row's updated_at is now `now`, so any\n // subsequent UPDATE no longer satisfies `updated_at <= staleThreshold`\n // and matches zero rows.\n const staleThreshold = now - CLAIM_TTL_MS;\n const result = (await exec.execute({\n sql: `UPDATE settings SET value = ?, updated_at = ? WHERE key = ? AND updated_at <= ?`,\n args: [JSON.stringify({ at: now }), now, claimKey, staleThreshold],\n })) as { rowsAffected?: number };\n return (result.rowsAffected ?? 0) > 0;\n }\n}\n\nasync function releaseClaim(\n exec: ReturnType<typeof getDbExec>,\n claimKey: string,\n): Promise<void> {\n // Best-effort. If this fails (transient network/DB error), the\n // CLAIM_TTL_MS-based takeover in acquireClaim recovers automatically\n // on a future request — no permanent stuck state.\n await exec\n .execute({ sql: `DELETE FROM settings WHERE key = ?`, args: [claimKey] })\n .catch(() => {});\n}\n\n/**\n * Look up the `allowed_domain` for an org by its ID.\n * Used when making outbound A2A calls so the JWT includes the\n * caller's org domain for cross-app org resolution.\n */\nexport async function getOrgDomain(orgId: string): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT allowed_domain FROM organizations WHERE id = ? LIMIT 1`,\n args: [orgId],\n });\n if (!rows[0]) return null;\n const domain = String((rows[0] as any).allowed_domain || \"\");\n return domain || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Look up the org's A2A secret by org ID.\n * Used when making outbound A2A calls so the JWT is signed with the\n * org-specific secret rather than the global A2A_SECRET env var.\n */\nexport async function getOrgA2ASecret(orgId: string): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT a2a_secret FROM organizations WHERE id = ? LIMIT 1`,\n args: [orgId],\n });\n if (!rows[0]) return null;\n const secret = String((rows[0] as any).a2a_secret || \"\");\n return secret || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Look up an org's A2A secret by its `allowed_domain`.\n * Used on the A2A receiving side: the caller's JWT includes `org_domain`,\n * and the receiver looks up which local org matches that domain to find\n * the secret used to verify the JWT signature.\n */\nexport async function getA2ASecretByDomain(\n domain: string,\n): Promise<string | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT a2a_secret FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain.toLowerCase()],\n });\n if (!rows[0]) return null;\n const secret = String((rows[0] as any).a2a_secret || \"\");\n return secret || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve a local org by its `allowed_domain`.\n * Used on the A2A receiving side: the caller sends `org_domain` in the JWT,\n * and the receiver looks up which local org matches that domain.\n */\nexport async function resolveOrgByDomain(\n domain: string,\n): Promise<{ orgId: string; orgName: string } | null> {\n try {\n const exec = getDbExec();\n const { rows } = await exec.execute({\n sql: `SELECT id, name FROM organizations WHERE LOWER(allowed_domain) = ? LIMIT 1`,\n args: [domain.toLowerCase()],\n });\n if (!rows[0]) return null;\n return {\n orgId: String((rows[0] as any).id),\n orgName: String((rows[0] as any).name),\n };\n } catch {\n return null;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"app-url.d.ts","sourceRoot":"","sources":["../../src/server/app-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC;AAkEjD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAKzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAsD3D"}
1
+ {"version":3,"file":"app-url.d.ts","sourceRoot":"","sources":["../../src/server/app-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,IAAI,CAAC;AA0FjD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAKzD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAsD3D"}
@@ -63,6 +63,19 @@ function firstConfiguredUrl(keys) {
63
63
  }
64
64
  return undefined;
65
65
  }
66
+ function firstConfiguredPublicUrl(keys) {
67
+ const allowLoopback = !isHostedRuntime();
68
+ for (const key of keys) {
69
+ const value = process.env[key];
70
+ if (!value)
71
+ continue;
72
+ const url = stripTrailingSlash(value);
73
+ if (!allowLoopback && isLoopbackUrl(url))
74
+ continue;
75
+ return url;
76
+ }
77
+ return undefined;
78
+ }
66
79
  function isLoopbackUrl(value) {
67
80
  if (!value)
68
81
  return false;
@@ -74,6 +87,15 @@ function isLoopbackUrl(value) {
74
87
  return false;
75
88
  }
76
89
  }
90
+ function isHostedRuntime() {
91
+ return Boolean(process.env.NODE_ENV === "production" ||
92
+ process.env.NETLIFY ||
93
+ process.env.URL ||
94
+ process.env.DEPLOY_URL ||
95
+ process.env.VERCEL ||
96
+ process.env.VERCEL_URL ||
97
+ process.env.VERCEL_PROJECT_PRODUCTION_URL);
98
+ }
77
99
  function workspaceGatewayUrl(options) {
78
100
  const url = firstConfiguredUrl([
79
101
  "WORKSPACE_GATEWAY_URL",
@@ -98,7 +120,7 @@ export function getFirstPartyProdUrl() {
98
120
  return t?.prodUrl;
99
121
  }
100
122
  export function getAppProductionUrl(event) {
101
- const envUrl = firstConfiguredUrl([
123
+ const envUrl = firstConfiguredPublicUrl([
102
124
  "APP_URL",
103
125
  "WORKSPACE_OAUTH_ORIGIN",
104
126
  "VITE_WORKSPACE_OAUTH_ORIGIN",
@@ -1 +1 @@
1
- {"version":3,"file":"app-url.js","sourceRoot":"","sources":["../../src/server/app-url.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resolve the canonical URL of this app — used in transactional emails,\n * invite links, and anywhere we need an absolute URL that remains valid\n * outside the current request context.\n *\n * Resolution order:\n * 1. Explicit public URL env vars (`APP_URL`, workspace OAuth origin,\n * `BETTER_AUTH_URL`) — operator overrides\n * 2. Incoming request's origin (when an H3Event is available)\n * 3. First-party template `prodUrl` from the registry (matched by\n * package.json name) — lets deployed first-party apps (mail,\n * calendar, analytics, …) use e.g. `analytics.agent-native.com`\n * instead of their Netlify preview hostname.\n * 4. Platform-injected URL (Netlify `URL`, Vercel `VERCEL_URL`) —\n * automatically set by the hosting platform, so user-deployed apps\n * get a real hostname in emails without needing to set `APP_URL`.\n * 5. Public `WORKSPACE_GATEWAY_URL` — multi-app workspace gateway\n * 6. Local `WORKSPACE_GATEWAY_URL` — local multi-app workspace gateway\n * 7. `http://localhost:3000`\n *\n * Older versions preferred `WORKSPACE_GATEWAY_URL` before platform URLs.\n * That is fine for local development, but in hosted Builder Desktop sessions\n * the gateway can be `127.0.0.1`, which must not become Better Auth's\n * production base URL.\n */\nimport { getRequestURL, type H3Event } from \"h3\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport { TEMPLATES } from \"../cli/templates-meta.js\";\nimport { isLocalDatabase } from \"../db/client.js\";\n\nlet cachedPkgName: string | undefined | null = null;\n\n/**\n * Read the app's package name, validated against the first-party template\n * registry. On serverless runtimes (Netlify Functions, Cloudflare Workers),\n * `process.cwd()` may point at a bundler-generated package.json with a\n * bogus name (e.g. Nitro's \"traced-node-modules\"). Only trust the name if\n * it matches a known template.\n */\nfunction readPackageName(): string | undefined {\n if (cachedPkgName !== null) return cachedPkgName ?? undefined;\n try {\n const pkgPath = path.join(process.cwd(), \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const name = typeof pkg?.name === \"string\" ? pkg.name : undefined;\n const isKnown = name && TEMPLATES.some((t) => t.name === name);\n cachedPkgName = isKnown ? name : undefined;\n } catch {\n cachedPkgName = undefined;\n }\n return cachedPkgName ?? undefined;\n}\n\n/** Strip trailing slashes for consistent URL concatenation. */\nfunction stripTrailingSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction firstConfiguredUrl(keys: readonly string[]): string | undefined {\n for (const key of keys) {\n const value = process.env[key];\n if (value) return stripTrailingSlash(value);\n }\n return undefined;\n}\n\nfunction isLoopbackUrl(value: string | undefined): boolean {\n if (!value) return false;\n try {\n const hostname = new URL(value).hostname.toLowerCase();\n return (\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"::1\"\n );\n } catch {\n return false;\n }\n}\n\nfunction workspaceGatewayUrl(options: {\n allowLoopback: boolean;\n}): string | undefined {\n const url = firstConfiguredUrl([\n \"WORKSPACE_GATEWAY_URL\",\n \"VITE_WORKSPACE_GATEWAY_URL\",\n ]);\n if (!url) return undefined;\n if (!options.allowLoopback && isLoopbackUrl(url)) return undefined;\n return url;\n}\n\n/**\n * Look up the first-party template `prodUrl` for the current app based on\n * its `package.json` name. Returns undefined if the app isn't a known\n * first-party template or the template has no `prodUrl`.\n */\nexport function getFirstPartyProdUrl(): string | undefined {\n const name = readPackageName();\n if (!name) return undefined;\n const t = TEMPLATES.find((t) => t.name === name);\n return t?.prodUrl;\n}\n\nexport function getAppProductionUrl(event?: H3Event): string {\n const envUrl = firstConfiguredUrl([\n \"APP_URL\",\n \"WORKSPACE_OAUTH_ORIGIN\",\n \"VITE_WORKSPACE_OAUTH_ORIGIN\",\n \"BETTER_AUTH_URL\",\n \"VITE_BETTER_AUTH_URL\",\n ]);\n if (envUrl) return envUrl;\n\n // Prefer the incoming request's origin when we have one — for local dev\n // this is `http://localhost:3000`, which keeps Better Auth from setting\n // `Secure` cookies on plain-HTTP dev servers.\n if (event) {\n try {\n const url = getRequestURL(event);\n return `${url.protocol}//${url.host}`;\n } catch {\n // fall through\n }\n }\n\n // Fall back to a first-party template's hard-coded prod URL when we're\n // running in production OR on a remote database (Neon/Postgres/Turso).\n // A remote DB means we're deployed even if NODE_ENV isn't explicitly\n // \"production\" (e.g. Netlify Functions). In local dev with SQLite, skip\n // this — the hard-coded URL breaks auth via Secure cookies on HTTP.\n if (process.env.NODE_ENV === \"production\" || !isLocalDatabase()) {\n const firstParty = getFirstPartyProdUrl();\n if (firstParty) return stripTrailingSlash(firstParty);\n\n // Netlify injects `URL` (main site URL, always https) and `DEPLOY_URL`\n // (deploy-specific URL). Prefer `URL` so emails always link to the\n // primary domain rather than a preview branch URL.\n const netlifyUrl = process.env.URL || process.env.DEPLOY_URL;\n if (netlifyUrl) return stripTrailingSlash(netlifyUrl);\n\n // Vercel injects `VERCEL_PROJECT_PRODUCTION_URL` (custom/primary domain,\n // no protocol) and `VERCEL_URL` (ephemeral deployment hostname). Prefer\n // the production URL so emails use the real domain, not *.vercel.app.\n const vercelUrl =\n process.env.VERCEL_PROJECT_PRODUCTION_URL || process.env.VERCEL_URL;\n if (vercelUrl) return `https://${stripTrailingSlash(vercelUrl)}`;\n\n const publicWorkspaceGateway = workspaceGatewayUrl({\n allowLoopback: false,\n });\n if (publicWorkspaceGateway) return publicWorkspaceGateway;\n }\n\n const localWorkspaceGateway = workspaceGatewayUrl({ allowLoopback: true });\n if (localWorkspaceGateway) return localWorkspaceGateway;\n\n return \"http://localhost:3000\";\n}\n"]}
1
+ {"version":3,"file":"app-url.js","sourceRoot":"","sources":["../../src/server/app-url.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Resolve the canonical URL of this app — used in transactional emails,\n * invite links, and anywhere we need an absolute URL that remains valid\n * outside the current request context.\n *\n * Resolution order:\n * 1. Explicit public URL env vars (`APP_URL`, workspace OAuth origin,\n * `BETTER_AUTH_URL`) — operator overrides\n * 2. Incoming request's origin (when an H3Event is available)\n * 3. First-party template `prodUrl` from the registry (matched by\n * package.json name) — lets deployed first-party apps (mail,\n * calendar, analytics, …) use e.g. `analytics.agent-native.com`\n * instead of their Netlify preview hostname.\n * 4. Platform-injected URL (Netlify `URL`, Vercel `VERCEL_URL`) —\n * automatically set by the hosting platform, so user-deployed apps\n * get a real hostname in emails without needing to set `APP_URL`.\n * 5. Public `WORKSPACE_GATEWAY_URL` — multi-app workspace gateway\n * 6. Local `WORKSPACE_GATEWAY_URL` — local multi-app workspace gateway\n * 7. `http://localhost:3000`\n *\n * Older versions preferred `WORKSPACE_GATEWAY_URL` before platform URLs.\n * That is fine for local development, but in hosted Builder Desktop sessions\n * the gateway can be `127.0.0.1`, which must not become Better Auth's\n * production base URL.\n */\nimport { getRequestURL, type H3Event } from \"h3\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport { TEMPLATES } from \"../cli/templates-meta.js\";\nimport { isLocalDatabase } from \"../db/client.js\";\n\nlet cachedPkgName: string | undefined | null = null;\n\n/**\n * Read the app's package name, validated against the first-party template\n * registry. On serverless runtimes (Netlify Functions, Cloudflare Workers),\n * `process.cwd()` may point at a bundler-generated package.json with a\n * bogus name (e.g. Nitro's \"traced-node-modules\"). Only trust the name if\n * it matches a known template.\n */\nfunction readPackageName(): string | undefined {\n if (cachedPkgName !== null) return cachedPkgName ?? undefined;\n try {\n const pkgPath = path.join(process.cwd(), \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const name = typeof pkg?.name === \"string\" ? pkg.name : undefined;\n const isKnown = name && TEMPLATES.some((t) => t.name === name);\n cachedPkgName = isKnown ? name : undefined;\n } catch {\n cachedPkgName = undefined;\n }\n return cachedPkgName ?? undefined;\n}\n\n/** Strip trailing slashes for consistent URL concatenation. */\nfunction stripTrailingSlash(u: string): string {\n return u.replace(/\\/+$/, \"\");\n}\n\nfunction firstConfiguredUrl(keys: readonly string[]): string | undefined {\n for (const key of keys) {\n const value = process.env[key];\n if (value) return stripTrailingSlash(value);\n }\n return undefined;\n}\n\nfunction firstConfiguredPublicUrl(keys: readonly string[]): string | undefined {\n const allowLoopback = !isHostedRuntime();\n for (const key of keys) {\n const value = process.env[key];\n if (!value) continue;\n const url = stripTrailingSlash(value);\n if (!allowLoopback && isLoopbackUrl(url)) continue;\n return url;\n }\n return undefined;\n}\n\nfunction isLoopbackUrl(value: string | undefined): boolean {\n if (!value) return false;\n try {\n const hostname = new URL(value).hostname.toLowerCase();\n return (\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"::1\"\n );\n } catch {\n return false;\n }\n}\n\nfunction isHostedRuntime(): boolean {\n return Boolean(\n process.env.NODE_ENV === \"production\" ||\n process.env.NETLIFY ||\n process.env.URL ||\n process.env.DEPLOY_URL ||\n process.env.VERCEL ||\n process.env.VERCEL_URL ||\n process.env.VERCEL_PROJECT_PRODUCTION_URL,\n );\n}\n\nfunction workspaceGatewayUrl(options: {\n allowLoopback: boolean;\n}): string | undefined {\n const url = firstConfiguredUrl([\n \"WORKSPACE_GATEWAY_URL\",\n \"VITE_WORKSPACE_GATEWAY_URL\",\n ]);\n if (!url) return undefined;\n if (!options.allowLoopback && isLoopbackUrl(url)) return undefined;\n return url;\n}\n\n/**\n * Look up the first-party template `prodUrl` for the current app based on\n * its `package.json` name. Returns undefined if the app isn't a known\n * first-party template or the template has no `prodUrl`.\n */\nexport function getFirstPartyProdUrl(): string | undefined {\n const name = readPackageName();\n if (!name) return undefined;\n const t = TEMPLATES.find((t) => t.name === name);\n return t?.prodUrl;\n}\n\nexport function getAppProductionUrl(event?: H3Event): string {\n const envUrl = firstConfiguredPublicUrl([\n \"APP_URL\",\n \"WORKSPACE_OAUTH_ORIGIN\",\n \"VITE_WORKSPACE_OAUTH_ORIGIN\",\n \"BETTER_AUTH_URL\",\n \"VITE_BETTER_AUTH_URL\",\n ]);\n if (envUrl) return envUrl;\n\n // Prefer the incoming request's origin when we have one — for local dev\n // this is `http://localhost:3000`, which keeps Better Auth from setting\n // `Secure` cookies on plain-HTTP dev servers.\n if (event) {\n try {\n const url = getRequestURL(event);\n return `${url.protocol}//${url.host}`;\n } catch {\n // fall through\n }\n }\n\n // Fall back to a first-party template's hard-coded prod URL when we're\n // running in production OR on a remote database (Neon/Postgres/Turso).\n // A remote DB means we're deployed even if NODE_ENV isn't explicitly\n // \"production\" (e.g. Netlify Functions). In local dev with SQLite, skip\n // this — the hard-coded URL breaks auth via Secure cookies on HTTP.\n if (process.env.NODE_ENV === \"production\" || !isLocalDatabase()) {\n const firstParty = getFirstPartyProdUrl();\n if (firstParty) return stripTrailingSlash(firstParty);\n\n // Netlify injects `URL` (main site URL, always https) and `DEPLOY_URL`\n // (deploy-specific URL). Prefer `URL` so emails always link to the\n // primary domain rather than a preview branch URL.\n const netlifyUrl = process.env.URL || process.env.DEPLOY_URL;\n if (netlifyUrl) return stripTrailingSlash(netlifyUrl);\n\n // Vercel injects `VERCEL_PROJECT_PRODUCTION_URL` (custom/primary domain,\n // no protocol) and `VERCEL_URL` (ephemeral deployment hostname). Prefer\n // the production URL so emails use the real domain, not *.vercel.app.\n const vercelUrl =\n process.env.VERCEL_PROJECT_PRODUCTION_URL || process.env.VERCEL_URL;\n if (vercelUrl) return `https://${stripTrailingSlash(vercelUrl)}`;\n\n const publicWorkspaceGateway = workspaceGatewayUrl({\n allowLoopback: false,\n });\n if (publicWorkspaceGateway) return publicWorkspaceGateway;\n }\n\n const localWorkspaceGateway = workspaceGatewayUrl({ allowLoopback: true });\n if (localWorkspaceGateway) return localWorkspaceGateway;\n\n return \"http://localhost:3000\";\n}\n"]}