crca 1.4.0__py3-none-any.whl

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 (501) hide show
  1. .github/ISSUE_TEMPLATE/bug_report.md +65 -0
  2. .github/ISSUE_TEMPLATE/feature_request.md +41 -0
  3. .github/PULL_REQUEST_TEMPLATE.md +20 -0
  4. .github/workflows/publish-manual.yml +61 -0
  5. .github/workflows/publish.yml +64 -0
  6. .gitignore +214 -0
  7. CRCA.py +4156 -0
  8. LICENSE +201 -0
  9. MANIFEST.in +43 -0
  10. PKG-INFO +5035 -0
  11. README.md +4959 -0
  12. __init__.py +17 -0
  13. branches/CRCA-Q.py +2728 -0
  14. branches/crca_cg/corposwarm.py +9065 -0
  15. branches/crca_cg/fix_rancher_docker_creds.ps1 +155 -0
  16. branches/crca_cg/package.json +5 -0
  17. branches/crca_cg/test_bolt_integration.py +446 -0
  18. branches/crca_cg/test_corposwarm_comprehensive.py +773 -0
  19. branches/crca_cg/test_new_features.py +163 -0
  20. branches/crca_sd/__init__.py +149 -0
  21. branches/crca_sd/crca_sd_core.py +770 -0
  22. branches/crca_sd/crca_sd_governance.py +1325 -0
  23. branches/crca_sd/crca_sd_mpc.py +1130 -0
  24. branches/crca_sd/crca_sd_realtime.py +1844 -0
  25. branches/crca_sd/crca_sd_tui.py +1133 -0
  26. crca-1.4.0.dist-info/METADATA +5035 -0
  27. crca-1.4.0.dist-info/RECORD +501 -0
  28. crca-1.4.0.dist-info/WHEEL +4 -0
  29. crca-1.4.0.dist-info/licenses/LICENSE +201 -0
  30. docs/CRCA-Q.md +2333 -0
  31. examples/config.yaml.example +25 -0
  32. examples/crca_sd_example.py +513 -0
  33. examples/data_broker_example.py +294 -0
  34. examples/logistics_corporation.py +861 -0
  35. examples/palantir_example.py +299 -0
  36. examples/policy_bench.py +934 -0
  37. examples/pridnestrovia-sd.py +705 -0
  38. examples/pridnestrovia_realtime.py +1902 -0
  39. prompts/__init__.py +10 -0
  40. prompts/default_crca.py +101 -0
  41. pyproject.toml +151 -0
  42. requirements.txt +76 -0
  43. schemas/__init__.py +43 -0
  44. schemas/mcpSchemas.py +51 -0
  45. schemas/policy.py +458 -0
  46. templates/__init__.py +38 -0
  47. templates/base_specialized_agent.py +195 -0
  48. templates/drift_detection.py +325 -0
  49. templates/examples/causal_agent_template.py +309 -0
  50. templates/examples/drag_drop_example.py +213 -0
  51. templates/examples/logistics_agent_template.py +207 -0
  52. templates/examples/trading_agent_template.py +206 -0
  53. templates/feature_mixins.py +253 -0
  54. templates/graph_management.py +442 -0
  55. templates/llm_integration.py +194 -0
  56. templates/module_registry.py +276 -0
  57. templates/mpc_planner.py +280 -0
  58. templates/policy_loop.py +1168 -0
  59. templates/prediction_framework.py +448 -0
  60. templates/statistical_methods.py +778 -0
  61. tests/sanity.yml +31 -0
  62. tests/sanity_check +406 -0
  63. tests/test_core.py +47 -0
  64. tests/test_crca_excel.py +166 -0
  65. tests/test_crca_sd.py +780 -0
  66. tests/test_data_broker.py +424 -0
  67. tests/test_palantir.py +349 -0
  68. tools/__init__.py +38 -0
  69. tools/actuators.py +437 -0
  70. tools/bolt.diy/Dockerfile +103 -0
  71. tools/bolt.diy/app/components/@settings/core/AvatarDropdown.tsx +175 -0
  72. tools/bolt.diy/app/components/@settings/core/ControlPanel.tsx +345 -0
  73. tools/bolt.diy/app/components/@settings/core/constants.tsx +108 -0
  74. tools/bolt.diy/app/components/@settings/core/types.ts +114 -0
  75. tools/bolt.diy/app/components/@settings/index.ts +12 -0
  76. tools/bolt.diy/app/components/@settings/shared/components/TabTile.tsx +151 -0
  77. tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionForm.tsx +193 -0
  78. tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionTestIndicator.tsx +60 -0
  79. tools/bolt.diy/app/components/@settings/shared/service-integration/ErrorState.tsx +102 -0
  80. tools/bolt.diy/app/components/@settings/shared/service-integration/LoadingState.tsx +94 -0
  81. tools/bolt.diy/app/components/@settings/shared/service-integration/ServiceHeader.tsx +72 -0
  82. tools/bolt.diy/app/components/@settings/shared/service-integration/index.ts +6 -0
  83. tools/bolt.diy/app/components/@settings/tabs/data/DataTab.tsx +721 -0
  84. tools/bolt.diy/app/components/@settings/tabs/data/DataVisualization.tsx +384 -0
  85. tools/bolt.diy/app/components/@settings/tabs/event-logs/EventLogsTab.tsx +1013 -0
  86. tools/bolt.diy/app/components/@settings/tabs/features/FeaturesTab.tsx +295 -0
  87. tools/bolt.diy/app/components/@settings/tabs/github/GitHubTab.tsx +281 -0
  88. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubAuthDialog.tsx +173 -0
  89. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubCacheManager.tsx +367 -0
  90. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubConnection.tsx +233 -0
  91. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubErrorBoundary.tsx +105 -0
  92. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubProgressiveLoader.tsx +266 -0
  93. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositoryCard.tsx +121 -0
  94. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositorySelector.tsx +312 -0
  95. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubStats.tsx +291 -0
  96. tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubUserProfile.tsx +46 -0
  97. tools/bolt.diy/app/components/@settings/tabs/github/components/shared/GitHubStateIndicators.tsx +264 -0
  98. tools/bolt.diy/app/components/@settings/tabs/github/components/shared/RepositoryCard.tsx +361 -0
  99. tools/bolt.diy/app/components/@settings/tabs/github/components/shared/index.ts +11 -0
  100. tools/bolt.diy/app/components/@settings/tabs/gitlab/GitLabTab.tsx +305 -0
  101. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabAuthDialog.tsx +186 -0
  102. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabConnection.tsx +253 -0
  103. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabRepositorySelector.tsx +358 -0
  104. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryCard.tsx +79 -0
  105. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryList.tsx +142 -0
  106. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/StatsDisplay.tsx +91 -0
  107. tools/bolt.diy/app/components/@settings/tabs/gitlab/components/index.ts +4 -0
  108. tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerList.tsx +99 -0
  109. tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerListItem.tsx +70 -0
  110. tools/bolt.diy/app/components/@settings/tabs/mcp/McpStatusBadge.tsx +37 -0
  111. tools/bolt.diy/app/components/@settings/tabs/mcp/McpTab.tsx +239 -0
  112. tools/bolt.diy/app/components/@settings/tabs/netlify/NetlifyTab.tsx +1393 -0
  113. tools/bolt.diy/app/components/@settings/tabs/netlify/components/NetlifyConnection.tsx +990 -0
  114. tools/bolt.diy/app/components/@settings/tabs/netlify/components/index.ts +1 -0
  115. tools/bolt.diy/app/components/@settings/tabs/notifications/NotificationsTab.tsx +300 -0
  116. tools/bolt.diy/app/components/@settings/tabs/profile/ProfileTab.tsx +181 -0
  117. tools/bolt.diy/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx +308 -0
  118. tools/bolt.diy/app/components/@settings/tabs/providers/local/ErrorBoundary.tsx +68 -0
  119. tools/bolt.diy/app/components/@settings/tabs/providers/local/HealthStatusBadge.tsx +64 -0
  120. tools/bolt.diy/app/components/@settings/tabs/providers/local/LoadingSkeleton.tsx +107 -0
  121. tools/bolt.diy/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx +556 -0
  122. tools/bolt.diy/app/components/@settings/tabs/providers/local/ModelCard.tsx +106 -0
  123. tools/bolt.diy/app/components/@settings/tabs/providers/local/ProviderCard.tsx +120 -0
  124. tools/bolt.diy/app/components/@settings/tabs/providers/local/SetupGuide.tsx +671 -0
  125. tools/bolt.diy/app/components/@settings/tabs/providers/local/StatusDashboard.tsx +91 -0
  126. tools/bolt.diy/app/components/@settings/tabs/providers/local/types.ts +44 -0
  127. tools/bolt.diy/app/components/@settings/tabs/settings/SettingsTab.tsx +215 -0
  128. tools/bolt.diy/app/components/@settings/tabs/supabase/SupabaseTab.tsx +1089 -0
  129. tools/bolt.diy/app/components/@settings/tabs/vercel/VercelTab.tsx +909 -0
  130. tools/bolt.diy/app/components/@settings/tabs/vercel/components/VercelConnection.tsx +368 -0
  131. tools/bolt.diy/app/components/@settings/tabs/vercel/components/index.ts +1 -0
  132. tools/bolt.diy/app/components/@settings/utils/tab-helpers.ts +54 -0
  133. tools/bolt.diy/app/components/chat/APIKeyManager.tsx +169 -0
  134. tools/bolt.diy/app/components/chat/Artifact.tsx +296 -0
  135. tools/bolt.diy/app/components/chat/AssistantMessage.tsx +192 -0
  136. tools/bolt.diy/app/components/chat/BaseChat.module.scss +47 -0
  137. tools/bolt.diy/app/components/chat/BaseChat.tsx +522 -0
  138. tools/bolt.diy/app/components/chat/Chat.client.tsx +670 -0
  139. tools/bolt.diy/app/components/chat/ChatAlert.tsx +108 -0
  140. tools/bolt.diy/app/components/chat/ChatBox.tsx +334 -0
  141. tools/bolt.diy/app/components/chat/CodeBlock.module.scss +10 -0
  142. tools/bolt.diy/app/components/chat/CodeBlock.tsx +85 -0
  143. tools/bolt.diy/app/components/chat/DicussMode.tsx +17 -0
  144. tools/bolt.diy/app/components/chat/ExamplePrompts.tsx +37 -0
  145. tools/bolt.diy/app/components/chat/FilePreview.tsx +38 -0
  146. tools/bolt.diy/app/components/chat/GitCloneButton.tsx +327 -0
  147. tools/bolt.diy/app/components/chat/ImportFolderButton.tsx +141 -0
  148. tools/bolt.diy/app/components/chat/LLMApiAlert.tsx +109 -0
  149. tools/bolt.diy/app/components/chat/MCPTools.tsx +129 -0
  150. tools/bolt.diy/app/components/chat/Markdown.module.scss +171 -0
  151. tools/bolt.diy/app/components/chat/Markdown.spec.ts +48 -0
  152. tools/bolt.diy/app/components/chat/Markdown.tsx +252 -0
  153. tools/bolt.diy/app/components/chat/Messages.client.tsx +102 -0
  154. tools/bolt.diy/app/components/chat/ModelSelector.tsx +797 -0
  155. tools/bolt.diy/app/components/chat/NetlifyDeploymentLink.client.tsx +51 -0
  156. tools/bolt.diy/app/components/chat/ProgressCompilation.tsx +110 -0
  157. tools/bolt.diy/app/components/chat/ScreenshotStateManager.tsx +33 -0
  158. tools/bolt.diy/app/components/chat/SendButton.client.tsx +39 -0
  159. tools/bolt.diy/app/components/chat/SpeechRecognition.tsx +28 -0
  160. tools/bolt.diy/app/components/chat/StarterTemplates.tsx +38 -0
  161. tools/bolt.diy/app/components/chat/SupabaseAlert.tsx +199 -0
  162. tools/bolt.diy/app/components/chat/SupabaseConnection.tsx +339 -0
  163. tools/bolt.diy/app/components/chat/ThoughtBox.tsx +43 -0
  164. tools/bolt.diy/app/components/chat/ToolInvocations.tsx +409 -0
  165. tools/bolt.diy/app/components/chat/UserMessage.tsx +101 -0
  166. tools/bolt.diy/app/components/chat/VercelDeploymentLink.client.tsx +158 -0
  167. tools/bolt.diy/app/components/chat/chatExportAndImport/ExportChatButton.tsx +49 -0
  168. tools/bolt.diy/app/components/chat/chatExportAndImport/ImportButtons.tsx +96 -0
  169. tools/bolt.diy/app/components/deploy/DeployAlert.tsx +197 -0
  170. tools/bolt.diy/app/components/deploy/DeployButton.tsx +277 -0
  171. tools/bolt.diy/app/components/deploy/GitHubDeploy.client.tsx +171 -0
  172. tools/bolt.diy/app/components/deploy/GitHubDeploymentDialog.tsx +1041 -0
  173. tools/bolt.diy/app/components/deploy/GitLabDeploy.client.tsx +171 -0
  174. tools/bolt.diy/app/components/deploy/GitLabDeploymentDialog.tsx +764 -0
  175. tools/bolt.diy/app/components/deploy/NetlifyDeploy.client.tsx +246 -0
  176. tools/bolt.diy/app/components/deploy/VercelDeploy.client.tsx +235 -0
  177. tools/bolt.diy/app/components/editor/codemirror/BinaryContent.tsx +7 -0
  178. tools/bolt.diy/app/components/editor/codemirror/CodeMirrorEditor.tsx +555 -0
  179. tools/bolt.diy/app/components/editor/codemirror/EnvMasking.ts +80 -0
  180. tools/bolt.diy/app/components/editor/codemirror/cm-theme.ts +192 -0
  181. tools/bolt.diy/app/components/editor/codemirror/indent.ts +68 -0
  182. tools/bolt.diy/app/components/editor/codemirror/languages.ts +112 -0
  183. tools/bolt.diy/app/components/git/GitUrlImport.client.tsx +147 -0
  184. tools/bolt.diy/app/components/header/Header.tsx +42 -0
  185. tools/bolt.diy/app/components/header/HeaderActionButtons.client.tsx +54 -0
  186. tools/bolt.diy/app/components/mandate/MandateSubmission.tsx +167 -0
  187. tools/bolt.diy/app/components/observability/DeploymentStatus.tsx +168 -0
  188. tools/bolt.diy/app/components/observability/EventTimeline.tsx +119 -0
  189. tools/bolt.diy/app/components/observability/FileDiffViewer.tsx +121 -0
  190. tools/bolt.diy/app/components/observability/GovernanceStatus.tsx +197 -0
  191. tools/bolt.diy/app/components/observability/GovernorMetrics.tsx +246 -0
  192. tools/bolt.diy/app/components/observability/LogStream.tsx +244 -0
  193. tools/bolt.diy/app/components/observability/MandateDetails.tsx +201 -0
  194. tools/bolt.diy/app/components/observability/ObservabilityDashboard.tsx +200 -0
  195. tools/bolt.diy/app/components/sidebar/HistoryItem.tsx +187 -0
  196. tools/bolt.diy/app/components/sidebar/Menu.client.tsx +536 -0
  197. tools/bolt.diy/app/components/sidebar/date-binning.ts +59 -0
  198. tools/bolt.diy/app/components/txt +1 -0
  199. tools/bolt.diy/app/components/ui/BackgroundRays/index.tsx +18 -0
  200. tools/bolt.diy/app/components/ui/BackgroundRays/styles.module.scss +246 -0
  201. tools/bolt.diy/app/components/ui/Badge.tsx +53 -0
  202. tools/bolt.diy/app/components/ui/BranchSelector.tsx +270 -0
  203. tools/bolt.diy/app/components/ui/Breadcrumbs.tsx +101 -0
  204. tools/bolt.diy/app/components/ui/Button.tsx +46 -0
  205. tools/bolt.diy/app/components/ui/Card.tsx +55 -0
  206. tools/bolt.diy/app/components/ui/Checkbox.tsx +32 -0
  207. tools/bolt.diy/app/components/ui/CloseButton.tsx +49 -0
  208. tools/bolt.diy/app/components/ui/CodeBlock.tsx +103 -0
  209. tools/bolt.diy/app/components/ui/Collapsible.tsx +9 -0
  210. tools/bolt.diy/app/components/ui/ColorSchemeDialog.tsx +378 -0
  211. tools/bolt.diy/app/components/ui/Dialog.tsx +449 -0
  212. tools/bolt.diy/app/components/ui/Dropdown.tsx +63 -0
  213. tools/bolt.diy/app/components/ui/EmptyState.tsx +154 -0
  214. tools/bolt.diy/app/components/ui/FileIcon.tsx +346 -0
  215. tools/bolt.diy/app/components/ui/FilterChip.tsx +92 -0
  216. tools/bolt.diy/app/components/ui/GlowingEffect.tsx +192 -0
  217. tools/bolt.diy/app/components/ui/GradientCard.tsx +100 -0
  218. tools/bolt.diy/app/components/ui/IconButton.tsx +84 -0
  219. tools/bolt.diy/app/components/ui/Input.tsx +22 -0
  220. tools/bolt.diy/app/components/ui/Label.tsx +20 -0
  221. tools/bolt.diy/app/components/ui/LoadingDots.tsx +27 -0
  222. tools/bolt.diy/app/components/ui/LoadingOverlay.tsx +32 -0
  223. tools/bolt.diy/app/components/ui/PanelHeader.tsx +20 -0
  224. tools/bolt.diy/app/components/ui/PanelHeaderButton.tsx +36 -0
  225. tools/bolt.diy/app/components/ui/Popover.tsx +29 -0
  226. tools/bolt.diy/app/components/ui/Progress.tsx +22 -0
  227. tools/bolt.diy/app/components/ui/RepositoryStats.tsx +87 -0
  228. tools/bolt.diy/app/components/ui/ScrollArea.tsx +41 -0
  229. tools/bolt.diy/app/components/ui/SearchInput.tsx +80 -0
  230. tools/bolt.diy/app/components/ui/SearchResultItem.tsx +134 -0
  231. tools/bolt.diy/app/components/ui/Separator.tsx +22 -0
  232. tools/bolt.diy/app/components/ui/SettingsButton.tsx +35 -0
  233. tools/bolt.diy/app/components/ui/Slider.tsx +73 -0
  234. tools/bolt.diy/app/components/ui/StatusIndicator.tsx +90 -0
  235. tools/bolt.diy/app/components/ui/Switch.tsx +37 -0
  236. tools/bolt.diy/app/components/ui/Tabs.tsx +52 -0
  237. tools/bolt.diy/app/components/ui/TabsWithSlider.tsx +112 -0
  238. tools/bolt.diy/app/components/ui/ThemeSwitch.tsx +29 -0
  239. tools/bolt.diy/app/components/ui/Tooltip.tsx +122 -0
  240. tools/bolt.diy/app/components/ui/index.ts +38 -0
  241. tools/bolt.diy/app/components/ui/use-toast.ts +66 -0
  242. tools/bolt.diy/app/components/workbench/DiffView.tsx +796 -0
  243. tools/bolt.diy/app/components/workbench/EditorPanel.tsx +174 -0
  244. tools/bolt.diy/app/components/workbench/ExpoQrModal.tsx +55 -0
  245. tools/bolt.diy/app/components/workbench/FileBreadcrumb.tsx +150 -0
  246. tools/bolt.diy/app/components/workbench/FileTree.tsx +565 -0
  247. tools/bolt.diy/app/components/workbench/Inspector.tsx +126 -0
  248. tools/bolt.diy/app/components/workbench/InspectorPanel.tsx +146 -0
  249. tools/bolt.diy/app/components/workbench/LockManager.tsx +262 -0
  250. tools/bolt.diy/app/components/workbench/PortDropdown.tsx +91 -0
  251. tools/bolt.diy/app/components/workbench/Preview.tsx +1049 -0
  252. tools/bolt.diy/app/components/workbench/ScreenshotSelector.tsx +293 -0
  253. tools/bolt.diy/app/components/workbench/Search.tsx +257 -0
  254. tools/bolt.diy/app/components/workbench/Workbench.client.tsx +506 -0
  255. tools/bolt.diy/app/components/workbench/terminal/Terminal.tsx +131 -0
  256. tools/bolt.diy/app/components/workbench/terminal/TerminalManager.tsx +68 -0
  257. tools/bolt.diy/app/components/workbench/terminal/TerminalTabs.tsx +277 -0
  258. tools/bolt.diy/app/components/workbench/terminal/theme.ts +36 -0
  259. tools/bolt.diy/app/components/workflow/WorkflowPhase.tsx +109 -0
  260. tools/bolt.diy/app/components/workflow/WorkflowStatus.tsx +60 -0
  261. tools/bolt.diy/app/components/workflow/WorkflowTimeline.tsx +150 -0
  262. tools/bolt.diy/app/entry.client.tsx +7 -0
  263. tools/bolt.diy/app/entry.server.tsx +80 -0
  264. tools/bolt.diy/app/root.tsx +156 -0
  265. tools/bolt.diy/app/routes/_index.tsx +175 -0
  266. tools/bolt.diy/app/routes/api.bug-report.ts +254 -0
  267. tools/bolt.diy/app/routes/api.chat.ts +463 -0
  268. tools/bolt.diy/app/routes/api.check-env-key.ts +41 -0
  269. tools/bolt.diy/app/routes/api.configured-providers.ts +110 -0
  270. tools/bolt.diy/app/routes/api.corporate-swarm-status.ts +55 -0
  271. tools/bolt.diy/app/routes/api.enhancer.ts +137 -0
  272. tools/bolt.diy/app/routes/api.export-api-keys.ts +44 -0
  273. tools/bolt.diy/app/routes/api.git-info.ts +69 -0
  274. tools/bolt.diy/app/routes/api.git-proxy.$.ts +178 -0
  275. tools/bolt.diy/app/routes/api.github-branches.ts +166 -0
  276. tools/bolt.diy/app/routes/api.github-deploy.ts +67 -0
  277. tools/bolt.diy/app/routes/api.github-stats.ts +198 -0
  278. tools/bolt.diy/app/routes/api.github-template.ts +242 -0
  279. tools/bolt.diy/app/routes/api.github-user.ts +287 -0
  280. tools/bolt.diy/app/routes/api.gitlab-branches.ts +143 -0
  281. tools/bolt.diy/app/routes/api.gitlab-deploy.ts +67 -0
  282. tools/bolt.diy/app/routes/api.gitlab-projects.ts +105 -0
  283. tools/bolt.diy/app/routes/api.health.ts +8 -0
  284. tools/bolt.diy/app/routes/api.llmcall.ts +298 -0
  285. tools/bolt.diy/app/routes/api.mandate.ts +351 -0
  286. tools/bolt.diy/app/routes/api.mcp-check.ts +16 -0
  287. tools/bolt.diy/app/routes/api.mcp-update-config.ts +23 -0
  288. tools/bolt.diy/app/routes/api.models.$provider.ts +2 -0
  289. tools/bolt.diy/app/routes/api.models.ts +90 -0
  290. tools/bolt.diy/app/routes/api.netlify-deploy.ts +240 -0
  291. tools/bolt.diy/app/routes/api.netlify-user.ts +142 -0
  292. tools/bolt.diy/app/routes/api.supabase-user.ts +199 -0
  293. tools/bolt.diy/app/routes/api.supabase.query.ts +92 -0
  294. tools/bolt.diy/app/routes/api.supabase.ts +56 -0
  295. tools/bolt.diy/app/routes/api.supabase.variables.ts +32 -0
  296. tools/bolt.diy/app/routes/api.system.diagnostics.ts +142 -0
  297. tools/bolt.diy/app/routes/api.system.disk-info.ts +311 -0
  298. tools/bolt.diy/app/routes/api.system.git-info.ts +332 -0
  299. tools/bolt.diy/app/routes/api.update.ts +21 -0
  300. tools/bolt.diy/app/routes/api.vercel-deploy.ts +497 -0
  301. tools/bolt.diy/app/routes/api.vercel-user.ts +161 -0
  302. tools/bolt.diy/app/routes/api.workflow-status.$proposalId.ts +309 -0
  303. tools/bolt.diy/app/routes/chat.$id.tsx +8 -0
  304. tools/bolt.diy/app/routes/execute.$mandateId.tsx +432 -0
  305. tools/bolt.diy/app/routes/git.tsx +25 -0
  306. tools/bolt.diy/app/routes/observability.$mandateId.tsx +50 -0
  307. tools/bolt.diy/app/routes/webcontainer.connect.$id.tsx +32 -0
  308. tools/bolt.diy/app/routes/webcontainer.preview.$id.tsx +97 -0
  309. tools/bolt.diy/app/routes/workflow.$proposalId.tsx +170 -0
  310. tools/bolt.diy/app/styles/animations.scss +49 -0
  311. tools/bolt.diy/app/styles/components/code.scss +9 -0
  312. tools/bolt.diy/app/styles/components/editor.scss +135 -0
  313. tools/bolt.diy/app/styles/components/resize-handle.scss +30 -0
  314. tools/bolt.diy/app/styles/components/terminal.scss +3 -0
  315. tools/bolt.diy/app/styles/components/toast.scss +23 -0
  316. tools/bolt.diy/app/styles/diff-view.css +72 -0
  317. tools/bolt.diy/app/styles/index.scss +73 -0
  318. tools/bolt.diy/app/styles/variables.scss +255 -0
  319. tools/bolt.diy/app/styles/z-index.scss +37 -0
  320. tools/bolt.diy/app/types/GitHub.ts +182 -0
  321. tools/bolt.diy/app/types/GitLab.ts +103 -0
  322. tools/bolt.diy/app/types/actions.ts +85 -0
  323. tools/bolt.diy/app/types/artifact.ts +5 -0
  324. tools/bolt.diy/app/types/context.ts +26 -0
  325. tools/bolt.diy/app/types/design-scheme.ts +93 -0
  326. tools/bolt.diy/app/types/global.d.ts +13 -0
  327. tools/bolt.diy/app/types/mandate.ts +333 -0
  328. tools/bolt.diy/app/types/model.ts +25 -0
  329. tools/bolt.diy/app/types/netlify.ts +94 -0
  330. tools/bolt.diy/app/types/supabase.ts +54 -0
  331. tools/bolt.diy/app/types/template.ts +8 -0
  332. tools/bolt.diy/app/types/terminal.ts +9 -0
  333. tools/bolt.diy/app/types/theme.ts +1 -0
  334. tools/bolt.diy/app/types/vercel.ts +67 -0
  335. tools/bolt.diy/app/utils/buffer.ts +29 -0
  336. tools/bolt.diy/app/utils/classNames.ts +65 -0
  337. tools/bolt.diy/app/utils/constants.ts +147 -0
  338. tools/bolt.diy/app/utils/debounce.ts +13 -0
  339. tools/bolt.diy/app/utils/debugLogger.ts +1284 -0
  340. tools/bolt.diy/app/utils/diff.spec.ts +11 -0
  341. tools/bolt.diy/app/utils/diff.ts +117 -0
  342. tools/bolt.diy/app/utils/easings.ts +3 -0
  343. tools/bolt.diy/app/utils/fileLocks.ts +96 -0
  344. tools/bolt.diy/app/utils/fileUtils.ts +121 -0
  345. tools/bolt.diy/app/utils/folderImport.ts +73 -0
  346. tools/bolt.diy/app/utils/formatSize.ts +12 -0
  347. tools/bolt.diy/app/utils/getLanguageFromExtension.ts +24 -0
  348. tools/bolt.diy/app/utils/githubStats.ts +9 -0
  349. tools/bolt.diy/app/utils/gitlabStats.ts +54 -0
  350. tools/bolt.diy/app/utils/logger.ts +162 -0
  351. tools/bolt.diy/app/utils/markdown.ts +155 -0
  352. tools/bolt.diy/app/utils/mobile.ts +4 -0
  353. tools/bolt.diy/app/utils/os.ts +4 -0
  354. tools/bolt.diy/app/utils/path.ts +19 -0
  355. tools/bolt.diy/app/utils/projectCommands.ts +197 -0
  356. tools/bolt.diy/app/utils/promises.ts +19 -0
  357. tools/bolt.diy/app/utils/react.ts +6 -0
  358. tools/bolt.diy/app/utils/sampler.ts +49 -0
  359. tools/bolt.diy/app/utils/selectStarterTemplate.ts +255 -0
  360. tools/bolt.diy/app/utils/shell.ts +384 -0
  361. tools/bolt.diy/app/utils/stacktrace.ts +27 -0
  362. tools/bolt.diy/app/utils/stripIndent.ts +23 -0
  363. tools/bolt.diy/app/utils/terminal.ts +11 -0
  364. tools/bolt.diy/app/utils/unreachable.ts +3 -0
  365. tools/bolt.diy/app/vite-env.d.ts +2 -0
  366. tools/bolt.diy/assets/entitlements.mac.plist +25 -0
  367. tools/bolt.diy/assets/icons/icon.icns +0 -0
  368. tools/bolt.diy/assets/icons/icon.ico +0 -0
  369. tools/bolt.diy/assets/icons/icon.png +0 -0
  370. tools/bolt.diy/bindings.js +78 -0
  371. tools/bolt.diy/bindings.sh +33 -0
  372. tools/bolt.diy/docker-compose.yaml +145 -0
  373. tools/bolt.diy/electron/main/index.ts +201 -0
  374. tools/bolt.diy/electron/main/tsconfig.json +30 -0
  375. tools/bolt.diy/electron/main/ui/menu.ts +29 -0
  376. tools/bolt.diy/electron/main/ui/window.ts +54 -0
  377. tools/bolt.diy/electron/main/utils/auto-update.ts +110 -0
  378. tools/bolt.diy/electron/main/utils/constants.ts +4 -0
  379. tools/bolt.diy/electron/main/utils/cookie.ts +40 -0
  380. tools/bolt.diy/electron/main/utils/reload.ts +35 -0
  381. tools/bolt.diy/electron/main/utils/serve.ts +71 -0
  382. tools/bolt.diy/electron/main/utils/store.ts +3 -0
  383. tools/bolt.diy/electron/main/utils/vite-server.ts +44 -0
  384. tools/bolt.diy/electron/main/vite.config.ts +44 -0
  385. tools/bolt.diy/electron/preload/index.ts +22 -0
  386. tools/bolt.diy/electron/preload/tsconfig.json +7 -0
  387. tools/bolt.diy/electron/preload/vite.config.ts +31 -0
  388. tools/bolt.diy/electron-builder.yml +64 -0
  389. tools/bolt.diy/electron-update.yml +4 -0
  390. tools/bolt.diy/eslint.config.mjs +57 -0
  391. tools/bolt.diy/functions/[[path]].ts +12 -0
  392. tools/bolt.diy/icons/angular.svg +1 -0
  393. tools/bolt.diy/icons/astro.svg +8 -0
  394. tools/bolt.diy/icons/chat.svg +1 -0
  395. tools/bolt.diy/icons/expo-brand.svg +1 -0
  396. tools/bolt.diy/icons/expo.svg +4 -0
  397. tools/bolt.diy/icons/logo-text.svg +1 -0
  398. tools/bolt.diy/icons/logo.svg +4 -0
  399. tools/bolt.diy/icons/mcp.svg +1 -0
  400. tools/bolt.diy/icons/nativescript.svg +1 -0
  401. tools/bolt.diy/icons/netlify.svg +10 -0
  402. tools/bolt.diy/icons/nextjs.svg +1 -0
  403. tools/bolt.diy/icons/nuxt.svg +1 -0
  404. tools/bolt.diy/icons/qwik.svg +1 -0
  405. tools/bolt.diy/icons/react.svg +1 -0
  406. tools/bolt.diy/icons/remix.svg +24 -0
  407. tools/bolt.diy/icons/remotion.svg +1 -0
  408. tools/bolt.diy/icons/shadcn.svg +21 -0
  409. tools/bolt.diy/icons/slidev.svg +60 -0
  410. tools/bolt.diy/icons/solidjs.svg +1 -0
  411. tools/bolt.diy/icons/stars.svg +1 -0
  412. tools/bolt.diy/icons/svelte.svg +1 -0
  413. tools/bolt.diy/icons/typescript.svg +1 -0
  414. tools/bolt.diy/icons/vite.svg +1 -0
  415. tools/bolt.diy/icons/vue.svg +1 -0
  416. tools/bolt.diy/load-context.ts +9 -0
  417. tools/bolt.diy/notarize.cjs +31 -0
  418. tools/bolt.diy/package.json +218 -0
  419. tools/bolt.diy/playwright.config.preview.ts +35 -0
  420. tools/bolt.diy/pre-start.cjs +26 -0
  421. tools/bolt.diy/public/apple-touch-icon-precomposed.png +0 -0
  422. tools/bolt.diy/public/apple-touch-icon.png +0 -0
  423. tools/bolt.diy/public/favicon.ico +0 -0
  424. tools/bolt.diy/public/favicon.svg +4 -0
  425. tools/bolt.diy/public/icons/AmazonBedrock.svg +1 -0
  426. tools/bolt.diy/public/icons/Anthropic.svg +4 -0
  427. tools/bolt.diy/public/icons/Cohere.svg +4 -0
  428. tools/bolt.diy/public/icons/Deepseek.svg +5 -0
  429. tools/bolt.diy/public/icons/Default.svg +4 -0
  430. tools/bolt.diy/public/icons/Google.svg +4 -0
  431. tools/bolt.diy/public/icons/Groq.svg +4 -0
  432. tools/bolt.diy/public/icons/HuggingFace.svg +4 -0
  433. tools/bolt.diy/public/icons/Hyperbolic.svg +3 -0
  434. tools/bolt.diy/public/icons/LMStudio.svg +5 -0
  435. tools/bolt.diy/public/icons/Mistral.svg +4 -0
  436. tools/bolt.diy/public/icons/Ollama.svg +4 -0
  437. tools/bolt.diy/public/icons/OpenAI.svg +4 -0
  438. tools/bolt.diy/public/icons/OpenAILike.svg +4 -0
  439. tools/bolt.diy/public/icons/OpenRouter.svg +4 -0
  440. tools/bolt.diy/public/icons/Perplexity.svg +4 -0
  441. tools/bolt.diy/public/icons/Together.svg +4 -0
  442. tools/bolt.diy/public/icons/xAI.svg +5 -0
  443. tools/bolt.diy/public/inspector-script.js +292 -0
  444. tools/bolt.diy/public/logo-dark-styled.png +0 -0
  445. tools/bolt.diy/public/logo-dark.png +0 -0
  446. tools/bolt.diy/public/logo-light-styled.png +0 -0
  447. tools/bolt.diy/public/logo-light.png +0 -0
  448. tools/bolt.diy/public/logo.svg +15 -0
  449. tools/bolt.diy/public/social_preview_index.jpg +0 -0
  450. tools/bolt.diy/scripts/clean.js +45 -0
  451. tools/bolt.diy/scripts/electron-dev.mjs +181 -0
  452. tools/bolt.diy/scripts/setup-env.sh +41 -0
  453. tools/bolt.diy/scripts/update-imports.sh +7 -0
  454. tools/bolt.diy/scripts/update.sh +52 -0
  455. tools/bolt.diy/services/execution-governor/Dockerfile +41 -0
  456. tools/bolt.diy/services/execution-governor/config.ts +42 -0
  457. tools/bolt.diy/services/execution-governor/index.ts +683 -0
  458. tools/bolt.diy/services/execution-governor/metrics.ts +141 -0
  459. tools/bolt.diy/services/execution-governor/package.json +31 -0
  460. tools/bolt.diy/services/execution-governor/priority-queue.ts +139 -0
  461. tools/bolt.diy/services/execution-governor/tsconfig.json +21 -0
  462. tools/bolt.diy/services/execution-governor/types.ts +145 -0
  463. tools/bolt.diy/services/headless-executor/Dockerfile +43 -0
  464. tools/bolt.diy/services/headless-executor/executor.ts +210 -0
  465. tools/bolt.diy/services/headless-executor/index.ts +323 -0
  466. tools/bolt.diy/services/headless-executor/package.json +27 -0
  467. tools/bolt.diy/services/headless-executor/tsconfig.json +21 -0
  468. tools/bolt.diy/services/headless-executor/types.ts +38 -0
  469. tools/bolt.diy/test-workflows.sh +240 -0
  470. tools/bolt.diy/tests/integration/corporate-swarm.test.ts +208 -0
  471. tools/bolt.diy/tests/mandates/budget-limited.json +34 -0
  472. tools/bolt.diy/tests/mandates/complex.json +53 -0
  473. tools/bolt.diy/tests/mandates/constraint-enforced.json +36 -0
  474. tools/bolt.diy/tests/mandates/simple.json +35 -0
  475. tools/bolt.diy/tsconfig.json +37 -0
  476. tools/bolt.diy/types/istextorbinary.d.ts +15 -0
  477. tools/bolt.diy/uno.config.ts +279 -0
  478. tools/bolt.diy/vite-electron.config.ts +76 -0
  479. tools/bolt.diy/vite.config.ts +112 -0
  480. tools/bolt.diy/worker-configuration.d.ts +22 -0
  481. tools/bolt.diy/wrangler.toml +6 -0
  482. tools/code_generator.py +461 -0
  483. tools/file_operations.py +465 -0
  484. tools/mandate_generator.py +337 -0
  485. tools/mcpClientUtils.py +1216 -0
  486. tools/sensors.py +285 -0
  487. utils/Agent_types.py +15 -0
  488. utils/AnyToStr.py +0 -0
  489. utils/HHCS.py +277 -0
  490. utils/__init__.py +30 -0
  491. utils/agent.py +3627 -0
  492. utils/aop.py +2948 -0
  493. utils/canonical.py +143 -0
  494. utils/conversation.py +1195 -0
  495. utils/doctrine_versioning +230 -0
  496. utils/formatter.py +474 -0
  497. utils/ledger.py +311 -0
  498. utils/out_types.py +16 -0
  499. utils/rollback.py +339 -0
  500. utils/router.py +929 -0
  501. utils/tui.py +1908 -0
@@ -0,0 +1,990 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { toast } from 'react-toastify';
3
+ import { classNames } from '~/utils/classNames';
4
+ import { useStore } from '@nanostores/react';
5
+ import { netlifyConnection, updateNetlifyConnection, initializeNetlifyConnection } from '~/lib/stores/netlify';
6
+ import type { NetlifySite, NetlifyDeploy, NetlifyBuild, NetlifyUser } from '~/types/netlify';
7
+ import {
8
+ CloudIcon,
9
+ BuildingLibraryIcon,
10
+ ClockIcon,
11
+ CodeBracketIcon,
12
+ CheckCircleIcon,
13
+ XCircleIcon,
14
+ TrashIcon,
15
+ ArrowPathIcon,
16
+ LockClosedIcon,
17
+ LockOpenIcon,
18
+ RocketLaunchIcon,
19
+ ChartBarIcon,
20
+ CogIcon,
21
+ } from '@heroicons/react/24/outline';
22
+ import { Button } from '~/components/ui/Button';
23
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '~/components/ui/Collapsible';
24
+ import { formatDistanceToNow } from 'date-fns';
25
+ import { Badge } from '~/components/ui/Badge';
26
+
27
+ // Add the Netlify logo SVG component at the top of the file
28
+ const NetlifyLogo = () => (
29
+ <svg viewBox="0 0 40 40" className="w-5 h-5">
30
+ <path
31
+ fill="currentColor"
32
+ d="M28.589 14.135l-.014-.006c-.008-.003-.016-.006-.023-.013a.11.11 0 0 1-.028-.093l.773-4.726 3.625 3.626-3.77 1.604a.083.083 0 0 1-.033.006h-.015c-.005-.003-.01-.007-.02-.017a1.716 1.716 0 0 0-.495-.381zm5.258-.288l3.876 3.876c.805.806 1.208 1.208 1.674 1.355a2 2 0 0 1 1.206 0c.466-.148.869-.55 1.674-1.356L8.73 28.73l2.349-3.643c.011-.018.022-.034.04-.047.025-.018.061-.01.091 0a2.434 2.434 0 0 0 1.638-.083c.027-.01.054-.017.075.002a.19.19 0 0 1 .028.032L21.95 38.05zM7.863 27.863L5.8 25.8l4.074-1.738a.084.084 0 0 1 .033-.007c.034 0 .054.034.072.065a2.91 2.91 0 0 0 .13.184l.013.016c.012.017.004.034-.008.05l-2.25 3.493zm-2.976-2.976l-2.61-2.61c-.444-.444-.766-.766-.99-1.043l7.936 1.646a.84.84 0 0 0 .03.005c.049.008.103.017.103.063 0 .05-.059.073-.109.092l-.023.01-4.337 1.837zM.831 19.892a2 2 0 0 1 .09-.495c.148-.466.55-.868 1.356-1.674l3.34-3.34a2175.525 2175.525 0 0 0 4.626 6.687c.027.036.057.076.026.106-.146.161-.292.337-.395.528a.16.16 0 0 1-.05.062c-.013.008-.027.005-.042.002H9.78L.831 19.892zm5.68-6.403l4.491-4.491c.422.185 1.958.834 3.332 1.414 1.04.44 1.988.84 2.286.97.03.012.057.024.07.054.008.018.004.041 0 .06a2.003 2.003 0 0 0 .523 1.828c.03.03 0 .073-.026.11l-.014.021-4.56 7.063c-.012.02-.023.037-.043.05-.024.015-.058.008-.086.001a2.274 2.274 0 0 0-.543-.074c-.164 0-.342.03-.522.063h-.001c-.02.003-.038.007-.054-.005a.21.21 0 0 1-.045-.051l-4.808-7.013zm5.398-5.398l5.814-5.814c.805-.805 1.208-1.208 1.674-1.355a2 2 0 0 1 1.206 0c.466.147.869.55 1.674 1.355l1.26 1.26-4.135 6.404a.155.155 0 0 1-.041.048c-.025.017-.06.01-.09 0a2.097 2.097 0 0 0-1.92.37c-.027.028-.067.012-.101-.003-.54-.235-4.74-2.01-5.341-2.265zm12.506-3.676l3.818 3.818-.92 5.698v.015a.135.135 0 0 1-.008.038c-.01.02-.03.024-.05.03a1.83 1.83 0 0 0-.548.273.154.154 0 0 0-.02.017c-.011.012-.022.023-.04.025a.114.114 0 0 1-.043-.007l-5.818-2.472-.011-.005c-.037-.015-.081-.033-.081-.071a2.198 2.198 0 0 0-.31-.915c-.028-.046-.059-.094-.035-.141l4.066-6.303zm-3.932 8.606l5.454 2.31c.03.014.063.027.076.058a.106.106 0 0 1 0 .057c-.016.08-.03.171-.03.263v.153c0 .038-.039.054-.075.069l-.011.004c-.864.369-12.13 5.173-12.147 5.173-.017 0-.035 0-.052-.017-.03-.03 0-.072.027-.11a.76.76 0 0 0 .014-.02l4.482-6.94.008-.012c.026-.042.056-.089.104-.089l.045.007c.102.014.192.027.283.027.68 0 1.31-.331 1.69-.897a.16.16 0 0 1 .034-.04c.027-.02.067-.01.098.004zm-6.246 9.185l12.28-5.237s.018 0 .035.017c.067.067.124.112.179.154l.027.017c.025.014.05.03.052.056 0 .01 0 .016-.002.025L25.756 23.7l-.004.026c-.007.05-.014.107-.061.107a1.729 1.729 0 0 0-1.373.847l-.005.008c-.014.023-.027.045-.05.057-.021.01-.048.006-.07.001l-9.793-2.02c-.01-.002-.152-.519-.163-.52z"
33
+ />
34
+ </svg>
35
+ );
36
+
37
+ // Add new interface for site actions
38
+ interface SiteAction {
39
+ name: string;
40
+ icon: React.ComponentType<any>;
41
+ action: (siteId: string) => Promise<void>;
42
+ requiresConfirmation?: boolean;
43
+ variant?: 'default' | 'destructive' | 'outline';
44
+ }
45
+
46
+ export default function NetlifyConnection() {
47
+ console.log('NetlifyConnection component mounted');
48
+
49
+ const connection = useStore(netlifyConnection);
50
+ const [tokenInput, setTokenInput] = useState('');
51
+ const [fetchingStats, setFetchingStats] = useState(false);
52
+ const [sites, setSites] = useState<NetlifySite[]>([]);
53
+ const [deploys, setDeploys] = useState<NetlifyDeploy[]>([]);
54
+ const [builds, setBuilds] = useState<NetlifyBuild[]>([]);
55
+
56
+ console.log('NetlifyConnection initial state:', {
57
+ connection: {
58
+ user: connection.user,
59
+ token: connection.token ? '[TOKEN_EXISTS]' : '[NO_TOKEN]',
60
+ },
61
+ envToken: import.meta.env?.VITE_NETLIFY_ACCESS_TOKEN ? '[ENV_TOKEN_EXISTS]' : '[NO_ENV_TOKEN]',
62
+ });
63
+
64
+ const [deploymentCount, setDeploymentCount] = useState(0);
65
+ const [lastUpdated, setLastUpdated] = useState('');
66
+ const [isStatsOpen, setIsStatsOpen] = useState(false);
67
+ const [activeSiteIndex, setActiveSiteIndex] = useState(0);
68
+ const [isActionLoading, setIsActionLoading] = useState(false);
69
+ const [isConnecting, setIsConnecting] = useState(false);
70
+
71
+ // Add site actions
72
+ const siteActions: SiteAction[] = [
73
+ {
74
+ name: 'Clear Cache',
75
+ icon: ArrowPathIcon,
76
+ action: async (siteId: string) => {
77
+ try {
78
+ // Try to get site details first to check for build hooks
79
+ const siteResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}`, {
80
+ headers: {
81
+ Authorization: `Bearer ${connection.token}`,
82
+ },
83
+ });
84
+
85
+ if (!siteResponse.ok) {
86
+ const errorText = await siteResponse.text();
87
+
88
+ if (siteResponse.status === 404) {
89
+ toast.error('Site not found. This may be a free account limitation.');
90
+ return;
91
+ }
92
+
93
+ throw new Error(`Failed to get site details: ${errorText}`);
94
+ }
95
+
96
+ const siteData = (await siteResponse.json()) as any;
97
+
98
+ // Check if this looks like a free account (limited features)
99
+ const isFreeAccount = !siteData.plan || siteData.plan === 'free' || siteData.plan === 'starter';
100
+
101
+ // If site has build hooks, try triggering a build instead
102
+ if (siteData.build_settings && siteData.build_settings.repo_url) {
103
+ // Try to trigger a build by making a POST to the site's build endpoint
104
+ const buildResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/builds`, {
105
+ method: 'POST',
106
+ headers: {
107
+ Authorization: `Bearer ${connection.token}`,
108
+ 'Content-Type': 'application/json',
109
+ },
110
+ body: JSON.stringify({
111
+ clear_cache: true,
112
+ }),
113
+ });
114
+
115
+ if (buildResponse.ok) {
116
+ toast.success('Build triggered with cache clear');
117
+ return;
118
+ } else if (buildResponse.status === 422) {
119
+ // Often indicates free account limitation
120
+ toast.warning('Build trigger failed. This feature may not be available on free accounts.');
121
+ return;
122
+ }
123
+ }
124
+
125
+ // Fallback: Try the standard cache purge endpoint
126
+ const cacheResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/purge_cache`, {
127
+ method: 'POST',
128
+ headers: {
129
+ Authorization: `Bearer ${connection.token}`,
130
+ },
131
+ });
132
+
133
+ if (!cacheResponse.ok) {
134
+ if (cacheResponse.status === 404) {
135
+ if (isFreeAccount) {
136
+ toast.warning('Cache purge not available on free accounts. Try triggering a build instead.');
137
+ } else {
138
+ toast.error('Cache purge endpoint not found. This feature may not be available.');
139
+ }
140
+
141
+ return;
142
+ }
143
+
144
+ const errorText = await cacheResponse.text();
145
+ throw new Error(`Cache purge failed: ${errorText}`);
146
+ }
147
+
148
+ toast.success('Site cache cleared successfully');
149
+ } catch (err: unknown) {
150
+ const error = err instanceof Error ? err.message : 'Unknown error';
151
+ toast.error(`Failed to clear site cache: ${error}`);
152
+ }
153
+ },
154
+ },
155
+ {
156
+ name: 'Manage Environment',
157
+ icon: CogIcon,
158
+ action: async (siteId: string) => {
159
+ try {
160
+ // Get site info first to check account type
161
+ const siteResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}`, {
162
+ headers: {
163
+ Authorization: `Bearer ${connection.token}`,
164
+ },
165
+ });
166
+
167
+ if (!siteResponse.ok) {
168
+ throw new Error('Failed to get site details');
169
+ }
170
+
171
+ const siteData = (await siteResponse.json()) as any;
172
+ const isFreeAccount = !siteData.plan || siteData.plan === 'free' || siteData.plan === 'starter';
173
+
174
+ // Get environment variables
175
+ const envResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/env`, {
176
+ headers: {
177
+ Authorization: `Bearer ${connection.token}`,
178
+ },
179
+ });
180
+
181
+ if (envResponse.ok) {
182
+ const envVars = (await envResponse.json()) as any[];
183
+ toast.success(`Environment variables loaded: ${envVars.length} variables`);
184
+ } else if (envResponse.status === 404) {
185
+ if (isFreeAccount) {
186
+ toast.info('Environment variables management is limited on free accounts');
187
+ } else {
188
+ toast.info('Site has no environment variables configured');
189
+ }
190
+ } else {
191
+ const errorText = await envResponse.text();
192
+ toast.error(`Failed to load environment variables: ${errorText}`);
193
+ }
194
+ } catch (err: unknown) {
195
+ const error = err instanceof Error ? err.message : 'Unknown error';
196
+ toast.error(`Failed to load environment variables: ${error}`);
197
+ }
198
+ },
199
+ },
200
+ {
201
+ name: 'Trigger Build',
202
+ icon: RocketLaunchIcon,
203
+ action: async (siteId: string) => {
204
+ try {
205
+ const buildResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/builds`, {
206
+ method: 'POST',
207
+ headers: {
208
+ Authorization: `Bearer ${connection.token}`,
209
+ 'Content-Type': 'application/json',
210
+ },
211
+ });
212
+
213
+ if (!buildResponse.ok) {
214
+ throw new Error('Failed to trigger build');
215
+ }
216
+
217
+ const buildData = (await buildResponse.json()) as any;
218
+ toast.success(`Build triggered successfully! ID: ${buildData.id}`);
219
+ } catch (err: unknown) {
220
+ const error = err instanceof Error ? err.message : 'Unknown error';
221
+ toast.error(`Failed to trigger build: ${error}`);
222
+ }
223
+ },
224
+ },
225
+ {
226
+ name: 'View Functions',
227
+ icon: CodeBracketIcon,
228
+ action: async (siteId: string) => {
229
+ try {
230
+ // Get site info first to check account type
231
+ const siteResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}`, {
232
+ headers: {
233
+ Authorization: `Bearer ${connection.token}`,
234
+ },
235
+ });
236
+
237
+ if (!siteResponse.ok) {
238
+ throw new Error('Failed to get site details');
239
+ }
240
+
241
+ const siteData = (await siteResponse.json()) as any;
242
+ const isFreeAccount = !siteData.plan || siteData.plan === 'free' || siteData.plan === 'starter';
243
+
244
+ const functionsResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/functions`, {
245
+ headers: {
246
+ Authorization: `Bearer ${connection.token}`,
247
+ },
248
+ });
249
+
250
+ if (functionsResponse.ok) {
251
+ const functions = (await functionsResponse.json()) as any[];
252
+ toast.success(`Site has ${functions.length} serverless functions`);
253
+ } else if (functionsResponse.status === 404) {
254
+ if (isFreeAccount) {
255
+ toast.info('Functions may be limited or unavailable on free accounts');
256
+ } else {
257
+ toast.info('Site has no serverless functions');
258
+ }
259
+ } else {
260
+ const errorText = await functionsResponse.text();
261
+ toast.error(`Failed to load functions: ${errorText}`);
262
+ }
263
+ } catch (err: unknown) {
264
+ const error = err instanceof Error ? err.message : 'Unknown error';
265
+ toast.error(`Failed to load functions: ${error}`);
266
+ }
267
+ },
268
+ },
269
+ {
270
+ name: 'Site Analytics',
271
+ icon: ChartBarIcon,
272
+ action: async (siteId: string) => {
273
+ try {
274
+ // Get site info first to check account type
275
+ const siteResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}`, {
276
+ headers: {
277
+ Authorization: `Bearer ${connection.token}`,
278
+ },
279
+ });
280
+
281
+ if (!siteResponse.ok) {
282
+ throw new Error('Failed to get site details');
283
+ }
284
+
285
+ const siteData = (await siteResponse.json()) as any;
286
+ const isFreeAccount = !siteData.plan || siteData.plan === 'free' || siteData.plan === 'starter';
287
+
288
+ // Get site traffic data (if available)
289
+ const analyticsResponse = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}/traffic`, {
290
+ headers: {
291
+ Authorization: `Bearer ${connection.token}`,
292
+ },
293
+ });
294
+
295
+ if (analyticsResponse.ok) {
296
+ await analyticsResponse.json(); // Analytics data received
297
+ toast.success('Site analytics loaded successfully');
298
+ } else if (analyticsResponse.status === 404) {
299
+ if (isFreeAccount) {
300
+ toast.info('Analytics not available on free accounts. Showing basic site info instead.');
301
+ }
302
+
303
+ // Fallback to basic site info
304
+ toast.info(`Site: ${siteData.name} - Status: ${siteData.state || 'Unknown'}`);
305
+ } else {
306
+ const errorText = await analyticsResponse.text();
307
+
308
+ if (isFreeAccount) {
309
+ toast.info(
310
+ 'Analytics unavailable on free accounts. Site info: ' +
311
+ `${siteData.name} (${siteData.state || 'Unknown'})`,
312
+ );
313
+ } else {
314
+ toast.error(`Failed to load analytics: ${errorText}`);
315
+ }
316
+ }
317
+ } catch (err: unknown) {
318
+ const error = err instanceof Error ? err.message : 'Unknown error';
319
+ toast.error(`Failed to load site analytics: ${error}`);
320
+ }
321
+ },
322
+ },
323
+ {
324
+ name: 'Delete Site',
325
+ icon: TrashIcon,
326
+ action: async (siteId: string) => {
327
+ try {
328
+ const response = await fetch(`https://api.netlify.com/api/v1/sites/${siteId}`, {
329
+ method: 'DELETE',
330
+ headers: {
331
+ Authorization: `Bearer ${connection.token}`,
332
+ },
333
+ });
334
+
335
+ if (!response.ok) {
336
+ throw new Error('Failed to delete site');
337
+ }
338
+
339
+ toast.success('Site deleted successfully');
340
+ fetchNetlifyStats(connection.token);
341
+ } catch (err: unknown) {
342
+ const error = err instanceof Error ? err.message : 'Unknown error';
343
+ toast.error(`Failed to delete site: ${error}`);
344
+ }
345
+ },
346
+ requiresConfirmation: true,
347
+ variant: 'destructive',
348
+ },
349
+ ];
350
+
351
+ // Add deploy management functions
352
+ const handleDeploy = async (siteId: string, deployId: string, action: 'lock' | 'unlock' | 'publish') => {
353
+ try {
354
+ setIsActionLoading(true);
355
+
356
+ const endpoint =
357
+ action === 'publish'
358
+ ? `https://api.netlify.com/api/v1/sites/${siteId}/deploys/${deployId}/restore`
359
+ : `https://api.netlify.com/api/v1/deploys/${deployId}/${action}`;
360
+
361
+ const response = await fetch(endpoint, {
362
+ method: 'POST',
363
+ headers: {
364
+ Authorization: `Bearer ${connection.token}`,
365
+ },
366
+ });
367
+
368
+ if (!response.ok) {
369
+ throw new Error(`Failed to ${action} deploy`);
370
+ }
371
+
372
+ toast.success(`Deploy ${action}ed successfully`);
373
+ fetchNetlifyStats(connection.token);
374
+ } catch (err: unknown) {
375
+ const error = err instanceof Error ? err.message : 'Unknown error';
376
+ toast.error(`Failed to ${action} deploy: ${error}`);
377
+ } finally {
378
+ setIsActionLoading(false);
379
+ }
380
+ };
381
+
382
+ useEffect(() => {
383
+ console.log('Netlify: Running initialization useEffect');
384
+
385
+ // Initialize connection with environment token if available
386
+ initializeNetlifyConnection();
387
+ }, []);
388
+
389
+ useEffect(() => {
390
+ // Check if we have a connection with a token but no stats
391
+ if (connection.user && connection.token && (!connection.stats || !connection.stats.sites)) {
392
+ fetchNetlifyStats(connection.token);
393
+ }
394
+
395
+ // Update local state from connection
396
+ if (connection.stats) {
397
+ setSites(connection.stats.sites || []);
398
+ setDeploys(connection.stats.deploys || []);
399
+ setBuilds(connection.stats.builds || []);
400
+ setDeploymentCount(connection.stats.deploys?.length || 0);
401
+ setLastUpdated(connection.stats.lastDeployTime || '');
402
+ }
403
+ }, [connection]);
404
+
405
+ const handleConnect = async () => {
406
+ if (!tokenInput) {
407
+ toast.error('Please enter a Netlify API token');
408
+ return;
409
+ }
410
+
411
+ setIsConnecting(true);
412
+
413
+ try {
414
+ const response = await fetch('https://api.netlify.com/api/v1/user', {
415
+ headers: {
416
+ Authorization: `Bearer ${tokenInput}`,
417
+ },
418
+ });
419
+
420
+ if (!response.ok) {
421
+ throw new Error(`HTTP error! Status: ${response.status}`);
422
+ }
423
+
424
+ const userData = (await response.json()) as NetlifyUser;
425
+
426
+ // Update the connection store
427
+ updateNetlifyConnection({
428
+ user: userData,
429
+ token: tokenInput,
430
+ });
431
+
432
+ toast.success('Connected to Netlify successfully');
433
+
434
+ // Fetch stats after successful connection
435
+ fetchNetlifyStats(tokenInput);
436
+ } catch (error) {
437
+ console.error('Error connecting to Netlify:', error);
438
+ toast.error(`Failed to connect to Netlify: ${error instanceof Error ? error.message : 'Unknown error'}`);
439
+ } finally {
440
+ setIsConnecting(false);
441
+ setTokenInput('');
442
+ }
443
+ };
444
+
445
+ const handleDisconnect = () => {
446
+ // Clear from localStorage
447
+ localStorage.removeItem('netlify_connection');
448
+
449
+ // Remove cookies
450
+ document.cookie = 'netlifyToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
451
+
452
+ // Update the store
453
+ updateNetlifyConnection({ user: null, token: '' });
454
+ toast.success('Disconnected from Netlify');
455
+ };
456
+
457
+ const fetchNetlifyStats = async (token: string) => {
458
+ setFetchingStats(true);
459
+
460
+ try {
461
+ // Fetch sites
462
+ const sitesResponse = await fetch('https://api.netlify.com/api/v1/sites', {
463
+ headers: {
464
+ Authorization: `Bearer ${token}`,
465
+ },
466
+ });
467
+
468
+ if (!sitesResponse.ok) {
469
+ throw new Error(`Failed to fetch sites: ${sitesResponse.statusText}`);
470
+ }
471
+
472
+ const sitesData = (await sitesResponse.json()) as NetlifySite[];
473
+ setSites(sitesData);
474
+
475
+ // Fetch recent deploys for the first site (if any)
476
+ let deploysData: NetlifyDeploy[] = [];
477
+ let buildsData: NetlifyBuild[] = [];
478
+ let lastDeployTime = '';
479
+
480
+ if (sitesData && sitesData.length > 0) {
481
+ const firstSite = sitesData[0];
482
+
483
+ // Fetch deploys
484
+ const deploysResponse = await fetch(`https://api.netlify.com/api/v1/sites/${firstSite.id}/deploys`, {
485
+ headers: {
486
+ Authorization: `Bearer ${token}`,
487
+ },
488
+ });
489
+
490
+ if (deploysResponse.ok) {
491
+ deploysData = (await deploysResponse.json()) as NetlifyDeploy[];
492
+ setDeploys(deploysData);
493
+ setDeploymentCount(deploysData.length);
494
+
495
+ // Get the latest deploy time
496
+ if (deploysData.length > 0) {
497
+ lastDeployTime = deploysData[0].created_at;
498
+ setLastUpdated(lastDeployTime);
499
+
500
+ // Fetch builds for the site
501
+ const buildsResponse = await fetch(`https://api.netlify.com/api/v1/sites/${firstSite.id}/builds`, {
502
+ headers: {
503
+ Authorization: `Bearer ${token}`,
504
+ },
505
+ });
506
+
507
+ if (buildsResponse.ok) {
508
+ buildsData = (await buildsResponse.json()) as NetlifyBuild[];
509
+ setBuilds(buildsData);
510
+ }
511
+ }
512
+ }
513
+ }
514
+
515
+ // Update the stats in the store
516
+ updateNetlifyConnection({
517
+ stats: {
518
+ sites: sitesData,
519
+ deploys: deploysData,
520
+ builds: buildsData,
521
+ lastDeployTime,
522
+ totalSites: sitesData.length,
523
+ },
524
+ });
525
+
526
+ toast.success('Netlify stats updated');
527
+ } catch (error) {
528
+ console.error('Error fetching Netlify stats:', error);
529
+ toast.error(`Failed to fetch Netlify stats: ${error instanceof Error ? error.message : 'Unknown error'}`);
530
+ } finally {
531
+ setFetchingStats(false);
532
+ }
533
+ };
534
+
535
+ const renderStats = () => {
536
+ if (!connection.user || !connection.stats) {
537
+ return null;
538
+ }
539
+
540
+ return (
541
+ <div className="mt-6">
542
+ <Collapsible open={isStatsOpen} onOpenChange={setIsStatsOpen}>
543
+ <CollapsibleTrigger asChild>
544
+ <div className="flex items-center justify-between p-4 rounded-lg bg-bolt-elements-background dark:bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor hover:border-bolt-elements-borderColorActive/70 dark:hover:border-bolt-elements-borderColorActive/70 transition-all duration-200">
545
+ <div className="flex items-center gap-2">
546
+ <div className="i-ph:chart-bar w-4 h-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
547
+ <span className="text-sm font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
548
+ Netlify Stats
549
+ </span>
550
+ </div>
551
+ <div
552
+ className={classNames(
553
+ 'i-ph:caret-down w-4 h-4 transform transition-transform duration-200 text-bolt-elements-textSecondary',
554
+ isStatsOpen ? 'rotate-180' : '',
555
+ )}
556
+ />
557
+ </div>
558
+ </CollapsibleTrigger>
559
+ <CollapsibleContent className="overflow-hidden">
560
+ <div className="space-y-4 mt-4">
561
+ <div className="flex flex-wrap items-center gap-4">
562
+ <Badge
563
+ variant="outline"
564
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
565
+ >
566
+ <BuildingLibraryIcon className="h-4 w-4 text-bolt-elements-item-contentAccent" />
567
+ <span>{connection.stats.totalSites} Sites</span>
568
+ </Badge>
569
+ <Badge
570
+ variant="outline"
571
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
572
+ >
573
+ <RocketLaunchIcon className="h-4 w-4 text-bolt-elements-item-contentAccent" />
574
+ <span>{deploymentCount} Deployments</span>
575
+ </Badge>
576
+ {lastUpdated && (
577
+ <Badge
578
+ variant="outline"
579
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
580
+ >
581
+ <ClockIcon className="h-4 w-4 text-bolt-elements-item-contentAccent" />
582
+ <span>Updated {formatDistanceToNow(new Date(lastUpdated))} ago</span>
583
+ </Badge>
584
+ )}
585
+ </div>
586
+ {sites.length > 0 && (
587
+ <div className="mt-4 space-y-4">
588
+ <div className="bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg p-4">
589
+ <div className="flex items-center justify-between mb-4">
590
+ <h4 className="text-sm font-medium flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
591
+ <BuildingLibraryIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
592
+ Your Sites
593
+ </h4>
594
+ <Button
595
+ variant="outline"
596
+ size="sm"
597
+ onClick={() => fetchNetlifyStats(connection.token)}
598
+ disabled={fetchingStats}
599
+ className="flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive/10"
600
+ >
601
+ <ArrowPathIcon
602
+ className={classNames(
603
+ 'h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent',
604
+ { 'animate-spin': fetchingStats },
605
+ )}
606
+ />
607
+ {fetchingStats ? 'Refreshing...' : 'Refresh'}
608
+ </Button>
609
+ </div>
610
+ <div className="space-y-3">
611
+ {sites.map((site, index) => (
612
+ <div
613
+ key={site.id}
614
+ className={classNames(
615
+ 'bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border rounded-lg p-4 transition-all',
616
+ activeSiteIndex === index
617
+ ? 'border-bolt-elements-item-contentAccent bg-bolt-elements-item-backgroundActive/10'
618
+ : 'border-bolt-elements-borderColor hover:border-bolt-elements-borderColorActive/70',
619
+ )}
620
+ onClick={() => {
621
+ setActiveSiteIndex(index);
622
+ }}
623
+ >
624
+ <div className="flex items-center justify-between">
625
+ <div className="flex items-center gap-2">
626
+ <CloudIcon className="h-5 w-5 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
627
+ <span className="font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
628
+ {site.name}
629
+ </span>
630
+ </div>
631
+ <div className="flex items-center gap-2">
632
+ <Badge
633
+ variant={site.published_deploy?.state === 'ready' ? 'default' : 'destructive'}
634
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
635
+ >
636
+ {site.published_deploy?.state === 'ready' ? (
637
+ <CheckCircleIcon className="h-4 w-4 text-green-500" />
638
+ ) : (
639
+ <XCircleIcon className="h-4 w-4 text-red-500" />
640
+ )}
641
+ <span className="text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
642
+ {site.published_deploy?.state || 'Unknown'}
643
+ </span>
644
+ </Badge>
645
+ </div>
646
+ </div>
647
+
648
+ <div className="mt-3 flex items-center gap-2">
649
+ <a
650
+ href={site.ssl_url || site.url}
651
+ target="_blank"
652
+ rel="noopener noreferrer"
653
+ className="text-sm flex items-center gap-1 transition-colors text-bolt-elements-link-text hover:text-bolt-elements-link-textHover dark:text-white dark:hover:text-bolt-elements-link-textHover"
654
+ onClick={(e) => e.stopPropagation()}
655
+ >
656
+ <CloudIcon className="h-3 w-3 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
657
+ <span className="underline decoration-1 underline-offset-2">
658
+ {site.ssl_url || site.url}
659
+ </span>
660
+ </a>
661
+ </div>
662
+
663
+ {activeSiteIndex === index && (
664
+ <>
665
+ <div className="mt-4 pt-3 border-t border-bolt-elements-borderColor">
666
+ <div className="flex items-center gap-2">
667
+ {siteActions.map((action) => (
668
+ <Button
669
+ key={action.name}
670
+ variant={action.variant || 'outline'}
671
+ size="sm"
672
+ onClick={async (e) => {
673
+ e.stopPropagation();
674
+
675
+ if (action.requiresConfirmation) {
676
+ if (!confirm(`Are you sure you want to ${action.name.toLowerCase()}?`)) {
677
+ return;
678
+ }
679
+ }
680
+
681
+ setIsActionLoading(true);
682
+ await action.action(site.id);
683
+ setIsActionLoading(false);
684
+ }}
685
+ disabled={isActionLoading}
686
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
687
+ >
688
+ <action.icon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
689
+ {action.name}
690
+ </Button>
691
+ ))}
692
+ </div>
693
+ </div>
694
+ {site.published_deploy && (
695
+ <div className="mt-3 text-sm">
696
+ <div className="flex items-center gap-1">
697
+ <ClockIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
698
+ <span className="text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary">
699
+ Published {formatDistanceToNow(new Date(site.published_deploy.published_at))} ago
700
+ </span>
701
+ </div>
702
+ {site.published_deploy.branch && (
703
+ <div className="flex items-center gap-1 mt-1">
704
+ <CodeBracketIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
705
+ <span className="text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary">
706
+ Branch: {site.published_deploy.branch}
707
+ </span>
708
+ </div>
709
+ )}
710
+ </div>
711
+ )}
712
+ </>
713
+ )}
714
+ </div>
715
+ ))}
716
+ </div>
717
+ </div>
718
+ {activeSiteIndex !== -1 && deploys.length > 0 && (
719
+ <div className="bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg p-4">
720
+ <div className="flex items-center justify-between mb-3">
721
+ <h4 className="text-sm font-medium flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
722
+ <BuildingLibraryIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
723
+ Recent Deployments
724
+ </h4>
725
+ </div>
726
+ <div className="space-y-2">
727
+ {deploys.map((deploy) => (
728
+ <div
729
+ key={deploy.id}
730
+ className="bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg p-3"
731
+ >
732
+ <div className="flex items-center justify-between">
733
+ <div className="flex items-center gap-2">
734
+ <Badge
735
+ variant={
736
+ deploy.state === 'ready'
737
+ ? 'default'
738
+ : deploy.state === 'error'
739
+ ? 'destructive'
740
+ : 'outline'
741
+ }
742
+ className="flex items-center gap-1"
743
+ >
744
+ {deploy.state === 'ready' ? (
745
+ <CheckCircleIcon className="h-4 w-4 text-green-500" />
746
+ ) : deploy.state === 'error' ? (
747
+ <XCircleIcon className="h-4 w-4 text-red-500" />
748
+ ) : (
749
+ <BuildingLibraryIcon className="h-4 w-4 text-bolt-elements-item-contentAccent" />
750
+ )}
751
+ <span className="text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
752
+ {deploy.state}
753
+ </span>
754
+ </Badge>
755
+ </div>
756
+ <span className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary">
757
+ {formatDistanceToNow(new Date(deploy.created_at))} ago
758
+ </span>
759
+ </div>
760
+ {deploy.branch && (
761
+ <div className="mt-2 text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary flex items-center gap-1">
762
+ <CodeBracketIcon className="h-3 w-3 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
763
+ <span className="text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary">
764
+ Branch: {deploy.branch}
765
+ </span>
766
+ </div>
767
+ )}
768
+ {deploy.deploy_url && (
769
+ <div className="mt-2 text-xs">
770
+ <a
771
+ href={deploy.deploy_url}
772
+ target="_blank"
773
+ rel="noopener noreferrer"
774
+ className="flex items-center gap-1 transition-colors text-bolt-elements-link-text hover:text-bolt-elements-link-textHover dark:text-white dark:hover:text-bolt-elements-link-textHover"
775
+ onClick={(e) => e.stopPropagation()}
776
+ >
777
+ <CloudIcon className="h-3 w-3 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
778
+ <span className="underline decoration-1 underline-offset-2">{deploy.deploy_url}</span>
779
+ </a>
780
+ </div>
781
+ )}
782
+ <div className="flex items-center gap-2 mt-2">
783
+ <Button
784
+ variant="outline"
785
+ size="sm"
786
+ onClick={() => handleDeploy(sites[activeSiteIndex].id, deploy.id, 'publish')}
787
+ disabled={isActionLoading}
788
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
789
+ >
790
+ <BuildingLibraryIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
791
+ Publish
792
+ </Button>
793
+ {deploy.state === 'ready' ? (
794
+ <Button
795
+ variant="outline"
796
+ size="sm"
797
+ onClick={() => handleDeploy(sites[activeSiteIndex].id, deploy.id, 'lock')}
798
+ disabled={isActionLoading}
799
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
800
+ >
801
+ <LockClosedIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
802
+ Lock
803
+ </Button>
804
+ ) : (
805
+ <Button
806
+ variant="outline"
807
+ size="sm"
808
+ onClick={() => handleDeploy(sites[activeSiteIndex].id, deploy.id, 'unlock')}
809
+ disabled={isActionLoading}
810
+ className="flex items-center gap-1 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary"
811
+ >
812
+ <LockOpenIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
813
+ Unlock
814
+ </Button>
815
+ )}
816
+ </div>
817
+ </div>
818
+ ))}
819
+ </div>
820
+ </div>
821
+ )}
822
+ {activeSiteIndex !== -1 && builds.length > 0 && (
823
+ <div className="bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg p-4">
824
+ <div className="flex items-center justify-between mb-3">
825
+ <h4 className="text-sm font-medium flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
826
+ <CodeBracketIcon className="h-4 w-4 text-bolt-elements-item-contentAccent dark:text-bolt-elements-item-contentAccent" />
827
+ Recent Builds
828
+ </h4>
829
+ </div>
830
+ <div className="space-y-2">
831
+ {builds.map((build) => (
832
+ <div
833
+ key={build.id}
834
+ className="bg-bolt-elements-background dark:bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg p-3"
835
+ >
836
+ <div className="flex items-center justify-between">
837
+ <div className="flex items-center gap-2">
838
+ <Badge
839
+ variant={
840
+ build.done && !build.error ? 'default' : build.error ? 'destructive' : 'outline'
841
+ }
842
+ className="flex items-center gap-1"
843
+ >
844
+ {build.done && !build.error ? (
845
+ <CheckCircleIcon className="h-4 w-4" />
846
+ ) : build.error ? (
847
+ <XCircleIcon className="h-4 w-4" />
848
+ ) : (
849
+ <CodeBracketIcon className="h-4 w-4" />
850
+ )}
851
+ <span className="text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary">
852
+ {build.done ? (build.error ? 'Failed' : 'Completed') : 'In Progress'}
853
+ </span>
854
+ </Badge>
855
+ </div>
856
+ <span className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary">
857
+ {formatDistanceToNow(new Date(build.created_at))} ago
858
+ </span>
859
+ </div>
860
+ {build.error && (
861
+ <div className="mt-2 text-xs text-bolt-elements-textDestructive dark:text-bolt-elements-textDestructive flex items-center gap-1">
862
+ <XCircleIcon className="h-3 w-3 text-bolt-elements-textDestructive dark:text-bolt-elements-textDestructive" />
863
+ Error: {build.error}
864
+ </div>
865
+ )}
866
+ </div>
867
+ ))}
868
+ </div>
869
+ </div>
870
+ )}
871
+ </div>
872
+ )}
873
+ </div>
874
+ </CollapsibleContent>
875
+ </Collapsible>
876
+ </div>
877
+ );
878
+ };
879
+
880
+ return (
881
+ <div className="space-y-6 bg-bolt-elements-background dark:bg-bolt-elements-background border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor rounded-lg">
882
+ <div className="p-6">
883
+ <div className="flex items-center justify-between">
884
+ <div className="flex items-center gap-2">
885
+ <div className="text-[#00AD9F]">
886
+ <NetlifyLogo />
887
+ </div>
888
+ <h2 className="text-lg font-medium text-bolt-elements-textPrimary">Netlify Connection</h2>
889
+ </div>
890
+ </div>
891
+
892
+ {!connection.user ? (
893
+ <div className="mt-4">
894
+ <label className="block text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary mb-2">
895
+ API Token
896
+ </label>
897
+ <input
898
+ type="password"
899
+ value={tokenInput}
900
+ onChange={(e) => setTokenInput(e.target.value)}
901
+ placeholder="Enter your Netlify API token"
902
+ className={classNames(
903
+ 'w-full px-3 py-2 rounded-lg text-sm',
904
+ 'bg-[#F8F8F8] dark:bg-[#1A1A1A]',
905
+ 'border border-[#E5E5E5] dark:border-[#333333]',
906
+ 'text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary',
907
+ 'focus:outline-none focus:ring-1 focus:ring-bolt-elements-borderColorActive',
908
+ 'disabled:opacity-50',
909
+ )}
910
+ />
911
+ <div className="mt-2 text-sm text-bolt-elements-textSecondary">
912
+ <a
913
+ href="https://app.netlify.com/user/applications#personal-access-tokens"
914
+ target="_blank"
915
+ rel="noopener noreferrer"
916
+ className="text-bolt-elements-borderColorActive hover:underline inline-flex items-center gap-1"
917
+ >
918
+ Get your token
919
+ <div className="i-ph:arrow-square-out w-4 h-4" />
920
+ </a>
921
+ </div>
922
+ {/* Debug info - remove this later */}
923
+ <div className="mt-2 text-xs text-gray-500">
924
+ <p>Debug: Token present: {connection.token ? '✅' : '❌'}</p>
925
+ <p>Debug: User present: {connection.user ? '✅' : '❌'}</p>
926
+ <p>Debug: Env token: {import.meta.env?.VITE_NETLIFY_ACCESS_TOKEN ? '✅' : '❌'}</p>
927
+ </div>
928
+ <div className="flex gap-2 mt-4">
929
+ <button
930
+ onClick={handleConnect}
931
+ disabled={isConnecting || !tokenInput}
932
+ className={classNames(
933
+ 'px-4 py-2 rounded-lg text-sm flex items-center gap-2',
934
+ 'bg-[#303030] text-white',
935
+ 'hover:bg-[#5E41D0] hover:text-white',
936
+ 'disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200',
937
+ 'transform active:scale-95',
938
+ )}
939
+ >
940
+ {isConnecting ? (
941
+ <>
942
+ <div className="i-ph:spinner-gap animate-spin" />
943
+ Connecting...
944
+ </>
945
+ ) : (
946
+ <>
947
+ <div className="i-ph:plug-charging w-4 h-4" />
948
+ Connect
949
+ </>
950
+ )}
951
+ </button>
952
+
953
+ {/* Debug button - remove this later */}
954
+ <button
955
+ onClick={async () => {
956
+ console.log('Manual Netlify auto-connect test');
957
+ await initializeNetlifyConnection();
958
+ }}
959
+ className="px-3 py-2 rounded-lg text-xs bg-blue-500 text-white hover:bg-blue-600"
960
+ >
961
+ Test Auto-Connect
962
+ </button>
963
+ </div>
964
+ </div>
965
+ ) : (
966
+ <div className="flex flex-col w-full gap-4 mt-4">
967
+ <div className="flex items-center gap-3">
968
+ <button
969
+ onClick={handleDisconnect}
970
+ className={classNames(
971
+ 'px-4 py-2 rounded-lg text-sm flex items-center gap-2',
972
+ 'bg-red-500 text-white',
973
+ 'hover:bg-red-600',
974
+ )}
975
+ >
976
+ <div className="i-ph:plug w-4 h-4" />
977
+ Disconnect
978
+ </button>
979
+ <span className="text-sm text-bolt-elements-textSecondary flex items-center gap-1">
980
+ <div className="i-ph:check-circle w-4 h-4 text-green-500" />
981
+ Connected to Netlify
982
+ </span>
983
+ </div>
984
+ {renderStats()}
985
+ </div>
986
+ )}
987
+ </div>
988
+ </div>
989
+ );
990
+ }