@agent-native/core 0.7.19 → 0.7.20

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.
Files changed (258) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  3. package/dist/agent/engine/builder-engine.js +45 -2
  4. package/dist/agent/engine/builder-engine.js.map +1 -1
  5. package/dist/agent/loop-settings.d.ts +37 -0
  6. package/dist/agent/loop-settings.d.ts.map +1 -0
  7. package/dist/agent/loop-settings.js +127 -0
  8. package/dist/agent/loop-settings.js.map +1 -0
  9. package/dist/agent/production-agent.d.ts +8 -0
  10. package/dist/agent/production-agent.d.ts.map +1 -1
  11. package/dist/agent/production-agent.js +268 -29
  12. package/dist/agent/production-agent.js.map +1 -1
  13. package/dist/agent/run-manager.d.ts.map +1 -1
  14. package/dist/agent/run-manager.js +76 -3
  15. package/dist/agent/run-manager.js.map +1 -1
  16. package/dist/agent/run-store.d.ts +1 -1
  17. package/dist/agent/run-store.d.ts.map +1 -1
  18. package/dist/agent/run-store.js +65 -2
  19. package/dist/agent/run-store.js.map +1 -1
  20. package/dist/agent/thread-data-builder.d.ts +3 -0
  21. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  22. package/dist/agent/thread-data-builder.js +52 -10
  23. package/dist/agent/thread-data-builder.js.map +1 -1
  24. package/dist/agent/tool-search.d.ts +37 -0
  25. package/dist/agent/tool-search.d.ts.map +1 -0
  26. package/dist/agent/tool-search.js +201 -0
  27. package/dist/agent/tool-search.js.map +1 -0
  28. package/dist/agent/types.d.ts +8 -1
  29. package/dist/agent/types.d.ts.map +1 -1
  30. package/dist/agent/types.js.map +1 -1
  31. package/dist/cli/create.d.ts.map +1 -1
  32. package/dist/cli/create.js +44 -9
  33. package/dist/cli/create.js.map +1 -1
  34. package/dist/cli/workspacify.d.ts +2 -0
  35. package/dist/cli/workspacify.d.ts.map +1 -1
  36. package/dist/cli/workspacify.js +34 -1
  37. package/dist/cli/workspacify.js.map +1 -1
  38. package/dist/client/AssistantChat.d.ts.map +1 -1
  39. package/dist/client/AssistantChat.js +277 -18
  40. package/dist/client/AssistantChat.js.map +1 -1
  41. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  42. package/dist/client/ConnectBuilderCard.js +1 -1
  43. package/dist/client/ConnectBuilderCard.js.map +1 -1
  44. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  45. package/dist/client/MultiTabAssistantChat.js +14 -6
  46. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  47. package/dist/client/NewWorkspaceAppFlow.d.ts +14 -0
  48. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -0
  49. package/dist/client/NewWorkspaceAppFlow.js +200 -0
  50. package/dist/client/NewWorkspaceAppFlow.js.map +1 -0
  51. package/dist/client/PoweredByBadge.d.ts +10 -1
  52. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  53. package/dist/client/PoweredByBadge.js +120 -8
  54. package/dist/client/PoweredByBadge.js.map +1 -1
  55. package/dist/client/agent-chat-adapter.d.ts +3 -5
  56. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  57. package/dist/client/agent-chat-adapter.js +26 -19
  58. package/dist/client/agent-chat-adapter.js.map +1 -1
  59. package/dist/client/agent-chat.d.ts.map +1 -1
  60. package/dist/client/agent-chat.js +15 -3
  61. package/dist/client/agent-chat.js.map +1 -1
  62. package/dist/client/analytics.d.ts +1 -1
  63. package/dist/client/analytics.d.ts.map +1 -1
  64. package/dist/client/analytics.js +141 -1
  65. package/dist/client/analytics.js.map +1 -1
  66. package/dist/client/builder-frame.d.ts +10 -0
  67. package/dist/client/builder-frame.d.ts.map +1 -0
  68. package/dist/client/builder-frame.js +94 -0
  69. package/dist/client/builder-frame.js.map +1 -0
  70. package/dist/client/composer/MentionPopover.d.ts.map +1 -1
  71. package/dist/client/composer/MentionPopover.js +5 -1
  72. package/dist/client/composer/MentionPopover.js.map +1 -1
  73. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  74. package/dist/client/composer/TiptapComposer.js +11 -6
  75. package/dist/client/composer/TiptapComposer.js.map +1 -1
  76. package/dist/client/error-format.d.ts +20 -1
  77. package/dist/client/error-format.d.ts.map +1 -1
  78. package/dist/client/error-format.js +53 -5
  79. package/dist/client/error-format.js.map +1 -1
  80. package/dist/client/index.d.ts +3 -1
  81. package/dist/client/index.d.ts.map +1 -1
  82. package/dist/client/index.js +3 -1
  83. package/dist/client/index.js.map +1 -1
  84. package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
  85. package/dist/client/onboarding/OnboardingPanel.js +88 -6
  86. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  87. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  88. package/dist/client/settings/SettingsPanel.js +145 -9
  89. package/dist/client/settings/SettingsPanel.js.map +1 -1
  90. package/dist/client/settings/useBuilderStatus.d.ts +13 -0
  91. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  92. package/dist/client/settings/useBuilderStatus.js +50 -9
  93. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  94. package/dist/client/sse-event-processor.d.ts +3 -0
  95. package/dist/client/sse-event-processor.d.ts.map +1 -1
  96. package/dist/client/sse-event-processor.js +88 -7
  97. package/dist/client/sse-event-processor.js.map +1 -1
  98. package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
  99. package/dist/client/tools/ToolsListPage.js +16 -1
  100. package/dist/client/tools/ToolsListPage.js.map +1 -1
  101. package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
  102. package/dist/client/tools/ToolsSidebarSection.js +63 -8
  103. package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
  104. package/dist/client/tools/tool-order.d.ts +7 -0
  105. package/dist/client/tools/tool-order.d.ts.map +1 -0
  106. package/dist/client/tools/tool-order.js +47 -0
  107. package/dist/client/tools/tool-order.js.map +1 -0
  108. package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
  109. package/dist/client/transcription/BuilderTranscriptionCta.js +71 -6
  110. package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
  111. package/dist/client/use-send-to-agent-chat.d.ts.map +1 -1
  112. package/dist/client/use-send-to-agent-chat.js +11 -3
  113. package/dist/client/use-send-to-agent-chat.js.map +1 -1
  114. package/dist/client/useProductionAgent.d.ts.map +1 -1
  115. package/dist/client/useProductionAgent.js +1 -1
  116. package/dist/client/useProductionAgent.js.map +1 -1
  117. package/dist/db/client.d.ts.map +1 -1
  118. package/dist/db/client.js +5 -1
  119. package/dist/db/client.js.map +1 -1
  120. package/dist/deploy/build.d.ts +1 -0
  121. package/dist/deploy/build.d.ts.map +1 -1
  122. package/dist/deploy/build.js +4 -1
  123. package/dist/deploy/build.js.map +1 -1
  124. package/dist/oauth-tokens/index.d.ts +1 -1
  125. package/dist/oauth-tokens/index.d.ts.map +1 -1
  126. package/dist/oauth-tokens/index.js +1 -1
  127. package/dist/oauth-tokens/index.js.map +1 -1
  128. package/dist/oauth-tokens/store.d.ts.map +1 -1
  129. package/dist/oauth-tokens/store.js +6 -0
  130. package/dist/oauth-tokens/store.js.map +1 -1
  131. package/dist/observability/store.d.ts.map +1 -1
  132. package/dist/observability/store.js +19 -19
  133. package/dist/observability/store.js.map +1 -1
  134. package/dist/onboarding/default-steps.d.ts.map +1 -1
  135. package/dist/onboarding/default-steps.js +95 -61
  136. package/dist/onboarding/default-steps.js.map +1 -1
  137. package/dist/onboarding/plugin.d.ts.map +1 -1
  138. package/dist/onboarding/plugin.js +17 -8
  139. package/dist/onboarding/plugin.js.map +1 -1
  140. package/dist/org/migrations.js +2 -2
  141. package/dist/org/migrations.js.map +1 -1
  142. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  143. package/dist/scripts/agent-engines/list-agent-engines.js +2 -3
  144. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  145. package/dist/scripts/db/exec.d.ts +2 -1
  146. package/dist/scripts/db/exec.d.ts.map +1 -1
  147. package/dist/scripts/db/exec.js +264 -61
  148. package/dist/scripts/db/exec.js.map +1 -1
  149. package/dist/scripts/db/schema.d.ts.map +1 -1
  150. package/dist/scripts/db/schema.js +16 -4
  151. package/dist/scripts/db/schema.js.map +1 -1
  152. package/dist/scripts/dev/index.d.ts.map +1 -1
  153. package/dist/scripts/dev/index.js +36 -11
  154. package/dist/scripts/dev/index.js.map +1 -1
  155. package/dist/scripts/manage-agent-loop-settings.d.ts +7 -0
  156. package/dist/scripts/manage-agent-loop-settings.d.ts.map +1 -0
  157. package/dist/scripts/manage-agent-loop-settings.js +63 -0
  158. package/dist/scripts/manage-agent-loop-settings.js.map +1 -0
  159. package/dist/scripts/runner.d.ts.map +1 -1
  160. package/dist/scripts/runner.js +11 -0
  161. package/dist/scripts/runner.js.map +1 -1
  162. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  163. package/dist/server/agent-chat-plugin.js +60 -18
  164. package/dist/server/agent-chat-plugin.js.map +1 -1
  165. package/dist/server/app-url.d.ts +5 -4
  166. package/dist/server/app-url.d.ts.map +1 -1
  167. package/dist/server/app-url.js +8 -4
  168. package/dist/server/app-url.js.map +1 -1
  169. package/dist/server/auth.d.ts +8 -0
  170. package/dist/server/auth.d.ts.map +1 -1
  171. package/dist/server/auth.js +82 -29
  172. package/dist/server/auth.js.map +1 -1
  173. package/dist/server/better-auth-instance.d.ts.map +1 -1
  174. package/dist/server/better-auth-instance.js +16 -5
  175. package/dist/server/better-auth-instance.js.map +1 -1
  176. package/dist/server/builder-browser.d.ts +12 -0
  177. package/dist/server/builder-browser.d.ts.map +1 -1
  178. package/dist/server/builder-browser.js +36 -4
  179. package/dist/server/builder-browser.js.map +1 -1
  180. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  181. package/dist/server/core-routes-plugin.js +350 -53
  182. package/dist/server/core-routes-plugin.js.map +1 -1
  183. package/dist/server/credential-provider.d.ts +21 -3
  184. package/dist/server/credential-provider.d.ts.map +1 -1
  185. package/dist/server/credential-provider.js +51 -21
  186. package/dist/server/credential-provider.js.map +1 -1
  187. package/dist/server/google-oauth.d.ts +3 -0
  188. package/dist/server/google-oauth.d.ts.map +1 -1
  189. package/dist/server/google-oauth.js +27 -3
  190. package/dist/server/google-oauth.js.map +1 -1
  191. package/dist/server/index.d.ts +4 -3
  192. package/dist/server/index.d.ts.map +1 -1
  193. package/dist/server/index.js +4 -3
  194. package/dist/server/index.js.map +1 -1
  195. package/dist/server/schema-prompt.d.ts.map +1 -1
  196. package/dist/server/schema-prompt.js +2 -1
  197. package/dist/server/schema-prompt.js.map +1 -1
  198. package/dist/server/security-headers.d.ts +3 -0
  199. package/dist/server/security-headers.d.ts.map +1 -1
  200. package/dist/server/security-headers.js +7 -1
  201. package/dist/server/security-headers.js.map +1 -1
  202. package/dist/server/ssr-handler.d.ts.map +1 -1
  203. package/dist/server/ssr-handler.js +24 -4
  204. package/dist/server/ssr-handler.js.map +1 -1
  205. package/dist/templates/default/_gitignore +5 -1
  206. package/dist/templates/default/app/root.tsx +1 -0
  207. package/dist/templates/default/public/favicon.svg +3 -3
  208. package/dist/templates/default/public/icon-180.svg +3 -3
  209. package/dist/templates/default/public/icon-192.svg +3 -3
  210. package/dist/templates/default/public/icon-512.svg +3 -3
  211. package/dist/templates/workspace-core/AGENTS.md +23 -7
  212. package/dist/templates/workspace-core/package.json +2 -1
  213. package/dist/templates/workspace-core/src/credentials.ts +22 -11
  214. package/dist/templates/workspace-root/.env.example +7 -0
  215. package/dist/templates/workspace-root/README.md +6 -3
  216. package/dist/templates/workspace-root/_gitignore +3 -0
  217. package/dist/templates/workspace-root/package.json +3 -1
  218. package/dist/templates/workspace-root/scripts/workspace-dev.ts +410 -0
  219. package/dist/tools/actions.d.ts.map +1 -1
  220. package/dist/tools/actions.js +2 -0
  221. package/dist/tools/actions.js.map +1 -1
  222. package/dist/tools/html-shell.d.ts.map +1 -1
  223. package/dist/tools/html-shell.js +13 -1
  224. package/dist/tools/html-shell.js.map +1 -1
  225. package/dist/tools/store.d.ts.map +1 -1
  226. package/dist/tools/store.js +10 -10
  227. package/dist/tools/store.js.map +1 -1
  228. package/dist/tracking/providers.d.ts +1 -0
  229. package/dist/tracking/providers.d.ts.map +1 -1
  230. package/dist/tracking/providers.js +72 -0
  231. package/dist/tracking/providers.js.map +1 -1
  232. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  233. package/dist/vite/action-types-plugin.js +106 -9
  234. package/dist/vite/action-types-plugin.js.map +1 -1
  235. package/dist/vite/client.d.ts.map +1 -1
  236. package/dist/vite/client.js +67 -2
  237. package/dist/vite/client.js.map +1 -1
  238. package/docs/content/authentication.md +17 -13
  239. package/docs/content/deployment.md +11 -11
  240. package/docs/content/mcp-clients.md +2 -2
  241. package/docs/content/onboarding.md +32 -30
  242. package/docs/content/security.md +1 -1
  243. package/docs/content/tools.md +4 -0
  244. package/package.json +2 -2
  245. package/src/templates/default/_gitignore +5 -1
  246. package/src/templates/default/app/root.tsx +1 -0
  247. package/src/templates/default/public/favicon.svg +3 -3
  248. package/src/templates/default/public/icon-180.svg +3 -3
  249. package/src/templates/default/public/icon-192.svg +3 -3
  250. package/src/templates/default/public/icon-512.svg +3 -3
  251. package/src/templates/workspace-core/AGENTS.md +23 -7
  252. package/src/templates/workspace-core/package.json +2 -1
  253. package/src/templates/workspace-core/src/credentials.ts +22 -11
  254. package/src/templates/workspace-root/.env.example +7 -0
  255. package/src/templates/workspace-root/README.md +6 -3
  256. package/src/templates/workspace-root/_gitignore +3 -0
  257. package/src/templates/workspace-root/package.json +3 -1
  258. package/src/templates/workspace-root/scripts/workspace-dev.ts +410 -0
@@ -35,8 +35,18 @@ export declare class FeatureNotConfiguredError extends Error {
35
35
  export declare function readDeployCredentialEnv(key: string): string | undefined;
36
36
  export declare function resolveBuilderCredential(key: string): Promise<string | null>;
37
37
  /**
38
- * Resolve the current user's Builder private key.
39
- * Checks per-user app_secrets first, then falls back to process.env.
38
+ * True when `BUILDER_PRIVATE_KEY` is set at the deployment level every
39
+ * user of this deploy shares the operator's Builder identity, and per-user
40
+ * connect/disconnect is disabled. UIs read this via `/builder/status` to
41
+ * swap the "Connect Builder" prompts for a read-only "managed by deployment"
42
+ * chip and to suppress the disconnect button.
43
+ */
44
+ export declare function isBuilderEnvManaged(): boolean;
45
+ /**
46
+ * Resolve the Builder private key for the current request. In env-managed
47
+ * mode (deploy-level `BUILDER_PRIVATE_KEY` set) returns the env value for
48
+ * every caller. Otherwise reads the current user's per-user OAuth-stored
49
+ * key from `app_secrets`.
40
50
  */
41
51
  export declare function resolveBuilderPrivateKey(): Promise<string | null>;
42
52
  /**
@@ -80,7 +90,15 @@ export declare function deleteBuilderCredentials(email: string): Promise<void>;
80
90
  * only for unauthenticated/CLI/background contexts.
81
91
  */
82
92
  export declare function resolveSecret(key: string): Promise<string | null>;
83
- /** True when a Builder private key is configured at the deployment level. */
93
+ /**
94
+ * True when a Builder private key is configured at the deployment level.
95
+ *
96
+ * This is the same check as `isBuilderEnvManaged()` (env-managed mode is
97
+ * defined as "deploy-level BUILDER_PRIVATE_KEY is set"). Prefer
98
+ * `isBuilderEnvManaged()` for new call sites — its name reflects what the
99
+ * boolean means semantically. For "does this user have access to Builder
100
+ * (env or per-user)?" use the async `resolveHasBuilderPrivateKey()`.
101
+ */
84
102
  export declare function hasBuilderPrivateKey(): boolean;
85
103
  /** The origin for Builder-proxied API calls. Overridable for testing. */
86
104
  export declare function getBuilderProxyOrigin(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"credential-provider.d.ts","sourceRoot":"","sources":["../../src/server/credential-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,IAAI,EAAE;QAChB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;CAUF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAWD,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2BxB;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEvE;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGvE;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC,CASD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IACL,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3E;AAcD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsBvE;AAOD,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAO9C;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAKjD;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAGpD"}
1
+ {"version":3,"file":"credential-provider.d.ts","sourceRoot":"","sources":["../../src/server/credential-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,IAAI,EAAE;QAChB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;CAUF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAwBD,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEvE;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGvE;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC,CASD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IACL,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3E;AAcD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsBvE;AAOD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAO9C;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAKjD;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAGpD"}
@@ -40,14 +40,35 @@ export function readDeployCredentialEnv(key) {
40
40
  return process.env[key] || undefined;
41
41
  }
42
42
  // ---------------------------------------------------------------------------
43
- // Per-user Builder credential resolution
43
+ // Builder credential resolution — two mutually-exclusive deployment modes:
44
44
  //
45
- // Builder keys are stored per-user in `app_secrets` (scope=user,
46
- // scopeId=email). The OAuth callback writes them there; the status/disconnect
47
- // endpoints read/delete them. `process.env` is the deployment-level fallback
48
- // (e.g. a single BUILDER_PRIVATE_KEY set in Netlify).
45
+ // 1. **Single-tenant / env-managed.** When BUILDER_PRIVATE_KEY is set at
46
+ // the deployment level, it is THE Builder identity for every user of
47
+ // this deploy. The operator setting the env explicitly opts in to
48
+ // "everyone shares one Builder space" same shape as DATABASE_URL or
49
+ // BETTER_AUTH_SECRET. The UI hides the per-user connect/disconnect
50
+ // flow when env-managed (see `isBuilderEnvManaged`).
51
+ //
52
+ // 2. **Multi-tenant / per-user OAuth.** When the env is unset, each user
53
+ // OAuth-connects their own Builder via the cli-auth flow. Their keys
54
+ // land in `app_secrets` (scope=user, scopeId=email) via the callback
55
+ // handler. They can disconnect via the settings panel.
56
+ //
57
+ // To run multi-tenant SaaS: leave the env unset. Setting BUILDER_PRIVATE_KEY
58
+ // on a multi-tenant deploy will silently route every authenticated user
59
+ // through the env-key owner's Builder identity — that was the KVesta Space
60
+ // cross-tenant attribution leak (2026-04). The mode is binary: env-set
61
+ // means single-tenant intent.
49
62
  // ---------------------------------------------------------------------------
50
63
  export async function resolveBuilderCredential(key) {
64
+ // Env-managed mode wins when set: deploy-level Builder identity for
65
+ // every user. Per-user app_secrets (left over from a previous OAuth
66
+ // connection or a mode switch) are intentionally ignored — the
67
+ // operator's deploy-level config is authoritative.
68
+ const envValue = readDeployCredentialEnv(key);
69
+ if (envValue)
70
+ return envValue;
71
+ // No env value: per-user OAuth fallback.
51
72
  const email = getRequestUserEmail();
52
73
  if (email) {
53
74
  try {
@@ -61,25 +82,26 @@ export async function resolveBuilderCredential(key) {
61
82
  return secret.value;
62
83
  }
63
84
  catch {
64
- // Secrets table not ready — fall through to the env-fallback decision below
65
- }
66
- // Refuse the deploy-level env fallback for authenticated users in a
67
- // multi-tenant context. In a hosted shared-DB deploy `process.env.BUILDER_*`
68
- // would silently identify every user as whoever set the deploy-level keys —
69
- // exactly the cross-tenant leak we hit on the analytics demo (KVesta Space,
70
- // 2026-04). Per-user creds live in `app_secrets`; users without their own
71
- // connection get null here and see the "Connect Builder" prompt. The
72
- // local-dev session (`local@localhost`) is the only authenticated context
73
- // where the env fallback is safe — it identifies a single-user dev box.
74
- if (email !== DEV_MODE_USER_EMAIL) {
75
- return null;
85
+ // Secrets table not ready — treat as missing.
76
86
  }
77
87
  }
78
- return readDeployCredentialEnv(key) || null;
88
+ return null;
79
89
  }
80
90
  /**
81
- * Resolve the current user's Builder private key.
82
- * Checks per-user app_secrets first, then falls back to process.env.
91
+ * True when `BUILDER_PRIVATE_KEY` is set at the deployment level every
92
+ * user of this deploy shares the operator's Builder identity, and per-user
93
+ * connect/disconnect is disabled. UIs read this via `/builder/status` to
94
+ * swap the "Connect Builder" prompts for a read-only "managed by deployment"
95
+ * chip and to suppress the disconnect button.
96
+ */
97
+ export function isBuilderEnvManaged() {
98
+ return !!process.env.BUILDER_PRIVATE_KEY;
99
+ }
100
+ /**
101
+ * Resolve the Builder private key for the current request. In env-managed
102
+ * mode (deploy-level `BUILDER_PRIVATE_KEY` set) returns the env value for
103
+ * every caller. Otherwise reads the current user's per-user OAuth-stored
104
+ * key from `app_secrets`.
83
105
  */
84
106
  export async function resolveBuilderPrivateKey() {
85
107
  return resolveBuilderCredential("BUILDER_PRIVATE_KEY");
@@ -192,7 +214,15 @@ export async function resolveSecret(key) {
192
214
  // Synchronous helpers — env-only fallbacks for contexts where per-user
193
215
  // lookup isn't possible (sync isConfigured checks, CLI scripts).
194
216
  // ---------------------------------------------------------------------------
195
- /** True when a Builder private key is configured at the deployment level. */
217
+ /**
218
+ * True when a Builder private key is configured at the deployment level.
219
+ *
220
+ * This is the same check as `isBuilderEnvManaged()` (env-managed mode is
221
+ * defined as "deploy-level BUILDER_PRIVATE_KEY is set"). Prefer
222
+ * `isBuilderEnvManaged()` for new call sites — its name reflects what the
223
+ * boolean means semantically. For "does this user have access to Builder
224
+ * (env or per-user)?" use the async `resolveHasBuilderPrivateKey()`.
225
+ */
196
226
  export function hasBuilderPrivateKey() {
197
227
  return !!process.env.BUILDER_PRIVATE_KEY;
198
228
  }
@@ -1 +1 @@
1
- {"version":3,"file":"credential-provider.js","sourceRoot":"","sources":["../../src/server/credential-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential provider abstraction.\n *\n * Every feature that needs an external credential (Anthropic API key,\n * Google OAuth tokens, OpenAI key, Slack bot token, etc.) should go through\n * one of the resolve*() helpers here instead of reading `process.env`\n * directly. That way the same feature can work in three modes:\n *\n * 1. User set their own key in .env → use it directly\n * 2. User connected Builder via `/cli-auth` → route through Builder proxy\n * 3. Neither → throw FeatureNotConfigured\n *\n * Templates catch FeatureNotConfigured and show a \"Connect Builder (1 click) /\n * set up your own key (guide)\" card.\n *\n * Today these helpers are used by the Builder-hosted LLM gateway, and the\n * shape is meant to grow to cover future managed credential integrations\n * (e.g. additional Builder-hosted services) without rewrites.\n */\n\nimport { getRequestUserEmail } from \"./request-context.js\";\nimport { DEV_MODE_USER_EMAIL } from \"./auth.js\";\n\nexport class FeatureNotConfiguredError extends Error {\n readonly requiredCredential: string;\n readonly builderConnectUrl?: string;\n readonly byokDocsUrl?: string;\n\n constructor(opts: {\n requiredCredential: string;\n message?: string;\n builderConnectUrl?: string;\n byokDocsUrl?: string;\n }) {\n super(\n opts.message ??\n `Feature requires credential \"${opts.requiredCredential}\". Connect Builder or set your own key.`,\n );\n this.name = \"FeatureNotConfiguredError\";\n this.requiredCredential = opts.requiredCredential;\n this.builderConnectUrl = opts.builderConnectUrl;\n this.byokDocsUrl = opts.byokDocsUrl;\n }\n}\n\n/**\n * Deployment-level credential fallback for single-tenant/local operation.\n * Multi-tenant call sites must gate this explicitly before calling.\n */\nexport function readDeployCredentialEnv(key: string): string | undefined {\n return process.env[key] || undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Per-user Builder credential resolution\n//\n// Builder keys are stored per-user in `app_secrets` (scope=user,\n// scopeId=email). The OAuth callback writes them there; the status/disconnect\n// endpoints read/delete them. `process.env` is the deployment-level fallback\n// (e.g. a single BUILDER_PRIVATE_KEY set in Netlify).\n// ---------------------------------------------------------------------------\n\nexport async function resolveBuilderCredential(\n key: string,\n): Promise<string | null> {\n const email = getRequestUserEmail();\n if (email) {\n try {\n const { readAppSecret } = await import(\"../secrets/storage.js\");\n const secret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: email,\n });\n if (secret) return secret.value;\n } catch {\n // Secrets table not ready — fall through to the env-fallback decision below\n }\n // Refuse the deploy-level env fallback for authenticated users in a\n // multi-tenant context. In a hosted shared-DB deploy `process.env.BUILDER_*`\n // would silently identify every user as whoever set the deploy-level keys —\n // exactly the cross-tenant leak we hit on the analytics demo (KVesta Space,\n // 2026-04). Per-user creds live in `app_secrets`; users without their own\n // connection get null here and see the \"Connect Builder\" prompt. The\n // local-dev session (`local@localhost`) is the only authenticated context\n // where the env fallback is safe — it identifies a single-user dev box.\n if (email !== DEV_MODE_USER_EMAIL) {\n return null;\n }\n }\n return readDeployCredentialEnv(key) || null;\n}\n\n/**\n * Resolve the current user's Builder private key.\n * Checks per-user app_secrets first, then falls back to process.env.\n */\nexport async function resolveBuilderPrivateKey(): Promise<string | null> {\n return resolveBuilderCredential(\"BUILDER_PRIVATE_KEY\");\n}\n\n/**\n * Resolve the current user's Builder auth header.\n * Returns `\"Bearer <key>\"` or null.\n */\nexport async function resolveBuilderAuthHeader(): Promise<string | null> {\n const key = await resolveBuilderPrivateKey();\n return key ? `Bearer ${key}` : null;\n}\n\n/**\n * Check whether the current user has a Builder private key configured\n * (per-user or deployment-level).\n */\nexport async function resolveHasBuilderPrivateKey(): Promise<boolean> {\n return !!(await resolveBuilderPrivateKey());\n}\n\n/**\n * Resolve all per-user Builder credentials. Used by the status endpoint\n * and agent-chat-plugin to get orgName, userId, etc.\n */\nexport async function resolveBuilderCredentials(): Promise<{\n privateKey: string | null;\n publicKey: string | null;\n userId: string | null;\n orgName: string | null;\n orgKind: string | null;\n}> {\n const [privateKey, publicKey, userId, orgName, orgKind] = await Promise.all([\n resolveBuilderCredential(\"BUILDER_PRIVATE_KEY\"),\n resolveBuilderCredential(\"BUILDER_PUBLIC_KEY\"),\n resolveBuilderCredential(\"BUILDER_USER_ID\"),\n resolveBuilderCredential(\"BUILDER_ORG_NAME\"),\n resolveBuilderCredential(\"BUILDER_ORG_KIND\"),\n ]);\n return { privateKey, publicKey, userId, orgName, orgKind };\n}\n\n/**\n * Write Builder credentials for the current user to per-user app_secrets.\n */\nexport async function writeBuilderCredentials(\n email: string,\n creds: {\n privateKey: string;\n publicKey: string;\n userId?: string | null;\n orgName?: string | null;\n orgKind?: string | null;\n },\n): Promise<void> {\n const { writeAppSecret } = await import(\"../secrets/storage.js\");\n const entries: Array<{ key: string; value: string }> = [\n { key: \"BUILDER_PRIVATE_KEY\", value: creds.privateKey },\n { key: \"BUILDER_PUBLIC_KEY\", value: creds.publicKey },\n ];\n if (creds.userId) {\n entries.push({ key: \"BUILDER_USER_ID\", value: creds.userId });\n }\n if (creds.orgName) {\n entries.push({ key: \"BUILDER_ORG_NAME\", value: creds.orgName });\n }\n if (creds.orgKind) {\n entries.push({ key: \"BUILDER_ORG_KIND\", value: creds.orgKind });\n }\n await Promise.all(\n entries.map(({ key, value }) =>\n writeAppSecret({ key, value, scope: \"user\", scopeId: email }),\n ),\n );\n}\n\n/**\n * Delete Builder credentials for the current user from app_secrets.\n */\nexport async function deleteBuilderCredentials(email: string): Promise<void> {\n const { deleteAppSecret } = await import(\"../secrets/storage.js\");\n const keys = [\n \"BUILDER_PRIVATE_KEY\",\n \"BUILDER_PUBLIC_KEY\",\n \"BUILDER_USER_ID\",\n \"BUILDER_ORG_NAME\",\n \"BUILDER_ORG_KIND\",\n ];\n await Promise.all(\n keys.map((key) =>\n deleteAppSecret({ key, scope: \"user\", scopeId: email }).catch(() => {}),\n ),\n );\n}\n\n// ---------------------------------------------------------------------------\n// Generic per-user secret resolution\n//\n// New consumers should prefer this over reading `process.env.X` directly.\n// User-pasted secrets live in `app_secrets` (encrypted, scope=user); the\n// settings UI / onboarding panels write here. Deploy-level env vars are\n// the fallback for unauthenticated/CLI/background contexts where there's\n// no user to scope by — never the silent fallback for an authenticated\n// request, since on a multi-tenant deploy that would silently identify\n// every user as whoever set the deploy-level key (KVesta Space, 2026-04).\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a per-user secret. Reads from `app_secrets` first (scoped by\n * the current request's authenticated user); falls back to `process.env`\n * only for unauthenticated/CLI/background contexts.\n */\nexport async function resolveSecret(key: string): Promise<string | null> {\n const email = getRequestUserEmail();\n if (email && email !== DEV_MODE_USER_EMAIL) {\n try {\n const { readAppSecret } = await import(\"../secrets/storage.js\");\n const secret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: email,\n });\n if (secret?.value) return secret.value;\n } catch {\n // Secrets table not ready — treat as missing.\n }\n // Authenticated multi-tenant context: never fall back to process.env.\n // The deploy-level value would silently impersonate the actual key\n // owner across every tenant.\n return null;\n }\n // Unauthenticated / local-dev / CLI / background context: env fallback\n // is safe because there's no user to mis-identify.\n return process.env[key] || null;\n}\n\n// ---------------------------------------------------------------------------\n// Synchronous helpers — env-only fallbacks for contexts where per-user\n// lookup isn't possible (sync isConfigured checks, CLI scripts).\n// ---------------------------------------------------------------------------\n\n/** True when a Builder private key is configured at the deployment level. */\nexport function hasBuilderPrivateKey(): boolean {\n return !!process.env.BUILDER_PRIVATE_KEY;\n}\n\n/** The origin for Builder-proxied API calls. Overridable for testing. */\nexport function getBuilderProxyOrigin(): string {\n return (\n process.env.BUILDER_PROXY_ORIGIN ||\n process.env.AIR_HOST ||\n process.env.BUILDER_API_HOST ||\n \"https://ai-services.builder.io\"\n );\n}\n\n/**\n * Base URL for the public Builder LLM gateway (distinct from the internal\n * proxy origin above — the public gateway lives at api.builder.io/codegen,\n * while the internal origin is ai-services.builder.io).\n * Override via BUILDER_GATEWAY_BASE_URL for staging / testing.\n */\nexport function getBuilderGatewayBaseUrl(): string {\n return (\n process.env.BUILDER_GATEWAY_BASE_URL ||\n \"https://api.builder.io/codegen/gateway/v1\"\n );\n}\n\n/** Authorization header value for Builder-proxied calls (env-only). */\nexport function getBuilderAuthHeader(): string | null {\n const key = process.env.BUILDER_PRIVATE_KEY;\n return key ? `Bearer ${key}` : null;\n}\n"]}
1
+ {"version":3,"file":"credential-provider.js","sourceRoot":"","sources":["../../src/server/credential-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential provider abstraction.\n *\n * Every feature that needs an external credential (Anthropic API key,\n * Google OAuth tokens, OpenAI key, Slack bot token, etc.) should go through\n * one of the resolve*() helpers here instead of reading `process.env`\n * directly. That way the same feature can work in three modes:\n *\n * 1. User set their own key in .env → use it directly\n * 2. User connected Builder via `/cli-auth` → route through Builder proxy\n * 3. Neither → throw FeatureNotConfigured\n *\n * Templates catch FeatureNotConfigured and show a \"Connect Builder (1 click) /\n * set up your own key (guide)\" card.\n *\n * Today these helpers are used by the Builder-hosted LLM gateway, and the\n * shape is meant to grow to cover future managed credential integrations\n * (e.g. additional Builder-hosted services) without rewrites.\n */\n\nimport { getRequestUserEmail } from \"./request-context.js\";\nimport { DEV_MODE_USER_EMAIL } from \"./auth.js\";\n\nexport class FeatureNotConfiguredError extends Error {\n readonly requiredCredential: string;\n readonly builderConnectUrl?: string;\n readonly byokDocsUrl?: string;\n\n constructor(opts: {\n requiredCredential: string;\n message?: string;\n builderConnectUrl?: string;\n byokDocsUrl?: string;\n }) {\n super(\n opts.message ??\n `Feature requires credential \"${opts.requiredCredential}\". Connect Builder or set your own key.`,\n );\n this.name = \"FeatureNotConfiguredError\";\n this.requiredCredential = opts.requiredCredential;\n this.builderConnectUrl = opts.builderConnectUrl;\n this.byokDocsUrl = opts.byokDocsUrl;\n }\n}\n\n/**\n * Deployment-level credential fallback for single-tenant/local operation.\n * Multi-tenant call sites must gate this explicitly before calling.\n */\nexport function readDeployCredentialEnv(key: string): string | undefined {\n return process.env[key] || undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Builder credential resolution — two mutually-exclusive deployment modes:\n//\n// 1. **Single-tenant / env-managed.** When BUILDER_PRIVATE_KEY is set at\n// the deployment level, it is THE Builder identity for every user of\n// this deploy. The operator setting the env explicitly opts in to\n// \"everyone shares one Builder space\" — same shape as DATABASE_URL or\n// BETTER_AUTH_SECRET. The UI hides the per-user connect/disconnect\n// flow when env-managed (see `isBuilderEnvManaged`).\n//\n// 2. **Multi-tenant / per-user OAuth.** When the env is unset, each user\n// OAuth-connects their own Builder via the cli-auth flow. Their keys\n// land in `app_secrets` (scope=user, scopeId=email) via the callback\n// handler. They can disconnect via the settings panel.\n//\n// To run multi-tenant SaaS: leave the env unset. Setting BUILDER_PRIVATE_KEY\n// on a multi-tenant deploy will silently route every authenticated user\n// through the env-key owner's Builder identity — that was the KVesta Space\n// cross-tenant attribution leak (2026-04). The mode is binary: env-set\n// means single-tenant intent.\n// ---------------------------------------------------------------------------\n\nexport async function resolveBuilderCredential(\n key: string,\n): Promise<string | null> {\n // Env-managed mode wins when set: deploy-level Builder identity for\n // every user. Per-user app_secrets (left over from a previous OAuth\n // connection or a mode switch) are intentionally ignored — the\n // operator's deploy-level config is authoritative.\n const envValue = readDeployCredentialEnv(key);\n if (envValue) return envValue;\n\n // No env value: per-user OAuth fallback.\n const email = getRequestUserEmail();\n if (email) {\n try {\n const { readAppSecret } = await import(\"../secrets/storage.js\");\n const secret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: email,\n });\n if (secret) return secret.value;\n } catch {\n // Secrets table not ready — treat as missing.\n }\n }\n return null;\n}\n\n/**\n * True when `BUILDER_PRIVATE_KEY` is set at the deployment level — every\n * user of this deploy shares the operator's Builder identity, and per-user\n * connect/disconnect is disabled. UIs read this via `/builder/status` to\n * swap the \"Connect Builder\" prompts for a read-only \"managed by deployment\"\n * chip and to suppress the disconnect button.\n */\nexport function isBuilderEnvManaged(): boolean {\n return !!process.env.BUILDER_PRIVATE_KEY;\n}\n\n/**\n * Resolve the Builder private key for the current request. In env-managed\n * mode (deploy-level `BUILDER_PRIVATE_KEY` set) returns the env value for\n * every caller. Otherwise reads the current user's per-user OAuth-stored\n * key from `app_secrets`.\n */\nexport async function resolveBuilderPrivateKey(): Promise<string | null> {\n return resolveBuilderCredential(\"BUILDER_PRIVATE_KEY\");\n}\n\n/**\n * Resolve the current user's Builder auth header.\n * Returns `\"Bearer <key>\"` or null.\n */\nexport async function resolveBuilderAuthHeader(): Promise<string | null> {\n const key = await resolveBuilderPrivateKey();\n return key ? `Bearer ${key}` : null;\n}\n\n/**\n * Check whether the current user has a Builder private key configured\n * (per-user or deployment-level).\n */\nexport async function resolveHasBuilderPrivateKey(): Promise<boolean> {\n return !!(await resolveBuilderPrivateKey());\n}\n\n/**\n * Resolve all per-user Builder credentials. Used by the status endpoint\n * and agent-chat-plugin to get orgName, userId, etc.\n */\nexport async function resolveBuilderCredentials(): Promise<{\n privateKey: string | null;\n publicKey: string | null;\n userId: string | null;\n orgName: string | null;\n orgKind: string | null;\n}> {\n const [privateKey, publicKey, userId, orgName, orgKind] = await Promise.all([\n resolveBuilderCredential(\"BUILDER_PRIVATE_KEY\"),\n resolveBuilderCredential(\"BUILDER_PUBLIC_KEY\"),\n resolveBuilderCredential(\"BUILDER_USER_ID\"),\n resolveBuilderCredential(\"BUILDER_ORG_NAME\"),\n resolveBuilderCredential(\"BUILDER_ORG_KIND\"),\n ]);\n return { privateKey, publicKey, userId, orgName, orgKind };\n}\n\n/**\n * Write Builder credentials for the current user to per-user app_secrets.\n */\nexport async function writeBuilderCredentials(\n email: string,\n creds: {\n privateKey: string;\n publicKey: string;\n userId?: string | null;\n orgName?: string | null;\n orgKind?: string | null;\n },\n): Promise<void> {\n const { writeAppSecret } = await import(\"../secrets/storage.js\");\n const entries: Array<{ key: string; value: string }> = [\n { key: \"BUILDER_PRIVATE_KEY\", value: creds.privateKey },\n { key: \"BUILDER_PUBLIC_KEY\", value: creds.publicKey },\n ];\n if (creds.userId) {\n entries.push({ key: \"BUILDER_USER_ID\", value: creds.userId });\n }\n if (creds.orgName) {\n entries.push({ key: \"BUILDER_ORG_NAME\", value: creds.orgName });\n }\n if (creds.orgKind) {\n entries.push({ key: \"BUILDER_ORG_KIND\", value: creds.orgKind });\n }\n await Promise.all(\n entries.map(({ key, value }) =>\n writeAppSecret({ key, value, scope: \"user\", scopeId: email }),\n ),\n );\n}\n\n/**\n * Delete Builder credentials for the current user from app_secrets.\n */\nexport async function deleteBuilderCredentials(email: string): Promise<void> {\n const { deleteAppSecret } = await import(\"../secrets/storage.js\");\n const keys = [\n \"BUILDER_PRIVATE_KEY\",\n \"BUILDER_PUBLIC_KEY\",\n \"BUILDER_USER_ID\",\n \"BUILDER_ORG_NAME\",\n \"BUILDER_ORG_KIND\",\n ];\n await Promise.all(\n keys.map((key) =>\n deleteAppSecret({ key, scope: \"user\", scopeId: email }).catch(() => {}),\n ),\n );\n}\n\n// ---------------------------------------------------------------------------\n// Generic per-user secret resolution\n//\n// New consumers should prefer this over reading `process.env.X` directly.\n// User-pasted secrets live in `app_secrets` (encrypted, scope=user); the\n// settings UI / onboarding panels write here. Deploy-level env vars are\n// the fallback for unauthenticated/CLI/background contexts where there's\n// no user to scope by — never the silent fallback for an authenticated\n// request, since on a multi-tenant deploy that would silently identify\n// every user as whoever set the deploy-level key (KVesta Space, 2026-04).\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a per-user secret. Reads from `app_secrets` first (scoped by\n * the current request's authenticated user); falls back to `process.env`\n * only for unauthenticated/CLI/background contexts.\n */\nexport async function resolveSecret(key: string): Promise<string | null> {\n const email = getRequestUserEmail();\n if (email && email !== DEV_MODE_USER_EMAIL) {\n try {\n const { readAppSecret } = await import(\"../secrets/storage.js\");\n const secret = await readAppSecret({\n key,\n scope: \"user\",\n scopeId: email,\n });\n if (secret?.value) return secret.value;\n } catch {\n // Secrets table not ready — treat as missing.\n }\n // Authenticated multi-tenant context: never fall back to process.env.\n // The deploy-level value would silently impersonate the actual key\n // owner across every tenant.\n return null;\n }\n // Unauthenticated / local-dev / CLI / background context: env fallback\n // is safe because there's no user to mis-identify.\n return process.env[key] || null;\n}\n\n// ---------------------------------------------------------------------------\n// Synchronous helpers — env-only fallbacks for contexts where per-user\n// lookup isn't possible (sync isConfigured checks, CLI scripts).\n// ---------------------------------------------------------------------------\n\n/**\n * True when a Builder private key is configured at the deployment level.\n *\n * This is the same check as `isBuilderEnvManaged()` (env-managed mode is\n * defined as \"deploy-level BUILDER_PRIVATE_KEY is set\"). Prefer\n * `isBuilderEnvManaged()` for new call sites — its name reflects what the\n * boolean means semantically. For \"does this user have access to Builder\n * (env or per-user)?\" use the async `resolveHasBuilderPrivateKey()`.\n */\nexport function hasBuilderPrivateKey(): boolean {\n return !!process.env.BUILDER_PRIVATE_KEY;\n}\n\n/** The origin for Builder-proxied API calls. Overridable for testing. */\nexport function getBuilderProxyOrigin(): string {\n return (\n process.env.BUILDER_PROXY_ORIGIN ||\n process.env.AIR_HOST ||\n process.env.BUILDER_API_HOST ||\n \"https://ai-services.builder.io\"\n );\n}\n\n/**\n * Base URL for the public Builder LLM gateway (distinct from the internal\n * proxy origin above — the public gateway lives at api.builder.io/codegen,\n * while the internal origin is ai-services.builder.io).\n * Override via BUILDER_GATEWAY_BASE_URL for staging / testing.\n */\nexport function getBuilderGatewayBaseUrl(): string {\n return (\n process.env.BUILDER_GATEWAY_BASE_URL ||\n \"https://api.builder.io/codegen/gateway/v1\"\n );\n}\n\n/** Authorization header value for Builder-proxied calls (env-only). */\nexport function getBuilderAuthHeader(): string | null {\n const key = process.env.BUILDER_PRIVATE_KEY;\n return key ? `Bearer ${key}` : null;\n}\n"]}
@@ -72,6 +72,7 @@ export interface OAuthStatePayload {
72
72
  owner?: string;
73
73
  desktop?: boolean;
74
74
  addAccount?: boolean;
75
+ app?: string;
75
76
  /**
76
77
  * Same-origin path to redirect to after a successful web-flow sign-in.
77
78
  * Threaded through the (HMAC-signed) state so it survives the round trip
@@ -162,10 +163,12 @@ export declare function oauthCallbackResponse(event: H3Event, email: string, opt
162
163
  */
163
164
  returnUrl?: string;
164
165
  flowId?: string;
166
+ appName?: string;
165
167
  }): Response | string | unknown | Promise<Response | string | unknown>;
166
168
  /** HTML error page for OAuth failures. The message is HTML-escaped — most
167
169
  * callers pass `error.message` from a token-exchange or userinfo failure,
168
170
  * which can echo upstream provider strings (and historically attacker-
169
171
  * controlled query params via the `error_description` field). */
170
172
  export declare function oauthErrorPage(message: string): Response;
173
+ export declare function oauthDesktopExchangePage(message?: string): Response;
171
174
  //# sourceMappingURL=google-oauth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"google-oauth.d.ts","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@
9
9
  import crypto from "node:crypto";
10
10
  import { getHeader, getQuery, setCookie, setResponseStatus, setResponseHeader, } from "h3";
11
11
  import { addSession, getSession, COOKIE_NAME, getSessionMaxAge, safeReturnPath, } from "./auth.js";
12
+ import { getAppName } from "./app-name.js";
12
13
  import { writeDesktopSso } from "./desktop-sso.js";
13
14
  // ─── Platform Detection ─────────────────────────────────────────────────────
14
15
  /** Return an HTML response with the correct Content-Type.
@@ -286,6 +287,7 @@ export function decodeOAuthState(stateParam, fallbackUri) {
286
287
  owner: parsed.o || undefined,
287
288
  desktop: !!parsed.d,
288
289
  addAccount: !!parsed.a,
290
+ app: typeof parsed.app === "string" ? parsed.app : undefined,
289
291
  // Pass returnUrl through as-is — same-origin validation runs at the
290
292
  // consumer (oauthCallbackResponse → safeReturnPath). The state is
291
293
  // HMAC-signed, but we still validate at consumption as defence in
@@ -375,15 +377,23 @@ export function oauthCallbackResponse(event, email, opts) {
375
377
  // to ensure no deep link fires and the existing session is never switched).
376
378
  if (opts.desktop && opts.addAccount) {
377
379
  const safeEmail = email ? escapeHtml(email) : "";
380
+ const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));
378
381
  const msg = safeEmail ? `Connected ${safeEmail}!` : "Connected!";
379
- return htmlResponse(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Connected</title></head><body style="background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px"><p style="font-size:16px">${msg}</p><p style="font-size:13px;color:#888">You can close this tab and return to Agent Native.</p></body></html>`);
382
+ return htmlResponse(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Connected</title></head><body style="background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px"><p style="font-size:16px">${msg}</p><p style="font-size:13px;color:#888">You can close this tab and return to ${safeAppName}.</p></body></html>`);
380
383
  }
381
- // Desktop exchange flow (Tauri tray app): the tray app polls the
384
+ // Electron desktop exchange flow: mail/calendar still pass a flow id so the
385
+ // renderer can poll as a fallback, but the main handoff should use the
386
+ // protocol deep link so the popup returns focus to the desktop app.
387
+ if (opts.desktop && opts.flowId && isElectron(event) && opts.sessionToken) {
388
+ return desktopSuccessPage(event, email, opts.sessionToken, callbackState);
389
+ }
390
+ // Desktop exchange flow (non-Electron tray app): the tray app polls the
382
391
  // desktop-exchange endpoint for the token — no deep link needed.
383
392
  if (opts.desktop && opts.flowId) {
384
393
  const safeEmail = email ? escapeHtml(email) : "";
394
+ const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));
385
395
  const msg = safeEmail ? `Signed in as ${safeEmail}!` : "Signed in!";
386
- return htmlResponse(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Connected</title></head><body style="background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px"><p style="font-size:16px">${msg}</p><p style="font-size:13px;color:#888">You can close this tab and return to Clips.</p></body></html>`);
396
+ return htmlResponse(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Connected</title></head><body style="background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px"><p style="font-size:16px">${msg}</p><p style="font-size:13px;color:#888">You can close this tab and return to ${safeAppName}.</p></body></html>`);
387
397
  }
388
398
  // Desktop login: deep link back to Electron app
389
399
  if (opts.desktop) {
@@ -426,7 +436,21 @@ export function oauthErrorPage(message) {
426
436
  </div>
427
437
  </body></html>`, 400);
428
438
  }
439
+ export function oauthDesktopExchangePage(message = "Returning to the app...") {
440
+ const safe = escapeHtml(message);
441
+ return htmlResponse(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>Returning</title></head><body style="background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p style="font-size:14px">${safe}</p><script>window.close()</script></body></html>`);
442
+ }
429
443
  // ─── Internal ────────────────────────────────────────────────────────────────
444
+ function resolveOAuthAppName(explicit) {
445
+ const raw = explicit || getAppName() || "Agent Native";
446
+ if (!/^[a-z0-9_-]+$/.test(raw))
447
+ return raw;
448
+ return raw
449
+ .split(/[-_]+/)
450
+ .filter(Boolean)
451
+ .map((word) => word[0].toUpperCase() + word.slice(1))
452
+ .join(" ");
453
+ }
430
454
  function buildOAuthCompleteDeepLink(sessionToken, state) {
431
455
  const params = new URLSearchParams();
432
456
  if (sessionToken)
@@ -1 +1 @@
1
- {"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,IAAI,CAAC;AACZ,OAAO,EACL,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+EAA+E;AAE/E;;oDAEoD;AACpD,SAAS,YAAY,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;IAC9C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;SACnB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,2BAA2B,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;;GAKG;AACH,SAAS,4BAA4B;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,UAAU,GACd,SAAS,CAAC,KAAK,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IACrD,MAAM,WAAW,GACf,SAAS,CAAC,KAAK,EAAE,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAEvE,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,4BAA4B,EAAE,CAAC;QAC7C,yEAAyE;QACzE,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,WAAW,MAAM,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,IAAI,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAClD,mEAAmE;YACnE,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC;QACD,kEAAkE;QAClE,+DAA+D;QAC/D,OAAO,GAAG,WAAW,MAAM,UAAU,IAAI,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,GAAG,WAAW,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB;IACrD,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc;IAC5B,OAAO,oBAAoB,CACzB,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAC5D,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,IAAI,GAAG,GAAG;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC3D,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,cAAc,EAAE,GAAG,SAAS,EAAE,CAAC;AAC9D,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,KAAc;IAEd,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,qCAAqC;IACrC,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,WAAgB,CAAC;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,GAAG,QAAQ,iBAAiB,CAAC;IAC9C,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAc,EACd,WAAW,GAAG,gCAAgC;IAE9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC;IAC9C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACtE,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AACvC,CAAC;AAoBD;;;;;GAKG;AACH,IAAI,mBAAuC,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,SAAS,kBAAkB;IACzB,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IACnE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IACrD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,gDAAgD;YAC9C,6DAA6D,CAChE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,mBAAmB,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AA0CD,MAAM,UAAU,gBAAgB,CAC9B,iBAAmD,EACnD,KAAc,EACd,OAAiB,EACjB,UAAoB,EACpB,GAAY,EACZ,SAAkB,EAClB,MAAe;IAEf,MAAM,IAAI,GACR,OAAO,iBAAiB,KAAK,QAAQ;QACnC,CAAC,CAAC;YACE,WAAW,EAAE,iBAAiB;YAC9B,KAAK;YACL,OAAO;YACP,UAAU;YACV,GAAG;YACH,SAAS;YACT,MAAM;SACP;QACH,CAAC,CAAC,iBAAiB,CAAC;IAExB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,OAAO,GAAqC;QAChD,CAAC,EAAE,KAAK;QACR,CAAC,EAAE,IAAI,CAAC,WAAW;KACpB,CAAC;IACF,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IACvC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACnC,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IACtC,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACrC,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAChD,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,MAAM;SACf,UAAU,CAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;SAC1C,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,WAAW,CAAC,CAAC;IACvB,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA8B,EAC9B,WAAmB;IAEnB,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,MAAM,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;YAEvD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACzC,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM;iBACpB,UAAU,CAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;iBAC1C,MAAM,CAAC,IAAI,CAAC;iBACZ,MAAM,CAAC,WAAW,CAAC,CAAC;YAEvB,IACE,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;gBAC9B,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAChE,CAAC;gBACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;YACtC,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YACrE,OAAO;gBACL,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,WAAW;gBACpC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,SAAS;gBAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;gBACnB,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;gBACtB,oEAAoE;gBACpE,kEAAkE;gBAClE,kEAAkE;gBAClE,4CAA4C;gBAC5C,SAAS,EAAE,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;gBAChE,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,SAAS;aAC9B,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACtC,CAAC;AAUD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAc,EACd,UAAmB;IAEnB,MAAM,eAAe,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,eAAe,EAAE,KAAK,KAAK,iBAAiB,CAAC;IAClE,MAAM,oBAAoB,GAAG,CAAC,CAAC,CAAC,eAAe,EAAE,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC;IAEzE,6EAA6E;IAC7E,qDAAqD;IACrD,MAAM,KAAK,GAAG,oBAAoB;QAChC,CAAC,CAAC,eAAgB,CAAC,KAAK;QACxB,CAAC,CAAC,UAAU,IAAI,SAAS,CAAC;IAE5B,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC;AACvD,CAAC;AAMD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAc,EACd,KAAa,EACb,IAGC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;IAC7C,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,YAAgC,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,aAAa,EAAE,CAAC;QAChD,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACtC,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE;YAC1C,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;YAC7C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,GAAG;YACT,MAAM;SACP,CAAC,CAAC;QACH,kEAAkE;QAClE,iEAAiE;QACjE,6DAA6D;QAC7D,8DAA8D;QAC9D,8DAA8D;QAC9D,gEAAgE;QAChE,iCAAiC;QACjC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/C,MAAM,eAAe,CAAC;gBACpB,KAAK;gBACL,KAAK,EAAE,YAAY;gBACnB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI;aACtC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC;AAED,gFAAgF;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAc,EACd,KAAa,EACb,IAYC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,aAAa,GACjB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACvD,CAAC,CAAC,KAAK,CAAC,KAAK;QACb,CAAC,CAAC,SAAS,CAAC;IAEhB,uCAAuC;IACvC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,0BAA0B,CACzC,IAAI,CAAC,YAAY,EACjB,aAAa,CACd,CAAC;QACF,OAAO,YAAY,CACjB,sYAAsY,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,+EAA+E,CAC9e,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACjE,OAAO,YAAY,CACjB,uRAAuR,GAAG,+GAA+G,CAC1Y,CAAC;IACJ,CAAC;IAED,iEAAiE;IACjE,iEAAiE;IACjE,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QACpE,OAAO,YAAY,CACjB,uRAAuR,GAAG,wGAAwG,CACnY,CAAC;IACJ,CAAC;IAED,gDAAgD;IAChD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAC5E,CAAC;IAED,uEAAuE;IACvE,sEAAsE;IACtE,qEAAqE;IACrE,oEAAoE;IACpE,mDAAmD;IACnD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzE,OAAO,YAAY,CAAC;;;;yCAIiB,SAAS;;8BAEpB,CAAC,CAAC;IAC9B,CAAC;IAED,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,wEAAwE;IACxE,oCAAoC;IACpC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9B,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;kEAGkE;AAClE,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,YAAY,CACjB;;6CAEyC,IAAI;;;iBAGhC,EACb,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,SAAS,0BAA0B,CACjC,YAAqB,EACrB,KAAc;IAEd,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,YAAY;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpD,IAAI,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IACjC,OAAO,MAAM;QACX,CAAC,CAAC,gCAAgC,MAAM,EAAE;QAC1C,CAAC,CAAC,8BAA8B,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAe,EACf,KAAc,EACd,YAAqB,EACrB,KAAc;IAEd,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,aAAa,SAAS,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;IACjE,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,0BAA0B,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9C,OAAO,YAAY,CACjB,isBAAisB,GAAG,2FAA2F,YAAY,gUAAgU,YAAY,wKAAwK,CAChyC,CAAC;IACJ,CAAC;IACD,OAAO,YAAY,CACjB,uRAAuR,GAAG,+GAA+G,CAC1Y,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Shared Google OAuth utilities for all templates.\n *\n * Handles platform detection (desktop/mobile), state encoding,\n * session token creation, and deep-link responses — the logic\n * that was previously copy-pasted across every template's\n * google-auth.ts handler.\n */\n\nimport crypto from \"node:crypto\";\nimport {\n getHeader,\n getQuery,\n setCookie,\n setResponseStatus,\n setResponseHeader,\n type H3Event,\n} from \"h3\";\nimport {\n addSession,\n getSession,\n COOKIE_NAME,\n getSessionMaxAge,\n safeReturnPath,\n} from \"./auth.js\";\nimport { writeDesktopSso } from \"./desktop-sso.js\";\n\n// ─── Platform Detection ─────────────────────────────────────────────────────\n\n/** Return an HTML response with the correct Content-Type.\n * Uses a web-standard Response to ensure the header survives\n * Nitro dev mode's mock-node-response pipeline. */\nfunction htmlResponse(html: string, status = 200): Response {\n return new Response(html, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/**\n * HTML escape — minimal but covers the cases that matter when interpolating\n * user-controlled values into our OAuth callback HTML. Mirrors the helper in\n * email-template.ts; kept inline here to avoid a circular import.\n */\nfunction escapeHtml(s: string): string {\n return String(s ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/** Detect requests from the Electron desktop app webview. */\nexport function isElectron(event: H3Event): boolean {\n return /Electron/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/** Detect requests from a mobile browser (iOS/Android). */\nexport function isMobile(event: H3Event): boolean {\n return /iPhone|iPad|iPod|Android/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/**\n * Build the static allowlist of origins we trust for `getOrigin`. Reads\n * `APP_URL` and `BETTER_AUTH_URL` (both are deployment-known public URLs).\n * Each entry is normalised to `${proto}://${host}` (no path). Duplicates\n * collapse, invalid entries are dropped silently.\n */\nfunction getConfiguredOriginAllowlist(): Set<string> {\n const out = new Set<string>();\n for (const raw of [process.env.APP_URL, process.env.BETTER_AUTH_URL]) {\n if (!raw) continue;\n try {\n const u = new URL(raw);\n out.add(`${u.protocol}//${u.host}`);\n } catch {\n // Ignore — env value isn't a parseable URL.\n }\n }\n return out;\n}\n\n/**\n * Get the origin from forwarded headers or Host.\n *\n * Defends against Host-header injection: in production we require the\n * resolved origin to match `APP_URL` / `BETTER_AUTH_URL`, falling back to\n * those values when the inbound headers are missing or don't match. In\n * dev we accept the inbound `Host` so localhost / ngrok / preview hosts\n * keep working without configuration. The protocol defaults to `https`\n * in production (so a TLS-terminating proxy that drops `x-forwarded-proto`\n * doesn't downgrade us to plain HTTP).\n */\nexport function getOrigin(event: H3Event): string {\n const headerHost =\n getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const isProd = process.env.NODE_ENV === \"production\";\n const headerProto =\n getHeader(event, \"x-forwarded-proto\") || (isProd ? \"https\" : \"http\");\n\n if (isProd) {\n const allow = getConfiguredOriginAllowlist();\n // If the deploy declares its public URL, prefer it over inbound headers.\n if (allow.size > 0) {\n const inbound = headerHost ? `${headerProto}://${headerHost}` : \"\";\n if (inbound && allow.has(inbound)) return inbound;\n // Inbound didn't match — fall back to the first configured origin.\n return [...allow][0];\n }\n // No allowlist configured: still default to https, but accept the\n // inbound Host (best we can do without a configured base URL).\n return `${headerProto}://${headerHost ?? \"\"}`;\n }\n\n return `${headerProto}://${headerHost ?? \"localhost\"}`;\n}\n\nfunction normalizeAppBasePath(value: string | undefined): string {\n if (!value || value === \"/\") return \"\";\n const trimmed = value.trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n return `/${trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\n/** App mount prefix, if the template is served under APP_BASE_PATH. */\nexport function getAppBasePath(): string {\n return normalizeAppBasePath(\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH,\n );\n}\n\n/** Build an absolute same-origin URL that preserves APP_BASE_PATH. */\nexport function getAppUrl(event: H3Event, path = \"/\"): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${getOrigin(event)}${getAppBasePath()}${cleanPath}`;\n}\n\n// ─── redirect_uri Allowlist ──────────────────────────────────────────────────\n\n/**\n * Validate a user-supplied `redirect_uri` for OAuth flows.\n *\n * Defends against authorization-code interception (RFC 6819 §4.4.1.7):\n * even though the upstream provider (Google/Atlassian/Zoom) refuses\n * unregistered redirect URIs, prefix-style registrations and side\n * registrations on the same host let a malicious caller swap in an\n * attacker-controlled URI that the provider still accepts. We reject any\n * candidate that isn't on this server's own origin AND under the\n * framework's `/_agent-native/` namespace. Returns the validated URI on\n * success, or `undefined` on rejection — callers must treat `undefined`\n * as a 400.\n *\n * The intentional shape is exact-prefix:\n * - Origin must equal `getOrigin(event)` — no Host-header injection\n * reusing somebody else's registered redirect URI.\n * - Path must start with `${appBasePath}/_agent-native/` so we never\n * hand auth codes to a public marketing or open-redirect endpoint\n * on the same registered host.\n *\n * For desktop / native flows that need ephemeral `http://127.0.0.1:<port>`\n * loopback URIs, callers should validate those at the template level\n * with a dedicated allowlist — this helper rejects them by design.\n */\nexport function isAllowedOAuthRedirectUri(\n candidate: string,\n event: H3Event,\n): boolean {\n if (typeof candidate !== \"string\" || candidate.length === 0) return false;\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n return false;\n }\n // Must be same origin as our server.\n const expectedOrigin = getOrigin(event);\n let expectedUrl: URL;\n try {\n expectedUrl = new URL(expectedOrigin);\n } catch {\n return false;\n }\n if (url.protocol !== expectedUrl.protocol) return false;\n if (url.host !== expectedUrl.host) return false;\n // Must live under the framework's namespace.\n const basePath = getAppBasePath();\n const required = `${basePath}/_agent-native/`;\n if (!url.pathname.startsWith(required)) return false;\n return true;\n}\n\n/**\n * Resolve the `redirect_uri` for an outbound OAuth `auth-url` request.\n *\n * Reads `?redirect_uri=` from the query and validates it via\n * `isAllowedOAuthRedirectUri`. Returns:\n * - the validated URI when supplied and allowed, OR\n * - the framework default when no override was supplied, OR\n * - `null` when an override was supplied but rejected — callers must\n * respond with 400 in that case.\n *\n * Templates that need a non-default redirect path can pass it via\n * `defaultPath` (e.g. `\"/_agent-native/google/desktop-callback\"` for\n * desktop flows).\n */\nexport function resolveOAuthRedirectUri(\n event: H3Event,\n defaultPath = \"/_agent-native/google/callback\",\n): string | null {\n const supplied = getQuery(event).redirect_uri;\n if (typeof supplied === \"string\" && supplied.length > 0) {\n return isAllowedOAuthRedirectUri(supplied, event) ? supplied : null;\n }\n return getAppUrl(event, defaultPath);\n}\n\n// ─── OAuth State ─────────────────────────────────────────────────────────────\n\nexport interface OAuthStatePayload {\n redirectUri: string;\n owner?: string;\n desktop?: boolean;\n addAccount?: boolean;\n /**\n * Same-origin path to redirect to after a successful web-flow sign-in.\n * Threaded through the (HMAC-signed) state so it survives the round trip\n * to Google. Validated again on decode via safeReturnPath as defence in\n * depth. Has no effect on desktop / mobile / add-account flows, which\n * use their own deep-link / close-tab handling.\n */\n returnUrl?: string;\n flowId?: string;\n}\n\n/**\n * Ephemeral in-memory state-signing key for development. Generated lazily\n * on first read so dev sessions don't depend on filesystem writability or\n * env-var configuration. Sessions reset on each restart, which is fine\n * for dev — no real users / production data are involved.\n */\nlet _devStateSigningKey: string | undefined;\n\n/**\n * Derive a server-only signing key for HMAC verification of OAuth state.\n *\n * Uses a dedicated secret — never an OAuth client secret. Reusing a\n * client_secret (which is shared with Google / GitHub / Atlassian) as our\n * own HMAC key conflates two trust domains: rotating the client secret\n * silently invalidates every in-flight OAuth state, and any leak of the\n * client secret also lets an attacker forge our state envelopes.\n *\n * Resolution order:\n * 1. OAUTH_STATE_SECRET (preferred — dedicated to this purpose)\n * 2. BETTER_AUTH_SECRET (already used by Better Auth as a server secret)\n * 3. In dev only, an ephemeral random key (per-process)\n *\n * In production, throws if neither secret is set.\n */\nfunction getStateSigningKey(): string {\n const secret =\n process.env.OAUTH_STATE_SECRET || process.env.BETTER_AUTH_SECRET;\n if (secret) return secret;\n\n const isProd = process.env.NODE_ENV === \"production\";\n if (isProd) {\n throw new Error(\n \"OAuth state signing requires a server secret. \" +\n \"Set OAUTH_STATE_SECRET or BETTER_AUTH_SECRET in production.\",\n );\n }\n\n if (!_devStateSigningKey) {\n _devStateSigningKey = crypto.randomBytes(32).toString(\"hex\");\n }\n return _devStateSigningKey;\n}\n\n/**\n * Options for the named-argument form of {@link encodeOAuthState}.\n * Prefer this form — the positional overload is easy to misuse (the mail\n * and calendar templates historically passed `flowId` in the `returnUrl`\n * slot, smuggling state into a defence-in-depth path).\n */\nexport interface EncodeOAuthStateOptions {\n redirectUri: string;\n owner?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n returnUrl?: string;\n flowId?: string;\n}\n\n/**\n * Encode OAuth state into a signed base64url string.\n * The state is HMAC-signed so the callback can verify it wasn't forged,\n * preventing CSRF attacks on the OAuth flow.\n *\n * Two call shapes are supported:\n * - Recommended: pass an options object — clear, mismatch-proof.\n * `encodeOAuthState({ redirectUri, owner, desktop, ... })`\n * - Legacy positional form (kept working for backward compatibility):\n * `encodeOAuthState(redirectUri, owner, desktop, addAccount, app, returnUrl, flowId)`.\n * Callers should migrate to the options form — see the audit on\n * templates/mail and templates/calendar where the positional shape\n * led to `flowId` being smuggled in via the `returnUrl` slot.\n */\nexport function encodeOAuthState(opts: EncodeOAuthStateOptions): string;\nexport function encodeOAuthState(\n redirectUri: string,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string;\nexport function encodeOAuthState(\n redirectUriOrOpts: string | EncodeOAuthStateOptions,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string {\n const opts: EncodeOAuthStateOptions =\n typeof redirectUriOrOpts === \"string\"\n ? {\n redirectUri: redirectUriOrOpts,\n owner,\n desktop,\n addAccount,\n app,\n returnUrl,\n flowId,\n }\n : redirectUriOrOpts;\n\n const nonce = crypto.randomBytes(8).toString(\"hex\");\n const payload: Record<string, string | boolean> = {\n n: nonce,\n r: opts.redirectUri,\n };\n if (opts.owner) payload.o = opts.owner;\n if (opts.desktop) payload.d = true;\n if (opts.addAccount) payload.a = true;\n if (opts.app) payload.app = opts.app;\n if (opts.returnUrl) payload.r2 = opts.returnUrl;\n if (opts.flowId) payload.f = opts.flowId;\n const data = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n return `${data}.${sig}`;\n}\n\n/**\n * Decode and verify OAuth state from the callback's state query parameter.\n * Rejects forged or tampered state by checking the HMAC signature.\n * Falls back to the provided URI if decoding or verification fails.\n */\nexport function decodeOAuthState(\n stateParam: string | undefined,\n fallbackUri: string,\n): OAuthStatePayload {\n if (stateParam) {\n try {\n const dotIdx = stateParam.lastIndexOf(\".\");\n if (dotIdx === -1) return { redirectUri: fallbackUri };\n\n const data = stateParam.slice(0, dotIdx);\n const sig = stateParam.slice(dotIdx + 1);\n const expected = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n\n if (\n sig.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))\n ) {\n return { redirectUri: fallbackUri };\n }\n\n const parsed = JSON.parse(Buffer.from(data, \"base64url\").toString());\n return {\n redirectUri: parsed.r || fallbackUri,\n owner: parsed.o || undefined,\n desktop: !!parsed.d,\n addAccount: !!parsed.a,\n // Pass returnUrl through as-is — same-origin validation runs at the\n // consumer (oauthCallbackResponse → safeReturnPath). The state is\n // HMAC-signed, but we still validate at consumption as defence in\n // depth in case the signing key ever leaks.\n returnUrl: typeof parsed.r2 === \"string\" ? parsed.r2 : undefined,\n flowId: parsed.f || undefined,\n };\n } catch {}\n }\n return { redirectUri: fallbackUri };\n}\n\n// ─── Session Creation ────────────────────────────────────────────────────────\n\nexport interface OAuthOwnerResult {\n owner: string | undefined;\n isDevSession: boolean;\n hasProductionSession: boolean;\n}\n\n/**\n * Determine the token owner from the current session and OAuth state.\n * Call this BEFORE exchangeCode to get the owner parameter.\n */\nexport async function resolveOAuthOwner(\n event: H3Event,\n stateOwner?: string,\n): Promise<OAuthOwnerResult> {\n const existingSession = await getSession(event);\n const isDevSession = existingSession?.email === \"local@localhost\";\n const hasProductionSession = !!(existingSession?.email && !isDevSession);\n\n // Never use \"local@localhost\" as a token owner — it creates shared-ownership\n // bugs where multiple users can see the same tokens.\n const owner = hasProductionSession\n ? existingSession!.email\n : stateOwner || undefined;\n\n return { owner, isDevSession, hasProductionSession };\n}\n\nexport interface OAuthSessionResult {\n sessionToken: string | undefined;\n}\n\n/**\n * Create a session token after a successful OAuth exchange.\n *\n * Desktop and mobile apps have separate cookie jars from the system\n * browser, so they always get a fresh session token (even if the browser\n * already has one). The token is then passed via deep link so the native\n * app can inject it.\n */\nexport async function createOAuthSession(\n event: H3Event,\n email: string,\n opts: {\n hasProductionSession: boolean;\n desktop?: boolean;\n },\n): Promise<OAuthSessionResult> {\n const mobile = isMobile(event);\n const needsDeepLink = opts.desktop || mobile;\n const maxAge = getSessionMaxAge();\n\n let sessionToken: string | undefined;\n if (!opts.hasProductionSession || needsDeepLink) {\n sessionToken = crypto.randomBytes(32).toString(\"hex\");\n await addSession(sessionToken, email);\n setCookie(event, COOKIE_NAME, sessionToken, {\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge,\n });\n // Desktop SSO: record this session in the home-dir broker file so\n // sibling templates (each with its own database) can resolve the\n // same token without a DB row of their own. Only the PRIMARY\n // sign-in writes the broker — if a production session already\n // exists, this is an add-account flow (connecting a secondary\n // Google account for scraping) and must never switch the active\n // user across sibling templates.\n if (opts.desktop && !opts.hasProductionSession) {\n await writeDesktopSso({\n email,\n token: sessionToken,\n expiresAt: Date.now() + maxAge * 1000,\n });\n }\n }\n\n return { sessionToken };\n}\n\n// ─── Callback Responses ──────────────────────────────────────────────────────\n\n/**\n * Return the appropriate response after a successful OAuth callback.\n *\n * Handles mobile deep links, desktop deep links, add-account close-tab\n * pages, and plain web redirects — so templates don't have to.\n */\nexport function oauthCallbackResponse(\n event: H3Event,\n email: string,\n opts: {\n sessionToken?: string;\n desktop?: boolean;\n addAccount?: boolean;\n /**\n * Same-origin path to return the viewer to after a successful web\n * sign-in. Validated via safeReturnPath; falls back to \"/\" for any\n * shape that escapes same-origin. Has no effect on desktop / mobile\n * / add-account flows — those use their own deep-link handling.\n */\n returnUrl?: string;\n flowId?: string;\n },\n): Response | string | unknown | Promise<Response | string | unknown> {\n const mobile = isMobile(event);\n const query = getQuery(event);\n const callbackState =\n typeof query.state === \"string\" && query.state.length > 0\n ? query.state\n : undefined;\n\n // Mobile: deep link back to native app\n if (mobile) {\n const deepLink = buildOAuthCompleteDeepLink(\n opts.sessionToken,\n callbackState,\n );\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"><title>Connected</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p>Connected! Returning to app…</p><script>window.location.href=${JSON.stringify(deepLink)};setTimeout(function(){window.location.href=\"/\"},1500)</script></body></html>`,\n );\n }\n\n // Desktop add-account: close-tab page (must come before general desktop check\n // to ensure no deep link fires and the existing session is never switched).\n if (opts.desktop && opts.addAccount) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to Agent Native.</p></body></html>`,\n );\n }\n\n // Desktop exchange flow (Tauri tray app): the tray app polls the\n // desktop-exchange endpoint for the token — no deep link needed.\n if (opts.desktop && opts.flowId) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Signed in as ${safeEmail}!` : \"Signed in!\";\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to Clips.</p></body></html>`,\n );\n }\n\n // Desktop login: deep link back to Electron app\n if (opts.desktop) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Add-account web flow: close-tab page. The email is rendered into the\n // page via DOM `textContent` (safe), but we still JSON-stringify so a\n // payload containing `</script>` can't break out of the script tag —\n // and explicitly assert it's a string so a callbacks like `null` or\n // an object won't end up serialised into the page.\n if (opts.addAccount) {\n const safeEmail = JSON.stringify(typeof email === \"string\" ? email : \"\");\n return htmlResponse(`<!DOCTYPE html><html><body><script>\n window.close();\n var p = document.createElement('p');\n p.style.cssText = 'font-family:system-ui;text-align:center;margin-top:40vh';\n p.textContent = 'Connected ' + ${safeEmail} + '! You can close this tab.';\n document.body.appendChild(p);\n </script></body></html>`);\n }\n\n // Web: redirect to the requested return path (validated same-origin) or\n // \"/\" if no return was supplied / the return failed validation. Returning\n // an empty string body keeps h3's `prepareResponseBody` → `FastResponse`\n // path, which merges the prepared event headers (Location + any cookies\n // set via `setCookie(event, ...)`).\n setResponseStatus(event, 302);\n setResponseHeader(event, \"Location\", safeReturnPath(opts.returnUrl));\n return \"\";\n}\n\n/** HTML error page for OAuth failures. The message is HTML-escaped — most\n * callers pass `error.message` from a token-exchange or userinfo failure,\n * which can echo upstream provider strings (and historically attacker-\n * controlled query params via the `error_description` field). */\nexport function oauthErrorPage(message: string): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><body>\n <div style=\"font-family:system-ui;max-width:420px;margin:30vh auto;text-align:center\">\n <p style=\"font-size:15px;color:#e55\">${safe}</p>\n <p style=\"margin-top:16px;font-size:13px;color:#888\"><a href=\"/\" style=\"color:#888\">Back to login</a></p>\n </div>\n </body></html>`,\n 400,\n );\n}\n\n// ─── Internal ────────────────────────────────────────────────────────────────\n\nfunction buildOAuthCompleteDeepLink(\n sessionToken?: string,\n state?: string,\n): string {\n const params = new URLSearchParams();\n if (sessionToken) params.set(\"token\", sessionToken);\n if (state) params.set(\"state\", state);\n const suffix = params.toString();\n return suffix\n ? `agentnative://oauth-complete?${suffix}`\n : \"agentnative://oauth-complete\";\n}\n\nfunction desktopSuccessPage(\n _event: H3Event,\n email?: string,\n sessionToken?: string,\n state?: string,\n): Response {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n if (sessionToken) {\n const deepLink = buildOAuthCompleteDeepLink(sessionToken, state);\n const deepLinkJson = JSON.stringify(deepLink);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title><style>@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.spinner{width:28px;height:28px;border:2px solid #333;border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}.fallback{display:none;flex-direction:column;align-items:center;gap:8px;animation:fadeIn .2s ease-out}.fallback.show{display:flex}</style></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:16px\"><p style=\"font-size:16px;margin:0\">${msg}</p><div id=\"loading\" class=\"spinner\"></div><div id=\"fallback\" class=\"fallback\"><a href=${deepLinkJson} style=\"display:inline-block;padding:10px 24px;background:#fff;color:#000;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500\">Open Agent Native</a><p style=\"font-size:12px;color:#666;margin:0\">If the app didn\\u2019t open automatically, click the button above.</p></div><script>window.location.href=${deepLinkJson};setTimeout(function(){document.getElementById(\"loading\").style.display=\"none\";document.getElementById(\"fallback\").classList.add(\"show\")},3000)</script></body></html>`,\n );\n }\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to Agent Native.</p></body></html>`,\n );\n}\n"]}
1
+ {"version":3,"file":"google-oauth.js","sourceRoot":"","sources":["../../src/server/google-oauth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared Google OAuth utilities for all templates.\n *\n * Handles platform detection (desktop/mobile), state encoding,\n * session token creation, and deep-link responses — the logic\n * that was previously copy-pasted across every template's\n * google-auth.ts handler.\n */\n\nimport crypto from \"node:crypto\";\nimport {\n getHeader,\n getQuery,\n setCookie,\n setResponseStatus,\n setResponseHeader,\n type H3Event,\n} from \"h3\";\nimport {\n addSession,\n getSession,\n COOKIE_NAME,\n getSessionMaxAge,\n safeReturnPath,\n} from \"./auth.js\";\nimport { getAppName } from \"./app-name.js\";\nimport { writeDesktopSso } from \"./desktop-sso.js\";\n\n// ─── Platform Detection ─────────────────────────────────────────────────────\n\n/** Return an HTML response with the correct Content-Type.\n * Uses a web-standard Response to ensure the header survives\n * Nitro dev mode's mock-node-response pipeline. */\nfunction htmlResponse(html: string, status = 200): Response {\n return new Response(html, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/**\n * HTML escape — minimal but covers the cases that matter when interpolating\n * user-controlled values into our OAuth callback HTML. Mirrors the helper in\n * email-template.ts; kept inline here to avoid a circular import.\n */\nfunction escapeHtml(s: string): string {\n return String(s ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/** Detect requests from the Electron desktop app webview. */\nexport function isElectron(event: H3Event): boolean {\n return /Electron/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/** Detect requests from a mobile browser (iOS/Android). */\nexport function isMobile(event: H3Event): boolean {\n return /iPhone|iPad|iPod|Android/i.test(getHeader(event, \"user-agent\") || \"\");\n}\n\n/**\n * Build the static allowlist of origins we trust for `getOrigin`. Reads\n * `APP_URL` and `BETTER_AUTH_URL` (both are deployment-known public URLs).\n * Each entry is normalised to `${proto}://${host}` (no path). Duplicates\n * collapse, invalid entries are dropped silently.\n */\nfunction getConfiguredOriginAllowlist(): Set<string> {\n const out = new Set<string>();\n for (const raw of [process.env.APP_URL, process.env.BETTER_AUTH_URL]) {\n if (!raw) continue;\n try {\n const u = new URL(raw);\n out.add(`${u.protocol}//${u.host}`);\n } catch {\n // Ignore — env value isn't a parseable URL.\n }\n }\n return out;\n}\n\n/**\n * Get the origin from forwarded headers or Host.\n *\n * Defends against Host-header injection: in production we require the\n * resolved origin to match `APP_URL` / `BETTER_AUTH_URL`, falling back to\n * those values when the inbound headers are missing or don't match. In\n * dev we accept the inbound `Host` so localhost / ngrok / preview hosts\n * keep working without configuration. The protocol defaults to `https`\n * in production (so a TLS-terminating proxy that drops `x-forwarded-proto`\n * doesn't downgrade us to plain HTTP).\n */\nexport function getOrigin(event: H3Event): string {\n const headerHost =\n getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const isProd = process.env.NODE_ENV === \"production\";\n const headerProto =\n getHeader(event, \"x-forwarded-proto\") || (isProd ? \"https\" : \"http\");\n\n if (isProd) {\n const allow = getConfiguredOriginAllowlist();\n // If the deploy declares its public URL, prefer it over inbound headers.\n if (allow.size > 0) {\n const inbound = headerHost ? `${headerProto}://${headerHost}` : \"\";\n if (inbound && allow.has(inbound)) return inbound;\n // Inbound didn't match — fall back to the first configured origin.\n return [...allow][0];\n }\n // No allowlist configured: still default to https, but accept the\n // inbound Host (best we can do without a configured base URL).\n return `${headerProto}://${headerHost ?? \"\"}`;\n }\n\n return `${headerProto}://${headerHost ?? \"localhost\"}`;\n}\n\nfunction normalizeAppBasePath(value: string | undefined): string {\n if (!value || value === \"/\") return \"\";\n const trimmed = value.trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n return `/${trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\n/** App mount prefix, if the template is served under APP_BASE_PATH. */\nexport function getAppBasePath(): string {\n return normalizeAppBasePath(\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH,\n );\n}\n\n/** Build an absolute same-origin URL that preserves APP_BASE_PATH. */\nexport function getAppUrl(event: H3Event, path = \"/\"): string {\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${getOrigin(event)}${getAppBasePath()}${cleanPath}`;\n}\n\n// ─── redirect_uri Allowlist ──────────────────────────────────────────────────\n\n/**\n * Validate a user-supplied `redirect_uri` for OAuth flows.\n *\n * Defends against authorization-code interception (RFC 6819 §4.4.1.7):\n * even though the upstream provider (Google/Atlassian/Zoom) refuses\n * unregistered redirect URIs, prefix-style registrations and side\n * registrations on the same host let a malicious caller swap in an\n * attacker-controlled URI that the provider still accepts. We reject any\n * candidate that isn't on this server's own origin AND under the\n * framework's `/_agent-native/` namespace. Returns the validated URI on\n * success, or `undefined` on rejection — callers must treat `undefined`\n * as a 400.\n *\n * The intentional shape is exact-prefix:\n * - Origin must equal `getOrigin(event)` — no Host-header injection\n * reusing somebody else's registered redirect URI.\n * - Path must start with `${appBasePath}/_agent-native/` so we never\n * hand auth codes to a public marketing or open-redirect endpoint\n * on the same registered host.\n *\n * For desktop / native flows that need ephemeral `http://127.0.0.1:<port>`\n * loopback URIs, callers should validate those at the template level\n * with a dedicated allowlist — this helper rejects them by design.\n */\nexport function isAllowedOAuthRedirectUri(\n candidate: string,\n event: H3Event,\n): boolean {\n if (typeof candidate !== \"string\" || candidate.length === 0) return false;\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n return false;\n }\n // Must be same origin as our server.\n const expectedOrigin = getOrigin(event);\n let expectedUrl: URL;\n try {\n expectedUrl = new URL(expectedOrigin);\n } catch {\n return false;\n }\n if (url.protocol !== expectedUrl.protocol) return false;\n if (url.host !== expectedUrl.host) return false;\n // Must live under the framework's namespace.\n const basePath = getAppBasePath();\n const required = `${basePath}/_agent-native/`;\n if (!url.pathname.startsWith(required)) return false;\n return true;\n}\n\n/**\n * Resolve the `redirect_uri` for an outbound OAuth `auth-url` request.\n *\n * Reads `?redirect_uri=` from the query and validates it via\n * `isAllowedOAuthRedirectUri`. Returns:\n * - the validated URI when supplied and allowed, OR\n * - the framework default when no override was supplied, OR\n * - `null` when an override was supplied but rejected — callers must\n * respond with 400 in that case.\n *\n * Templates that need a non-default redirect path can pass it via\n * `defaultPath` (e.g. `\"/_agent-native/google/desktop-callback\"` for\n * desktop flows).\n */\nexport function resolveOAuthRedirectUri(\n event: H3Event,\n defaultPath = \"/_agent-native/google/callback\",\n): string | null {\n const supplied = getQuery(event).redirect_uri;\n if (typeof supplied === \"string\" && supplied.length > 0) {\n return isAllowedOAuthRedirectUri(supplied, event) ? supplied : null;\n }\n return getAppUrl(event, defaultPath);\n}\n\n// ─── OAuth State ─────────────────────────────────────────────────────────────\n\nexport interface OAuthStatePayload {\n redirectUri: string;\n owner?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n /**\n * Same-origin path to redirect to after a successful web-flow sign-in.\n * Threaded through the (HMAC-signed) state so it survives the round trip\n * to Google. Validated again on decode via safeReturnPath as defence in\n * depth. Has no effect on desktop / mobile / add-account flows, which\n * use their own deep-link / close-tab handling.\n */\n returnUrl?: string;\n flowId?: string;\n}\n\n/**\n * Ephemeral in-memory state-signing key for development. Generated lazily\n * on first read so dev sessions don't depend on filesystem writability or\n * env-var configuration. Sessions reset on each restart, which is fine\n * for dev — no real users / production data are involved.\n */\nlet _devStateSigningKey: string | undefined;\n\n/**\n * Derive a server-only signing key for HMAC verification of OAuth state.\n *\n * Uses a dedicated secret — never an OAuth client secret. Reusing a\n * client_secret (which is shared with Google / GitHub / Atlassian) as our\n * own HMAC key conflates two trust domains: rotating the client secret\n * silently invalidates every in-flight OAuth state, and any leak of the\n * client secret also lets an attacker forge our state envelopes.\n *\n * Resolution order:\n * 1. OAUTH_STATE_SECRET (preferred — dedicated to this purpose)\n * 2. BETTER_AUTH_SECRET (already used by Better Auth as a server secret)\n * 3. In dev only, an ephemeral random key (per-process)\n *\n * In production, throws if neither secret is set.\n */\nfunction getStateSigningKey(): string {\n const secret =\n process.env.OAUTH_STATE_SECRET || process.env.BETTER_AUTH_SECRET;\n if (secret) return secret;\n\n const isProd = process.env.NODE_ENV === \"production\";\n if (isProd) {\n throw new Error(\n \"OAuth state signing requires a server secret. \" +\n \"Set OAUTH_STATE_SECRET or BETTER_AUTH_SECRET in production.\",\n );\n }\n\n if (!_devStateSigningKey) {\n _devStateSigningKey = crypto.randomBytes(32).toString(\"hex\");\n }\n return _devStateSigningKey;\n}\n\n/**\n * Options for the named-argument form of {@link encodeOAuthState}.\n * Prefer this form — the positional overload is easy to misuse (the mail\n * and calendar templates historically passed `flowId` in the `returnUrl`\n * slot, smuggling state into a defence-in-depth path).\n */\nexport interface EncodeOAuthStateOptions {\n redirectUri: string;\n owner?: string;\n desktop?: boolean;\n addAccount?: boolean;\n app?: string;\n returnUrl?: string;\n flowId?: string;\n}\n\n/**\n * Encode OAuth state into a signed base64url string.\n * The state is HMAC-signed so the callback can verify it wasn't forged,\n * preventing CSRF attacks on the OAuth flow.\n *\n * Two call shapes are supported:\n * - Recommended: pass an options object — clear, mismatch-proof.\n * `encodeOAuthState({ redirectUri, owner, desktop, ... })`\n * - Legacy positional form (kept working for backward compatibility):\n * `encodeOAuthState(redirectUri, owner, desktop, addAccount, app, returnUrl, flowId)`.\n * Callers should migrate to the options form — see the audit on\n * templates/mail and templates/calendar where the positional shape\n * led to `flowId` being smuggled in via the `returnUrl` slot.\n */\nexport function encodeOAuthState(opts: EncodeOAuthStateOptions): string;\nexport function encodeOAuthState(\n redirectUri: string,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string;\nexport function encodeOAuthState(\n redirectUriOrOpts: string | EncodeOAuthStateOptions,\n owner?: string,\n desktop?: boolean,\n addAccount?: boolean,\n app?: string,\n returnUrl?: string,\n flowId?: string,\n): string {\n const opts: EncodeOAuthStateOptions =\n typeof redirectUriOrOpts === \"string\"\n ? {\n redirectUri: redirectUriOrOpts,\n owner,\n desktop,\n addAccount,\n app,\n returnUrl,\n flowId,\n }\n : redirectUriOrOpts;\n\n const nonce = crypto.randomBytes(8).toString(\"hex\");\n const payload: Record<string, string | boolean> = {\n n: nonce,\n r: opts.redirectUri,\n };\n if (opts.owner) payload.o = opts.owner;\n if (opts.desktop) payload.d = true;\n if (opts.addAccount) payload.a = true;\n if (opts.app) payload.app = opts.app;\n if (opts.returnUrl) payload.r2 = opts.returnUrl;\n if (opts.flowId) payload.f = opts.flowId;\n const data = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n return `${data}.${sig}`;\n}\n\n/**\n * Decode and verify OAuth state from the callback's state query parameter.\n * Rejects forged or tampered state by checking the HMAC signature.\n * Falls back to the provided URI if decoding or verification fails.\n */\nexport function decodeOAuthState(\n stateParam: string | undefined,\n fallbackUri: string,\n): OAuthStatePayload {\n if (stateParam) {\n try {\n const dotIdx = stateParam.lastIndexOf(\".\");\n if (dotIdx === -1) return { redirectUri: fallbackUri };\n\n const data = stateParam.slice(0, dotIdx);\n const sig = stateParam.slice(dotIdx + 1);\n const expected = crypto\n .createHmac(\"sha256\", getStateSigningKey())\n .update(data)\n .digest(\"base64url\");\n\n if (\n sig.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))\n ) {\n return { redirectUri: fallbackUri };\n }\n\n const parsed = JSON.parse(Buffer.from(data, \"base64url\").toString());\n return {\n redirectUri: parsed.r || fallbackUri,\n owner: parsed.o || undefined,\n desktop: !!parsed.d,\n addAccount: !!parsed.a,\n app: typeof parsed.app === \"string\" ? parsed.app : undefined,\n // Pass returnUrl through as-is — same-origin validation runs at the\n // consumer (oauthCallbackResponse → safeReturnPath). The state is\n // HMAC-signed, but we still validate at consumption as defence in\n // depth in case the signing key ever leaks.\n returnUrl: typeof parsed.r2 === \"string\" ? parsed.r2 : undefined,\n flowId: parsed.f || undefined,\n };\n } catch {}\n }\n return { redirectUri: fallbackUri };\n}\n\n// ─── Session Creation ────────────────────────────────────────────────────────\n\nexport interface OAuthOwnerResult {\n owner: string | undefined;\n isDevSession: boolean;\n hasProductionSession: boolean;\n}\n\n/**\n * Determine the token owner from the current session and OAuth state.\n * Call this BEFORE exchangeCode to get the owner parameter.\n */\nexport async function resolveOAuthOwner(\n event: H3Event,\n stateOwner?: string,\n): Promise<OAuthOwnerResult> {\n const existingSession = await getSession(event);\n const isDevSession = existingSession?.email === \"local@localhost\";\n const hasProductionSession = !!(existingSession?.email && !isDevSession);\n\n // Never use \"local@localhost\" as a token owner — it creates shared-ownership\n // bugs where multiple users can see the same tokens.\n const owner = hasProductionSession\n ? existingSession!.email\n : stateOwner || undefined;\n\n return { owner, isDevSession, hasProductionSession };\n}\n\nexport interface OAuthSessionResult {\n sessionToken: string | undefined;\n}\n\n/**\n * Create a session token after a successful OAuth exchange.\n *\n * Desktop and mobile apps have separate cookie jars from the system\n * browser, so they always get a fresh session token (even if the browser\n * already has one). The token is then passed via deep link so the native\n * app can inject it.\n */\nexport async function createOAuthSession(\n event: H3Event,\n email: string,\n opts: {\n hasProductionSession: boolean;\n desktop?: boolean;\n },\n): Promise<OAuthSessionResult> {\n const mobile = isMobile(event);\n const needsDeepLink = opts.desktop || mobile;\n const maxAge = getSessionMaxAge();\n\n let sessionToken: string | undefined;\n if (!opts.hasProductionSession || needsDeepLink) {\n sessionToken = crypto.randomBytes(32).toString(\"hex\");\n await addSession(sessionToken, email);\n setCookie(event, COOKIE_NAME, sessionToken, {\n httpOnly: true,\n secure: process.env.NODE_ENV === \"production\",\n sameSite: \"lax\",\n path: \"/\",\n maxAge,\n });\n // Desktop SSO: record this session in the home-dir broker file so\n // sibling templates (each with its own database) can resolve the\n // same token without a DB row of their own. Only the PRIMARY\n // sign-in writes the broker — if a production session already\n // exists, this is an add-account flow (connecting a secondary\n // Google account for scraping) and must never switch the active\n // user across sibling templates.\n if (opts.desktop && !opts.hasProductionSession) {\n await writeDesktopSso({\n email,\n token: sessionToken,\n expiresAt: Date.now() + maxAge * 1000,\n });\n }\n }\n\n return { sessionToken };\n}\n\n// ─── Callback Responses ──────────────────────────────────────────────────────\n\n/**\n * Return the appropriate response after a successful OAuth callback.\n *\n * Handles mobile deep links, desktop deep links, add-account close-tab\n * pages, and plain web redirects — so templates don't have to.\n */\nexport function oauthCallbackResponse(\n event: H3Event,\n email: string,\n opts: {\n sessionToken?: string;\n desktop?: boolean;\n addAccount?: boolean;\n /**\n * Same-origin path to return the viewer to after a successful web\n * sign-in. Validated via safeReturnPath; falls back to \"/\" for any\n * shape that escapes same-origin. Has no effect on desktop / mobile\n * / add-account flows — those use their own deep-link handling.\n */\n returnUrl?: string;\n flowId?: string;\n appName?: string;\n },\n): Response | string | unknown | Promise<Response | string | unknown> {\n const mobile = isMobile(event);\n const query = getQuery(event);\n const callbackState =\n typeof query.state === \"string\" && query.state.length > 0\n ? query.state\n : undefined;\n\n // Mobile: deep link back to native app\n if (mobile) {\n const deepLink = buildOAuthCompleteDeepLink(\n opts.sessionToken,\n callbackState,\n );\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"><title>Connected</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p>Connected! Returning to app…</p><script>window.location.href=${JSON.stringify(deepLink)};setTimeout(function(){window.location.href=\"/\"},1500)</script></body></html>`,\n );\n }\n\n // Desktop add-account: close-tab page (must come before general desktop check\n // to ensure no deep link fires and the existing session is never switched).\n if (opts.desktop && opts.addAccount) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to ${safeAppName}.</p></body></html>`,\n );\n }\n\n // Electron desktop exchange flow: mail/calendar still pass a flow id so the\n // renderer can poll as a fallback, but the main handoff should use the\n // protocol deep link so the popup returns focus to the desktop app.\n if (opts.desktop && opts.flowId && isElectron(event) && opts.sessionToken) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Desktop exchange flow (non-Electron tray app): the tray app polls the\n // desktop-exchange endpoint for the token — no deep link needed.\n if (opts.desktop && opts.flowId) {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const safeAppName = escapeHtml(resolveOAuthAppName(opts.appName));\n const msg = safeEmail ? `Signed in as ${safeEmail}!` : \"Signed in!\";\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to ${safeAppName}.</p></body></html>`,\n );\n }\n\n // Desktop login: deep link back to Electron app\n if (opts.desktop) {\n return desktopSuccessPage(event, email, opts.sessionToken, callbackState);\n }\n\n // Add-account web flow: close-tab page. The email is rendered into the\n // page via DOM `textContent` (safe), but we still JSON-stringify so a\n // payload containing `</script>` can't break out of the script tag —\n // and explicitly assert it's a string so a callbacks like `null` or\n // an object won't end up serialised into the page.\n if (opts.addAccount) {\n const safeEmail = JSON.stringify(typeof email === \"string\" ? email : \"\");\n return htmlResponse(`<!DOCTYPE html><html><body><script>\n window.close();\n var p = document.createElement('p');\n p.style.cssText = 'font-family:system-ui;text-align:center;margin-top:40vh';\n p.textContent = 'Connected ' + ${safeEmail} + '! You can close this tab.';\n document.body.appendChild(p);\n </script></body></html>`);\n }\n\n // Web: redirect to the requested return path (validated same-origin) or\n // \"/\" if no return was supplied / the return failed validation. Returning\n // an empty string body keeps h3's `prepareResponseBody` → `FastResponse`\n // path, which merges the prepared event headers (Location + any cookies\n // set via `setCookie(event, ...)`).\n setResponseStatus(event, 302);\n setResponseHeader(event, \"Location\", safeReturnPath(opts.returnUrl));\n return \"\";\n}\n\n/** HTML error page for OAuth failures. The message is HTML-escaped — most\n * callers pass `error.message` from a token-exchange or userinfo failure,\n * which can echo upstream provider strings (and historically attacker-\n * controlled query params via the `error_description` field). */\nexport function oauthErrorPage(message: string): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><body>\n <div style=\"font-family:system-ui;max-width:420px;margin:30vh auto;text-align:center\">\n <p style=\"font-size:15px;color:#e55\">${safe}</p>\n <p style=\"margin-top:16px;font-size:13px;color:#888\"><a href=\"/\" style=\"color:#888\">Back to login</a></p>\n </div>\n </body></html>`,\n 400,\n );\n}\n\nexport function oauthDesktopExchangePage(\n message = \"Returning to the app...\",\n): Response {\n const safe = escapeHtml(message);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Returning</title></head><body style=\"background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0\"><p style=\"font-size:14px\">${safe}</p><script>window.close()</script></body></html>`,\n );\n}\n\n// ─── Internal ────────────────────────────────────────────────────────────────\n\nfunction resolveOAuthAppName(explicit?: string): string {\n const raw = explicit || getAppName() || \"Agent Native\";\n if (!/^[a-z0-9_-]+$/.test(raw)) return raw;\n return raw\n .split(/[-_]+/)\n .filter(Boolean)\n .map((word) => word[0].toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\nfunction buildOAuthCompleteDeepLink(\n sessionToken?: string,\n state?: string,\n): string {\n const params = new URLSearchParams();\n if (sessionToken) params.set(\"token\", sessionToken);\n if (state) params.set(\"state\", state);\n const suffix = params.toString();\n return suffix\n ? `agentnative://oauth-complete?${suffix}`\n : \"agentnative://oauth-complete\";\n}\n\nfunction desktopSuccessPage(\n _event: H3Event,\n email?: string,\n sessionToken?: string,\n state?: string,\n): Response {\n const safeEmail = email ? escapeHtml(email) : \"\";\n const msg = safeEmail ? `Connected ${safeEmail}!` : \"Connected!\";\n if (sessionToken) {\n const deepLink = buildOAuthCompleteDeepLink(sessionToken, state);\n const deepLinkJson = JSON.stringify(deepLink);\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title><style>@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.spinner{width:28px;height:28px;border:2px solid #333;border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}.fallback{display:none;flex-direction:column;align-items:center;gap:8px;animation:fadeIn .2s ease-out}.fallback.show{display:flex}</style></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:16px\"><p style=\"font-size:16px;margin:0\">${msg}</p><div id=\"loading\" class=\"spinner\"></div><div id=\"fallback\" class=\"fallback\"><a href=${deepLinkJson} style=\"display:inline-block;padding:10px 24px;background:#fff;color:#000;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500\">Open Agent Native</a><p style=\"font-size:12px;color:#666;margin:0\">If the app didn\\u2019t open automatically, click the button above.</p></div><script>window.location.href=${deepLinkJson};setTimeout(function(){document.getElementById(\"loading\").style.display=\"none\";document.getElementById(\"fallback\").classList.add(\"show\")},3000)</script></body></html>`,\n );\n }\n return htmlResponse(\n `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Connected</title></head><body style=\"background:#111;color:#ccc;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:8px\"><p style=\"font-size:16px\">${msg}</p><p style=\"font-size:13px;color:#888\">You can close this tab and return to Agent Native.</p></body></html>`,\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  export { createServer, upsertEnvFile, type CreateServerOptions, type EnvKeyConfig, } from "./create-server.js";
2
2
  export { readBody, streamFile } from "./h3-helpers.js";
3
3
  export { createSSEHandler, type SSEHandlerOptions } from "./sse.js";
4
- export { mountAuthMiddleware, autoMountAuth, getSession, addSession, removeSession, getSessionEmail, runAuthGuard, setDesktopExchange, DEV_MODE_USER_EMAIL, safeReturnPath, type AuthSession, type AuthOptions, } from "./auth.js";
4
+ export { mountAuthMiddleware, autoMountAuth, getSession, addSession, removeSession, getSessionEmail, runAuthGuard, setDesktopExchange, setDesktopExchangeError, DEV_MODE_USER_EMAIL, safeReturnPath, type DesktopExchangeErrorPayload, type AuthSession, type AuthOptions, } from "./auth.js";
5
5
  export { requireEnvKey, type MissingKeyResponse } from "./missing-key.js";
6
6
  export { verifyCaptcha, type CaptchaVerifyResult } from "./captcha.js";
7
7
  export { createProductionAgentHandler, type ActionEntry, type ScriptEntry, type ProductionAgentOptions, type ActionTool, type ScriptTool, type AgentMessage, type AgentChatRequest, type AgentChatEvent, type AgentChatReference, type MentionProvider, type MentionProviderItem, } from "../agent/index.js";
@@ -27,8 +27,9 @@ export { formatDateInTimezone, todayInTimezone } from "./date-utils.js";
27
27
  export { createOnboardingPlugin, defaultOnboardingPlugin, } from "../onboarding/plugin.js";
28
28
  export { registerFileUploadProvider, unregisterFileUploadProvider, listFileUploadProviders, getActiveFileUploadProvider, uploadFile, builderFileUploadProvider, type FileUploadInput, type FileUploadProvider, type FileUploadResult, } from "../file-upload/index.js";
29
29
  export { createIntegrationsPlugin, defaultIntegrationsPlugin, slackAdapter, telegramAdapter, whatsappAdapter, emailAdapter, type PlatformAdapter, type IncomingMessage, type OutgoingMessage, type IntegrationStatus, type IntegrationsPluginOptions, } from "../integrations/index.js";
30
- export { isElectron, isMobile, getOrigin, getAppBasePath, getAppUrl, encodeOAuthState, decodeOAuthState, resolveOAuthOwner, createOAuthSession, oauthCallbackResponse, oauthErrorPage, type OAuthStatePayload, type OAuthOwnerResult, type OAuthSessionResult, } from "./google-oauth.js";
31
- export { FeatureNotConfiguredError, hasBuilderPrivateKey, getBuilderProxyOrigin, getBuilderAuthHeader, resolveBuilderPrivateKey, resolveBuilderAuthHeader, resolveHasBuilderPrivateKey, resolveBuilderCredentials, resolveBuilderCredential, writeBuilderCredentials, deleteBuilderCredentials, resolveSecret, } from "./credential-provider.js";
30
+ export { isElectron, isMobile, getOrigin, getAppBasePath, getAppUrl, encodeOAuthState, decodeOAuthState, resolveOAuthOwner, createOAuthSession, oauthCallbackResponse, oauthErrorPage, oauthDesktopExchangePage, type OAuthStatePayload, type OAuthOwnerResult, type OAuthSessionResult, } from "./google-oauth.js";
31
+ export { FeatureNotConfiguredError, hasBuilderPrivateKey, isBuilderEnvManaged, getBuilderProxyOrigin, getBuilderAuthHeader, resolveBuilderPrivateKey, resolveBuilderAuthHeader, resolveHasBuilderPrivateKey, resolveBuilderCredentials, resolveBuilderCredential, writeBuilderCredentials, deleteBuilderCredentials, resolveSecret, } from "./credential-provider.js";
32
+ export { getBuilderBranchProjectId, isBuilderBranchingEnabled, runBuilderAgent, type RunBuilderAgentResult, } from "./builder-browser.js";
32
33
  export { sendEmail, isEmailConfigured, getEmailProvider, type EmailProvider, type SendEmailArgs, } from "./email.js";
33
34
  export { renderEmail, emailStrong, type RenderEmailArgs, type RenderedEmail, type EmailCta, } from "./email-template.js";
34
35
  export { getAppProductionUrl, getFirstPartyProdUrl } from "./app-url.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}