@agent-native/core 0.115.4 → 0.116.0

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 (408) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/corpus/README.md +3 -3
  3. package/corpus/core/CHANGELOG.md +28 -0
  4. package/corpus/core/agent-native.eject.json +1 -0
  5. package/corpus/core/docs/AGENTS.md +1 -1
  6. package/corpus/core/docs/content/agent-mentions.mdx +2 -2
  7. package/corpus/core/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
  8. package/corpus/core/docs/content/agent-teams.mdx +4 -4
  9. package/corpus/core/docs/content/dispatch.mdx +10 -10
  10. package/corpus/core/docs/content/drop-in-agent.mdx +4 -4
  11. package/corpus/core/docs/content/external-agents.mdx +1 -1
  12. package/corpus/core/docs/content/frames.mdx +1 -1
  13. package/corpus/core/docs/content/integrations.mdx +13 -2
  14. package/corpus/core/docs/content/key-concepts.mdx +2 -2
  15. package/corpus/core/docs/content/mcp-clients.mdx +7 -1
  16. package/corpus/core/docs/content/multi-app-workspace.mdx +3 -3
  17. package/corpus/core/docs/content/real-time-collaboration.mdx +33 -15
  18. package/corpus/core/docs/content/recurring-jobs.mdx +7 -7
  19. package/corpus/core/docs/content/skills-guide.mdx +4 -4
  20. package/corpus/core/docs/content/template-brain.mdx +1 -1
  21. package/corpus/core/docs/content/toolkit-collaboration.mdx +1 -1
  22. package/corpus/core/docs/content/using-your-agent.mdx +1 -1
  23. package/corpus/core/docs/content/what-is-agent-native.mdx +4 -4
  24. package/corpus/core/docs/content/workspace-management.mdx +4 -4
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/agent/production-agent.ts +13 -3
  27. package/corpus/core/src/cli/doctor.ts +6 -1
  28. package/corpus/core/src/client/AgentPanel.tsx +79 -41
  29. package/corpus/core/src/client/AssistantChat.tsx +71 -25
  30. package/corpus/core/src/client/FeedbackButton.tsx +33 -7
  31. package/corpus/core/src/client/MultiTabAssistantChat.tsx +6 -8
  32. package/corpus/core/src/client/agent-chat-adapter.ts +11 -2
  33. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +22 -3
  34. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +34 -19
  35. package/corpus/core/src/client/chat/message-components.tsx +43 -16
  36. package/corpus/core/src/client/chat/tool-call-display.tsx +30 -18
  37. package/corpus/core/src/client/chat/tool-render-registry.tsx +2 -0
  38. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  39. package/corpus/core/src/client/resources/ResourceTree.tsx +7 -10
  40. package/corpus/core/src/client/resources/ResourcesPanel.tsx +33 -70
  41. package/corpus/core/src/client/resources/mcp-connection-resume.ts +107 -0
  42. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +65 -12
  43. package/corpus/core/src/client/resources/mcp-integration-logos.ts +4 -0
  44. package/corpus/core/src/client/sse-event-processor.ts +50 -14
  45. package/corpus/core/src/client/use-db-sync.ts +16 -6
  46. package/corpus/core/src/connections/catalog.ts +7 -1
  47. package/corpus/core/src/eject/remote-mcp-presets.ts +37 -5
  48. package/corpus/core/src/guards/explicit-collab-access.ts +229 -0
  49. package/corpus/core/src/guards/index.ts +1 -0
  50. package/corpus/core/src/localization/default-messages.ts +54 -5
  51. package/corpus/core/src/mcp/connect-route.ts +34 -51
  52. package/corpus/core/src/provider-api/index.ts +1 -1
  53. package/corpus/core/src/server/collab-plugin.ts +137 -18
  54. package/corpus/core/src/server/index.ts +2 -0
  55. package/corpus/core/src/styles/agent-native.css +40 -0
  56. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  57. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  58. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  59. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  60. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  61. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  62. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  63. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  64. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  65. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  66. package/corpus/templates/analytics/AGENTS.md +7 -2
  67. package/corpus/templates/analytics/actions/export-dashboard-panel-to-google-sheet.ts +144 -0
  68. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -88
  69. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +34 -37
  70. package/corpus/templates/analytics/app/global.css +0 -4
  71. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -7
  72. package/corpus/templates/analytics/app/i18n-data.ts +107 -0
  73. package/corpus/templates/analytics/app/lib/data-source-status.ts +9 -0
  74. package/corpus/templates/analytics/app/lib/sql-query.ts +1 -2
  75. package/corpus/templates/analytics/app/pages/Ask.tsx +2 -7
  76. package/corpus/templates/analytics/app/pages/DataSources.tsx +48 -0
  77. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +59 -0
  78. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -0
  79. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -1
  80. package/corpus/templates/analytics/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  81. package/corpus/templates/analytics/changelog/2026-07-22-daily-dashboard-email-query-batches.md +6 -0
  82. package/corpus/templates/analytics/changelog/2026-07-22-export-table-panels-directly-to-google-sheets.md +6 -0
  83. package/corpus/templates/analytics/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  84. package/corpus/templates/analytics/changelog/2026-07-22-recent-chats-can-expand-in-the-sidebar.md +6 -0
  85. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +8 -8
  86. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +31 -0
  87. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +3 -5
  88. package/corpus/templates/analytics/server/lib/dashboard-report.ts +12 -0
  89. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +3 -5
  90. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +150 -21
  91. package/corpus/templates/analytics/server/lib/google-sheets-export.ts +188 -0
  92. package/corpus/templates/analytics/server/lib/provider-api.ts +7 -2
  93. package/corpus/templates/analytics/server/plugins/collab.ts +6 -3
  94. package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
  95. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  96. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +35 -13
  97. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  98. package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
  99. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +54 -62
  100. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -6
  101. package/corpus/templates/assets/app/routes/_index.tsx +8 -5
  102. package/corpus/templates/assets/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  103. package/corpus/templates/assets/changelog/2026-07-22-recent-chats-expand-from-a-compact-list.md +6 -0
  104. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  105. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  106. package/corpus/templates/brain/actions/rotate-source-ingest-token.ts +77 -0
  107. package/corpus/templates/brain/app/components/layout/Layout.tsx +5 -1
  108. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -51
  109. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +2 -9
  110. package/corpus/templates/brain/app/i18n-data.ts +120 -0
  111. package/corpus/templates/brain/app/routes/sources.tsx +156 -5
  112. package/corpus/templates/brain/changelog/2026-07-22-chat-runs-always-end-with-an-answer.md +6 -0
  113. package/corpus/templates/brain/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
  114. package/corpus/templates/brain/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  115. package/corpus/templates/brain/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  116. package/corpus/templates/brain/changelog/2026-07-22-long-chat-titles-stay-inside-the-sidebar.md +6 -0
  117. package/corpus/templates/brain/changelog/2026-07-22-new-clips-and-webhook-sources-now-show-their-secure-connecti.md +6 -0
  118. package/corpus/templates/brain/jobs/emit-netlify-brain-queue-cron.ts +125 -0
  119. package/corpus/templates/brain/netlify.toml +5 -1
  120. package/corpus/templates/brain/scripts/configure-prod-clips-source.ts +50 -0
  121. package/corpus/templates/brain/server/lib/capture-sanitization.ts +64 -6
  122. package/corpus/templates/brain/server/plugins/agent-chat.ts +4 -0
  123. package/corpus/templates/brain/server/plugins/brain-jobs.ts +1 -0
  124. package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +55 -0
  125. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  126. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  127. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  128. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  129. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -0
  130. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  131. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  132. package/corpus/templates/chat/app/routes/_index.tsx +8 -5
  133. package/corpus/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  134. package/corpus/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  135. package/corpus/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  136. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  137. package/corpus/templates/clips/AGENTS.md +9 -1
  138. package/corpus/templates/clips/actions/export-to-brain.ts +82 -6
  139. package/corpus/templates/clips/actions/request-transcript.ts +25 -14
  140. package/corpus/templates/clips/changelog/2026-07-22-clips-transcripts-now-retry-brain-export.md +5 -0
  141. package/corpus/templates/clips/changelog/2026-07-22-german-umlauts-and-other-non-ascii-characters-now-paste-corr.md +6 -0
  142. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +26 -3
  143. package/corpus/templates/clips/jobs/emit-netlify-brain-export-cron.ts +118 -0
  144. package/corpus/templates/clips/netlify.toml +4 -1
  145. package/corpus/templates/clips/scripts/configure-prod-brain-export.ts +92 -0
  146. package/corpus/templates/clips/server/jobs/brain-export.ts +182 -0
  147. package/corpus/templates/clips/server/lib/brain-export-state.ts +42 -0
  148. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +5 -1
  149. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  150. package/corpus/templates/clips/server/register-secrets.ts +42 -0
  151. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +10 -1
  152. package/corpus/templates/clips/server/routes/api/clips/brain-export/run.post.ts +45 -0
  153. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  154. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  155. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -1
  156. package/corpus/templates/content/server/plugins/collab.ts +1 -1
  157. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  158. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  159. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +2 -0
  160. package/corpus/templates/crm/actions/run.ts +2 -6
  161. package/corpus/templates/crm/actions/update-crm-record.ts +4 -2
  162. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +6 -0
  163. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +6 -0
  164. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +6 -0
  165. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +6 -0
  166. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  167. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +4 -1
  168. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  169. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  170. package/corpus/templates/design/server/plugins/collab.ts +11 -8
  171. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  172. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  173. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  174. package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -0
  175. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +1 -1
  176. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +2 -9
  177. package/corpus/templates/forms/app/root.tsx +1 -6
  178. package/corpus/templates/forms/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  179. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  180. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  181. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  182. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  183. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  184. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  185. package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -1
  186. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +57 -72
  187. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +2 -9
  188. package/corpus/templates/plan/app/lib/create-plan-routing.ts +2 -2
  189. package/corpus/templates/plan/app/pages/PlanChatPage.tsx +0 -1
  190. package/corpus/templates/plan/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  191. package/corpus/templates/plan/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  192. package/corpus/templates/plan/changelog/2026-07-22-recent-chats-and-sidebar-controls-are-more-compact.md +6 -0
  193. package/corpus/templates/plan/server/plugins/collab.ts +5 -2
  194. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  195. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +1 -1
  196. package/corpus/templates/slides/server/plugins/collab.ts +11 -8
  197. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  198. package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
  199. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +5 -22
  200. package/corpus/templates/tasks/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  201. package/corpus/toolkit/CHANGELOG.md +7 -0
  202. package/corpus/toolkit/README.md +12 -3
  203. package/corpus/toolkit/package.json +1 -1
  204. package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +89 -0
  205. package/corpus/toolkit/src/chat-history/index.ts +5 -0
  206. package/corpus/toolkit/src/chat-history.css +59 -0
  207. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +106 -36
  208. package/corpus/toolkit/src/composer/VoiceButton.tsx +49 -0
  209. package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +2 -1
  210. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +5 -3
  211. package/corpus/toolkit/src/styles.css +35 -0
  212. package/dist/agent/production-agent.d.ts.map +1 -1
  213. package/dist/agent/production-agent.js +13 -3
  214. package/dist/agent/production-agent.js.map +1 -1
  215. package/dist/cli/doctor.d.ts +1 -1
  216. package/dist/cli/doctor.d.ts.map +1 -1
  217. package/dist/cli/doctor.js +5 -2
  218. package/dist/cli/doctor.js.map +1 -1
  219. package/dist/client/AgentPanel.d.ts +1 -0
  220. package/dist/client/AgentPanel.d.ts.map +1 -1
  221. package/dist/client/AgentPanel.js +38 -18
  222. package/dist/client/AgentPanel.js.map +1 -1
  223. package/dist/client/AssistantChat.d.ts.map +1 -1
  224. package/dist/client/AssistantChat.js +55 -20
  225. package/dist/client/AssistantChat.js.map +1 -1
  226. package/dist/client/FeedbackButton.d.ts +4 -2
  227. package/dist/client/FeedbackButton.d.ts.map +1 -1
  228. package/dist/client/FeedbackButton.js +18 -2
  229. package/dist/client/FeedbackButton.js.map +1 -1
  230. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  231. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  232. package/dist/client/MultiTabAssistantChat.js +2 -3
  233. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  234. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  235. package/dist/client/agent-chat-adapter.js +11 -3
  236. package/dist/client/agent-chat-adapter.js.map +1 -1
  237. package/dist/client/agent-page/AgentTabFrame.d.ts +3 -1
  238. package/dist/client/agent-page/AgentTabFrame.d.ts.map +1 -1
  239. package/dist/client/agent-page/AgentTabFrame.js +3 -2
  240. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  241. package/dist/client/agent-page/AgentTabsPage.d.ts +2 -0
  242. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  243. package/dist/client/agent-page/AgentTabsPage.js +26 -15
  244. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  245. package/dist/client/chat/message-components.d.ts +6 -1
  246. package/dist/client/chat/message-components.d.ts.map +1 -1
  247. package/dist/client/chat/message-components.js +17 -11
  248. package/dist/client/chat/message-components.js.map +1 -1
  249. package/dist/client/chat/tool-call-display.d.ts +11 -1
  250. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  251. package/dist/client/chat/tool-call-display.js +16 -17
  252. package/dist/client/chat/tool-call-display.js.map +1 -1
  253. package/dist/client/chat/tool-render-registry.d.ts +2 -0
  254. package/dist/client/chat/tool-render-registry.d.ts.map +1 -1
  255. package/dist/client/chat/tool-render-registry.js.map +1 -1
  256. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  257. package/dist/client/resources/McpConnectionSuggestion.js +15 -1
  258. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  259. package/dist/client/resources/ResourceTree.d.ts.map +1 -1
  260. package/dist/client/resources/ResourceTree.js +3 -3
  261. package/dist/client/resources/ResourceTree.js.map +1 -1
  262. package/dist/client/resources/ResourcesPanel.d.ts +1 -0
  263. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  264. package/dist/client/resources/ResourcesPanel.js +34 -20
  265. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  266. package/dist/client/resources/mcp-connection-resume.d.ts +11 -0
  267. package/dist/client/resources/mcp-connection-resume.d.ts.map +1 -0
  268. package/dist/client/resources/mcp-connection-resume.js +95 -0
  269. package/dist/client/resources/mcp-connection-resume.js.map +1 -0
  270. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  271. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  272. package/dist/client/resources/mcp-integration-catalog.js +63 -8
  273. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  274. package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -1
  275. package/dist/client/resources/mcp-integration-logos.js +4 -0
  276. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  277. package/dist/client/sse-event-processor.d.ts +5 -0
  278. package/dist/client/sse-event-processor.d.ts.map +1 -1
  279. package/dist/client/sse-event-processor.js +39 -13
  280. package/dist/client/sse-event-processor.js.map +1 -1
  281. package/dist/client/use-db-sync.d.ts +3 -2
  282. package/dist/client/use-db-sync.d.ts.map +1 -1
  283. package/dist/client/use-db-sync.js +17 -6
  284. package/dist/client/use-db-sync.js.map +1 -1
  285. package/dist/collab/routes.d.ts +1 -1
  286. package/dist/connections/catalog.d.ts +1 -1
  287. package/dist/connections/catalog.d.ts.map +1 -1
  288. package/dist/connections/catalog.js +7 -1
  289. package/dist/connections/catalog.js.map +1 -1
  290. package/dist/eject/remote-mcp-presets.d.ts.map +1 -1
  291. package/dist/eject/remote-mcp-presets.js +33 -3
  292. package/dist/eject/remote-mcp-presets.js.map +1 -1
  293. package/dist/guards/explicit-collab-access.d.ts +8 -0
  294. package/dist/guards/explicit-collab-access.d.ts.map +1 -0
  295. package/dist/guards/explicit-collab-access.js +214 -0
  296. package/dist/guards/explicit-collab-access.js.map +1 -0
  297. package/dist/guards/index.d.ts +1 -0
  298. package/dist/guards/index.d.ts.map +1 -1
  299. package/dist/guards/index.js +1 -0
  300. package/dist/guards/index.js.map +1 -1
  301. package/dist/localization/default-messages.d.ts +43 -0
  302. package/dist/localization/default-messages.d.ts.map +1 -1
  303. package/dist/localization/default-messages.js +48 -5
  304. package/dist/localization/default-messages.js.map +1 -1
  305. package/dist/mcp/connect-route.d.ts.map +1 -1
  306. package/dist/mcp/connect-route.js +34 -48
  307. package/dist/mcp/connect-route.js.map +1 -1
  308. package/dist/notifications/routes.d.ts +3 -3
  309. package/dist/observability/routes.d.ts +1 -1
  310. package/dist/progress/routes.d.ts +1 -1
  311. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  312. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  313. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  314. package/dist/provider-api/index.js +1 -1
  315. package/dist/provider-api/index.js.map +1 -1
  316. package/dist/server/collab-plugin.d.ts +34 -3
  317. package/dist/server/collab-plugin.d.ts.map +1 -1
  318. package/dist/server/collab-plugin.js +65 -12
  319. package/dist/server/collab-plugin.js.map +1 -1
  320. package/dist/server/index.d.ts +1 -1
  321. package/dist/server/index.d.ts.map +1 -1
  322. package/dist/server/index.js.map +1 -1
  323. package/dist/styles/agent-native.css +40 -0
  324. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  325. package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  326. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -0
  327. package/dist/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  328. package/dist/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  329. package/dist/templates/chat/app/routes/_index.tsx +8 -5
  330. package/dist/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  331. package/dist/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  332. package/dist/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  333. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  334. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  335. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  336. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  337. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  338. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  339. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  340. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
  341. package/docs/AGENTS.md +1 -1
  342. package/docs/content/agent-mentions.mdx +2 -2
  343. package/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
  344. package/docs/content/agent-teams.mdx +4 -4
  345. package/docs/content/dispatch.mdx +10 -10
  346. package/docs/content/drop-in-agent.mdx +4 -4
  347. package/docs/content/external-agents.mdx +1 -1
  348. package/docs/content/frames.mdx +1 -1
  349. package/docs/content/integrations.mdx +13 -2
  350. package/docs/content/key-concepts.mdx +2 -2
  351. package/docs/content/mcp-clients.mdx +7 -1
  352. package/docs/content/multi-app-workspace.mdx +3 -3
  353. package/docs/content/real-time-collaboration.mdx +33 -15
  354. package/docs/content/recurring-jobs.mdx +7 -7
  355. package/docs/content/skills-guide.mdx +4 -4
  356. package/docs/content/template-brain.mdx +1 -1
  357. package/docs/content/toolkit-collaboration.mdx +1 -1
  358. package/docs/content/using-your-agent.mdx +1 -1
  359. package/docs/content/what-is-agent-native.mdx +4 -4
  360. package/docs/content/workspace-management.mdx +4 -4
  361. package/package.json +3 -3
  362. package/src/agent/production-agent.ts +13 -3
  363. package/src/cli/doctor.ts +6 -1
  364. package/src/client/AgentPanel.tsx +79 -41
  365. package/src/client/AssistantChat.tsx +71 -25
  366. package/src/client/FeedbackButton.tsx +33 -7
  367. package/src/client/MultiTabAssistantChat.tsx +6 -8
  368. package/src/client/agent-chat-adapter.ts +11 -2
  369. package/src/client/agent-page/AgentTabFrame.tsx +22 -3
  370. package/src/client/agent-page/AgentTabsPage.tsx +34 -19
  371. package/src/client/chat/message-components.tsx +43 -16
  372. package/src/client/chat/tool-call-display.tsx +30 -18
  373. package/src/client/chat/tool-render-registry.tsx +2 -0
  374. package/src/client/resources/McpConnectionSuggestion.tsx +19 -2
  375. package/src/client/resources/ResourceTree.tsx +7 -10
  376. package/src/client/resources/ResourcesPanel.tsx +33 -70
  377. package/src/client/resources/mcp-connection-resume.ts +107 -0
  378. package/src/client/resources/mcp-integration-catalog.ts +65 -12
  379. package/src/client/resources/mcp-integration-logos.ts +4 -0
  380. package/src/client/sse-event-processor.ts +50 -14
  381. package/src/client/use-db-sync.ts +16 -6
  382. package/src/connections/catalog.ts +7 -1
  383. package/src/eject/remote-mcp-presets.ts +37 -5
  384. package/src/guards/explicit-collab-access.ts +229 -0
  385. package/src/guards/index.ts +1 -0
  386. package/src/localization/default-messages.ts +54 -5
  387. package/src/mcp/connect-route.ts +34 -51
  388. package/src/provider-api/index.ts +1 -1
  389. package/src/server/collab-plugin.ts +137 -18
  390. package/src/server/index.ts +2 -0
  391. package/src/styles/agent-native.css +40 -0
  392. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  393. package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  394. package/src/templates/chat/app/components/layout/Layout.tsx +1 -0
  395. package/src/templates/chat/app/components/layout/Sidebar.tsx +43 -67
  396. package/src/templates/chat/app/hooks/use-navigation-state.ts +2 -7
  397. package/src/templates/chat/app/routes/_index.tsx +8 -5
  398. package/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
  399. package/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
  400. package/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
  401. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  402. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
  403. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  404. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  405. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
  406. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
  407. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  408. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
@@ -1 +1 @@
1
- {"version":3,"file":"connect-route.js","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `/mcp/connect` — frictionless external-agent connection. The legacy\n * `/_agent-native/mcp/connect` alias is mounted by the core route plugin.\n *\n * A logged-in user on a deployed agent-native app (e.g. mail.agent-native.com)\n * mints a per-user, scoped, revocable MCP bearer token WITHOUT ever copying a\n * shared deployment secret. Two surfaces:\n *\n * 1. Browser — `GET /mcp/connect` renders a minimal in-app page (same inline\n * HTML approach as the auth pages). The Authorize button POSTs to\n * `/connect/token`, then shows the ready-to-paste `.mcp.json` entry, the\n * `agent-native connect <origin>` one-liner, and the user's existing\n * tokens with Revoke buttons.\n * 2. CLI — an OAuth-2.0-device-authorization-style flow:\n * POST /mcp/connect/device/start (unauth) → device_code + user_code\n * GET /mcp/connect?user_code=… (browser) → user signs in & approves\n * POST /mcp/connect/device/authorize (session) → binds user to the code\n * POST /mcp/connect/device/poll (unauth) → mints + returns the token\n *\n * When A2A_SECRET exists, the minted token reuses the existing A2A signer\n * (`signA2AToken`) and adds a random `jti` + `scope: \"mcp-connect\"` claim so\n * it can be revoked. Deployments without A2A_SECRET mint the same standard MCP\n * OAuth access-token format used by remote MCP OAuth, signed with the auth\n * secret fallback and bound to the exact MCP resource URL.\n *\n * Node-only (crypto + the A2A signer), bundled alongside the other framework\n * routes. Dialect-agnostic SQL lives in `connect-store.ts`.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type { H3Event } from \"h3\";\nimport { getMethod, getHeader } from \"h3\";\n\nimport { signA2AToken } from \"../a2a/client.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport {\n getSession,\n getConfiguredLoginHtml,\n isLoopbackRequest,\n} from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n MCP_CONNECT_GUIDES,\n MCP_CONNECT_MCP_URL_TEMPLATE,\n MCP_STATIC_TOKEN_FALLBACK,\n interpolateMcpConnectTemplate,\n} from \"../shared/mcp-connect-content.js\";\nimport {\n recordMintedToken,\n listTokens,\n revokeToken,\n normalizeServiceName,\n serviceIdentityEmail,\n createDeviceCode,\n getDeviceCode,\n approveDeviceCode,\n consumeDeviceCode,\n claimDeviceCodeForMint,\n finishDeviceCodeMint,\n releaseDeviceCodeMint,\n expireDeviceCode,\n MCP_CONNECT_OAUTH_CLIENT_ID,\n MCP_CONNECT_SCOPE,\n DEFAULT_TOKEN_TTL_DAYS,\n MIN_TOKEN_TTL_DAYS,\n MAX_TOKEN_TTL_DAYS,\n DEVICE_CODE_TTL_MS,\n} from \"./connect-store.js\";\nimport {\n MCP_OAUTH_DEFAULT_SCOPE,\n signMcpOAuthAccessToken,\n} from \"./oauth-token.js\";\nimport { MCP_PUBLIC_ROUTE_PREFIX } from \"./route-paths.js\";\n\n/** Device-flow poll interval hint (seconds). */\nconst DEVICE_POLL_INTERVAL_S = 3;\n\n// Human-typable user code: 8 base32 chars, dashed XXXX-XXXX.\nconst USER_CODE_RE = /^[A-Z2-7]{4}-[A-Z2-7]{4}$/;\n\nexport interface McpConnectRouteOptions {\n /** App id (directory under apps/, e.g. `mail`). Used for the server name. */\n appId?: string;\n /** Human app name shown on the connect page. */\n appName?: string;\n /** Explicit MCP server id to return in copyable config/device-flow grants. */\n serverName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Derive the running app's origin from request headers (same logic mountMCP\n * uses) — `https` in prod / for non-loopback hosts, `http` for localhost. */\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nfunction isLoopbackOrigin(origin: string): boolean {\n try {\n const hostname = new URL(origin).hostname;\n return (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\" ||\n hostname.startsWith(\"127.\")\n );\n } catch {\n return false;\n }\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction joinAppPath(basePath: string, path: string): string {\n if (!basePath) return path;\n if (path === \"/\") return basePath;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n}\n\nfunction appLabel(origin: string, options: McpConnectRouteOptions): string {\n if (options.appId) return options.appId;\n try {\n const h = new URL(origin).hostname;\n return h.split(\".\")[0] || h;\n } catch {\n return options.appName || \"app\";\n }\n}\n\nfunction serverName(origin: string, options: McpConnectRouteOptions): string {\n const explicit = options.serverName?.trim();\n if (explicit) return explicit;\n return `agent-native-${appLabel(origin, options)}`;\n}\n\nfunction canUseDevOpenConnect(event: H3Event): boolean {\n // Loopback determined from the real socket peer (isLoopbackRequest →\n // getRequestIP without xForwardedFor), NOT a parsed `Host` header — the\n // header is client-controlled, and it also handles IPv6 `::1`. A\n // misconfigured public deploy with no secret thus can't unlock dev-open\n // by spoofing `Host: localhost`.\n return (\n isLoopbackRequest(event) &&\n isLoopbackOrigin(deriveOrigin(event)) &&\n !process.env.A2A_SECRET?.trim() &&\n !process.env.ACCESS_TOKEN?.trim() &&\n !process.env.ACCESS_TOKENS?.trim()\n );\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Resolve the org domain for a session. Used as the JWT `org_domain` claim so\n * the receiving MCP endpoint can map it back to an org id (same as A2A). Best\n * effort — a missing org just yields a user-scoped (no-org) token.\n */\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction clampTtlDays(input: unknown): number {\n const n = Number(input);\n if (!Number.isFinite(n)) return DEFAULT_TOKEN_TTL_DAYS;\n return Math.min(\n MAX_TOKEN_TTL_DAYS,\n Math.max(MIN_TOKEN_TTL_DAYS, Math.floor(n)),\n );\n}\n\n/**\n * Mint a connect-scoped JWT and record it. The token value is returned to the\n * caller exactly once and never persisted; only the random `jti` is stored for\n * revocation.\n */\nasync function mintConnectToken(params: {\n email: string;\n orgId: string | undefined;\n label: string | null;\n ttlDays: number;\n appUrl: string;\n /** When `\"full\"`, embed `catalog_scope: \"full\"` in the JWT so this token\n * bypasses the compact/connector-catalog tier (active by default whenever a\n * `connectorCatalog` is declared) and gets the complete action surface. */\n catalogScope?: \"full\";\n}): Promise<{ token: string; jti: string }> {\n const orgDomain = await resolveOrgDomain(params.orgId);\n const jti = randomUUID();\n const token = await signConnectToken({\n ownerEmail: params.email,\n orgId: params.orgId,\n orgDomain,\n appUrl: params.appUrl,\n expiresIn: `${params.ttlDays}d`,\n jti,\n ...(params.catalogScope === \"full\" ? { catalogScope: \"full\" } : {}),\n });\n await recordMintedToken({\n jti,\n ownerEmail: params.email,\n orgId: params.orgId ?? null,\n label: params.label,\n });\n return { token, jti };\n}\n\nasync function signConnectToken(params: {\n ownerEmail: string;\n orgId: string | null | undefined;\n orgDomain: string | undefined;\n appUrl: string;\n expiresIn: string;\n jti: string;\n /**\n * When true, embed the org id directly as an `org_id` claim on the\n * A2A-signed path (the OAuth-signed path already carries `params.orgId`).\n * Used for org SERVICE tokens, whose synthetic identity must resolve to the\n * org even when the org has no domain mapping. Personal tokens keep the\n * original domain-based resolution — behavior unchanged.\n */\n includeOrgIdClaim?: boolean;\n /**\n * When `\"full\"`, embed a `catalog_scope: \"full\"` claim so this token\n * bypasses the compact/connector-catalog tier filter (active by default\n * whenever a `connectorCatalog` is declared) and gets the complete action\n * surface. Minted when the user connects with `agent-native connect --full-catalog`.\n */\n catalogScope?: \"full\";\n}): Promise<string> {\n if (process.env.A2A_SECRET?.trim()) {\n return signA2AToken(params.ownerEmail, params.orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: params.expiresIn,\n extraClaims: {\n jti: params.jti,\n scope: MCP_CONNECT_SCOPE,\n ...(params.includeOrgIdClaim && params.orgId\n ? { org_id: params.orgId }\n : {}),\n ...(params.catalogScope === \"full\" ? { catalog_scope: \"full\" } : {}),\n },\n });\n }\n\n return signMcpOAuthAccessToken({\n ownerEmail: params.ownerEmail,\n orgId: params.orgId ?? null,\n orgDomain: params.orgDomain ?? null,\n clientId: MCP_CONNECT_OAUTH_CLIENT_ID,\n scope: MCP_OAUTH_DEFAULT_SCOPE,\n resource: mcpResourceUrl(params.appUrl),\n issuer: params.appUrl,\n jti: params.jti,\n expiresIn: params.expiresIn,\n ...(params.catalogScope === \"full\" ? { catalogScope: \"full\" } : {}),\n });\n}\n\n/**\n * Mint an ORG SERVICE token: a connect-scoped, revocable bearer whose subject\n * is the synthetic service identity `svc-<name>@service.<orgId>` instead of a\n * person. Built for CI (e.g. the `PLAN_RECAP_TOKEN` GitHub secret) so the\n * credential survives any individual leaving or revoking their personal\n * tokens, and so rows created by CI are org-scoped (visible to org members)\n * rather than owned by one person.\n *\n * The token value is returned exactly once and never persisted — only the\n * random `jti` is stored, so the standard revocation path\n * (`isJtiRevoked` in `verifyAuth`) applies to service tokens identically.\n *\n * Authorization is the CALLER'S responsibility: this function does not check\n * org membership/role. The `create-org-service-token` action gates on org\n * owner/admin before calling it.\n */\nexport async function mintOrgServiceToken(params: {\n /** Human-readable service principal name, e.g. \"ci\" or \"pr-recap\". */\n serviceName: string;\n /** Org the service token acts for; becomes the resolved session orgId. */\n orgId: string;\n /** The human minting the token — stored for audit, never used as identity. */\n createdBy: string;\n /** 1–365 days; clamped. Defaults to DEFAULT_TOKEN_TTL_DAYS. */\n ttlDays?: number;\n /** App origin used for OAuth-signed tokens (resource/issuer binding). */\n appUrl: string;\n}): Promise<{\n token: string;\n jti: string;\n id: string;\n serviceName: string;\n serviceEmail: string;\n ttlDays: number;\n}> {\n const serviceName = normalizeServiceName(params.serviceName);\n const serviceEmail = serviceIdentityEmail(serviceName, params.orgId);\n const orgDomain = await resolveOrgDomain(params.orgId);\n const ttlDays = clampTtlDays(params.ttlDays ?? DEFAULT_TOKEN_TTL_DAYS);\n const jti = randomUUID();\n const token = await signConnectToken({\n ownerEmail: serviceEmail,\n orgId: params.orgId,\n orgDomain,\n appUrl: params.appUrl,\n expiresIn: `${ttlDays}d`,\n jti,\n includeOrgIdClaim: true,\n });\n const id = await recordMintedToken({\n jti,\n ownerEmail: serviceEmail,\n orgId: params.orgId,\n label: `Service token: ${serviceName}`,\n kind: \"service\",\n serviceName,\n createdBy: params.createdBy,\n });\n return { token, jti, id, serviceName, serviceEmail, ttlDays };\n}\n\nfunction mcpResultPayload(\n appUrl: string,\n options: McpConnectRouteOptions,\n auth: { token?: string; ownerEmail?: string },\n) {\n const mcpUrl = mcpResourceUrl(appUrl);\n const name = serverName(appUrl, options);\n const headers: Record<string, string> = {};\n if (auth.token) headers.Authorization = `Bearer ${auth.token}`;\n if (!auth.token && auth.ownerEmail) {\n headers[\"X-Agent-Native-Owner-Email\"] = auth.ownerEmail;\n }\n // Intentionally do NOT inject the full-catalog header here. Every connector\n // used to receive it, which silently forced the ~105-tool full catalog on\n // every client. Full-catalog intent now lives durably in the token itself\n // (`catalog_scope: \"full\"`, minted only by `connect --full-catalog`), so a\n // normal connection defaults to the compact/connector catalog + tool-search.\n return {\n token: auth.token ?? \"\",\n mcpUrl,\n serverName: name,\n mcpServerEntry: {\n type: \"http\" as const,\n url: mcpUrl,\n ...(Object.keys(headers).length ? { headers } : {}),\n },\n cli: `npx @agent-native/core@latest connect ${appUrl}`,\n };\n}\n\nfunction mcpResourceUrl(appUrl: string): string {\n return `${appUrl}${MCP_PUBLIC_ROUTE_PREFIX}`;\n}\n\n// ---------------------------------------------------------------------------\n// Connect page (server-rendered HTML string)\n// ---------------------------------------------------------------------------\n\nfunction agentNativeMarkSvg(className: string, gradientId: string): string {\n return `<svg class=\"${className}\" width=\"114\" height=\"66\" viewBox=\"0 0 114 66\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#${gradientId})\"/>\n <defs>\n <linearGradient id=\"${gradientId}\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#00B5FF\"/>\n <stop offset=\"1\" stop-color=\"#48FFE4\"/>\n </linearGradient>\n </defs>\n</svg>`;\n}\n\nfunction renderConnectGuide(\n guide: (typeof MCP_CONNECT_GUIDES)[number],\n values: Parameters<typeof interpolateMcpConnectTemplate>[1],\n): string {\n const guideId = escapeHtml(guide.id);\n const content = [\n guide.steps?.length\n ? `<ol>${guide.steps\n .map(\n (step) =>\n `<li>${escapeHtml(interpolateMcpConnectTemplate(step, values))}</li>`,\n )\n .join(\"\")}</ol>`\n : \"\",\n guide.intro\n ? `<p>${escapeHtml(interpolateMcpConnectTemplate(guide.intro, values))}</p>`\n : \"\",\n guide.commandTemplate\n ? `<pre id=\"${guideId}Command\">${escapeHtml(interpolateMcpConnectTemplate(guide.commandTemplate, values))}</pre>\n <button type=\"button\" class=\"primary-link compact\" data-copy=\"${guideId}Command\">${escapeHtml(guide.action?.label ?? \"Copy\")}</button>`\n : \"\",\n guide.configTemplate\n ? `<pre id=\"${guideId}Config\">${escapeHtml(interpolateMcpConnectTemplate(guide.configTemplate, values))}</pre>\n <button type=\"button\" class=\"primary-link compact\" data-copy=\"${guideId}Config\">${escapeHtml(guide.action?.label ?? \"Copy\")}</button>`\n : \"\",\n guide.action?.kind === \"link\" && guide.action.href\n ? `<a class=\"primary-link\" href=\"${escapeHtml(guide.action.href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(guide.action.label)}</a>`\n : \"\",\n guide.note\n ? `<p class=\"hint\">${escapeHtml(interpolateMcpConnectTemplate(guide.note, values))}</p>`\n : \"\",\n ].join(\"\\n\");\n\n return `<div class=\"tab-panel${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \" is-active\" : \"\"}\" role=\"tabpanel\" data-panel=\"${guideId}\">${content}</div>`;\n}\n\nfunction renderConnectPage(params: {\n connectBasePath: string;\n email: string;\n appName: string;\n appUrl: string;\n serverId: string;\n userCode: string | null;\n}): string {\n const { connectBasePath, email, appName, appUrl, serverId, userCode } =\n params;\n const safeEmail = escapeHtml(email);\n const safeApp = escapeHtml(appName);\n const mcpUrl = interpolateMcpConnectTemplate(MCP_CONNECT_MCP_URL_TEMPLATE, {\n appName,\n appUrl,\n mcpUrl: \"\",\n serverId,\n });\n const safeMcpUrl = escapeHtml(mcpUrl);\n const connectTemplateValues = { appName, appUrl, mcpUrl, serverId };\n const brandMarkSvg = agentNativeMarkSvg(\n \"brand-mark\",\n \"agent-native-connect-brand-gradient\",\n );\n const flowMarkSvg = agentNativeMarkSvg(\n \"flow-mark\",\n \"agent-native-connect-flow-gradient\",\n );\n const safeUserCode =\n userCode && USER_CODE_RE.test(userCode) ? escapeHtml(userCode) : \"\";\n const guideTabsHtml = MCP_CONNECT_GUIDES.map(\n (guide) =>\n `<button type=\"button\" class=\"tab${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \" is-active\" : \"\"}\" role=\"tab\" data-tab=\"${escapeHtml(guide.id)}\" aria-selected=\"${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \"true\" : \"false\"}\">${escapeHtml(guide.label)}</button>`,\n ).join(\"\\n\");\n const guidePanelsHtml = MCP_CONNECT_GUIDES.map((guide) =>\n renderConnectGuide(guide, connectTemplateValues),\n ).join(\"\\n\");\n const setupHtml = safeUserCode\n ? \"\"\n : `\n <div class=\"mcp-url-block\">\n <div class=\"section-label\">Your MCP URL</div>\n <div class=\"url-row\">\n <code id=\"mcpUrlValue\">${safeMcpUrl}</code>\n <button type=\"button\" class=\"ghost\" data-copy=\"mcpUrlValue\" aria-label=\"Copy MCP URL\">Copy</button>\n </div>\n </div>\n\n <details id=\"assistantSetup\" class=\"hosts\">\n <summary>\n <span class=\"connections-title\">Assistant setup</span>\n <span class=\"connections-state\">MCP URL guides</span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"hosts-body\">\n <div class=\"section-label\">Pick your AI assistant</div>\n <div class=\"tabs\" role=\"tablist\" aria-label=\"Choose your AI assistant\">\n ${guideTabsHtml}\n </div>\n ${guidePanelsHtml}\n </div>\n </details>`;\n const tokenAdvancedOptionsHtml = safeUserCode\n ? \"\"\n : `\n <details class=\"advanced\">\n <summary>\n Advanced options\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"advanced-body\">\n <div class=\"field\">\n <label for=\"label\">Label (optional)</label>\n <input id=\"label\" type=\"text\" placeholder=\"e.g. Claude Code on my laptop\" maxlength=\"120\" />\n </div>\n <div class=\"field\">\n <label for=\"ttl\">Expires in (days, 1–365)</label>\n <input id=\"ttl\" type=\"number\" min=\"1\" max=\"365\" value=\"${DEFAULT_TOKEN_TTL_DAYS}\" />\n </div>\n </div>\n </details>`;\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Connect ${safeApp}</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n color-scheme: dark;\n --bg: #09090b; --panel: #121214; --panel-2: #0c0c0e;\n --panel-soft: rgba(255,255,255,0.025);\n --border: rgba(255,255,255,0.075); --border-strong: rgba(255,255,255,0.14);\n --text: #f7f7f8; --muted: #a1a1aa; --subtle: #74747d;\n --accent: #f4f4f5; --accent-fg: #09090b;\n --ring: rgba(250,250,250,0.55);\n --error: #fca5a5; --error-bg: rgba(127,29,29,0.18);\n --ok: #86efac; --ok-bg: rgba(20,83,45,0.12); --ok-border: rgba(134,239,172,0.18);\n }\n html, body { -webkit-font-smoothing: antialiased; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: linear-gradient(180deg, #101013 0%, var(--bg) 58%);\n color: var(--text); display: flex; align-items: center;\n justify-content: center; min-height: 100vh; padding: 1.5rem 1rem;\n }\n .card {\n width: 100%; max-width: 440px;\n background: var(--panel); border: 1px solid var(--border);\n border-radius: 8px; box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,\n 0 30px 90px rgba(0,0,0,0.5);\n padding: 1.25rem;\n }\n .topbar {\n display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; margin-bottom: 1.75rem;\n }\n .brand-lockup {\n display: flex; align-items: center; gap: 0.55rem;\n color: var(--muted); font-size: 0.78rem; font-weight: 600;\n }\n .brand-mark { width: 18px; height: auto; display: block; }\n .app-pill {\n max-width: 50%; border: 1px solid var(--border);\n border-radius: 999px; padding: 0.28rem 0.55rem;\n color: var(--subtle); font-size: 0.72rem; line-height: 1;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .hero { padding: 0 0.75rem; text-align: center; }\n .flow {\n display: flex; align-items: center; justify-content: center;\n gap: 0; margin: 0 auto 1.1rem; width: fit-content;\n }\n .flow .tile {\n width: 42px; height: 42px; border-radius: 8px;\n display: flex; align-items: center; justify-content: center;\n background: var(--panel-2); border: 1px solid var(--border-strong);\n color: var(--text); flex-shrink: 0;\n }\n .flow-mark { width: 26px; height: auto; display: block; }\n .flow .agent-symbol {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.95rem; font-weight: 700; letter-spacing: -0.04em;\n }\n .flow .conn {\n width: 30px; height: 1px; flex-shrink: 0;\n background: linear-gradient(90deg, transparent, var(--border-strong), transparent);\n background-position: center;\n }\n .eyebrow {\n text-align: center; font-size: 0.72rem; font-weight: 600;\n letter-spacing: 0.08em; text-transform: uppercase;\n color: var(--subtle); margin-bottom: 0.55rem;\n }\n h1 {\n text-align: center; font-size: 1.45rem; font-weight: 680;\n line-height: 1.25; margin-bottom: 0.7rem;\n letter-spacing: -0.01em;\n }\n .identity {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: center;\n gap: 0.25rem 0.45rem; color: var(--subtle); font-size: 0.78rem;\n line-height: 1.35; margin: 0 auto 1.5rem; max-width: 34ch;\n }\n .identity strong { color: var(--muted); font-weight: 600; }\n .device-strip {\n display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; border: 1px solid var(--border);\n border-radius: 8px; padding: 0.5rem 0.65rem; margin: 0 0 0.9rem;\n background: var(--panel-soft); color: var(--muted);\n }\n .device-strip .label {\n font-size: 0.76rem; font-weight: 560; color: var(--subtle);\n }\n .device-strip .value {\n font-size: 0.78rem; font-weight: 650;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em; color: var(--muted);\n }\n button {\n cursor: pointer; font: inherit; font-weight: 600; border: none;\n border-radius: 8px; padding: 0.78rem 1rem;\n }\n button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }\n .primary {\n background: var(--accent); color: var(--accent-fg); width: 100%;\n font-size: 0.95rem;\n }\n .primary:hover:not(:disabled) { background: #e4e4e7; }\n .primary:disabled { opacity: 0.55; cursor: default; }\n .primary.is-loading {\n display: inline-flex; align-items: center; justify-content: center;\n gap: 0.55rem; opacity: 1;\n }\n .primary.is-loading::before {\n content: \"\"; width: 1rem; height: 1rem; flex: 0 0 auto;\n border-radius: 999px; border: 2px solid rgba(0,0,0,0.22);\n border-top-color: var(--accent-fg);\n animation: spin 0.75s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n @media (prefers-reduced-motion: reduce) {\n .primary.is-loading::before { animation: none; }\n }\n .ghost {\n background: transparent; color: var(--muted);\n border: 1px solid var(--border-strong); padding: 0.35rem 0.7rem;\n font-size: 0.78rem; font-weight: 500; border-radius: 8px;\n }\n .ghost:hover:not(:disabled) { color: var(--text); border-color: var(--subtle); }\n pre {\n background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;\n padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #d4d4d8; margin: 0.5rem 0 1rem;\n }\n /* Advanced disclosure */\n .advanced { margin: 0 0 1rem; }\n .advanced > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; justify-content: center; gap: 0.35rem;\n color: var(--subtle); font-size: 0.8rem; font-weight: 500;\n padding: 0.5rem 0; text-align: center;\n }\n .advanced > summary::-webkit-details-marker { display: none; }\n .advanced > summary:hover { color: var(--muted); }\n .advanced > summary:focus-visible { outline: 2px solid var(--ring);\n outline-offset: 2px; border-radius: 6px; }\n .advanced > summary .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin-top: -3px;\n }\n .advanced[open] > summary .chev { transform: rotate(225deg); margin-top: 2px; }\n .advanced-body {\n padding: 0.85rem 0.1rem 0.25rem;\n }\n .field { margin-bottom: 0.9rem; }\n .field:last-child { margin-bottom: 0; }\n .field label { display: block; font-size: 0.78rem; color: var(--muted);\n margin-bottom: 0.35rem; }\n .field input {\n width: 100%; padding: 0.6rem 0.7rem; font: inherit; color: var(--text);\n background: var(--panel-2); border: 1px solid var(--border-strong);\n border-radius: 8px;\n }\n .field input:focus-visible {\n outline: none; border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(250,250,250,0.12);\n }\n .connections {\n margin-top: 1.1rem; border-top: 1px solid var(--border);\n padding-top: 0.35rem;\n }\n .connections > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; gap: 0.55rem;\n min-height: 2.2rem; color: var(--muted); font-size: 0.82rem;\n }\n .connections > summary::-webkit-details-marker { display: none; }\n .connections > summary:focus-visible {\n outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;\n }\n .connections-title { font-weight: 600; color: var(--muted); }\n .connections-state {\n margin-left: auto; color: var(--subtle); font-size: 0.73rem;\n border: 1px solid var(--border); border-radius: 999px;\n padding: 0.18rem 0.45rem; line-height: 1;\n }\n .connections .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin: -3px 0 0 0.15rem;\n }\n .connections[open] .chev { transform: rotate(225deg); margin-top: 2px; }\n .token-list { padding-top: 0.4rem; }\n .tok { display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border);\n font-size: 0.83rem; }\n .tok:last-child { border-bottom: none; }\n .tok .meta { color: var(--subtle); font-size: 0.74rem; margin-top: 0.1rem; }\n .tok.revoked { opacity: 0.45; }\n .empty-state {\n color: var(--subtle); font-size: 0.78rem; line-height: 1.45;\n padding: 0.3rem 0 0.45rem;\n }\n .msg { font-size: 0.83rem; padding: 0.7rem 0.8rem; border-radius: 8px;\n margin-bottom: 0.9rem; display: none; line-height: 1.4; }\n .msg.err { display: block; color: var(--error); background: var(--error-bg);\n border: 1px solid rgba(252,165,165,0.16); }\n .msg.ok { display: block; color: var(--ok); background: var(--ok-bg);\n border: 1px solid var(--ok-border); }\n .result-panel { padding-top: 0.15rem; }\n .result-title {\n color: var(--text); font-size: 0.95rem; font-weight: 650;\n text-align: center; margin-bottom: 0.35rem;\n }\n .result-copy {\n color: var(--muted); font-size: 0.83rem; line-height: 1.45;\n text-align: center; margin: 0 auto 0.85rem; max-width: 34ch;\n }\n .section-label {\n color: var(--subtle); font-size: 0.7rem; font-weight: 650;\n letter-spacing: 0.08em; text-transform: uppercase; margin-top: 0.85rem;\n }\n @media (max-width: 480px) {\n body { align-items: flex-start; padding: 0.75rem; }\n .card { padding: 1rem; }\n .hero { padding: 0; }\n .topbar { margin-bottom: 1.35rem; }\n h1 { font-size: 1.3rem; }\n .app-pill { max-width: 46%; }\n pre { font-size: 0.72rem; }\n }\n /* MCP URL display + per-host tabs (the non-dev path). */\n .mcp-url-block { margin: 0 0 1rem; }\n .url-row {\n display: flex; align-items: center; gap: 0.5rem;\n background: var(--panel-2); border: 1px solid var(--border-strong);\n border-radius: 8px; padding: 0.45rem 0.5rem 0.45rem 0.75rem;\n }\n .url-row code {\n flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.78rem; color: var(--text);\n }\n .url-row .ghost { flex: 0 0 auto; }\n .hosts {\n margin: 0 0 1rem; border-top: 1px solid var(--border);\n border-bottom: 1px solid var(--border); padding: 0.35rem 0;\n }\n .hosts > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; gap: 0.55rem;\n min-height: 2.2rem; color: var(--muted); font-size: 0.82rem;\n }\n .hosts > summary::-webkit-details-marker { display: none; }\n .hosts > summary:focus-visible {\n outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;\n }\n .hosts > summary .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin: -3px 0 0 0.15rem;\n }\n .hosts[open] > summary .chev { transform: rotate(225deg); margin-top: 2px; }\n .hosts-body { padding: 0.15rem 0 0.25rem; }\n .tabs {\n display: flex; flex-wrap: wrap; gap: 0.25rem;\n border-bottom: 1px solid var(--border); margin-bottom: 0.75rem;\n padding-bottom: 0.4rem;\n }\n .tab {\n background: transparent; color: var(--subtle);\n border: 1px solid transparent;\n padding: 0.35rem 0.65rem; font-size: 0.8rem; font-weight: 600;\n border-radius: 6px;\n }\n .tab:hover { color: var(--muted); background: var(--panel-soft); }\n .tab.is-active {\n color: var(--text); background: var(--panel-2);\n border-color: var(--border-strong);\n }\n .tab-panel { display: none; }\n .tab-panel.is-active { display: block; }\n .tab-panel ol { margin: 0 0 0.6rem 1.1rem; padding: 0; }\n .tab-panel li {\n margin-bottom: 0.3rem; font-size: 0.86rem; line-height: 1.5;\n color: var(--muted);\n }\n .tab-panel li strong { color: var(--text); font-weight: 650; }\n .tab-panel a {\n color: var(--text); text-decoration: underline;\n text-underline-offset: 2px;\n }\n .tab-panel p {\n font-size: 0.84rem; color: var(--muted); margin: 0.4rem 0;\n line-height: 1.5;\n }\n .tab-panel .hint {\n font-size: 0.78rem; color: var(--subtle); margin-top: 0.5rem;\n }\n .tab-panel code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.78rem; color: var(--text);\n background: var(--panel-2); padding: 0.05rem 0.3rem;\n border-radius: 4px;\n }\n .tab-panel pre { margin: 0.4rem 0 0.5rem; }\n /* Per-tab primary CTA — visually distinct from the static-token mint\n * button below. Either a link (Open Claude →) or a copy command button.\n */\n .primary-link {\n display: inline-flex; align-items: center; justify-content: center;\n gap: 0.35rem; min-height: 36px; padding: 0.45rem 0.85rem;\n background: var(--panel-2); color: var(--text);\n border: 1px solid var(--border-strong); border-radius: 8px;\n font-size: 0.86rem; font-weight: 650; text-decoration: none;\n cursor: pointer; width: auto; max-width: 100%; text-align: center;\n margin: 0.5rem 0 0.2rem;\n }\n .tab-panel a.primary-link {\n color: var(--text); text-decoration: none;\n }\n .primary-link:hover {\n background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.2);\n }\n .primary-link.compact { min-width: 0; }\n .copy-flash {\n color: var(--ok) !important;\n border-color: var(--ok-border) !important;\n }\n .static-token-mint .static-token-body { padding-top: 0.5rem; }\n .static-token-mint > summary .connections-state {\n font-style: normal;\n }\n @media (min-width: 560px) {\n .card { max-width: 580px; }\n }\n .hidden { display: none !important; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"topbar\">\n <div class=\"brand-lockup\">\n ${brandMarkSvg}\n <span>Agent Native</span>\n </div>\n <div class=\"app-pill\" title=\"${safeApp}\">${safeApp}</div>\n </div>\n\n <div class=\"hero\">\n <!-- \"Connect an external agent\" is kept as the accessible consent label. -->\n <div class=\"flow\" role=\"img\" aria-label=\"Connect an external agent to ${safeApp}\">\n <span class=\"tile\" aria-hidden=\"true\">\n ${flowMarkSvg}\n </span>\n <span class=\"conn\" aria-hidden=\"true\"></span>\n <span class=\"tile\" aria-hidden=\"true\">\n <span class=\"agent-symbol\">&lt;/&gt;</span>\n </span>\n </div>\n\n <div class=\"eyebrow\">Connect an external agent</div>\n <h1>${safeUserCode ? `Authorize ${safeApp} from your terminal?` : `Use ${safeApp} from your AI assistant`}</h1>\n <p class=\"identity\">\n <span>Signed in as <strong>${safeEmail}</strong></span>\n </p>\n </div>\n\n <div id=\"codeCallout\" class=\"device-strip ${safeUserCode ? \"\" : \"hidden\"}\">\n <span class=\"label\">Device code</span>\n <span class=\"value\" id=\"userCodeValue\">${safeUserCode}</span>\n </div>\n\n ${setupHtml}\n\n <details id=\"staticTokenMint\" class=\"connections static-token-mint\"${safeUserCode ? \" open\" : \"\"}>\n <summary>\n <span class=\"connections-title\">${safeUserCode ? \"Authorize this device\" : MCP_STATIC_TOKEN_FALLBACK.title}</span>\n <span class=\"connections-state\">${safeUserCode ? \"From your terminal\" : MCP_STATIC_TOKEN_FALLBACK.state}</span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"static-token-body\">\n <div id=\"msg\" class=\"msg\" role=\"status\" aria-live=\"polite\"></div>\n <div id=\"mintForm\">\n <button id=\"authorizeBtn\" class=\"primary\">${safeUserCode ? \"Authorize device\" : \"Create connection token\"}</button>\n ${tokenAdvancedOptionsHtml}\n </div>\n <div id=\"result\" class=\"result-panel hidden\">\n <div class=\"result-title\">${MCP_STATIC_TOKEN_FALLBACK.resultTitle}</div>\n <p class=\"result-copy\" id=\"resultMsg\">${MCP_STATIC_TOKEN_FALLBACK.resultCopy}</p>\n <div class=\"section-label\">MCP config</div>\n <pre id=\"mcpJson\"></pre>\n <details class=\"advanced\">\n <summary>\n Terminal alternative\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"advanced-body\">\n <pre id=\"cliLine\"></pre>\n </div>\n </details>\n </div>\n </div>\n </details>\n\n <details id=\"connections\" class=\"connections\">\n <summary>\n <span class=\"connections-title\">Existing connections</span>\n <span id=\"connectionsState\" class=\"connections-state\">Checking</span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div id=\"tokenList\" class=\"token-list\"><div class=\"empty-state\">Checking connections...</div></div>\n </details>\n</div>\n<script>\n(function () {\n var BASE = ${JSON.stringify(joinAppPath(connectBasePath, MCP_PUBLIC_ROUTE_PREFIX + \"/connect\"))};\n var USER_CODE = ${JSON.stringify(safeUserCode || null)};\n var msgEl = document.getElementById(\"msg\");\n var connectionsEl = document.getElementById(\"connections\");\n var connectionsStateEl = document.getElementById(\"connectionsState\");\n\n // Tab switching for the per-host instructions block.\n var tabBtns = document.querySelectorAll(\".tabs .tab\");\n var tabPanels = document.querySelectorAll(\".tab-panel\");\n for (var i = 0; i < tabBtns.length; i++) {\n tabBtns[i].addEventListener(\"click\", function (ev) {\n var btn = ev.currentTarget;\n var name = btn.getAttribute(\"data-tab\");\n for (var j = 0; j < tabBtns.length; j++) {\n var active = tabBtns[j] === btn;\n tabBtns[j].classList.toggle(\"is-active\", active);\n tabBtns[j].setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n }\n for (var k = 0; k < tabPanels.length; k++) {\n tabPanels[k].classList.toggle(\n \"is-active\",\n tabPanels[k].getAttribute(\"data-panel\") === name,\n );\n }\n });\n }\n\n // Copy buttons — any element with data-copy=\"<id>\" copies that node's text.\n document.addEventListener(\"click\", function (ev) {\n var btn = ev.target && ev.target.closest && ev.target.closest(\"[data-copy]\");\n if (!btn) return;\n var node = document.getElementById(btn.getAttribute(\"data-copy\"));\n if (!node || !navigator.clipboard) return;\n navigator.clipboard.writeText(node.textContent || \"\").then(function () {\n var prev = btn.textContent;\n btn.textContent = \"Copied\";\n btn.classList.add(\"copy-flash\");\n setTimeout(function () {\n btn.textContent = prev;\n btn.classList.remove(\"copy-flash\");\n }, 1400);\n });\n });\n function showMsg(text, kind) {\n msgEl.textContent = text;\n msgEl.className = \"msg \" + (kind || \"err\");\n }\n function clearMsg() { msgEl.className = \"msg\"; msgEl.textContent = \"\"; }\n\n function setButtonLoading(btn, text) {\n if (!btn.dataset.idleText) btn.dataset.idleText = btn.textContent || \"\";\n btn.textContent = text;\n btn.classList.add(\"is-loading\");\n btn.setAttribute(\"aria-busy\", \"true\");\n btn.disabled = true;\n }\n\n function resetButtonLoading(btn) {\n btn.disabled = false;\n btn.classList.remove(\"is-loading\");\n btn.removeAttribute(\"aria-busy\");\n if (btn.dataset.idleText) btn.textContent = btn.dataset.idleText;\n }\n\n function renderResult(data) {\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var entry = {};\n entry[data.serverName] = data.mcpServerEntry;\n document.getElementById(\"mcpJson\").textContent =\n JSON.stringify({ mcpServers: entry }, null, 2);\n document.getElementById(\"cliLine\").textContent = data.cli;\n document.getElementById(\"result\").classList.remove(\"hidden\");\n }\n\n async function postJson(path, body) {\n var res = await fetch(BASE + path, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify(body || {})\n });\n var data = null;\n try { data = await res.json(); } catch (e) {}\n return { ok: res.ok, status: res.status, data: data };\n }\n\n async function loadTokens() {\n var listEl = document.getElementById(\"tokenList\");\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (!res.ok) {\n connectionsStateEl.textContent = \"Unavailable\";\n listEl.innerHTML = '<div class=\"empty-state\">Could not load connections.</div>';\n return;\n }\n var data = await res.json();\n var tokens = (data && data.tokens) || [];\n if (!tokens.length) {\n connectionsStateEl.textContent = \"None\";\n connectionsEl.open = false;\n listEl.innerHTML = '<div class=\"empty-state\">Created connections will appear here for revoking later.</div>';\n return;\n }\n var activeCount = tokens.filter(function (t) { return !t.revokedAt; }).length;\n connectionsStateEl.textContent = activeCount === 1 ? \"1 active\" : activeCount + \" active\";\n listEl.innerHTML = \"\";\n tokens.forEach(function (t) {\n var div = document.createElement(\"div\");\n div.className = \"tok\" + (t.revokedAt ? \" revoked\" : \"\");\n var when = t.createdAt ? new Date(t.createdAt).toLocaleString() : \"\";\n var used = t.lastUsedAt ? \" · last used \" + new Date(t.lastUsedAt).toLocaleString() : \"\";\n var left = document.createElement(\"div\");\n var label = document.createElement(\"div\");\n label.textContent = t.label || \"(unlabeled)\";\n var meta = document.createElement(\"div\");\n meta.className = \"meta\";\n meta.textContent = (t.revokedAt ? \"Revoked · \" : \"Created \") + when + used;\n left.appendChild(label); left.appendChild(meta);\n div.appendChild(left);\n if (!t.revokedAt) {\n var btn = document.createElement(\"button\");\n btn.className = \"ghost\";\n btn.textContent = \"Revoke\";\n btn.onclick = async function () {\n btn.disabled = true;\n var r = await postJson(\"/tokens/revoke\", { id: t.id });\n if (r.ok) { loadTokens(); }\n else { btn.disabled = false; showMsg(\"Could not revoke token.\"); }\n };\n div.appendChild(btn);\n }\n listEl.appendChild(div);\n });\n } catch (e) {\n connectionsStateEl.textContent = \"Unavailable\";\n listEl.innerHTML = '<div class=\"empty-state\">Could not load connections.</div>';\n }\n }\n\n document.getElementById(\"authorizeBtn\").onclick = async function () {\n var btn = this;\n setButtonLoading(btn, USER_CODE ? \"Authorizing device...\" : \"Creating token...\");\n clearMsg();\n try {\n if (USER_CODE) {\n var a = await postJson(\"/device/authorize\", { user_code: USER_CODE });\n if (!a.ok) {\n resetButtonLoading(btn);\n showMsg((a.data && a.data.error) || \"Could not authorize this device code.\");\n return;\n }\n showMsg(\"Device authorized — finishing connection… you can return to your terminal.\", \"ok\");\n btn.classList.add(\"hidden\");\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var cc = document.getElementById(\"codeCallout\");\n if (cc) cc.classList.add(\"hidden\");\n // The token is minted a few seconds later, when the CLI next polls\n // /device/poll — so a single loadTokens() here runs BEFORE the row\n // exists and the list would wrongly read \"No connections yet\" until\n // a manual reload. Snapshot the EXISTING non-revoked token ids first\n // so we announce \"Connected\" only when THIS device's freshly-minted\n // token appears — a user who already has tokens must not get a false\n // success the instant they authorize.\n var priorIds = {};\n try {\n var pr = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (pr.ok) {\n var pd = await pr.json();\n ((pd && pd.tokens) || []).forEach(function (t) {\n if (!t.revokedAt) priorIds[t.id] = true;\n });\n }\n } catch (e) {}\n loadTokens();\n var tries = 0;\n var iv = setInterval(async function () {\n tries++;\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (res.ok) {\n var data = await res.json();\n var fresh = ((data && data.tokens) || []).filter(function (t) {\n return !t.revokedAt && !priorIds[t.id];\n });\n if (fresh.length > 0) {\n clearInterval(iv);\n showMsg(\"Connected. This device can now act as you — manage or revoke it below.\", \"ok\");\n loadTokens();\n return;\n }\n }\n } catch (e) {}\n if (tries >= 30) {\n // No new token appeared in the window — e.g. the loopback\n // dev-open path writes a header-only config and never mints.\n // Don't claim \"Connected\" (we couldn't confirm a device token);\n // keep the \"authorized\" message and just refresh the list.\n clearInterval(iv);\n loadTokens();\n }\n }, 2000);\n return;\n } else {\n var labelEl = document.getElementById(\"label\");\n var ttlEl = document.getElementById(\"ttl\");\n var label = labelEl ? labelEl.value || undefined : undefined;\n var ttlDays = ttlEl ? parseInt(ttlEl.value, 10) || undefined : undefined;\n var m = await postJson(\"/token\", { label: label, ttlDays: ttlDays });\n if (!m.ok) {\n resetButtonLoading(btn);\n showMsg((m.data && m.data.error) || \"Could not create token.\");\n return;\n }\n renderResult(m.data);\n }\n loadTokens();\n } catch (e) {\n resetButtonLoading(btn);\n showMsg(\"Network error. Please try again.\");\n }\n };\n\n loadTokens();\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Handler — single entry point; core-routes-plugin dispatches the subpath.\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a `/mcp/connect[...]` request. The legacy\n * `/_agent-native/mcp/connect` alias is mounted too. `subpath` is the part\n * after `/connect` (empty string = the page itself, otherwise e.g. `/token`,\n * `/device/start`). The core-routes-plugin computes it from the stripped event\n * path so this module stays mount-agnostic.\n */\nexport async function handleMcpConnect(\n event: H3Event,\n subpath: string,\n options: McpConnectRouteOptions = {},\n): Promise<Response> {\n const method = getMethod(event);\n const origin = deriveOrigin(event);\n const basePath = configuredBasePath();\n const appUrl = `${origin}${basePath}`;\n const sub = (\"/\" + subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")).replace(\n /^\\/$/,\n \"\",\n );\n\n // ---- The connect page (GET) ------------------------------------------\n if (sub === \"\") {\n if (method !== \"GET\" && method !== \"HEAD\") {\n return json({ error: \"Method not allowed\" }, 405);\n }\n const session = await getSession(event);\n if (!session?.email) {\n // Serve the SAME login form the guard would, at this same URL — the\n // login form reloads window.location so we re-enter here authed.\n const loginHtml = getConfiguredLoginHtml(event);\n if (loginHtml) return html(loginHtml, 200);\n // Fully-open app (no auth guard): nothing to scope a mint to.\n return html(\n renderConnectPage({\n connectBasePath: basePath,\n email: \"(no auth configured)\",\n appName: options.appName || appLabel(appUrl, options),\n appUrl,\n serverId: serverName(appUrl, options),\n userCode: null,\n }),\n );\n }\n let userCode: string | null = null;\n try {\n const u = new URL(\n event.node?.req?.url ?? event.path ?? \"/\",\n \"http://an.invalid\",\n );\n const raw = u.searchParams.get(\"user_code\");\n if (raw && USER_CODE_RE.test(raw)) userCode = raw;\n } catch {\n userCode = null;\n }\n return html(\n renderConnectPage({\n connectBasePath: basePath,\n email: session.email,\n appName: options.appName || appLabel(appUrl, options),\n appUrl,\n serverId: serverName(appUrl, options),\n userCode,\n }),\n );\n }\n\n // ---- POST /token (session-required) ---------------------------------\n if (sub === \"/token\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n if (!process.env.A2A_SECRET?.trim() && canUseDevOpenConnect(event)) {\n return json(\n mcpResultPayload(appUrl, options, { ownerEmail: session.email }),\n );\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n label?: unknown;\n ttlDays?: unknown;\n fullCatalog?: unknown;\n };\n const label =\n typeof body.label === \"string\" && body.label.trim()\n ? body.label.trim().slice(0, 120)\n : null;\n const ttlDays = clampTtlDays(body.ttlDays);\n const catalogScope: \"full\" | undefined =\n body.fullCatalog === true || body.fullCatalog === \"true\"\n ? \"full\"\n : undefined;\n try {\n const { token } = await mintConnectToken({\n email: session.email,\n orgId: session.orgId,\n label,\n ttlDays,\n appUrl,\n ...(catalogScope ? { catalogScope } : {}),\n });\n return json(mcpResultPayload(appUrl, options, { token }));\n } catch {\n return json({ error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- POST /device/start (UNAUTH) ------------------------------------\n if (sub === \"/device/start\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n try {\n const row = await createDeviceCode();\n const verificationUri = `${appUrl}${MCP_PUBLIC_ROUTE_PREFIX}/connect`;\n return json({\n device_code: row.deviceCode,\n user_code: row.userCode,\n verification_uri: verificationUri,\n verification_uri_complete: `${verificationUri}?user_code=${row.userCode}`,\n interval: DEVICE_POLL_INTERVAL_S,\n expires_in: Math.floor(DEVICE_CODE_TTL_MS / 1000),\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return json({ error: \"Rate limited. Try again shortly.\" }, 429);\n }\n return json({ error: \"Could not start device flow.\" }, 500);\n }\n }\n\n // ---- POST /device/authorize (session-required) ----------------------\n if (sub === \"/device/authorize\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n user_code?: unknown;\n };\n const userCode =\n typeof body.user_code === \"string\" ? body.user_code.trim() : \"\";\n if (!USER_CODE_RE.test(userCode)) {\n return json({ error: \"Invalid user code.\" }, 400);\n }\n const orgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const result = await approveDeviceCode(userCode, session.email, orgId);\n if (result === \"not_found\") {\n return json({ error: \"Unknown device code.\" }, 404);\n }\n if (result === \"expired\") {\n return json({ error: \"This device code has expired.\" }, 410);\n }\n if (result === \"already\") {\n return json({ error: \"This device code was already used.\" }, 409);\n }\n return json({ status: \"approved\" });\n }\n\n // ---- POST /device/poll (UNAUTH) -------------------------------------\n if (sub === \"/device/poll\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n device_code?: unknown;\n };\n const deviceCode =\n typeof body.device_code === \"string\" ? body.device_code : \"\";\n if (!deviceCode) return json({ error: \"device_code required\" }, 400);\n const row = await getDeviceCode(deviceCode);\n if (!row) return json({ status: \"not_found\" }, 404);\n if (row.status === \"consumed\") return json({ status: \"consumed\" });\n if (\n row.status === \"expired\" ||\n (row.expiresAt != null && row.expiresAt < Date.now())\n ) {\n if (row.status !== \"expired\") void expireDeviceCode(deviceCode);\n return json({ status: \"expired\" });\n }\n if (\n row.status === \"pending\" ||\n row.status === \"minting\" ||\n !row.ownerEmail\n ) {\n return json({ status: \"pending\" });\n }\n // status === \"approved\" && ownerEmail bound → mint exactly once.\n if (!process.env.A2A_SECRET?.trim() && canUseDevOpenConnect(event)) {\n const consumed = await consumeDeviceCode(\n deviceCode,\n `dev-open-${randomUUID()}`,\n );\n if (!consumed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, options, {\n ownerEmail: row.ownerEmail,\n }),\n });\n }\n try {\n const jti = randomUUID();\n // Claim a retryable minting state first. If signing or recording fails,\n // release the row back to approved so the CLI can poll again.\n const claimed = await claimDeviceCodeForMint(deviceCode, jti);\n if (!claimed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n let token: string;\n try {\n const orgDomain = await resolveOrgDomain(claimed.orgId ?? undefined);\n token = await signConnectToken({\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n orgDomain,\n appUrl,\n expiresIn: `${DEFAULT_TOKEN_TTL_DAYS}d`,\n jti,\n });\n await recordMintedToken({\n jti,\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n label: \"Device connection\",\n });\n if (!(await finishDeviceCodeMint(deviceCode, jti))) {\n return json({ status: \"pending\" });\n }\n } catch (err) {\n await releaseDeviceCodeMint(deviceCode, jti);\n throw err;\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, options, { token }),\n });\n } catch {\n return json({ status: \"error\", error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- GET /tokens (session-required) ---------------------------------\n if (sub === \"/tokens\") {\n if (method !== \"GET\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const rows = await listTokens(session.email);\n return json({\n tokens: rows.map((r) => ({\n id: r.id,\n label: r.label,\n createdAt: r.createdAt,\n lastUsedAt: r.lastUsedAt,\n revokedAt: r.revokedAt,\n })),\n });\n }\n\n // ---- POST /tokens/revoke (session-required) -------------------------\n if (sub === \"/tokens/revoke\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n id?: unknown;\n };\n const id = typeof body.id === \"string\" ? body.id : \"\";\n if (!id) return json({ error: \"id required\" }, 400);\n const revoked = await revokeToken(session.email, id);\n return json({ ok: revoked });\n }\n\n return json({ error: \"Not found\" }, 404);\n}\n"]}
1
+ {"version":3,"file":"connect-route.js","sourceRoot":"","sources":["../../src/mcp/connect-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `/mcp/connect` — frictionless external-agent connection. The legacy\n * `/_agent-native/mcp/connect` alias is mounted by the core route plugin.\n *\n * A logged-in user on a deployed agent-native app (e.g. mail.agent-native.com)\n * mints a per-user, scoped, revocable MCP bearer token WITHOUT ever copying a\n * shared deployment secret. Two surfaces:\n *\n * 1. Browser — `GET /mcp/connect` renders a minimal in-app page (same inline\n * HTML approach as the auth pages). The Authorize button POSTs to\n * `/connect/token`, then shows the ready-to-paste `.mcp.json` entry, the\n * `agent-native connect <origin>` one-liner, and the user's existing\n * tokens with Revoke buttons.\n * 2. CLI — an OAuth-2.0-device-authorization-style flow:\n * POST /mcp/connect/device/start (unauth) → device_code + user_code\n * GET /mcp/connect?user_code=… (browser) → user signs in & approves\n * POST /mcp/connect/device/authorize (session) → binds user to the code\n * POST /mcp/connect/device/poll (unauth) → mints + returns the token\n *\n * When A2A_SECRET exists, the minted token reuses the existing A2A signer\n * (`signA2AToken`) and adds a random `jti` + `scope: \"mcp-connect\"` claim so\n * it can be revoked. Deployments without A2A_SECRET mint the same standard MCP\n * OAuth access-token format used by remote MCP OAuth, signed with the auth\n * secret fallback and bound to the exact MCP resource URL.\n *\n * Node-only (crypto + the A2A signer), bundled alongside the other framework\n * routes. Dialect-agnostic SQL lives in `connect-store.ts`.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport type { H3Event } from \"h3\";\nimport { getMethod, getHeader } from \"h3\";\n\nimport { signA2AToken } from \"../a2a/client.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport {\n getSession,\n getConfiguredLoginHtml,\n isLoopbackRequest,\n} from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n MCP_CONNECT_GUIDES,\n MCP_CONNECT_MCP_URL_TEMPLATE,\n MCP_STATIC_TOKEN_FALLBACK,\n interpolateMcpConnectTemplate,\n} from \"../shared/mcp-connect-content.js\";\nimport {\n recordMintedToken,\n listTokens,\n revokeToken,\n normalizeServiceName,\n serviceIdentityEmail,\n createDeviceCode,\n getDeviceCode,\n approveDeviceCode,\n consumeDeviceCode,\n claimDeviceCodeForMint,\n finishDeviceCodeMint,\n releaseDeviceCodeMint,\n expireDeviceCode,\n MCP_CONNECT_OAUTH_CLIENT_ID,\n MCP_CONNECT_SCOPE,\n DEFAULT_TOKEN_TTL_DAYS,\n MIN_TOKEN_TTL_DAYS,\n MAX_TOKEN_TTL_DAYS,\n DEVICE_CODE_TTL_MS,\n} from \"./connect-store.js\";\nimport {\n MCP_OAUTH_DEFAULT_SCOPE,\n signMcpOAuthAccessToken,\n} from \"./oauth-token.js\";\nimport { MCP_PUBLIC_ROUTE_PREFIX } from \"./route-paths.js\";\n\n/** Device-flow poll interval hint (seconds). */\nconst DEVICE_POLL_INTERVAL_S = 3;\n\n// Human-typable user code: 8 base32 chars, dashed XXXX-XXXX.\nconst USER_CODE_RE = /^[A-Z2-7]{4}-[A-Z2-7]{4}$/;\n\nexport interface McpConnectRouteOptions {\n /** App id (directory under apps/, e.g. `mail`). Used for the server name. */\n appId?: string;\n /** Human app name shown on the connect page. */\n appName?: string;\n /** Explicit MCP server id to return in copyable config/device-flow grants. */\n serverName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n}\n\n/** Derive the running app's origin from request headers (same logic mountMCP\n * uses) — `https` in prod / for non-loopback hosts, `http` for localhost. */\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1)(:|$)/.test(host) ? \"http\" : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nfunction isLoopbackOrigin(origin: string): boolean {\n try {\n const hostname = new URL(origin).hostname;\n return (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"::1\" ||\n hostname === \"[::1]\" ||\n hostname.startsWith(\"127.\")\n );\n } catch {\n return false;\n }\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction joinAppPath(basePath: string, path: string): string {\n if (!basePath) return path;\n if (path === \"/\") return basePath;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n}\n\nfunction appLabel(origin: string, options: McpConnectRouteOptions): string {\n if (options.appId) return options.appId;\n try {\n const h = new URL(origin).hostname;\n return h.split(\".\")[0] || h;\n } catch {\n return options.appName || \"app\";\n }\n}\n\nfunction serverName(origin: string, options: McpConnectRouteOptions): string {\n const explicit = options.serverName?.trim();\n if (explicit) return explicit;\n return `agent-native-${appLabel(origin, options)}`;\n}\n\nfunction canUseDevOpenConnect(event: H3Event): boolean {\n // Loopback determined from the real socket peer (isLoopbackRequest →\n // getRequestIP without xForwardedFor), NOT a parsed `Host` header — the\n // header is client-controlled, and it also handles IPv6 `::1`. A\n // misconfigured public deploy with no secret thus can't unlock dev-open\n // by spoofing `Host: localhost`.\n return (\n isLoopbackRequest(event) &&\n isLoopbackOrigin(deriveOrigin(event)) &&\n !process.env.A2A_SECRET?.trim() &&\n !process.env.ACCESS_TOKEN?.trim() &&\n !process.env.ACCESS_TOKENS?.trim()\n );\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Resolve the org domain for a session. Used as the JWT `org_domain` claim so\n * the receiving MCP endpoint can map it back to an org id (same as A2A). Best\n * effort — a missing org just yields a user-scoped (no-org) token.\n */\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction clampTtlDays(input: unknown): number {\n const n = Number(input);\n if (!Number.isFinite(n)) return DEFAULT_TOKEN_TTL_DAYS;\n return Math.min(\n MAX_TOKEN_TTL_DAYS,\n Math.max(MIN_TOKEN_TTL_DAYS, Math.floor(n)),\n );\n}\n\n/**\n * Mint a connect-scoped JWT and record it. The token value is returned to the\n * caller exactly once and never persisted; only the random `jti` is stored for\n * revocation.\n */\nasync function mintConnectToken(params: {\n email: string;\n orgId: string | undefined;\n label: string | null;\n ttlDays: number;\n appUrl: string;\n /** When `\"full\"`, embed `catalog_scope: \"full\"` in the JWT so this token\n * bypasses the compact/connector-catalog tier (active by default whenever a\n * `connectorCatalog` is declared) and gets the complete action surface. */\n catalogScope?: \"full\";\n}): Promise<{ token: string; jti: string }> {\n const orgDomain = await resolveOrgDomain(params.orgId);\n const jti = randomUUID();\n const token = await signConnectToken({\n ownerEmail: params.email,\n orgId: params.orgId,\n orgDomain,\n appUrl: params.appUrl,\n expiresIn: `${params.ttlDays}d`,\n jti,\n ...(params.catalogScope === \"full\" ? { catalogScope: \"full\" } : {}),\n });\n await recordMintedToken({\n jti,\n ownerEmail: params.email,\n orgId: params.orgId ?? null,\n label: params.label,\n });\n return { token, jti };\n}\n\nasync function signConnectToken(params: {\n ownerEmail: string;\n orgId: string | null | undefined;\n orgDomain: string | undefined;\n appUrl: string;\n expiresIn: string;\n jti: string;\n /**\n * When true, embed the org id directly as an `org_id` claim on the\n * A2A-signed path (the OAuth-signed path already carries `params.orgId`).\n * Used for org SERVICE tokens, whose synthetic identity must resolve to the\n * org even when the org has no domain mapping. Personal tokens keep the\n * original domain-based resolution — behavior unchanged.\n */\n includeOrgIdClaim?: boolean;\n /**\n * When `\"full\"`, embed a `catalog_scope: \"full\"` claim so this token\n * bypasses the compact/connector-catalog tier filter (active by default\n * whenever a `connectorCatalog` is declared) and gets the complete action\n * surface. Minted when the user connects with `agent-native connect --full-catalog`.\n */\n catalogScope?: \"full\";\n}): Promise<string> {\n if (process.env.A2A_SECRET?.trim()) {\n return signA2AToken(params.ownerEmail, params.orgDomain, undefined, {\n preferGlobalSecret: true,\n expiresIn: params.expiresIn,\n extraClaims: {\n jti: params.jti,\n scope: MCP_CONNECT_SCOPE,\n ...(params.includeOrgIdClaim && params.orgId\n ? { org_id: params.orgId }\n : {}),\n ...(params.catalogScope === \"full\" ? { catalog_scope: \"full\" } : {}),\n },\n });\n }\n\n return signMcpOAuthAccessToken({\n ownerEmail: params.ownerEmail,\n orgId: params.orgId ?? null,\n orgDomain: params.orgDomain ?? null,\n clientId: MCP_CONNECT_OAUTH_CLIENT_ID,\n scope: MCP_OAUTH_DEFAULT_SCOPE,\n resource: mcpResourceUrl(params.appUrl),\n issuer: params.appUrl,\n jti: params.jti,\n expiresIn: params.expiresIn,\n ...(params.catalogScope === \"full\" ? { catalogScope: \"full\" } : {}),\n });\n}\n\n/**\n * Mint an ORG SERVICE token: a connect-scoped, revocable bearer whose subject\n * is the synthetic service identity `svc-<name>@service.<orgId>` instead of a\n * person. Built for CI (e.g. the `PLAN_RECAP_TOKEN` GitHub secret) so the\n * credential survives any individual leaving or revoking their personal\n * tokens, and so rows created by CI are org-scoped (visible to org members)\n * rather than owned by one person.\n *\n * The token value is returned exactly once and never persisted — only the\n * random `jti` is stored, so the standard revocation path\n * (`isJtiRevoked` in `verifyAuth`) applies to service tokens identically.\n *\n * Authorization is the CALLER'S responsibility: this function does not check\n * org membership/role. The `create-org-service-token` action gates on org\n * owner/admin before calling it.\n */\nexport async function mintOrgServiceToken(params: {\n /** Human-readable service principal name, e.g. \"ci\" or \"pr-recap\". */\n serviceName: string;\n /** Org the service token acts for; becomes the resolved session orgId. */\n orgId: string;\n /** The human minting the token — stored for audit, never used as identity. */\n createdBy: string;\n /** 1–365 days; clamped. Defaults to DEFAULT_TOKEN_TTL_DAYS. */\n ttlDays?: number;\n /** App origin used for OAuth-signed tokens (resource/issuer binding). */\n appUrl: string;\n}): Promise<{\n token: string;\n jti: string;\n id: string;\n serviceName: string;\n serviceEmail: string;\n ttlDays: number;\n}> {\n const serviceName = normalizeServiceName(params.serviceName);\n const serviceEmail = serviceIdentityEmail(serviceName, params.orgId);\n const orgDomain = await resolveOrgDomain(params.orgId);\n const ttlDays = clampTtlDays(params.ttlDays ?? DEFAULT_TOKEN_TTL_DAYS);\n const jti = randomUUID();\n const token = await signConnectToken({\n ownerEmail: serviceEmail,\n orgId: params.orgId,\n orgDomain,\n appUrl: params.appUrl,\n expiresIn: `${ttlDays}d`,\n jti,\n includeOrgIdClaim: true,\n });\n const id = await recordMintedToken({\n jti,\n ownerEmail: serviceEmail,\n orgId: params.orgId,\n label: `Service token: ${serviceName}`,\n kind: \"service\",\n serviceName,\n createdBy: params.createdBy,\n });\n return { token, jti, id, serviceName, serviceEmail, ttlDays };\n}\n\nfunction mcpResultPayload(\n appUrl: string,\n options: McpConnectRouteOptions,\n auth: { token?: string; ownerEmail?: string },\n) {\n const mcpUrl = mcpResourceUrl(appUrl);\n const name = serverName(appUrl, options);\n const headers: Record<string, string> = {};\n if (auth.token) headers.Authorization = `Bearer ${auth.token}`;\n if (!auth.token && auth.ownerEmail) {\n headers[\"X-Agent-Native-Owner-Email\"] = auth.ownerEmail;\n }\n // Intentionally do NOT inject the full-catalog header here. Every connector\n // used to receive it, which silently forced the ~105-tool full catalog on\n // every client. Full-catalog intent now lives durably in the token itself\n // (`catalog_scope: \"full\"`, minted only by `connect --full-catalog`), so a\n // normal connection defaults to the compact/connector catalog + tool-search.\n return {\n token: auth.token ?? \"\",\n mcpUrl,\n serverName: name,\n mcpServerEntry: {\n type: \"http\" as const,\n url: mcpUrl,\n ...(Object.keys(headers).length ? { headers } : {}),\n },\n cli: `npx @agent-native/core@latest connect ${appUrl}`,\n };\n}\n\nfunction mcpResourceUrl(appUrl: string): string {\n return `${appUrl}${MCP_PUBLIC_ROUTE_PREFIX}`;\n}\n\n// ---------------------------------------------------------------------------\n// Connect page (server-rendered HTML string)\n// ---------------------------------------------------------------------------\n\nfunction agentNativeMarkSvg(className: string, gradientId: string): string {\n return `<svg class=\"${className}\" width=\"114\" height=\"66\" viewBox=\"0 0 114 66\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z\" fill=\"white\"/>\n <path d=\"M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z\" fill=\"url(#${gradientId})\"/>\n <defs>\n <linearGradient id=\"${gradientId}\" x1=\"101.702\" y1=\"67.4791\" x2=\"113.672\" y2=\"-37.4275\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#00B5FF\"/>\n <stop offset=\"1\" stop-color=\"#48FFE4\"/>\n </linearGradient>\n </defs>\n</svg>`;\n}\n\nfunction renderConnectGuide(\n guide: (typeof MCP_CONNECT_GUIDES)[number],\n values: Parameters<typeof interpolateMcpConnectTemplate>[1],\n): string {\n const guideId = escapeHtml(guide.id);\n const content = [\n guide.steps?.length\n ? `<ol>${guide.steps\n .map(\n (step) =>\n `<li>${escapeHtml(interpolateMcpConnectTemplate(step, values))}</li>`,\n )\n .join(\"\")}</ol>`\n : \"\",\n guide.intro\n ? `<p>${escapeHtml(interpolateMcpConnectTemplate(guide.intro, values))}</p>`\n : \"\",\n guide.commandTemplate\n ? `<pre id=\"${guideId}Command\">${escapeHtml(interpolateMcpConnectTemplate(guide.commandTemplate, values))}</pre>\n <button type=\"button\" class=\"primary-link compact\" data-copy=\"${guideId}Command\">${escapeHtml(guide.action?.label ?? \"Copy\")}</button>`\n : \"\",\n guide.configTemplate\n ? `<pre id=\"${guideId}Config\">${escapeHtml(interpolateMcpConnectTemplate(guide.configTemplate, values))}</pre>\n <button type=\"button\" class=\"primary-link compact\" data-copy=\"${guideId}Config\">${escapeHtml(guide.action?.label ?? \"Copy\")}</button>`\n : \"\",\n guide.action?.kind === \"link\" && guide.action.href\n ? `<a class=\"primary-link\" href=\"${escapeHtml(guide.action.href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(guide.action.label)}</a>`\n : \"\",\n guide.note\n ? `<p class=\"hint\">${escapeHtml(interpolateMcpConnectTemplate(guide.note, values))}</p>`\n : \"\",\n ].join(\"\\n\");\n\n return `<div class=\"tab-panel${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \" is-active\" : \"\"}\" role=\"tabpanel\" data-panel=\"${guideId}\">${content}</div>`;\n}\n\nfunction renderConnectPage(params: {\n connectBasePath: string;\n email: string;\n appName: string;\n appUrl: string;\n serverId: string;\n userCode: string | null;\n}): string {\n const { connectBasePath, email, appName, appUrl, serverId, userCode } =\n params;\n const safeEmail = escapeHtml(email);\n const safeApp = escapeHtml(appName);\n const mcpUrl = interpolateMcpConnectTemplate(MCP_CONNECT_MCP_URL_TEMPLATE, {\n appName,\n appUrl,\n mcpUrl: \"\",\n serverId,\n });\n const safeMcpUrl = escapeHtml(mcpUrl);\n const connectTemplateValues = { appName, appUrl, mcpUrl, serverId };\n const flowMarkSvg = agentNativeMarkSvg(\n \"flow-mark\",\n \"agent-native-connect-flow-gradient\",\n );\n const safeUserCode =\n userCode && USER_CODE_RE.test(userCode) ? escapeHtml(userCode) : \"\";\n const guideTabsHtml = MCP_CONNECT_GUIDES.map(\n (guide) =>\n `<button type=\"button\" class=\"tab${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \" is-active\" : \"\"}\" role=\"tab\" data-tab=\"${escapeHtml(guide.id)}\" aria-selected=\"${guide.id === MCP_CONNECT_GUIDES[0]?.id ? \"true\" : \"false\"}\">${escapeHtml(guide.label)}</button>`,\n ).join(\"\\n\");\n const guidePanelsHtml = MCP_CONNECT_GUIDES.map((guide) =>\n renderConnectGuide(guide, connectTemplateValues),\n ).join(\"\\n\");\n const setupHtml = safeUserCode\n ? \"\"\n : `\n <div class=\"mcp-url-block\">\n <div class=\"section-label\">Your MCP URL</div>\n <div class=\"url-row\">\n <code id=\"mcpUrlValue\">${safeMcpUrl}</code>\n <button type=\"button\" class=\"ghost\" data-copy=\"mcpUrlValue\" aria-label=\"Copy MCP URL\">Copy</button>\n </div>\n </div>\n\n <details id=\"assistantSetup\" class=\"hosts\">\n <summary>\n <span class=\"connections-title\">Assistant setup</span>\n <span class=\"connections-state\">MCP URL guides</span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"hosts-body\">\n <div class=\"section-label\">Pick your AI assistant</div>\n <div class=\"tabs\" role=\"tablist\" aria-label=\"Choose your AI assistant\">\n ${guideTabsHtml}\n </div>\n ${guidePanelsHtml}\n </div>\n </details>`;\n const tokenAdvancedOptionsHtml = safeUserCode\n ? \"\"\n : `\n <details class=\"advanced\">\n <summary>\n Advanced options\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"advanced-body\">\n <div class=\"field\">\n <label for=\"label\">Label (optional)</label>\n <input id=\"label\" type=\"text\" placeholder=\"e.g. Claude Code on my laptop\" maxlength=\"120\" />\n </div>\n <div class=\"field\">\n <label for=\"ttl\">Expires in (days, 1–365)</label>\n <input id=\"ttl\" type=\"number\" min=\"1\" max=\"365\" value=\"${DEFAULT_TOKEN_TTL_DAYS}\" />\n </div>\n </div>\n </details>`;\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Connect ${safeApp}</title>\n<style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n color-scheme: dark;\n --bg: #09090b; --panel: #121214; --panel-2: #0c0c0e;\n --panel-soft: rgba(255,255,255,0.025);\n --border: rgba(255,255,255,0.075); --border-strong: rgba(255,255,255,0.14);\n --text: #f7f7f8; --muted: #a1a1aa; --subtle: #74747d;\n --accent: #f4f4f5; --accent-fg: #09090b;\n --ring: rgba(250,250,250,0.55);\n --error: #fca5a5; --error-bg: rgba(127,29,29,0.18);\n --ok: #86efac; --ok-bg: rgba(20,83,45,0.12); --ok-border: rgba(134,239,172,0.18);\n }\n html, body { -webkit-font-smoothing: antialiased; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: linear-gradient(180deg, #101013 0%, var(--bg) 58%);\n color: var(--text); display: flex; align-items: center;\n justify-content: center; min-height: 100vh; padding: 1.5rem 1rem;\n }\n .card {\n width: 100%; max-width: 440px;\n background: var(--panel); border: 1px solid var(--border);\n border-radius: 8px; box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,\n 0 30px 90px rgba(0,0,0,0.5);\n padding: 1.25rem;\n }\n .hero { padding: 0 0.75rem; text-align: center; }\n .flow {\n display: flex; align-items: center; justify-content: center;\n gap: 0; margin: 0 auto 1.1rem; width: fit-content;\n }\n .flow .tile {\n width: 42px; height: 42px; border-radius: 8px;\n display: flex; align-items: center; justify-content: center;\n background: var(--panel-2); border: 1px solid var(--border-strong);\n color: var(--text); flex-shrink: 0;\n }\n .flow-mark { width: 26px; height: auto; display: block; }\n .flow .agent-symbol {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.95rem; font-weight: 700; letter-spacing: -0.04em;\n }\n .flow .conn {\n width: 30px; height: 1px; flex-shrink: 0;\n background: linear-gradient(90deg, transparent, var(--border-strong), transparent);\n background-position: center;\n }\n h1 {\n text-align: center; font-size: 1.45rem; font-weight: 680;\n line-height: 1.25; margin-bottom: 0.7rem;\n letter-spacing: -0.01em;\n }\n .identity {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: center;\n gap: 0.25rem 0.45rem; color: var(--subtle); font-size: 0.78rem;\n line-height: 1.35; margin: 0 auto 1.5rem; max-width: 34ch;\n }\n .identity strong { color: var(--muted); font-weight: 600; }\n .device-strip {\n display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; border: 1px solid var(--border);\n border-radius: 8px; padding: 0.5rem 0.65rem; margin: 0 0 0.9rem;\n background: var(--panel-soft); color: var(--muted);\n }\n .device-strip .label {\n font-size: 0.76rem; font-weight: 560; color: var(--subtle);\n }\n .device-strip .value {\n font-size: 0.78rem; font-weight: 650;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0.08em; color: var(--muted);\n }\n button {\n cursor: pointer; font: inherit; font-weight: 600; border: none;\n border-radius: 8px; padding: 0.78rem 1rem;\n }\n button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }\n .primary {\n background: var(--accent); color: var(--accent-fg); width: 100%;\n font-size: 0.95rem;\n }\n .primary:hover:not(:disabled) { background: #e4e4e7; }\n .primary:disabled { opacity: 0.55; cursor: default; }\n .primary.is-loading {\n display: inline-flex; align-items: center; justify-content: center;\n gap: 0.55rem; opacity: 1;\n }\n .primary.is-loading::before {\n content: \"\"; width: 1rem; height: 1rem; flex: 0 0 auto;\n border-radius: 999px; border: 2px solid rgba(0,0,0,0.22);\n border-top-color: var(--accent-fg);\n animation: spin 0.75s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n @media (prefers-reduced-motion: reduce) {\n .primary.is-loading::before { animation: none; }\n }\n .ghost {\n background: transparent; color: var(--muted);\n border: 1px solid var(--border-strong); padding: 0.35rem 0.7rem;\n font-size: 0.78rem; font-weight: 500; border-radius: 8px;\n }\n .ghost:hover:not(:disabled) { color: var(--text); border-color: var(--subtle); }\n pre {\n background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;\n padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #d4d4d8; margin: 0.5rem 0 1rem;\n }\n /* Advanced disclosure */\n .advanced { margin: 0 0 1rem; }\n .advanced > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; justify-content: center; gap: 0.35rem;\n color: var(--subtle); font-size: 0.8rem; font-weight: 500;\n padding: 0.5rem 0; text-align: center;\n }\n .advanced > summary::-webkit-details-marker { display: none; }\n .advanced > summary:hover { color: var(--muted); }\n .advanced > summary:focus-visible { outline: 2px solid var(--ring);\n outline-offset: 2px; border-radius: 6px; }\n .advanced > summary .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin-top: -3px;\n }\n .advanced[open] > summary .chev { transform: rotate(225deg); margin-top: 2px; }\n .advanced-body {\n padding: 0.85rem 0.1rem 0.25rem;\n }\n .field { margin-bottom: 0.9rem; }\n .field:last-child { margin-bottom: 0; }\n .field label { display: block; font-size: 0.78rem; color: var(--muted);\n margin-bottom: 0.35rem; }\n .field input {\n width: 100%; padding: 0.6rem 0.7rem; font: inherit; color: var(--text);\n background: var(--panel-2); border: 1px solid var(--border-strong);\n border-radius: 8px;\n }\n .field input:focus-visible {\n outline: none; border-color: var(--ring);\n box-shadow: 0 0 0 3px rgba(250,250,250,0.12);\n }\n .connections {\n margin-top: 1.1rem; border-top: 1px solid var(--border);\n padding-top: 0.35rem;\n }\n .connections > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; gap: 0.55rem;\n min-height: 2.2rem; color: var(--muted); font-size: 0.82rem;\n }\n .connections > summary::-webkit-details-marker { display: none; }\n .connections > summary:focus-visible {\n outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;\n }\n .connections-title { font-weight: 600; color: var(--muted); }\n .connections-state {\n margin-left: auto; color: var(--subtle); font-size: 0.73rem;\n line-height: 1;\n }\n .connections .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin: -3px 0 0 0.15rem;\n }\n .connections[open] .chev { transform: rotate(225deg); margin-top: 2px; }\n .token-list { padding-top: 0.4rem; }\n .tok { display: flex; align-items: center; justify-content: space-between;\n gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border);\n font-size: 0.83rem; }\n .tok:last-child { border-bottom: none; }\n .tok .meta { color: var(--subtle); font-size: 0.74rem; margin-top: 0.1rem; }\n .tok.revoked { opacity: 0.45; }\n .empty-state {\n color: var(--subtle); font-size: 0.78rem; line-height: 1.45;\n padding: 0.3rem 0 0.45rem;\n }\n .msg { font-size: 0.83rem; padding: 0.7rem 0.8rem; border-radius: 8px;\n margin-bottom: 0.9rem; display: none; line-height: 1.4; }\n .msg.err { display: block; color: var(--error); background: var(--error-bg);\n border: 1px solid rgba(252,165,165,0.16); }\n .msg.ok { display: block; background: var(--ok-bg);\n border: 1px solid var(--ok-border); }\n .msg-title {\n display: block; color: var(--ok); font-size: 0.95rem;\n font-weight: 700; line-height: 1.25;\n }\n .msg-copy {\n display: block; color: rgba(134,239,172,0.72); font-size: 0.78rem;\n line-height: 1.4; margin-top: 0.2rem;\n }\n .result-panel { padding-top: 0.15rem; }\n .result-title {\n color: var(--text); font-size: 0.95rem; font-weight: 650;\n text-align: center; margin-bottom: 0.35rem;\n }\n .result-copy {\n color: var(--muted); font-size: 0.83rem; line-height: 1.45;\n text-align: center; margin: 0 auto 0.85rem; max-width: 34ch;\n }\n .section-label {\n color: var(--subtle); font-size: 0.7rem; font-weight: 650;\n letter-spacing: 0.08em; text-transform: uppercase; margin-top: 0.85rem;\n }\n @media (max-width: 480px) {\n body { align-items: flex-start; padding: 0.75rem; }\n .card { padding: 1rem; }\n .hero { padding: 0; }\n h1 { font-size: 1.3rem; }\n pre { font-size: 0.72rem; }\n }\n /* MCP URL display + per-host tabs (the non-dev path). */\n .mcp-url-block { margin: 0 0 1rem; }\n .url-row {\n display: flex; align-items: center; gap: 0.5rem;\n background: var(--panel-2); border: 1px solid var(--border-strong);\n border-radius: 8px; padding: 0.45rem 0.5rem 0.45rem 0.75rem;\n }\n .url-row code {\n flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.78rem; color: var(--text);\n }\n .url-row .ghost { flex: 0 0 auto; }\n .hosts {\n margin: 0 0 1rem; border-top: 1px solid var(--border);\n border-bottom: 1px solid var(--border); padding: 0.35rem 0;\n }\n .hosts > summary {\n list-style: none; cursor: pointer; user-select: none;\n display: flex; align-items: center; gap: 0.55rem;\n min-height: 2.2rem; color: var(--muted); font-size: 0.82rem;\n }\n .hosts > summary::-webkit-details-marker { display: none; }\n .hosts > summary:focus-visible {\n outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;\n }\n .hosts > summary .chev {\n width: 7px; height: 7px; border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor; transform: rotate(45deg);\n transition: transform 0.15s ease; margin: -3px 0 0 0.15rem;\n }\n .hosts[open] > summary .chev { transform: rotate(225deg); margin-top: 2px; }\n .hosts-body { padding: 0.15rem 0 0.25rem; }\n .tabs {\n display: flex; flex-wrap: wrap; gap: 0.25rem;\n border-bottom: 1px solid var(--border); margin-bottom: 0.75rem;\n padding-bottom: 0.4rem;\n }\n .tab {\n background: transparent; color: var(--subtle);\n border: 1px solid transparent;\n padding: 0.35rem 0.65rem; font-size: 0.8rem; font-weight: 600;\n border-radius: 6px;\n }\n .tab:hover { color: var(--muted); background: var(--panel-soft); }\n .tab.is-active {\n color: var(--text); background: var(--panel-2);\n border-color: var(--border-strong);\n }\n .tab-panel { display: none; }\n .tab-panel.is-active { display: block; }\n .tab-panel ol { margin: 0 0 0.6rem 1.1rem; padding: 0; }\n .tab-panel li {\n margin-bottom: 0.3rem; font-size: 0.86rem; line-height: 1.5;\n color: var(--muted);\n }\n .tab-panel li strong { color: var(--text); font-weight: 650; }\n .tab-panel a {\n color: var(--text); text-decoration: underline;\n text-underline-offset: 2px;\n }\n .tab-panel p {\n font-size: 0.84rem; color: var(--muted); margin: 0.4rem 0;\n line-height: 1.5;\n }\n .tab-panel .hint {\n font-size: 0.78rem; color: var(--subtle); margin-top: 0.5rem;\n }\n .tab-panel code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.78rem; color: var(--text);\n background: var(--panel-2); padding: 0.05rem 0.3rem;\n border-radius: 4px;\n }\n .tab-panel pre { margin: 0.4rem 0 0.5rem; }\n /* Per-tab primary CTA — visually distinct from the static-token mint\n * button below. Either a link (Open Claude →) or a copy command button.\n */\n .primary-link {\n display: inline-flex; align-items: center; justify-content: center;\n gap: 0.35rem; min-height: 36px; padding: 0.45rem 0.85rem;\n background: var(--panel-2); color: var(--text);\n border: 1px solid var(--border-strong); border-radius: 8px;\n font-size: 0.86rem; font-weight: 650; text-decoration: none;\n cursor: pointer; width: auto; max-width: 100%; text-align: center;\n margin: 0.5rem 0 0.2rem;\n }\n .tab-panel a.primary-link {\n color: var(--text); text-decoration: none;\n }\n .primary-link:hover {\n background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.2);\n }\n .primary-link.compact { min-width: 0; }\n .copy-flash {\n color: var(--ok) !important;\n border-color: var(--ok-border) !important;\n }\n .static-token-mint .static-token-body { padding-top: 0.5rem; }\n @media (min-width: 560px) {\n .card { max-width: 580px; }\n }\n .hidden { display: none !important; }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"hero\">\n <div class=\"flow\" role=\"img\" aria-label=\"Authorize ${safeApp}\">\n <span class=\"tile\" aria-hidden=\"true\">\n ${flowMarkSvg}\n </span>\n <span class=\"conn\" aria-hidden=\"true\"></span>\n <span class=\"tile\" aria-hidden=\"true\">\n <span class=\"agent-symbol\">&lt;/&gt;</span>\n </span>\n </div>\n\n <h1>${safeUserCode ? `Authorize ${safeApp} from your terminal?` : `Use ${safeApp} from your AI assistant`}</h1>\n <p class=\"identity\">\n <span>Signed in as <strong>${safeEmail}</strong></span>\n </p>\n </div>\n\n <div id=\"codeCallout\" class=\"device-strip ${safeUserCode ? \"\" : \"hidden\"}\">\n <span class=\"label\">Device code</span>\n <span class=\"value\" id=\"userCodeValue\">${safeUserCode}</span>\n </div>\n\n ${setupHtml}\n\n <details id=\"staticTokenMint\" class=\"connections static-token-mint\"${safeUserCode ? \" open\" : \"\"}>\n <summary>\n <span class=\"connections-title\">${safeUserCode ? \"Authorize this device\" : MCP_STATIC_TOKEN_FALLBACK.title}</span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"static-token-body\">\n <div id=\"msg\" class=\"msg\" role=\"status\" aria-live=\"polite\"></div>\n <div id=\"mintForm\">\n <button id=\"authorizeBtn\" class=\"primary\">${safeUserCode ? \"Authorize device\" : \"Create connection token\"}</button>\n ${tokenAdvancedOptionsHtml}\n </div>\n <div id=\"result\" class=\"result-panel hidden\">\n <div class=\"result-title\">${MCP_STATIC_TOKEN_FALLBACK.resultTitle}</div>\n <p class=\"result-copy\" id=\"resultMsg\">${MCP_STATIC_TOKEN_FALLBACK.resultCopy}</p>\n <div class=\"section-label\">MCP config</div>\n <pre id=\"mcpJson\"></pre>\n <details class=\"advanced\">\n <summary>\n Terminal alternative\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div class=\"advanced-body\">\n <pre id=\"cliLine\"></pre>\n </div>\n </details>\n </div>\n </div>\n </details>\n\n <details id=\"connections\" class=\"connections\">\n <summary>\n <span class=\"connections-title\">Existing connections</span>\n <span id=\"connectionsState\" class=\"connections-state hidden\" aria-live=\"polite\"></span>\n <span class=\"chev\" aria-hidden=\"true\"></span>\n </summary>\n <div id=\"tokenList\" class=\"token-list\"><div class=\"empty-state\">Checking connections...</div></div>\n </details>\n</div>\n<script>\n(function () {\n var BASE = ${JSON.stringify(joinAppPath(connectBasePath, MCP_PUBLIC_ROUTE_PREFIX + \"/connect\"))};\n var USER_CODE = ${JSON.stringify(safeUserCode || null)};\n var msgEl = document.getElementById(\"msg\");\n var connectionsEl = document.getElementById(\"connections\");\n var connectionsStateEl = document.getElementById(\"connectionsState\");\n\n // Tab switching for the per-host instructions block.\n var tabBtns = document.querySelectorAll(\".tabs .tab\");\n var tabPanels = document.querySelectorAll(\".tab-panel\");\n for (var i = 0; i < tabBtns.length; i++) {\n tabBtns[i].addEventListener(\"click\", function (ev) {\n var btn = ev.currentTarget;\n var name = btn.getAttribute(\"data-tab\");\n for (var j = 0; j < tabBtns.length; j++) {\n var active = tabBtns[j] === btn;\n tabBtns[j].classList.toggle(\"is-active\", active);\n tabBtns[j].setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n }\n for (var k = 0; k < tabPanels.length; k++) {\n tabPanels[k].classList.toggle(\n \"is-active\",\n tabPanels[k].getAttribute(\"data-panel\") === name,\n );\n }\n });\n }\n\n // Copy buttons — any element with data-copy=\"<id>\" copies that node's text.\n document.addEventListener(\"click\", function (ev) {\n var btn = ev.target && ev.target.closest && ev.target.closest(\"[data-copy]\");\n if (!btn) return;\n var node = document.getElementById(btn.getAttribute(\"data-copy\"));\n if (!node || !navigator.clipboard) return;\n navigator.clipboard.writeText(node.textContent || \"\").then(function () {\n var prev = btn.textContent;\n btn.textContent = \"Copied\";\n btn.classList.add(\"copy-flash\");\n setTimeout(function () {\n btn.textContent = prev;\n btn.classList.remove(\"copy-flash\");\n }, 1400);\n });\n });\n function showMsg(text, kind, title) {\n msgEl.textContent = \"\";\n if (title) {\n var titleEl = document.createElement(\"strong\");\n titleEl.className = \"msg-title\";\n titleEl.textContent = title;\n msgEl.appendChild(titleEl);\n var copyEl = document.createElement(\"span\");\n copyEl.className = \"msg-copy\";\n copyEl.textContent = text;\n msgEl.appendChild(copyEl);\n } else {\n msgEl.textContent = text;\n }\n msgEl.className = \"msg \" + (kind || \"err\");\n }\n function clearMsg() { msgEl.className = \"msg\"; msgEl.textContent = \"\"; }\n\n function setButtonLoading(btn, text) {\n if (!btn.dataset.idleText) btn.dataset.idleText = btn.textContent || \"\";\n btn.textContent = text;\n btn.classList.add(\"is-loading\");\n btn.setAttribute(\"aria-busy\", \"true\");\n btn.disabled = true;\n }\n\n function resetButtonLoading(btn) {\n btn.disabled = false;\n btn.classList.remove(\"is-loading\");\n btn.removeAttribute(\"aria-busy\");\n if (btn.dataset.idleText) btn.textContent = btn.dataset.idleText;\n }\n\n function renderResult(data) {\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var entry = {};\n entry[data.serverName] = data.mcpServerEntry;\n document.getElementById(\"mcpJson\").textContent =\n JSON.stringify({ mcpServers: entry }, null, 2);\n document.getElementById(\"cliLine\").textContent = data.cli;\n document.getElementById(\"result\").classList.remove(\"hidden\");\n }\n\n async function postJson(path, body) {\n var res = await fetch(BASE + path, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify(body || {})\n });\n var data = null;\n try { data = await res.json(); } catch (e) {}\n return { ok: res.ok, status: res.status, data: data };\n }\n\n async function loadTokens() {\n var listEl = document.getElementById(\"tokenList\");\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (!res.ok) {\n connectionsStateEl.textContent = \"Unavailable\";\n connectionsStateEl.classList.remove(\"hidden\");\n listEl.innerHTML = '<div class=\"empty-state\">Could not load connections.</div>';\n return;\n }\n var data = await res.json();\n var tokens = (data && data.tokens) || [];\n if (!tokens.length) {\n connectionsStateEl.textContent = \"\";\n connectionsStateEl.classList.add(\"hidden\");\n connectionsEl.open = false;\n listEl.innerHTML = '<div class=\"empty-state\">Created connections will appear here for revoking later.</div>';\n return;\n }\n var activeCount = tokens.filter(function (t) { return !t.revokedAt; }).length;\n connectionsStateEl.textContent = activeCount ? String(activeCount) : \"\";\n connectionsStateEl.classList.toggle(\"hidden\", activeCount === 0);\n listEl.innerHTML = \"\";\n tokens.forEach(function (t) {\n var div = document.createElement(\"div\");\n div.className = \"tok\" + (t.revokedAt ? \" revoked\" : \"\");\n var when = t.createdAt ? new Date(t.createdAt).toLocaleString() : \"\";\n var used = t.lastUsedAt ? \" · last used \" + new Date(t.lastUsedAt).toLocaleString() : \"\";\n var left = document.createElement(\"div\");\n var label = document.createElement(\"div\");\n label.textContent = t.label || \"(unlabeled)\";\n var meta = document.createElement(\"div\");\n meta.className = \"meta\";\n meta.textContent = (t.revokedAt ? \"Revoked · \" : \"Created \") + when + used;\n left.appendChild(label); left.appendChild(meta);\n div.appendChild(left);\n if (!t.revokedAt) {\n var btn = document.createElement(\"button\");\n btn.className = \"ghost\";\n btn.textContent = \"Revoke\";\n btn.onclick = async function () {\n btn.disabled = true;\n var r = await postJson(\"/tokens/revoke\", { id: t.id });\n if (r.ok) { loadTokens(); }\n else { btn.disabled = false; showMsg(\"Could not revoke token.\"); }\n };\n div.appendChild(btn);\n }\n listEl.appendChild(div);\n });\n } catch (e) {\n connectionsStateEl.textContent = \"Unavailable\";\n connectionsStateEl.classList.remove(\"hidden\");\n listEl.innerHTML = '<div class=\"empty-state\">Could not load connections.</div>';\n }\n }\n\n document.getElementById(\"authorizeBtn\").onclick = async function () {\n var btn = this;\n setButtonLoading(btn, USER_CODE ? \"Authorizing device...\" : \"Creating token...\");\n clearMsg();\n try {\n if (USER_CODE) {\n var a = await postJson(\"/device/authorize\", { user_code: USER_CODE });\n if (!a.ok) {\n resetButtonLoading(btn);\n showMsg((a.data && a.data.error) || \"Could not authorize this device code.\");\n return;\n }\n showMsg(\"Finishing connection… you can return to your terminal.\", \"ok\", \"Device authorized\");\n btn.classList.add(\"hidden\");\n document.getElementById(\"mintForm\").classList.add(\"hidden\");\n var cc = document.getElementById(\"codeCallout\");\n if (cc) cc.classList.add(\"hidden\");\n // The token is minted a few seconds later, when the CLI next polls\n // /device/poll — so a single loadTokens() here runs BEFORE the row\n // exists and the list would wrongly read \"No connections yet\" until\n // a manual reload. Snapshot the EXISTING non-revoked token ids first\n // so we announce \"Connected\" only when THIS device's freshly-minted\n // token appears — a user who already has tokens must not get a false\n // success the instant they authorize.\n var priorIds = {};\n try {\n var pr = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (pr.ok) {\n var pd = await pr.json();\n ((pd && pd.tokens) || []).forEach(function (t) {\n if (!t.revokedAt) priorIds[t.id] = true;\n });\n }\n } catch (e) {}\n loadTokens();\n var tries = 0;\n var iv = setInterval(async function () {\n tries++;\n try {\n var res = await fetch(BASE + \"/tokens\", { credentials: \"same-origin\" });\n if (res.ok) {\n var data = await res.json();\n var fresh = ((data && data.tokens) || []).filter(function (t) {\n return !t.revokedAt && !priorIds[t.id];\n });\n if (fresh.length > 0) {\n clearInterval(iv);\n showMsg(\"This device can now act as you — manage or revoke it below.\", \"ok\", \"Connected\");\n loadTokens();\n return;\n }\n }\n } catch (e) {}\n if (tries >= 30) {\n // No new token appeared in the window — e.g. the loopback\n // dev-open path writes a header-only config and never mints.\n // Don't claim \"Connected\" (we couldn't confirm a device token);\n // keep the \"authorized\" message and just refresh the list.\n clearInterval(iv);\n loadTokens();\n }\n }, 2000);\n return;\n } else {\n var labelEl = document.getElementById(\"label\");\n var ttlEl = document.getElementById(\"ttl\");\n var label = labelEl ? labelEl.value || undefined : undefined;\n var ttlDays = ttlEl ? parseInt(ttlEl.value, 10) || undefined : undefined;\n var m = await postJson(\"/token\", { label: label, ttlDays: ttlDays });\n if (!m.ok) {\n resetButtonLoading(btn);\n showMsg((m.data && m.data.error) || \"Could not create token.\");\n return;\n }\n renderResult(m.data);\n }\n loadTokens();\n } catch (e) {\n resetButtonLoading(btn);\n showMsg(\"Network error. Please try again.\");\n }\n };\n\n loadTokens();\n})();\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Handler — single entry point; core-routes-plugin dispatches the subpath.\n// ---------------------------------------------------------------------------\n\n/**\n * Handle a `/mcp/connect[...]` request. The legacy\n * `/_agent-native/mcp/connect` alias is mounted too. `subpath` is the part\n * after `/connect` (empty string = the page itself, otherwise e.g. `/token`,\n * `/device/start`). The core-routes-plugin computes it from the stripped event\n * path so this module stays mount-agnostic.\n */\nexport async function handleMcpConnect(\n event: H3Event,\n subpath: string,\n options: McpConnectRouteOptions = {},\n): Promise<Response> {\n const method = getMethod(event);\n const origin = deriveOrigin(event);\n const basePath = configuredBasePath();\n const appUrl = `${origin}${basePath}`;\n const sub = (\"/\" + subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")).replace(\n /^\\/$/,\n \"\",\n );\n\n // ---- The connect page (GET) ------------------------------------------\n if (sub === \"\") {\n if (method !== \"GET\" && method !== \"HEAD\") {\n return json({ error: \"Method not allowed\" }, 405);\n }\n const session = await getSession(event);\n if (!session?.email) {\n // Serve the SAME login form the guard would, at this same URL — the\n // login form reloads window.location so we re-enter here authed.\n const loginHtml = getConfiguredLoginHtml(event);\n if (loginHtml) return html(loginHtml, 200);\n // Fully-open app (no auth guard): nothing to scope a mint to.\n return html(\n renderConnectPage({\n connectBasePath: basePath,\n email: \"(no auth configured)\",\n appName: options.appName || appLabel(appUrl, options),\n appUrl,\n serverId: serverName(appUrl, options),\n userCode: null,\n }),\n );\n }\n let userCode: string | null = null;\n try {\n const u = new URL(\n event.node?.req?.url ?? event.path ?? \"/\",\n \"http://an.invalid\",\n );\n const raw = u.searchParams.get(\"user_code\");\n if (raw && USER_CODE_RE.test(raw)) userCode = raw;\n } catch {\n userCode = null;\n }\n return html(\n renderConnectPage({\n connectBasePath: basePath,\n email: session.email,\n appName: options.appName || appLabel(appUrl, options),\n appUrl,\n serverId: serverName(appUrl, options),\n userCode,\n }),\n );\n }\n\n // ---- POST /token (session-required) ---------------------------------\n if (sub === \"/token\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n if (!process.env.A2A_SECRET?.trim() && canUseDevOpenConnect(event)) {\n return json(\n mcpResultPayload(appUrl, options, { ownerEmail: session.email }),\n );\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n label?: unknown;\n ttlDays?: unknown;\n fullCatalog?: unknown;\n };\n const label =\n typeof body.label === \"string\" && body.label.trim()\n ? body.label.trim().slice(0, 120)\n : null;\n const ttlDays = clampTtlDays(body.ttlDays);\n const catalogScope: \"full\" | undefined =\n body.fullCatalog === true || body.fullCatalog === \"true\"\n ? \"full\"\n : undefined;\n try {\n const { token } = await mintConnectToken({\n email: session.email,\n orgId: session.orgId,\n label,\n ttlDays,\n appUrl,\n ...(catalogScope ? { catalogScope } : {}),\n });\n return json(mcpResultPayload(appUrl, options, { token }));\n } catch {\n return json({ error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- POST /device/start (UNAUTH) ------------------------------------\n if (sub === \"/device/start\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n try {\n const row = await createDeviceCode();\n const verificationUri = `${appUrl}${MCP_PUBLIC_ROUTE_PREFIX}/connect`;\n return json({\n device_code: row.deviceCode,\n user_code: row.userCode,\n verification_uri: verificationUri,\n verification_uri_complete: `${verificationUri}?user_code=${row.userCode}`,\n interval: DEVICE_POLL_INTERVAL_S,\n expires_in: Math.floor(DEVICE_CODE_TTL_MS / 1000),\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return json({ error: \"Rate limited. Try again shortly.\" }, 429);\n }\n return json({ error: \"Could not start device flow.\" }, 500);\n }\n }\n\n // ---- POST /device/authorize (session-required) ----------------------\n if (sub === \"/device/authorize\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n user_code?: unknown;\n };\n const userCode =\n typeof body.user_code === \"string\" ? body.user_code.trim() : \"\";\n if (!USER_CODE_RE.test(userCode)) {\n return json({ error: \"Invalid user code.\" }, 400);\n }\n const orgId =\n typeof session.orgId === \"string\" && session.orgId.trim()\n ? session.orgId.trim()\n : null;\n const result = await approveDeviceCode(userCode, session.email, orgId);\n if (result === \"not_found\") {\n return json({ error: \"Unknown device code.\" }, 404);\n }\n if (result === \"expired\") {\n return json({ error: \"This device code has expired.\" }, 410);\n }\n if (result === \"already\") {\n return json({ error: \"This device code was already used.\" }, 409);\n }\n return json({ status: \"approved\" });\n }\n\n // ---- POST /device/poll (UNAUTH) -------------------------------------\n if (sub === \"/device/poll\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n device_code?: unknown;\n };\n const deviceCode =\n typeof body.device_code === \"string\" ? body.device_code : \"\";\n if (!deviceCode) return json({ error: \"device_code required\" }, 400);\n const row = await getDeviceCode(deviceCode);\n if (!row) return json({ status: \"not_found\" }, 404);\n if (row.status === \"consumed\") return json({ status: \"consumed\" });\n if (\n row.status === \"expired\" ||\n (row.expiresAt != null && row.expiresAt < Date.now())\n ) {\n if (row.status !== \"expired\") void expireDeviceCode(deviceCode);\n return json({ status: \"expired\" });\n }\n if (\n row.status === \"pending\" ||\n row.status === \"minting\" ||\n !row.ownerEmail\n ) {\n return json({ status: \"pending\" });\n }\n // status === \"approved\" && ownerEmail bound → mint exactly once.\n if (!process.env.A2A_SECRET?.trim() && canUseDevOpenConnect(event)) {\n const consumed = await consumeDeviceCode(\n deviceCode,\n `dev-open-${randomUUID()}`,\n );\n if (!consumed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, options, {\n ownerEmail: row.ownerEmail,\n }),\n });\n }\n try {\n const jti = randomUUID();\n // Claim a retryable minting state first. If signing or recording fails,\n // release the row back to approved so the CLI can poll again.\n const claimed = await claimDeviceCodeForMint(deviceCode, jti);\n if (!claimed) {\n const fresh = await getDeviceCode(deviceCode);\n if (fresh?.status === \"consumed\") return json({ status: \"consumed\" });\n return json({ status: \"pending\" });\n }\n let token: string;\n try {\n const orgDomain = await resolveOrgDomain(claimed.orgId ?? undefined);\n token = await signConnectToken({\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n orgDomain,\n appUrl,\n expiresIn: `${DEFAULT_TOKEN_TTL_DAYS}d`,\n jti,\n });\n await recordMintedToken({\n jti,\n ownerEmail: claimed.ownerEmail!,\n orgId: claimed.orgId,\n label: \"Device connection\",\n });\n if (!(await finishDeviceCodeMint(deviceCode, jti))) {\n return json({ status: \"pending\" });\n }\n } catch (err) {\n await releaseDeviceCodeMint(deviceCode, jti);\n throw err;\n }\n return json({\n status: \"approved\",\n ...mcpResultPayload(appUrl, options, { token }),\n });\n } catch {\n return json({ status: \"error\", error: \"Failed to mint token.\" }, 500);\n }\n }\n\n // ---- GET /tokens (session-required) ---------------------------------\n if (sub === \"/tokens\") {\n if (method !== \"GET\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const rows = await listTokens(session.email);\n return json({\n tokens: rows.map((r) => ({\n id: r.id,\n label: r.label,\n createdAt: r.createdAt,\n lastUsedAt: r.lastUsedAt,\n revokedAt: r.revokedAt,\n })),\n });\n }\n\n // ---- POST /tokens/revoke (session-required) -------------------------\n if (sub === \"/tokens/revoke\") {\n if (method !== \"POST\") return json({ error: \"Method not allowed\" }, 405);\n const session = await getSession(event);\n if (!session?.email) return json({ error: \"Unauthorized\" }, 401);\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as {\n id?: unknown;\n };\n const id = typeof body.id === \"string\" ? body.id : \"\";\n if (!id) return json({ error: \"id required\" }, 400);\n const revoked = await revokeToken(session.email, id);\n return json({ ok: revoked });\n }\n\n return json({ error: \"Not found\" }, 404);\n}\n"]}
@@ -13,13 +13,13 @@
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
14
  count: number;
15
15
  updated?: undefined;
16
- ok?: undefined;
17
16
  error?: undefined;
17
+ ok?: undefined;
18
18
  } | {
19
19
  count?: undefined;
20
20
  updated: number;
21
- ok?: undefined;
22
21
  error?: undefined;
22
+ ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
25
25
  updated?: undefined;
@@ -28,7 +28,7 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
34
34
  //# sourceMappingURL=routes.d.ts.map
@@ -62,7 +62,7 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
62
62
  summary?: undefined;
63
63
  spans?: undefined;
64
64
  id?: undefined;
65
- ok: boolean;
66
65
  error?: undefined;
66
+ ok: boolean;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- error?: undefined;
19
18
  ok: boolean;
19
+ error?: undefined;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map
@@ -75,7 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
+ message?: undefined;
78
79
  id?: undefined;
80
+ found?: undefined;
79
81
  deleted?: undefined;
80
82
  providers: {
81
83
  id: string;
@@ -88,46 +90,44 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
88
90
  }[];
89
91
  count: number;
90
92
  provider?: undefined;
91
- found?: undefined;
92
93
  registered?: undefined;
93
94
  label?: undefined;
94
- message?: undefined;
95
95
  } | {
96
- count?: undefined;
96
+ message?: undefined;
97
97
  id?: undefined;
98
98
  deleted?: undefined;
99
99
  providers?: undefined;
100
+ count?: undefined;
100
101
  found: boolean;
101
102
  provider: import("../custom-registry.js").CustomProviderConfig;
102
103
  registered?: undefined;
103
104
  label?: undefined;
104
- message?: undefined;
105
105
  } | {
106
- count?: undefined;
106
+ message?: undefined;
107
107
  deleted?: undefined;
108
108
  providers?: undefined;
109
+ count?: undefined;
109
110
  provider?: undefined;
110
111
  found: boolean;
111
112
  id: string;
112
113
  registered?: undefined;
113
114
  label?: undefined;
114
- message?: undefined;
115
115
  } | {
116
- count?: undefined;
116
+ message?: undefined;
117
+ found?: undefined;
117
118
  providers?: undefined;
119
+ count?: undefined;
118
120
  provider?: undefined;
119
- found?: undefined;
120
121
  deleted: boolean;
121
122
  id: string;
122
123
  registered?: undefined;
123
124
  label?: undefined;
124
- message?: undefined;
125
125
  } | {
126
- count?: undefined;
126
+ found?: undefined;
127
127
  deleted?: undefined;
128
128
  providers?: undefined;
129
+ count?: undefined;
129
130
  provider?: undefined;
130
- found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -310,7 +310,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
310
310
  scope?: "org" | "user" | undefined;
311
311
  }, {
312
312
  id?: undefined;
313
+ provider?: undefined;
313
314
  deleted?: undefined;
315
+ found?: undefined;
314
316
  providers: {
315
317
  id: string;
316
318
  label: string;
@@ -321,8 +323,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
321
323
  updatedAt: number;
322
324
  }[];
323
325
  count: number;
324
- provider?: undefined;
325
- found?: undefined;
326
326
  registered?: undefined;
327
327
  label?: undefined;
328
328
  message?: undefined;
@@ -337,31 +337,31 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
337
337
  label?: undefined;
338
338
  message?: undefined;
339
339
  } | {
340
+ provider?: undefined;
340
341
  deleted?: undefined;
341
342
  count?: undefined;
342
343
  providers?: undefined;
343
- provider?: undefined;
344
344
  found: boolean;
345
345
  id: string;
346
346
  registered?: undefined;
347
347
  label?: undefined;
348
348
  message?: undefined;
349
349
  } | {
350
- count?: undefined;
351
- providers?: undefined;
352
350
  provider?: undefined;
353
351
  found?: undefined;
352
+ count?: undefined;
353
+ providers?: undefined;
354
354
  deleted: boolean;
355
355
  id: string;
356
356
  registered?: undefined;
357
357
  label?: undefined;
358
358
  message?: undefined;
359
359
  } | {
360
+ provider?: undefined;
360
361
  deleted?: undefined;
362
+ found?: undefined;
361
363
  count?: undefined;
362
364
  providers?: undefined;
363
- provider?: undefined;
364
- found?: undefined;
365
365
  registered: boolean;
366
366
  id: string;
367
367
  label: string;
@@ -73,7 +73,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
- deleted?: undefined;
77
76
  jobs: {
78
77
  id: string;
79
78
  name: string;
@@ -84,6 +83,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
84
83
  updatedAt: string;
85
84
  }[];
86
85
  total: number;
86
+ deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
- deleted?: undefined;
95
94
  jobs?: undefined;
96
95
  total?: undefined;
96
+ deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -560,7 +560,7 @@ const PROVIDER_CONFIGS = {
560
560
  docsUrls: ["https://developers.google.com/drive/api/reference/rest/v3"],
561
561
  specUrls: ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"],
562
562
  allowedHostSuffixes: ["googleapis.com"],
563
- templateUses: ["brain", "content", "slides", "dispatch"],
563
+ templateUses: ["brain", "content", "slides", "dispatch", "analytics"],
564
564
  examples: [
565
565
  { label: "List files", method: "GET", path: "/files" },
566
566
  { label: "Get file metadata", method: "GET", path: "/files/{fileId}" },