devpush 0.0.0-stage → 1.0.0-beta.4

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 (456) hide show
  1. package/README.md +22 -2
  2. package/agent-release.json +37 -0
  3. package/dist/package/LICENSE.md +21 -0
  4. package/dist/package/migrate.mjs +182 -0
  5. package/dist/package/plugins.mjs +125 -0
  6. package/dist/package/vite.mjs +439 -0
  7. package/package.json +168 -3
  8. package/public/apple-touch-icon.png +0 -0
  9. package/public/favicon.svg +1 -0
  10. package/public/logo-email.png +0 -0
  11. package/public/robots.txt +3 -0
  12. package/public/social.png +0 -0
  13. package/src/cloudflare-runtime.d.ts +78 -0
  14. package/src/components/about-dialog.tsx +70 -0
  15. package/src/components/copy-button.tsx +74 -0
  16. package/src/components/logo-mark.tsx +20 -0
  17. package/src/components/pending-collection.tsx +103 -0
  18. package/src/components/resource-avatar.tsx +65 -0
  19. package/src/components/route-error-state.tsx +95 -0
  20. package/src/components/section-navigation.tsx +39 -0
  21. package/src/components/settings-layout.tsx +111 -0
  22. package/src/components/storage-capabilities.tsx +11 -0
  23. package/src/components/table-pager.tsx +101 -0
  24. package/src/components/ui/alert-dialog.tsx +180 -0
  25. package/src/components/ui/alert.tsx +78 -0
  26. package/src/components/ui/avatar.tsx +58 -0
  27. package/src/components/ui/badge.tsx +49 -0
  28. package/src/components/ui/button-group.tsx +38 -0
  29. package/src/components/ui/button.tsx +67 -0
  30. package/src/components/ui/card.tsx +82 -0
  31. package/src/components/ui/chart.tsx +307 -0
  32. package/src/components/ui/checkbox.tsx +30 -0
  33. package/src/components/ui/combobox.tsx +256 -0
  34. package/src/components/ui/dialog.tsx +163 -0
  35. package/src/components/ui/dropdown-menu.tsx +110 -0
  36. package/src/components/ui/empty.tsx +65 -0
  37. package/src/components/ui/field.tsx +192 -0
  38. package/src/components/ui/input-group.tsx +118 -0
  39. package/src/components/ui/input-otp.tsx +70 -0
  40. package/src/components/ui/input.tsx +19 -0
  41. package/src/components/ui/label.tsx +24 -0
  42. package/src/components/ui/pagination.tsx +34 -0
  43. package/src/components/ui/popover.tsx +86 -0
  44. package/src/components/ui/select.tsx +145 -0
  45. package/src/components/ui/skeleton.tsx +19 -0
  46. package/src/components/ui/sonner.tsx +47 -0
  47. package/src/components/ui/switch.tsx +31 -0
  48. package/src/components/ui/table.tsx +81 -0
  49. package/src/components/ui/textarea.tsx +18 -0
  50. package/src/components/ui/tooltip.tsx +55 -0
  51. package/src/features/admin/admin-settings.tsx +1048 -0
  52. package/src/features/auth/current-user-context-query.ts +11 -0
  53. package/src/features/auth/login-form.tsx +442 -0
  54. package/src/features/auth/sign-out-menu-item.tsx +42 -0
  55. package/src/features/deployments/deployment-actions.tsx +336 -0
  56. package/src/features/deployments/deployment-alias-badge.tsx +35 -0
  57. package/src/features/deployments/deployment-detail.tsx +596 -0
  58. package/src/features/deployments/deployment-filters.ts +66 -0
  59. package/src/features/deployments/deployment-list.tsx +465 -0
  60. package/src/features/deployments/deployment-visit-links.tsx +104 -0
  61. package/src/features/deployments/deployments-page.tsx +376 -0
  62. package/src/features/logs/log-scroll-area.tsx +69 -0
  63. package/src/features/notifications/email-preferences.tsx +232 -0
  64. package/src/features/onboarding/profile-onboarding.tsx +162 -0
  65. package/src/features/plugins/contributions.tsx +118 -0
  66. package/src/features/projects/backup-job-logs.tsx +143 -0
  67. package/src/features/projects/backup-policy-delete-dialog.tsx +82 -0
  68. package/src/features/projects/backup-restore-actions.tsx +573 -0
  69. package/src/features/projects/backup-status-icon.tsx +33 -0
  70. package/src/features/projects/backup-target-list.tsx +222 -0
  71. package/src/features/projects/backup-timestamp.tsx +32 -0
  72. package/src/features/projects/backup-volume-picker.tsx +158 -0
  73. package/src/features/projects/compose-service-input.tsx +64 -0
  74. package/src/features/projects/deploy-readiness.ts +28 -0
  75. package/src/features/projects/environment-dot.tsx +91 -0
  76. package/src/features/projects/log-keyword-filter.tsx +48 -0
  77. package/src/features/projects/manual-deploy-dialog.tsx +563 -0
  78. package/src/features/projects/pending-section.ts +26 -0
  79. package/src/features/projects/project-backup-details-pending.tsx +305 -0
  80. package/src/features/projects/project-backup-details.tsx +936 -0
  81. package/src/features/projects/project-backup-policy.tsx +644 -0
  82. package/src/features/projects/project-backup-utils.ts +190 -0
  83. package/src/features/projects/project-backups-pending.tsx +136 -0
  84. package/src/features/projects/project-backups.tsx +876 -0
  85. package/src/features/projects/project-environment-settings.tsx +1128 -0
  86. package/src/features/projects/project-heading.tsx +68 -0
  87. package/src/features/projects/project-layout.tsx +188 -0
  88. package/src/features/projects/project-logs.tsx +461 -0
  89. package/src/features/projects/project-overview.tsx +280 -0
  90. package/src/features/projects/project-route-pending.tsx +1064 -0
  91. package/src/features/projects/project-routing-settings.tsx +663 -0
  92. package/src/features/projects/project-settings-section.tsx +64 -0
  93. package/src/features/projects/project-settings-utils.ts +101 -0
  94. package/src/features/projects/project-settings.tsx +1472 -0
  95. package/src/features/projects/repository-picker-dialog.tsx +404 -0
  96. package/src/features/repositories/new-project-dialog-launcher.tsx +44 -0
  97. package/src/features/repositories/new-project-dialog.tsx +1204 -0
  98. package/src/features/repositories/new-project-pending.tsx +28 -0
  99. package/src/features/repositories/repository-list-pending.tsx +59 -0
  100. package/src/features/servers/server-availability-alert.tsx +44 -0
  101. package/src/features/servers/server-availability.ts +29 -0
  102. package/src/features/servers/server-avatar.tsx +52 -0
  103. package/src/features/servers/server-detail-pending.tsx +315 -0
  104. package/src/features/servers/server-detail.tsx +855 -0
  105. package/src/features/servers/server-metric-chart-pending.tsx +38 -0
  106. package/src/features/servers/server-metric-chart.tsx +182 -0
  107. package/src/features/servers/server-metric-format.ts +50 -0
  108. package/src/features/servers/server-metrics-pending.tsx +75 -0
  109. package/src/features/servers/server-metrics.tsx +136 -0
  110. package/src/features/servers/team-servers.tsx +432 -0
  111. package/src/features/settings/settings-route-pending.tsx +406 -0
  112. package/src/features/teams/backup-storage-settings.tsx +483 -0
  113. package/src/features/teams/navigation.ts +51 -0
  114. package/src/features/teams/new-team-dialog.tsx +132 -0
  115. package/src/features/teams/pending-section.ts +19 -0
  116. package/src/features/teams/team-header.tsx +1286 -0
  117. package/src/features/teams/team-heading.tsx +57 -0
  118. package/src/features/teams/team-home.tsx +391 -0
  119. package/src/features/teams/team-invite.tsx +155 -0
  120. package/src/features/teams/team-route-pending.tsx +505 -0
  121. package/src/features/teams/team-settings.tsx +1555 -0
  122. package/src/features/teams/use-workspace-updates.ts +126 -0
  123. package/src/features/users/api-keys-settings.tsx +544 -0
  124. package/src/features/users/api-scope-selection.tsx +269 -0
  125. package/src/features/users/connected-apps-settings.tsx +449 -0
  126. package/src/features/users/mcp-consent.tsx +220 -0
  127. package/src/features/users/profile-picture.tsx +171 -0
  128. package/src/features/users/user-settings.tsx +1064 -0
  129. package/src/lib/account-settings-schema.ts +78 -0
  130. package/src/lib/action-feedback.ts +81 -0
  131. package/src/lib/admin-search-schema.ts +8 -0
  132. package/src/lib/agent-release.d.mts +21 -0
  133. package/src/lib/agent-release.mjs +90 -0
  134. package/src/lib/allowlist-schema.ts +79 -0
  135. package/src/lib/api-key-schema.ts +176 -0
  136. package/src/lib/auth/client.ts +20 -0
  137. package/src/lib/avatar-schema.ts +11 -0
  138. package/src/lib/backup-cron.ts +118 -0
  139. package/src/lib/backup-database.ts +41 -0
  140. package/src/lib/backup-feature.ts +2 -0
  141. package/src/lib/backup-paths.ts +7 -0
  142. package/src/lib/backup-policy-schema.ts +85 -0
  143. package/src/lib/backup-resource-schema.ts +12 -0
  144. package/src/lib/backup-restore-schema.ts +15 -0
  145. package/src/lib/backup-storage-schema.ts +55 -0
  146. package/src/lib/deferred-module.ts +38 -0
  147. package/src/lib/deployment-logs-query.ts +63 -0
  148. package/src/lib/display-name.ts +7 -0
  149. package/src/lib/error-message.ts +119 -0
  150. package/src/lib/form-errors.ts +10 -0
  151. package/src/lib/internal-redirect.ts +25 -0
  152. package/src/lib/manual-deploy-options-query.ts +21 -0
  153. package/src/lib/notification-preferences.ts +43 -0
  154. package/src/lib/onboarding.ts +11 -0
  155. package/src/lib/operation-logs-query.ts +51 -0
  156. package/src/lib/page-head.ts +14 -0
  157. package/src/lib/project-context-query.ts +257 -0
  158. package/src/lib/project-creation-schema.ts +64 -0
  159. package/src/lib/project-env-schema.ts +23 -0
  160. package/src/lib/project-environments-schema.ts +56 -0
  161. package/src/lib/project-routing-schema.ts +94 -0
  162. package/src/lib/project-runtime-config.ts +131 -0
  163. package/src/lib/project-settings-schema.ts +75 -0
  164. package/src/lib/public-api-schema.ts +128 -0
  165. package/src/lib/public-error.ts +337 -0
  166. package/src/lib/query.ts +13 -0
  167. package/src/lib/repository-selection-query.ts +11 -0
  168. package/src/lib/resource-cache.ts +30 -0
  169. package/src/lib/runtime-id.ts +19 -0
  170. package/src/lib/search.ts +14 -0
  171. package/src/lib/server-context-query.ts +43 -0
  172. package/src/lib/server-name-schema.ts +11 -0
  173. package/src/lib/settings-conflict.ts +45 -0
  174. package/src/lib/settings-version.ts +7 -0
  175. package/src/lib/team-context-query.ts +126 -0
  176. package/src/lib/team-context-schema.ts +8 -0
  177. package/src/lib/team-forms-schema.ts +45 -0
  178. package/src/lib/team-role.ts +13 -0
  179. package/src/lib/team-settings-schema.ts +48 -0
  180. package/src/lib/urls.ts +3 -0
  181. package/src/lib/use-avatar-input.ts +61 -0
  182. package/src/lib/use-refresh-error.ts +18 -0
  183. package/src/lib/use-settings-version.ts +23 -0
  184. package/src/lib/utils.ts +6 -0
  185. package/src/lib/validation.ts +5 -0
  186. package/src/lib/workspace-updates.ts +53 -0
  187. package/src/package/config.ts +10 -0
  188. package/src/package/env.mjs +54 -0
  189. package/src/package/migrate-api.d.ts +5 -0
  190. package/src/package/migrate.ts +56 -0
  191. package/src/package/plugins.ts +141 -0
  192. package/src/package/sqlite.mjs +2 -0
  193. package/src/package/vite-api.d.ts +5 -0
  194. package/src/package/vite.ts +324 -0
  195. package/src/package/watch-guard.cjs +26 -0
  196. package/src/plugins/client.ts +57 -0
  197. package/src/plugins/contract.ts +188 -0
  198. package/src/plugins/default.server.ts +4 -0
  199. package/src/plugins/default.ui.ts +4 -0
  200. package/src/plugins/operations.ts +45 -0
  201. package/src/plugins/server.ts +197 -0
  202. package/src/plugins/version.ts +1 -0
  203. package/src/routeTree.gen.ts +1275 -0
  204. package/src/router.tsx +46 -0
  205. package/src/routes/$teamSlug.index.tsx +53 -0
  206. package/src/routes/$teamSlug.new-project.tsx +13 -0
  207. package/src/routes/$teamSlug.projects.$projectName.backups.$backupId.tsx +40 -0
  208. package/src/routes/$teamSlug.projects.$projectName.backups.tsx +82 -0
  209. package/src/routes/$teamSlug.projects.$projectName.deployments.$deploymentId.tsx +52 -0
  210. package/src/routes/$teamSlug.projects.$projectName.deployments.tsx +86 -0
  211. package/src/routes/$teamSlug.projects.$projectName.logs.tsx +55 -0
  212. package/src/routes/$teamSlug.projects.$projectName.settings.tsx +51 -0
  213. package/src/routes/$teamSlug.projects.$projectName.tsx +106 -0
  214. package/src/routes/$teamSlug.projects.tsx +40 -0
  215. package/src/routes/$teamSlug.servers.$serverId.tsx +31 -0
  216. package/src/routes/$teamSlug.servers.index.tsx +59 -0
  217. package/src/routes/$teamSlug.servers.tsx +7 -0
  218. package/src/routes/$teamSlug.settings.tsx +56 -0
  219. package/src/routes/$teamSlug.tsx +86 -0
  220. package/src/routes/[.]well-known.oauth-authorization-server.api.auth.ts +13 -0
  221. package/src/routes/[.]well-known.oauth-protected-resource.mcp.ts +6 -0
  222. package/src/routes/__root.tsx +155 -0
  223. package/src/routes/admin.tsx +55 -0
  224. package/src/routes/api/agents/certificates.ts +21 -0
  225. package/src/routes/api/agents/credentials/rotate.ts +22 -0
  226. package/src/routes/api/agents/heartbeat.ts +42 -0
  227. package/src/routes/api/agents/job-logs.ts +22 -0
  228. package/src/routes/api/agents/jobs/complete.ts +22 -0
  229. package/src/routes/api/agents/jobs/next.ts +29 -0
  230. package/src/routes/api/agents/jobs/progress.ts +32 -0
  231. package/src/routes/api/agents/jobs/status.ts +22 -0
  232. package/src/routes/api/agents/register.ts +45 -0
  233. package/src/routes/api/agents/runtime-logs.ts +22 -0
  234. package/src/routes/api/agents/switch.ts +25 -0
  235. package/src/routes/api/auth/$.ts +38 -0
  236. package/src/routes/api/cron/backups.ts +33 -0
  237. package/src/routes/api/cron/maintenance.ts +30 -0
  238. package/src/routes/api/github/install.callback.ts +51 -0
  239. package/src/routes/api/github/install.ts +41 -0
  240. package/src/routes/api/github/webhook.ts +11 -0
  241. package/src/routes/api/health.ts +19 -0
  242. package/src/routes/api/install/agent.$token.ts +50 -0
  243. package/src/routes/api/install/agent.binary.ts +83 -0
  244. package/src/routes/api/install/agent.checksum.ts +45 -0
  245. package/src/routes/api/install/agent.ts +26 -0
  246. package/src/routes/api/plugins/$.ts +11 -0
  247. package/src/routes/api/v1/$.ts +15 -0
  248. package/src/routes/api/workspaces/updates.ts +8 -0
  249. package/src/routes/auth.login.tsx +50 -0
  250. package/src/routes/deployments.$deploymentId.unavailable.tsx +21 -0
  251. package/src/routes/index.tsx +23 -0
  252. package/src/routes/invites.$token.tsx +35 -0
  253. package/src/routes/mcp.ts +13 -0
  254. package/src/routes/oauth.consent.tsx +54 -0
  255. package/src/routes/onboarding.tsx +37 -0
  256. package/src/routes/onboarding.workspace.tsx +44 -0
  257. package/src/routes/settings.tsx +30 -0
  258. package/src/routes/uploads/avatars/$fileName.ts +44 -0
  259. package/src/server/admin/access.ts +67 -0
  260. package/src/server/admin/functions.ts +122 -0
  261. package/src/server/admin/service.ts +260 -0
  262. package/src/server/agents/backup-state.ts +210 -0
  263. package/src/server/agents/certificates.ts +96 -0
  264. package/src/server/agents/claims.ts +116 -0
  265. package/src/server/agents/cleanup.ts +52 -0
  266. package/src/server/agents/compatibility.ts +116 -0
  267. package/src/server/agents/completion.ts +83 -0
  268. package/src/server/agents/container-cleanup-state.ts +51 -0
  269. package/src/server/agents/deployment-state.ts +111 -0
  270. package/src/server/agents/job-payloads.ts +159 -0
  271. package/src/server/agents/jobs.ts +1293 -0
  272. package/src/server/agents/protocol.ts +400 -0
  273. package/src/server/agents/reconciliation.ts +43 -0
  274. package/src/server/agents/runtime-logs.ts +337 -0
  275. package/src/server/agents/runtime-state.ts +125 -0
  276. package/src/server/api/admission.ts +12 -0
  277. package/src/server/api/authorization.ts +115 -0
  278. package/src/server/api/credentials.ts +569 -0
  279. package/src/server/api/cursors.ts +72 -0
  280. package/src/server/api/errors.ts +24 -0
  281. package/src/server/api/functions.ts +60 -0
  282. package/src/server/api/handler.ts +274 -0
  283. package/src/server/api/mutation-operations.ts +913 -0
  284. package/src/server/api/operation-context.ts +139 -0
  285. package/src/server/api/operation-contract.ts +1099 -0
  286. package/src/server/api/operations.ts +64 -0
  287. package/src/server/api/read-operations.ts +891 -0
  288. package/src/server/api/retention.ts +33 -0
  289. package/src/server/auth/access.ts +109 -0
  290. package/src/server/auth/auth-methods.ts +28 -0
  291. package/src/server/auth/better-auth.ts +518 -0
  292. package/src/server/auth/context.ts +28 -0
  293. package/src/server/auth/email-change.ts +47 -0
  294. package/src/server/auth/email-delivery.ts +51 -0
  295. package/src/server/auth/provider-identity.ts +19 -0
  296. package/src/server/auth/provider-tokens.ts +27 -0
  297. package/src/server/auth/session.server.ts +19 -0
  298. package/src/server/backups/database-commands.ts +40 -0
  299. package/src/server/backups/destination.ts +12 -0
  300. package/src/server/backups/feature.ts +6 -0
  301. package/src/server/backups/policy-validation.ts +51 -0
  302. package/src/server/backups/queue-state.ts +137 -0
  303. package/src/server/backups/service.ts +918 -0
  304. package/src/server/backups/storage-functions.ts +42 -0
  305. package/src/server/backups/storage-state.ts +67 -0
  306. package/src/server/backups/storage.ts +237 -0
  307. package/src/server/config.ts +274 -0
  308. package/src/server/db/atomic.ts +16 -0
  309. package/src/server/db/d1-connection.ts +83 -0
  310. package/src/server/db/errors.ts +17 -0
  311. package/src/server/db/index.cloudflare.ts +15 -0
  312. package/src/server/db/index.ts +35 -0
  313. package/src/server/db/libsql-connection.ts +72 -0
  314. package/src/server/db/log-schema.ts +102 -0
  315. package/src/server/db/migrate-libsql.ts +29 -0
  316. package/src/server/db/migrate-sqlite.ts +47 -0
  317. package/src/server/db/migration-history.ts +12 -0
  318. package/src/server/db/migrations-logs/0000_initial.sql +48 -0
  319. package/src/server/db/migrations-logs/0001_runtime_log_order.sql +2 -0
  320. package/src/server/db/migrations-logs/meta/0000_snapshot.json +327 -0
  321. package/src/server/db/migrations-logs/meta/0001_snapshot.json +329 -0
  322. package/src/server/db/migrations-logs/meta/_journal.json +20 -0
  323. package/src/server/db/migrations-sqlite/0000_initial.sql +894 -0
  324. package/src/server/db/migrations-sqlite/0001_agent_indexes.sql +5 -0
  325. package/src/server/db/migrations-sqlite/meta/0000_snapshot.json +5816 -0
  326. package/src/server/db/migrations-sqlite/meta/0001_snapshot.json +5841 -0
  327. package/src/server/db/migrations-sqlite/meta/_journal.json +20 -0
  328. package/src/server/db/oauth-schema.ts +216 -0
  329. package/src/server/db/remote-config.ts +47 -0
  330. package/src/server/db/schema.ts +1218 -0
  331. package/src/server/db/sqlite-connection.ts +94 -0
  332. package/src/server/db/sqlite-files.ts +27 -0
  333. package/src/server/db/sqlite-params.ts +19 -0
  334. package/src/server/db/worker-connection.ts +47 -0
  335. package/src/server/deleted-resources.ts +133 -0
  336. package/src/server/deletion-recovery.ts +272 -0
  337. package/src/server/email/layout.tsx +85 -0
  338. package/src/server/email/notification-template.tsx +62 -0
  339. package/src/server/email/otp-template.tsx +48 -0
  340. package/src/server/email/service.ts +251 -0
  341. package/src/server/email/team-invite-template.tsx +59 -0
  342. package/src/server/email/theme.ts +10 -0
  343. package/src/server/github/http.ts +14 -0
  344. package/src/server/github/install-state.ts +48 -0
  345. package/src/server/github/service.ts +831 -0
  346. package/src/server/github/webhook-handler.ts +198 -0
  347. package/src/server/github/webhooks.ts +205 -0
  348. package/src/server/http/auth.ts +11 -0
  349. package/src/server/http/error-diagnostics.ts +66 -0
  350. package/src/server/http/plugin-request.ts +15 -0
  351. package/src/server/http/proxy-request.ts +53 -0
  352. package/src/server/http/public-errors.ts +48 -0
  353. package/src/server/http/rate-limit.ts +59 -0
  354. package/src/server/http/request-body.ts +54 -0
  355. package/src/server/http/request-ip.ts +54 -0
  356. package/src/server/http/security-headers.ts +36 -0
  357. package/src/server/install/agent-binary.ts +230 -0
  358. package/src/server/install/agent-script.ts +565 -0
  359. package/src/server/install/release-binary.ts +65 -0
  360. package/src/server/logs/budget.ts +72 -0
  361. package/src/server/logs/cleanup.ts +64 -0
  362. package/src/server/logs/deployments.ts +98 -0
  363. package/src/server/logs/operations.ts +145 -0
  364. package/src/server/logs/retention.ts +39 -0
  365. package/src/server/maintenance.ts +61 -0
  366. package/src/server/mcp/connections.ts +358 -0
  367. package/src/server/mcp/functions.ts +63 -0
  368. package/src/server/mcp/handler.ts +295 -0
  369. package/src/server/mcp/oauth-discovery.cloudflare.ts +6 -0
  370. package/src/server/mcp/oauth-discovery.ts +8 -0
  371. package/src/server/mcp/oauth-provider.ts +220 -0
  372. package/src/server/mcp/urls.ts +20 -0
  373. package/src/server/notifications/functions.ts +51 -0
  374. package/src/server/notifications/observe.ts +303 -0
  375. package/src/server/notifications/outbox.ts +335 -0
  376. package/src/server/notifications/preferences.ts +131 -0
  377. package/src/server/notifications/query.ts +16 -0
  378. package/src/server/plugins/scope.ts +17 -0
  379. package/src/server/plugins/server-context.ts +157 -0
  380. package/src/server/plugins/services.server.ts +10 -0
  381. package/src/server/plugins/workspace-access.ts +74 -0
  382. package/src/server/projects/backup-policy-state.ts +68 -0
  383. package/src/server/projects/backups.ts +219 -0
  384. package/src/server/projects/compose-metadata.ts +174 -0
  385. package/src/server/projects/configuration-state.ts +90 -0
  386. package/src/server/projects/context.ts +909 -0
  387. package/src/server/projects/creation-state.ts +46 -0
  388. package/src/server/projects/deletion-state.ts +69 -0
  389. package/src/server/projects/deployment-config.ts +139 -0
  390. package/src/server/projects/deployment-logs.ts +97 -0
  391. package/src/server/projects/deployments.ts +1123 -0
  392. package/src/server/projects/domain-state.ts +71 -0
  393. package/src/server/projects/environment-branches.ts +75 -0
  394. package/src/server/projects/environment-state.ts +158 -0
  395. package/src/server/projects/environments.ts +424 -0
  396. package/src/server/projects/functions.ts +748 -0
  397. package/src/server/projects/generated-hostname.ts +77 -0
  398. package/src/server/projects/hostnames.ts +244 -0
  399. package/src/server/projects/mappers.ts +457 -0
  400. package/src/server/projects/names.ts +41 -0
  401. package/src/server/projects/project-env-vars.ts +28 -0
  402. package/src/server/projects/queue-state.ts +118 -0
  403. package/src/server/projects/repository.ts +368 -0
  404. package/src/server/projects/route-state.ts +37 -0
  405. package/src/server/projects/runtime-logs.ts +97 -0
  406. package/src/server/projects/settings-context.ts +58 -0
  407. package/src/server/projects/settings.ts +738 -0
  408. package/src/server/projects/types.ts +355 -0
  409. package/src/server/realtime/agent-events.ts +32 -0
  410. package/src/server/realtime/node-broadcaster.ts +71 -0
  411. package/src/server/realtime/node-handler.ts +55 -0
  412. package/src/server/realtime/service.ts +125 -0
  413. package/src/server/realtime/workspace-object.cloudflare.ts +62 -0
  414. package/src/server/realtime/workspace-object.ts +3 -0
  415. package/src/server/resources/color.ts +13 -0
  416. package/src/server/runtime/background.ts +21 -0
  417. package/src/server/runtime/node-asset-cache-plugin.ts +12 -0
  418. package/src/server/runtime/node-scheduler-plugin.ts +17 -0
  419. package/src/server/runtime/node-scheduler.ts +51 -0
  420. package/src/server/runtime/platform-env.cloudflare.ts +17 -0
  421. package/src/server/runtime/platform-env.ts +19 -0
  422. package/src/server/runtime/scheduled-task-error.ts +9 -0
  423. package/src/server/scheduled-tasks.ts +28 -0
  424. package/src/server/secrets/crypto.ts +56 -0
  425. package/src/server/servers/functions.ts +233 -0
  426. package/src/server/servers/lifecycle.ts +122 -0
  427. package/src/server/servers/metric-history-schema.ts +24 -0
  428. package/src/server/servers/metric-history.ts +55 -0
  429. package/src/server/servers/monitoring.ts +592 -0
  430. package/src/server/servers/service.ts +1211 -0
  431. package/src/server/servers/status.ts +32 -0
  432. package/src/server/servers/switch.ts +96 -0
  433. package/src/server/settings-version.ts +43 -0
  434. package/src/server/storage/capabilities.ts +6 -0
  435. package/src/server/storage/local-object-store.cloudflare.ts +7 -0
  436. package/src/server/storage/local-object-store.ts +50 -0
  437. package/src/server/storage/object-store.ts +160 -0
  438. package/src/server/teams/creation.ts +51 -0
  439. package/src/server/teams/delete-policy.ts +13 -0
  440. package/src/server/teams/deletion-state.ts +80 -0
  441. package/src/server/teams/functions.ts +324 -0
  442. package/src/server/teams/invitations.ts +38 -0
  443. package/src/server/teams/membership.ts +86 -0
  444. package/src/server/teams/queries.ts +261 -0
  445. package/src/server/teams/service.ts +1127 -0
  446. package/src/server/teams/types.ts +101 -0
  447. package/src/server/uploads/avatar.ts +133 -0
  448. package/src/server/users/deletion-state.ts +93 -0
  449. package/src/server/users/functions.ts +160 -0
  450. package/src/server/users/onboarding.ts +17 -0
  451. package/src/server/users/service.ts +644 -0
  452. package/src/server.ts +55 -0
  453. package/src/styles/shadcn.css +653 -0
  454. package/src/styles/theme.css +76 -0
  455. package/src/styles.css +177 -0
  456. package/wrangler.template.jsonc +37 -0
@@ -0,0 +1,449 @@
1
+ import { mutationErrorOptions, afterMutation } from '#devpush/lib/action-feedback'
2
+ import { toast } from 'sonner'
3
+ import { useState } from 'react'
4
+ import { useQuery, useQueryClient } from '@tanstack/react-query'
5
+ import { ChevronRightIcon, LoaderIcon, Trash2Icon } from 'lucide-react'
6
+ import { SettingsSection } from '#devpush/components/settings-layout'
7
+ import { Button } from '#devpush/components/ui/button'
8
+ import { Badge } from '#devpush/components/ui/badge'
9
+ import { ResourceAvatar } from '#devpush/components/resource-avatar'
10
+ import {
11
+ Tooltip,
12
+ TooltipContent,
13
+ TooltipTrigger,
14
+ } from '#devpush/components/ui/tooltip'
15
+ import { FieldError } from '#devpush/components/ui/field'
16
+ import { Skeleton } from '#devpush/components/ui/skeleton'
17
+ import {
18
+ Table,
19
+ TableBody,
20
+ TableCell,
21
+ TableHead,
22
+ TableHeader,
23
+ TableRow,
24
+ } from '#devpush/components/ui/table'
25
+ import {
26
+ Empty,
27
+ EmptyDescription,
28
+ EmptyHeader,
29
+ EmptyTitle,
30
+ } from '#devpush/components/ui/empty'
31
+ import {
32
+ AlertDialog,
33
+ AlertDialogContent,
34
+ AlertDialogHeader,
35
+ AlertDialogTitle,
36
+ AlertDialogDescription,
37
+ AlertDialogFooter,
38
+ AlertDialogCancel,
39
+ AlertDialogAction,
40
+ } from '#devpush/components/ui/alert-dialog'
41
+ import { getPublicErrorMessage } from '#devpush/lib/public-error'
42
+ import { CopyButton } from '#devpush/components/copy-button'
43
+ import { apiKeyPresets, apiKeyPresetSchema } from '#devpush/lib/api-key-schema'
44
+ import {
45
+ getMcpConnectionsAction,
46
+ revokeMcpConnectionAction,
47
+ } from '#devpush/server/mcp/functions'
48
+
49
+ type Connection = Awaited<
50
+ ReturnType<typeof getMcpConnectionsAction>
51
+ >['connections'][number]
52
+ export function ConnectedAppsSettings({ userId }: { userId: string }) {
53
+ const client = useQueryClient()
54
+ const [page, setPage] = useState(1)
55
+ const queryKey = ['account-connected-apps', userId, page]
56
+ const query = useQuery({
57
+ queryKey,
58
+ queryFn: () => getMcpConnectionsAction({ data: { page } }),
59
+ staleTime: 30_000,
60
+ })
61
+ const [selected, setSelected] = useState<Connection | null>(null)
62
+ const [busy, setBusy] = useState(false)
63
+ async function revoke() {
64
+ if (!selected || busy) return
65
+ setBusy(true)
66
+
67
+ try {
68
+ await revokeMcpConnectionAction({ data: { id: selected.id } })
69
+ setSelected(null)
70
+ await afterMutation(
71
+ () =>
72
+ client.invalidateQueries({
73
+ queryKey: ['account-connected-apps', userId],
74
+ }),
75
+ 'Connection revoked, but refreshing the list failed. Please reload.',
76
+ )
77
+ } catch (cause) {
78
+ toast.error(
79
+ getPublicErrorMessage(
80
+ cause,
81
+ 'Unable to revoke this connection. Please try again.',
82
+ ),
83
+ ...mutationErrorOptions(cause),
84
+ )
85
+ } finally {
86
+ setBusy(false)
87
+ }
88
+ }
89
+ return (
90
+ <>
91
+ <SettingsSection
92
+ id="connected-apps"
93
+ title="Connected apps"
94
+ description={<ConnectedAppsDescription mcpUrl={query.data?.mcpUrl} />}
95
+ >
96
+ {query.isError ? (
97
+ <div className="space-y-2">
98
+ <FieldError>
99
+ {getPublicErrorMessage(
100
+ query.error,
101
+ 'Unable to load connected apps.',
102
+ )}
103
+ </FieldError>
104
+ <Button variant="outline" onClick={() => void query.refetch()}>
105
+ Retry
106
+ </Button>
107
+ </div>
108
+ ) : query.data ? (
109
+ query.data.connections.length ? (
110
+ <div className="overflow-hidden rounded-lg border">
111
+ <Table aria-label="Connected apps" className="min-w-[640px]">
112
+ <ConnectedAppsTableHeader />
113
+ <TableBody>
114
+ {query.data.connections.map((connection) => (
115
+ <TableRow key={connection.id}>
116
+ <TableCell className="pl-4">
117
+ <ConnectionStatus status={connection.status} />
118
+ </TableCell>
119
+ <TableCell>
120
+ <p
121
+ className="min-w-40 max-w-64 truncate font-medium"
122
+ title={connection.name}
123
+ >
124
+ {connection.name}
125
+ </p>
126
+ </TableCell>
127
+ <TableCell className="text-muted-foreground">
128
+ <ConnectionScope connection={connection} />
129
+ </TableCell>
130
+ <TableCell className="text-muted-foreground">
131
+ <p>{connectionAccessLabel(connection.scopes)}</p>
132
+ {connection.scopes.includes('logs:read') ? (
133
+ <p className="mt-1 text-xs text-muted-foreground">
134
+ Includes logs
135
+ </p>
136
+ ) : null}
137
+ </TableCell>
138
+ <TableCell className="text-muted-foreground">
139
+ <time dateTime={connection.expiresAt}>
140
+ {formatDate(connection.expiresAt)}
141
+ </time>
142
+ </TableCell>
143
+ <TableCell className="text-right">
144
+ <Tooltip>
145
+ <TooltipTrigger asChild>
146
+ <span className="inline-flex">
147
+ <Button
148
+ variant="ghost"
149
+ size="icon-sm"
150
+ className="text-destructive hover:text-destructive"
151
+ aria-label={`Revoke ${connection.name}`}
152
+ disabled={connection.status === 'revoked'}
153
+ onClick={() => {
154
+ setSelected(connection)
155
+ }}
156
+ >
157
+ <Trash2Icon />
158
+ </Button>
159
+ </span>
160
+ </TooltipTrigger>
161
+ <TooltipContent>Revoke access</TooltipContent>
162
+ </Tooltip>
163
+ </TableCell>
164
+ </TableRow>
165
+ ))}
166
+ </TableBody>
167
+ </Table>
168
+ </div>
169
+ ) : (
170
+ <Empty className="min-h-40">
171
+ <EmptyHeader>
172
+ <EmptyTitle>No connected apps.</EmptyTitle>
173
+ <EmptyDescription>
174
+ Apps you authorize will appear here.
175
+ </EmptyDescription>
176
+ </EmptyHeader>
177
+ </Empty>
178
+ )
179
+ ) : (
180
+ <ConnectedAppsPending />
181
+ )}
182
+ {query.data && query.data.pagination.totalPages > 1 ? (
183
+ <div className="mt-4 flex items-center justify-between gap-2 text-sm">
184
+ <span className="text-muted-foreground">
185
+ Page {query.data.pagination.page} of{' '}
186
+ {query.data.pagination.totalPages}
187
+ </span>
188
+ <div className="flex gap-2">
189
+ <Button
190
+ variant="outline"
191
+ size="sm"
192
+ disabled={page <= 1 || query.isFetching}
193
+ onClick={() => setPage((current) => current - 1)}
194
+ >
195
+ Previous
196
+ </Button>
197
+ <Button
198
+ variant="outline"
199
+ size="sm"
200
+ disabled={
201
+ page >= query.data.pagination.totalPages || query.isFetching
202
+ }
203
+ onClick={() => setPage((current) => current + 1)}
204
+ >
205
+ Next
206
+ </Button>
207
+ </div>
208
+ </div>
209
+ ) : null}
210
+ </SettingsSection>
211
+ <AlertDialog
212
+ open={Boolean(selected)}
213
+ onOpenChange={(open) => {
214
+ if (!busy && !open) {
215
+ setSelected(null)
216
+ }
217
+ }}
218
+ >
219
+ <AlertDialogContent>
220
+ <AlertDialogHeader>
221
+ <AlertDialogTitle>Revoke {selected?.name}?</AlertDialogTitle>
222
+ <AlertDialogDescription>
223
+ This app will lose access immediately. You can connect it again
224
+ through browser authorization.
225
+ </AlertDialogDescription>
226
+ </AlertDialogHeader>
227
+ <AlertDialogFooter>
228
+ <AlertDialogCancel disabled={busy}>Cancel</AlertDialogCancel>
229
+ <AlertDialogAction
230
+ variant="destructive"
231
+ disabled={busy}
232
+ onClick={(event) => {
233
+ event.preventDefault()
234
+ void revoke()
235
+ }}
236
+ >
237
+ {busy ? <LoaderIcon className="animate-spin" /> : null}Revoke
238
+ </AlertDialogAction>
239
+ </AlertDialogFooter>
240
+ </AlertDialogContent>
241
+ </AlertDialog>
242
+ </>
243
+ )
244
+ }
245
+
246
+ export function ConnectedAppsDescription({ mcpUrl }: { mcpUrl?: string }) {
247
+ const copyClassName =
248
+ 'inline-flex h-auto max-w-full cursor-pointer rounded-none border-0 p-0 align-baseline text-sm font-normal text-inherit whitespace-normal hover:bg-transparent hover:text-inherit dark:hover:bg-transparent [&_svg]:size-3 [&_svg]:opacity-50 [&_svg]:transition-opacity [&_svg]:duration-150 [&_svg]:ease-out motion-reduce:[&_svg]:transition-none hover:[&_svg]:opacity-100 focus-visible:[&_svg]:opacity-100'
249
+ return (
250
+ <div>
251
+ Connect apps with browser sign-in or an API key using this MCP URL;{' '}
252
+ {mcpUrl ? (
253
+ <CopyButton
254
+ value={mcpUrl}
255
+ label="Copy MCP URL"
256
+ className={copyClassName}
257
+ >
258
+ <code className="min-w-0 break-all font-sans">{mcpUrl}</code>
259
+ </CopyButton>
260
+ ) : (
261
+ <span role="status" aria-label="MCP URL loading">
262
+ <CopyButton
263
+ value=""
264
+ label="Copy MCP URL"
265
+ disabled
266
+ className={copyClassName}
267
+ >
268
+ <Skeleton className="h-4 w-56 max-w-full" />
269
+ </CopyButton>
270
+ </span>
271
+ )}
272
+ </div>
273
+ )
274
+ }
275
+
276
+ const connectionStatuses = {
277
+ active: { label: 'Active', variant: 'secondary', className: undefined },
278
+ expired: {
279
+ label: 'Expired',
280
+ variant: 'secondary',
281
+ className: 'text-muted-foreground',
282
+ },
283
+ revoked: { label: 'Revoked', variant: 'destructive', className: undefined },
284
+ unavailable: {
285
+ label: 'Unavailable',
286
+ variant: 'outline',
287
+ className: 'text-muted-foreground',
288
+ },
289
+ } as const
290
+
291
+ function ConnectionStatus({ status }: { status: Connection['status'] }) {
292
+ const presentation = connectionStatuses[status]
293
+ return (
294
+ <Badge variant={presentation.variant} className={presentation.className}>
295
+ {presentation.label}
296
+ </Badge>
297
+ )
298
+ }
299
+
300
+ function ConnectionScope({ connection }: { connection: Connection }) {
301
+ const { team, projects } = connection
302
+ const projectNames = projects?.map(
303
+ (project) => project.name ?? 'Unavailable project',
304
+ )
305
+ const projectLabel =
306
+ projects?.length === 1
307
+ ? projectNames![0]
308
+ : `${projects?.length ?? 0} projects`
309
+ return (
310
+ <div className="flex items-center gap-1.5">
311
+ <Tooltip>
312
+ <TooltipTrigger asChild>
313
+ <span
314
+ role="img"
315
+ aria-label={team.name}
316
+ tabIndex={0}
317
+ className="shrink-0 rounded outline-none focus-visible:ring-3 focus-visible:ring-ring/50"
318
+ >
319
+ <span aria-hidden="true">
320
+ <ResourceAvatar
321
+ name={team.name}
322
+ src={team.image ?? undefined}
323
+ color={team.color}
324
+ size="xs"
325
+ />
326
+ </span>
327
+ </span>
328
+ </TooltipTrigger>
329
+ <TooltipContent>{team.name}</TooltipContent>
330
+ </Tooltip>
331
+ <ChevronRightIcon aria-hidden="true" className="size-3 shrink-0" />
332
+ {projects === null ? (
333
+ <span>All projects</span>
334
+ ) : (
335
+ <Tooltip>
336
+ <TooltipTrigger asChild>
337
+ <span
338
+ tabIndex={0}
339
+ aria-label={projectNames?.join(', ')}
340
+ className="flex min-w-0 items-center gap-1.5 rounded outline-none focus-visible:ring-3 focus-visible:ring-ring/50"
341
+ >
342
+ <span aria-hidden="true" className="flex shrink-0 -space-x-1">
343
+ {projects.slice(0, 3).map((project) => (
344
+ <ResourceAvatar
345
+ key={project.id}
346
+ name={project.name ?? 'Unavailable project'}
347
+ src={project.image ?? undefined}
348
+ color={project.color}
349
+ size="xs"
350
+ />
351
+ ))}
352
+ </span>
353
+ <span className="max-w-40 truncate">{projectLabel}</span>
354
+ </span>
355
+ </TooltipTrigger>
356
+ <TooltipContent>{projectNames?.join(', ')}</TooltipContent>
357
+ </Tooltip>
358
+ )}
359
+ </div>
360
+ )
361
+ }
362
+
363
+ function ConnectedAppsTableHeader() {
364
+ return (
365
+ <TableHeader>
366
+ <TableRow>
367
+ <TableHead scope="col" className="pl-4">
368
+ Status
369
+ </TableHead>
370
+ <TableHead scope="col">App</TableHead>
371
+ <TableHead scope="col">Scope</TableHead>
372
+ <TableHead scope="col">Access</TableHead>
373
+ <TableHead scope="col">Expires</TableHead>
374
+ <TableHead scope="col" className="text-right">
375
+ <span className="sr-only">Actions</span>
376
+ </TableHead>
377
+ </TableRow>
378
+ </TableHeader>
379
+ )
380
+ }
381
+
382
+ export function ConnectedAppsPending() {
383
+ return (
384
+ <div
385
+ role="status"
386
+ aria-label="Connected apps loading"
387
+ className="overflow-hidden rounded-lg border"
388
+ >
389
+ <Table aria-label="Connected apps" className="min-w-[640px]">
390
+ <ConnectedAppsTableHeader />
391
+ <TableBody>
392
+ <TableRow>
393
+ <TableCell className="pl-4">
394
+ <Skeleton className="h-5 w-16 rounded-full" />
395
+ </TableCell>
396
+ <TableCell>
397
+ <Skeleton className="h-4 w-40" />
398
+ </TableCell>
399
+ <TableCell>
400
+ <div className="flex items-center gap-1.5">
401
+ <Skeleton className="size-5 rounded" />
402
+ <ChevronRightIcon
403
+ aria-hidden="true"
404
+ className="size-3 shrink-0 text-muted-foreground"
405
+ />
406
+ <Skeleton className="size-5 rounded" />
407
+ <Skeleton className="h-4 w-24" />
408
+ </div>
409
+ </TableCell>
410
+ <TableCell>
411
+ <Skeleton className="h-4 w-20" />
412
+ </TableCell>
413
+ <TableCell>
414
+ <Skeleton className="h-4 w-24" />
415
+ </TableCell>
416
+ <TableCell className="text-right">
417
+ <Button
418
+ variant="ghost"
419
+ size="icon-sm"
420
+ aria-label="Revoke access"
421
+ disabled
422
+ >
423
+ <Trash2Icon />
424
+ </Button>
425
+ </TableCell>
426
+ </TableRow>
427
+ </TableBody>
428
+ </Table>
429
+ </div>
430
+ )
431
+ }
432
+
433
+ function connectionAccessLabel(scopes: string[]) {
434
+ const permissions = scopes.filter((scope) => scope !== 'logs:read')
435
+ const preset = apiKeyPresetSchema.options.find((name) =>
436
+ permissions.every((scope) =>
437
+ apiKeyPresets[name].scopes.some((permission) => permission === scope),
438
+ ),
439
+ )
440
+ return preset ? apiKeyPresets[preset].label : 'Custom permissions'
441
+ }
442
+
443
+ function formatDate(value: string) {
444
+ return new Date(value).toLocaleDateString(undefined, {
445
+ month: 'short',
446
+ day: 'numeric',
447
+ year: 'numeric',
448
+ })
449
+ }
@@ -0,0 +1,220 @@
1
+ import { ApiScopeSelection } from './api-scope-selection'
2
+ import { useState } from 'react'
3
+ import { ShieldCheckIcon, LoaderIcon } from 'lucide-react'
4
+ import {
5
+ Card,
6
+ CardHeader,
7
+ CardTitle,
8
+ CardDescription,
9
+ CardContent,
10
+ CardFooter,
11
+ } from '#devpush/components/ui/card'
12
+ import { Button } from '#devpush/components/ui/button'
13
+ import {
14
+ Field,
15
+ FieldGroup,
16
+ FieldLabel,
17
+ FieldError,
18
+ } from '#devpush/components/ui/field'
19
+ import {
20
+ Select,
21
+ SelectTrigger,
22
+ SelectValue,
23
+ SelectContent,
24
+ SelectItem,
25
+ } from '#devpush/components/ui/select'
26
+ import { Checkbox } from '#devpush/components/ui/checkbox'
27
+ import { Skeleton } from '#devpush/components/ui/skeleton'
28
+ import { LogoMark } from '#devpush/components/logo-mark'
29
+ import { getPublicErrorMessage } from '#devpush/lib/public-error'
30
+ import { apiKeyPresets } from '#devpush/lib/api-key-schema'
31
+ import type { getMcpConsentAction } from '#devpush/server/mcp/functions'
32
+ import { completeMcpConsentAction } from '#devpush/server/mcp/functions'
33
+
34
+ type Consent = NonNullable<Awaited<ReturnType<typeof getMcpConsentAction>>>
35
+ export function McpConsentPending() {
36
+ return (
37
+ <main className="flex min-h-screen items-center justify-center bg-background p-6 text-foreground">
38
+ <Card className="w-full max-w-md" aria-busy="true">
39
+ <CardHeader>
40
+ <LogoMark className="mb-3 size-8" />
41
+ <CardTitle className="flex items-center gap-2">
42
+ Connect <Skeleton className="h-5 w-28" />
43
+ </CardTitle>
44
+ <CardDescription>
45
+ Authorize this app to use devpush on your behalf.
46
+ </CardDescription>
47
+ </CardHeader>
48
+ <CardContent>
49
+ <FieldGroup>
50
+ <Field>
51
+ <FieldLabel>Scope</FieldLabel>
52
+ {['API access team', 'API access project'].map((label) => (
53
+ <Select key={label} disabled>
54
+ <SelectTrigger aria-label={label}>
55
+ <Skeleton className="h-4 w-32" />
56
+ </SelectTrigger>
57
+ </Select>
58
+ ))}
59
+ </Field>
60
+ <Field>
61
+ <FieldLabel>Access</FieldLabel>
62
+ <Select disabled>
63
+ <SelectTrigger aria-label="Authorized access">
64
+ <Skeleton className="h-4 w-32" />
65
+ </SelectTrigger>
66
+ </Select>
67
+ <Skeleton className="h-3 w-full" />
68
+ </Field>
69
+ <p className="flex items-start gap-2 text-xs text-muted-foreground">
70
+ <ShieldCheckIcon className="size-4 shrink-0" />
71
+ Access follows your current permissions and expires in 30 days.
72
+ You can revoke this connection in Account → Connected apps.
73
+ </p>
74
+ </FieldGroup>
75
+ </CardContent>
76
+ <CardFooter className="justify-end gap-2">
77
+ <Button variant="ghost" disabled>
78
+ Deny
79
+ </Button>
80
+ <Button disabled>Authorize</Button>
81
+ </CardFooter>
82
+ </Card>
83
+ </main>
84
+ )
85
+ }
86
+ export function McpConsent({ context }: { context: Consent }) {
87
+ const [teamId, setTeamId] = useState(context.teams[0]?.id ?? '')
88
+ const [projectId, setProjectId] = useState('all')
89
+ const [preset, setPreset] = useState<'read' | 'deploy' | 'manage'>(
90
+ context.requestedPermissions[0] ?? 'read',
91
+ )
92
+ const [includeLogs, setIncludeLogs] = useState(false)
93
+ const [busy, setBusy] = useState(false)
94
+ const [error, setError] = useState<string | null>(null)
95
+ async function complete(accepted: boolean) {
96
+ if (busy) return
97
+ setBusy(true)
98
+ setError(null)
99
+ try {
100
+ const result = await completeMcpConsentAction({
101
+ data: {
102
+ oauthQuery: context.oauthQuery,
103
+ accepted,
104
+ teamId: accepted ? teamId : undefined,
105
+ projectIds: projectId === 'all' ? null : [projectId],
106
+ preset,
107
+ includeLogs,
108
+ },
109
+ })
110
+ window.location.assign(result.redirectUrl)
111
+ } catch (cause) {
112
+ setError(
113
+ getPublicErrorMessage(
114
+ cause,
115
+ 'Unable to complete authorization. Please try again.',
116
+ ),
117
+ )
118
+ setBusy(false)
119
+ }
120
+ }
121
+ return (
122
+ <main className="flex min-h-screen items-center justify-center bg-background p-6 text-foreground">
123
+ <Card className="w-full max-w-md">
124
+ <CardHeader>
125
+ <LogoMark className="mb-3 size-8" />
126
+ <CardTitle>Connect {context.clientName}</CardTitle>
127
+ <CardDescription>
128
+ Authorize this app to use devpush on your behalf.{' '}
129
+ {context.redirectHost ? (
130
+ <>
131
+ You’ll return to{' '}
132
+ <span className="font-medium text-foreground">
133
+ {context.redirectHost}
134
+ </span>
135
+ .
136
+ </>
137
+ ) : (
138
+ <>Client ID: {context.clientId}.</>
139
+ )}{' '}
140
+ App names are provided by their developers and are not verified by
141
+ devpush.
142
+ </CardDescription>
143
+ </CardHeader>
144
+ <CardContent>
145
+ <FieldGroup>
146
+ <ApiScopeSelection
147
+ userId={context.userId}
148
+ teamId={teamId}
149
+ projectId={projectId}
150
+ initialTeams={context.teams}
151
+ disabled={busy}
152
+ onChange={(team, project) => {
153
+ setTeamId(team)
154
+ setProjectId(project)
155
+ }}
156
+ />
157
+ <Field>
158
+ <FieldLabel>Access</FieldLabel>
159
+ <Select
160
+ value={preset}
161
+ onValueChange={(value) => setPreset(value as typeof preset)}
162
+ disabled={busy}
163
+ >
164
+ <SelectTrigger aria-label="Authorized access">
165
+ <SelectValue />
166
+ </SelectTrigger>
167
+ <SelectContent>
168
+ {context.requestedPermissions.map((permission) => (
169
+ <SelectItem key={permission} value={permission}>
170
+ {apiKeyPresets[permission].label}
171
+ </SelectItem>
172
+ ))}
173
+ </SelectContent>
174
+ </Select>
175
+ <p className="text-xs text-muted-foreground">
176
+ {apiKeyPresets[preset].description}
177
+ </p>
178
+ </Field>
179
+ {context.allowLogs ? (
180
+ <Field>
181
+ <label className="flex items-center gap-2 text-sm">
182
+ <Checkbox
183
+ checked={includeLogs}
184
+ onCheckedChange={(value) => setIncludeLogs(value === true)}
185
+ disabled={busy}
186
+ />
187
+ Allow access to logs
188
+ </label>
189
+ <p className="text-xs text-muted-foreground">
190
+ Deployment and application logs may contain sensitive output.
191
+ </p>
192
+ </Field>
193
+ ) : null}
194
+ <p className="flex items-start gap-2 text-xs text-muted-foreground">
195
+ <ShieldCheckIcon className="size-4 shrink-0" />
196
+ Access follows your current permissions and expires in 30 days.
197
+ You can revoke this connection in Account → Connected apps.
198
+ </p>
199
+ <FieldError>{error}</FieldError>
200
+ </FieldGroup>
201
+ </CardContent>
202
+ <CardFooter className="justify-end gap-2">
203
+ <Button
204
+ variant="ghost"
205
+ disabled={busy}
206
+ onClick={() => void complete(false)}
207
+ >
208
+ Deny
209
+ </Button>
210
+ <Button
211
+ disabled={busy || !teamId}
212
+ onClick={() => void complete(true)}
213
+ >
214
+ {busy ? <LoaderIcon className="animate-spin" /> : null}Authorize
215
+ </Button>
216
+ </CardFooter>
217
+ </Card>
218
+ </main>
219
+ )
220
+ }