agent-relay 2.0.21 → 2.0.23
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.
- package/bin/relay-pty-linux-arm64 +0 -0
- package/dist/src/cli/index.d.ts +3 -3
- package/dist/src/cli/index.js +31 -100
- package/package.json +22 -29
- package/packages/api-types/package.json +1 -1
- package/packages/bridge/package.json +8 -8
- package/packages/cli-tester/package.json +1 -1
- package/packages/cloud/dist/server.js +25 -4
- package/packages/cloud/package.json +6 -6
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +1 -1
- package/packages/daemon/dist/orchestrator.js +21 -1
- package/packages/daemon/dist/router.d.ts +5 -0
- package/packages/daemon/dist/router.js +31 -0
- package/packages/daemon/dist/server.d.ts +5 -0
- package/packages/daemon/dist/server.js +131 -1
- package/packages/daemon/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/dist/client.d.ts +15 -0
- package/packages/mcp/dist/client.js +9 -0
- package/packages/mcp/dist/server.js +13 -1
- package/packages/mcp/dist/tools/index.d.ts +2 -0
- package/packages/mcp/dist/tools/index.js +2 -0
- package/packages/mcp/dist/tools/relay-connected.d.ts +17 -0
- package/packages/mcp/dist/tools/relay-connected.js +40 -0
- package/packages/mcp/dist/tools/relay-remove-agent.d.ts +20 -0
- package/packages/mcp/dist/tools/relay-remove-agent.js +50 -0
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/dist/types.d.ts +46 -1
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/client.d.ts +22 -1
- package/packages/sdk/dist/client.js +31 -0
- package/packages/sdk/dist/protocol/index.d.ts +1 -1
- package/packages/sdk/dist/protocol/types.d.ts +35 -1
- package/packages/sdk/package.json +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/dist/adapter.d.ts +4 -0
- package/packages/storage/dist/sqlite-adapter.d.ts +10 -0
- package/packages/storage/dist/sqlite-adapter.js +26 -0
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/dist/update-checker.js +4 -0
- package/packages/utils/package.json +1 -1
- package/packages/wrapper/package.json +6 -6
- package/deploy/workspace/codex.config.toml +0 -20
- package/deploy/workspace/entrypoint-browser.sh +0 -118
- package/deploy/workspace/entrypoint.sh +0 -612
- package/deploy/workspace/gh-credential-relay +0 -90
- package/deploy/workspace/gh-relay +0 -156
- package/deploy/workspace/git-credential-relay +0 -330
- package/deploy/workspace/git-credential-relay.test.sh +0 -230
- package/dist/dashboard/out/404.html +0 -1
- package/dist/dashboard/out/_next/static/7MZPqYkVGw3EGzVBkVmY9/_buildManifest.js +0 -1
- package/dist/dashboard/out/_next/static/7MZPqYkVGw3EGzVBkVmY9/_ssgManifest.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/116-a883fca163f3a5bc.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/117-c8afed19e821a35d.js +0 -2
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/320-a6304232cd0ee2ce.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/631-16b905e5920f9b59.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/648-acb2ff9f77cbfbd3.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/766-2aea80818f7eb0d8.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/83-26d2bde54616ee90.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/891-5cb1513eeb97a891.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-366fb7c078d4e9e0.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-fa1d5842aa90e8a6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/complete-profile/page-dd64bbdf66b639cd.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-113060009ef35bc2.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/history/page-9965d2483011b846.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/layout-6b91e33784c20610.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-435eceb0073be027.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-1e37ef8e73940b40.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-8119d4246743574e.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-9db3ebdfa567a7c9.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-ecb16ffd3b36262b.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-4dbe33f0f7691b7c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-c7a0a28341365ae0.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-311c3db74dcfadb7.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-fdbeb09028f57c9f.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
- package/dist/dashboard/out/_next/static/css/4034f236dd1a3178.css +0 -1
- package/dist/dashboard/out/_next/static/css/6892f8422896ef7a.css +0 -1
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
- package/dist/dashboard/out/alt-logos/logo.svg +0 -38
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
- package/dist/dashboard/out/app/onboarding.html +0 -1
- package/dist/dashboard/out/app/onboarding.txt +0 -7
- package/dist/dashboard/out/app.html +0 -1
- package/dist/dashboard/out/app.txt +0 -7
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/cloud/link.html +0 -1
- package/dist/dashboard/out/cloud/link.txt +0 -7
- package/dist/dashboard/out/complete-profile.html +0 -5
- package/dist/dashboard/out/complete-profile.txt +0 -7
- package/dist/dashboard/out/connect-repos.html +0 -1
- package/dist/dashboard/out/connect-repos.txt +0 -7
- package/dist/dashboard/out/history.html +0 -1
- package/dist/dashboard/out/history.txt +0 -7
- package/dist/dashboard/out/index.html +0 -1
- package/dist/dashboard/out/index.txt +0 -7
- package/dist/dashboard/out/login.html +0 -5
- package/dist/dashboard/out/login.txt +0 -7
- package/dist/dashboard/out/metrics.html +0 -1
- package/dist/dashboard/out/metrics.txt +0 -7
- package/dist/dashboard/out/pricing.html +0 -13
- package/dist/dashboard/out/pricing.txt +0 -7
- package/dist/dashboard/out/providers/setup/claude.html +0 -1
- package/dist/dashboard/out/providers/setup/claude.txt +0 -8
- package/dist/dashboard/out/providers/setup/codex.html +0 -1
- package/dist/dashboard/out/providers/setup/codex.txt +0 -8
- package/dist/dashboard/out/providers/setup/cursor.html +0 -1
- package/dist/dashboard/out/providers/setup/cursor.txt +0 -8
- package/dist/dashboard/out/providers.html +0 -1
- package/dist/dashboard/out/providers.txt +0 -7
- package/dist/dashboard/out/signup.html +0 -6
- package/dist/dashboard/out/signup.txt +0 -7
- package/dist/src/dashboard-server/index.d.ts +0 -8
- package/dist/src/dashboard-server/index.js +0 -8
- package/packages/dashboard/README.md +0 -48
- package/packages/dashboard/dist/health-worker-manager.d.ts +0 -62
- package/packages/dashboard/dist/health-worker-manager.js +0 -144
- package/packages/dashboard/dist/health-worker.d.ts +0 -9
- package/packages/dashboard/dist/health-worker.js +0 -79
- package/packages/dashboard/dist/index.d.ts +0 -20
- package/packages/dashboard/dist/index.js +0 -19
- package/packages/dashboard/dist/metrics.d.ts +0 -105
- package/packages/dashboard/dist/metrics.js +0 -193
- package/packages/dashboard/dist/needs-attention.d.ts +0 -24
- package/packages/dashboard/dist/needs-attention.js +0 -78
- package/packages/dashboard/dist/server.d.ts +0 -25
- package/packages/dashboard/dist/server.js +0 -5270
- package/packages/dashboard/dist/start.d.ts +0 -6
- package/packages/dashboard/dist/start.js +0 -13
- package/packages/dashboard/dist/types/threading.d.ts +0 -8
- package/packages/dashboard/dist/types/threading.js +0 -2
- package/packages/dashboard/dist/user-bridge.d.ts +0 -154
- package/packages/dashboard/dist/user-bridge.js +0 -372
- package/packages/dashboard/package.json +0 -65
- package/packages/dashboard/ui/app/app/onboarding/page.tsx +0 -394
- package/packages/dashboard/ui/app/app/page.tsx +0 -667
- package/packages/dashboard/ui/app/apple-icon.png +0 -0
- package/packages/dashboard/ui/app/cloud/link/page.tsx +0 -464
- package/packages/dashboard/ui/app/complete-profile/page.tsx +0 -204
- package/packages/dashboard/ui/app/connect-repos/page.tsx +0 -410
- package/packages/dashboard/ui/app/favicon.png +0 -0
- package/packages/dashboard/ui/app/globals.css +0 -59
- package/packages/dashboard/ui/app/history/page.tsx +0 -658
- package/packages/dashboard/ui/app/layout.tsx +0 -25
- package/packages/dashboard/ui/app/login/page.tsx +0 -424
- package/packages/dashboard/ui/app/metrics/page.tsx +0 -751
- package/packages/dashboard/ui/app/page.tsx +0 -59
- package/packages/dashboard/ui/app/pricing/page.tsx +0 -7
- package/packages/dashboard/ui/app/providers/page.tsx +0 -193
- package/packages/dashboard/ui/app/providers/setup/[provider]/ProviderSetupClient.tsx +0 -148
- package/packages/dashboard/ui/app/providers/setup/[provider]/constants.ts +0 -35
- package/packages/dashboard/ui/app/providers/setup/[provider]/page.tsx +0 -42
- package/packages/dashboard/ui/app/signup/page.tsx +0 -533
- package/packages/dashboard/ui/index.ts +0 -49
- package/packages/dashboard/ui/landing/LandingPage.tsx +0 -713
- package/packages/dashboard/ui/landing/PricingPage.tsx +0 -559
- package/packages/dashboard/ui/landing/index.ts +0 -6
- package/packages/dashboard/ui/landing/styles.css +0 -2850
- package/packages/dashboard/ui/lib/agent-merge.ts +0 -35
- package/packages/dashboard/ui/lib/api.ts +0 -1155
- package/packages/dashboard/ui/lib/cloudApi.ts +0 -877
- package/packages/dashboard/ui/lib/colors.ts +0 -218
- package/packages/dashboard/ui/lib/hierarchy.ts +0 -242
- package/packages/dashboard/ui/lib/stuckDetection.ts +0 -142
- package/packages/dashboard/ui/next-env.d.ts +0 -5
- package/packages/dashboard/ui/next.config.js +0 -41
- package/packages/dashboard/ui/package-lock.json +0 -2882
- package/packages/dashboard/ui/package.json +0 -33
- package/packages/dashboard/ui/postcss.config.js +0 -5
- package/packages/dashboard/ui/react-components/ActivityFeed.tsx +0 -216
- package/packages/dashboard/ui/react-components/AddWorkspaceModal.tsx +0 -170
- package/packages/dashboard/ui/react-components/AgentCard.tsx +0 -587
- package/packages/dashboard/ui/react-components/AgentList.tsx +0 -411
- package/packages/dashboard/ui/react-components/AgentProfilePanel.tsx +0 -564
- package/packages/dashboard/ui/react-components/App.tsx +0 -3033
- package/packages/dashboard/ui/react-components/BillingPanel.tsx +0 -922
- package/packages/dashboard/ui/react-components/BillingResult.tsx +0 -447
- package/packages/dashboard/ui/react-components/BroadcastComposer.tsx +0 -690
- package/packages/dashboard/ui/react-components/ChannelAdminPanel.tsx +0 -773
- package/packages/dashboard/ui/react-components/ChannelBrowser.tsx +0 -385
- package/packages/dashboard/ui/react-components/ChannelChat.tsx +0 -261
- package/packages/dashboard/ui/react-components/ChannelSidebar.tsx +0 -399
- package/packages/dashboard/ui/react-components/CloudSessionProvider.tsx +0 -130
- package/packages/dashboard/ui/react-components/CommandPalette.tsx +0 -815
- package/packages/dashboard/ui/react-components/ConfirmationDialog.tsx +0 -133
- package/packages/dashboard/ui/react-components/ConversationHistory.tsx +0 -518
- package/packages/dashboard/ui/react-components/CoordinatorPanel.tsx +0 -944
- package/packages/dashboard/ui/react-components/DecisionQueue.tsx +0 -717
- package/packages/dashboard/ui/react-components/DirectMessageView.tsx +0 -164
- package/packages/dashboard/ui/react-components/FileAutocomplete.tsx +0 -368
- package/packages/dashboard/ui/react-components/FleetOverview.tsx +0 -278
- package/packages/dashboard/ui/react-components/LogViewer.tsx +0 -310
- package/packages/dashboard/ui/react-components/LogViewerPanel.tsx +0 -482
- package/packages/dashboard/ui/react-components/Logo.tsx +0 -284
- package/packages/dashboard/ui/react-components/MentionAutocomplete.tsx +0 -384
- package/packages/dashboard/ui/react-components/MessageComposer.tsx +0 -457
- package/packages/dashboard/ui/react-components/MessageList.tsx +0 -649
- package/packages/dashboard/ui/react-components/MessageSenderName.tsx +0 -91
- package/packages/dashboard/ui/react-components/MessageStatusIndicator.tsx +0 -142
- package/packages/dashboard/ui/react-components/NewConversationModal.tsx +0 -400
- package/packages/dashboard/ui/react-components/NotificationToast.tsx +0 -488
- package/packages/dashboard/ui/react-components/OnlineUsersIndicator.tsx +0 -164
- package/packages/dashboard/ui/react-components/Pagination.tsx +0 -124
- package/packages/dashboard/ui/react-components/PricingPlans.tsx +0 -386
- package/packages/dashboard/ui/react-components/ProjectList.tsx +0 -625
- package/packages/dashboard/ui/react-components/ProviderAuthFlow.tsx +0 -853
- package/packages/dashboard/ui/react-components/ProviderConnectionList.tsx +0 -378
- package/packages/dashboard/ui/react-components/ProvisioningProgress.tsx +0 -730
- package/packages/dashboard/ui/react-components/RepoAccessPanel.tsx +0 -549
- package/packages/dashboard/ui/react-components/ServerCard.tsx +0 -202
- package/packages/dashboard/ui/react-components/SessionExpiredModal.tsx +0 -128
- package/packages/dashboard/ui/react-components/SpawnModal.tsx +0 -804
- package/packages/dashboard/ui/react-components/TaskAssignmentUI.tsx +0 -375
- package/packages/dashboard/ui/react-components/TerminalProviderSetup.tsx +0 -608
- package/packages/dashboard/ui/react-components/ThemeProvider.tsx +0 -325
- package/packages/dashboard/ui/react-components/ThinkingIndicator.tsx +0 -231
- package/packages/dashboard/ui/react-components/ThreadList.tsx +0 -198
- package/packages/dashboard/ui/react-components/ThreadPanel.tsx +0 -346
- package/packages/dashboard/ui/react-components/TrajectoryViewer.tsx +0 -698
- package/packages/dashboard/ui/react-components/TypingIndicator.tsx +0 -69
- package/packages/dashboard/ui/react-components/UsageBanner.tsx +0 -231
- package/packages/dashboard/ui/react-components/UserProfilePanel.tsx +0 -233
- package/packages/dashboard/ui/react-components/WorkspaceContext.tsx +0 -107
- package/packages/dashboard/ui/react-components/WorkspaceSelector.tsx +0 -234
- package/packages/dashboard/ui/react-components/WorkspaceStatusIndicator.tsx +0 -370
- package/packages/dashboard/ui/react-components/XTermInteractive.tsx +0 -510
- package/packages/dashboard/ui/react-components/XTermLogViewer.tsx +0 -719
- package/packages/dashboard/ui/react-components/channels/ChannelDialogs.tsx +0 -1411
- package/packages/dashboard/ui/react-components/channels/ChannelHeader.tsx +0 -317
- package/packages/dashboard/ui/react-components/channels/ChannelMessageList.tsx +0 -463
- package/packages/dashboard/ui/react-components/channels/ChannelViewV1.tsx +0 -146
- package/packages/dashboard/ui/react-components/channels/MessageInput.tsx +0 -288
- package/packages/dashboard/ui/react-components/channels/SearchInput.tsx +0 -172
- package/packages/dashboard/ui/react-components/channels/SearchResults.tsx +0 -336
- package/packages/dashboard/ui/react-components/channels/api.ts +0 -697
- package/packages/dashboard/ui/react-components/channels/index.ts +0 -76
- package/packages/dashboard/ui/react-components/channels/mockApi.ts +0 -344
- package/packages/dashboard/ui/react-components/channels/types.ts +0 -566
- package/packages/dashboard/ui/react-components/hooks/index.ts +0 -57
- package/packages/dashboard/ui/react-components/hooks/useAgentLogs.ts +0 -394
- package/packages/dashboard/ui/react-components/hooks/useAgents.ts +0 -127
- package/packages/dashboard/ui/react-components/hooks/useBroadcastDedup.ts +0 -86
- package/packages/dashboard/ui/react-components/hooks/useChannelAdmin.ts +0 -329
- package/packages/dashboard/ui/react-components/hooks/useChannelBrowser.ts +0 -239
- package/packages/dashboard/ui/react-components/hooks/useChannelCommands.ts +0 -138
- package/packages/dashboard/ui/react-components/hooks/useChannels.ts +0 -328
- package/packages/dashboard/ui/react-components/hooks/useDebounce.ts +0 -29
- package/packages/dashboard/ui/react-components/hooks/useDirectMessage.ts +0 -141
- package/packages/dashboard/ui/react-components/hooks/useMessages.ts +0 -309
- package/packages/dashboard/ui/react-components/hooks/useOrchestrator.ts +0 -364
- package/packages/dashboard/ui/react-components/hooks/usePinnedAgents.ts +0 -140
- package/packages/dashboard/ui/react-components/hooks/usePresence.ts +0 -340
- package/packages/dashboard/ui/react-components/hooks/useRecentRepos.ts +0 -130
- package/packages/dashboard/ui/react-components/hooks/useSession.ts +0 -209
- package/packages/dashboard/ui/react-components/hooks/useTrajectory.ts +0 -265
- package/packages/dashboard/ui/react-components/hooks/useWebSocket.ts +0 -169
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceMembers.ts +0 -120
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceRepos.ts +0 -73
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceStatus.ts +0 -237
- package/packages/dashboard/ui/react-components/index.ts +0 -81
- package/packages/dashboard/ui/react-components/layout/Header.tsx +0 -355
- package/packages/dashboard/ui/react-components/layout/RepoContextHeader.tsx +0 -361
- package/packages/dashboard/ui/react-components/layout/Sidebar.archive.test.tsx +0 -126
- package/packages/dashboard/ui/react-components/layout/Sidebar.test.tsx +0 -691
- package/packages/dashboard/ui/react-components/layout/Sidebar.tsx +0 -930
- package/packages/dashboard/ui/react-components/layout/index.ts +0 -7
- package/packages/dashboard/ui/react-components/settings/BillingSettingsPanel.tsx +0 -564
- package/packages/dashboard/ui/react-components/settings/SettingsPage.tsx +0 -544
- package/packages/dashboard/ui/react-components/settings/TeamSettingsPanel.tsx +0 -560
- package/packages/dashboard/ui/react-components/settings/WorkspaceSettingsPanel.tsx +0 -1386
- package/packages/dashboard/ui/react-components/settings/index.ts +0 -11
- package/packages/dashboard/ui/react-components/settings/types.ts +0 -53
- package/packages/dashboard/ui/react-components/utils/messageFormatting.tsx +0 -370
- package/packages/dashboard/ui/tailwind.config.js +0 -148
- package/packages/dashboard/ui/types/index.ts +0 -304
- package/packages/dashboard/ui/types/threading.ts +0 -7
- package/packages/dashboard/ui-dist/404.html +0 -1
- package/packages/dashboard/ui-dist/_next/static/7MZPqYkVGw3EGzVBkVmY9/_buildManifest.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/7MZPqYkVGw3EGzVBkVmY9/_ssgManifest.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/116-a883fca163f3a5bc.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/117-c8afed19e821a35d.js +0 -2
- package/packages/dashboard/ui-dist/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/320-a6304232cd0ee2ce.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/532-bace199897eeab37.js +0 -9
- package/packages/dashboard/ui-dist/_next/static/chunks/631-16b905e5920f9b59.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/648-acb2ff9f77cbfbd3.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/766-2aea80818f7eb0d8.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/83-26d2bde54616ee90.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/847-f1f467060f32afff.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/891-5cb1513eeb97a891.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/page-366fb7c078d4e9e0.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/cloud/link/page-fa1d5842aa90e8a6.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/complete-profile/page-dd64bbdf66b639cd.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/connect-repos/page-113060009ef35bc2.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/history/page-9965d2483011b846.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/layout-6b91e33784c20610.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/login/page-435eceb0073be027.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/metrics/page-1e37ef8e73940b40.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/page-8119d4246743574e.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/pricing/page-9db3ebdfa567a7c9.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/page-ecb16ffd3b36262b.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/setup/[provider]/page-4dbe33f0f7691b7c.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/app/signup/page-c7a0a28341365ae0.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
- package/packages/dashboard/ui-dist/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/main-311c3db74dcfadb7.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/main-app-fdbeb09028f57c9f.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/css/4034f236dd1a3178.css +0 -1
- package/packages/dashboard/ui-dist/_next/static/css/6892f8422896ef7a.css +0 -1
- package/packages/dashboard/ui-dist/_next/static/iJ3Uiz3IrqUJL7IxKZHiV/_buildManifest.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/iJ3Uiz3IrqUJL7IxKZHiV/_ssgManifest.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/l-jd878zUJ_IlraqEWMZc/_buildManifest.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/l-jd878zUJ_IlraqEWMZc/_ssgManifest.js +0 -1
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-128.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-256.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-32.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-512.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-64.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo.svg +0 -45
- package/packages/dashboard/ui-dist/alt-logos/logo.svg +0 -38
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-128.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-256.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-32.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-512.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-64.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo.svg +0 -38
- package/packages/dashboard/ui-dist/app/onboarding.html +0 -1
- package/packages/dashboard/ui-dist/app/onboarding.txt +0 -7
- package/packages/dashboard/ui-dist/app.html +0 -1
- package/packages/dashboard/ui-dist/app.txt +0 -7
- package/packages/dashboard/ui-dist/apple-icon.png +0 -0
- package/packages/dashboard/ui-dist/cloud/link.html +0 -1
- package/packages/dashboard/ui-dist/cloud/link.txt +0 -7
- package/packages/dashboard/ui-dist/complete-profile.html +0 -5
- package/packages/dashboard/ui-dist/complete-profile.txt +0 -7
- package/packages/dashboard/ui-dist/connect-repos.html +0 -1
- package/packages/dashboard/ui-dist/connect-repos.txt +0 -7
- package/packages/dashboard/ui-dist/history.html +0 -1
- package/packages/dashboard/ui-dist/history.txt +0 -7
- package/packages/dashboard/ui-dist/index.html +0 -1
- package/packages/dashboard/ui-dist/index.txt +0 -7
- package/packages/dashboard/ui-dist/login.html +0 -5
- package/packages/dashboard/ui-dist/login.txt +0 -7
- package/packages/dashboard/ui-dist/metrics.html +0 -1
- package/packages/dashboard/ui-dist/metrics.txt +0 -7
- package/packages/dashboard/ui-dist/pricing.html +0 -13
- package/packages/dashboard/ui-dist/pricing.txt +0 -7
- package/packages/dashboard/ui-dist/providers/setup/claude.html +0 -1
- package/packages/dashboard/ui-dist/providers/setup/claude.txt +0 -8
- package/packages/dashboard/ui-dist/providers/setup/codex.html +0 -1
- package/packages/dashboard/ui-dist/providers/setup/codex.txt +0 -8
- package/packages/dashboard/ui-dist/providers/setup/cursor.html +0 -1
- package/packages/dashboard/ui-dist/providers/setup/cursor.txt +0 -8
- package/packages/dashboard/ui-dist/providers.html +0 -1
- package/packages/dashboard/ui-dist/providers.txt +0 -7
- package/packages/dashboard/ui-dist/signup.html +0 -6
- package/packages/dashboard/ui-dist/signup.txt +0 -7
- package/packages/dashboard-server/dist/health-worker-manager.d.ts +0 -62
- package/packages/dashboard-server/dist/health-worker-manager.js +0 -144
- package/packages/dashboard-server/dist/health-worker.d.ts +0 -9
- package/packages/dashboard-server/dist/health-worker.js +0 -79
- package/packages/dashboard-server/dist/index.d.ts +0 -18
- package/packages/dashboard-server/dist/index.js +0 -17
- package/packages/dashboard-server/dist/metrics.d.ts +0 -105
- package/packages/dashboard-server/dist/metrics.js +0 -193
- package/packages/dashboard-server/dist/needs-attention.d.ts +0 -24
- package/packages/dashboard-server/dist/needs-attention.js +0 -78
- package/packages/dashboard-server/dist/server.d.ts +0 -25
- package/packages/dashboard-server/dist/server.js +0 -5158
- package/packages/dashboard-server/dist/start.d.ts +0 -6
- package/packages/dashboard-server/dist/start.js +0 -13
- package/packages/dashboard-server/dist/types/threading.d.ts +0 -8
- package/packages/dashboard-server/dist/types/threading.js +0 -2
- package/packages/dashboard-server/dist/user-bridge.d.ts +0 -158
- package/packages/dashboard-server/dist/user-bridge.js +0 -390
- package/packages/dashboard-server/package.json +0 -55
|
@@ -1,667 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dashboard V2 - Main App Page
|
|
3
|
-
*
|
|
4
|
-
* In cloud mode: Shows workspace selection and connects to selected workspace's dashboard.
|
|
5
|
-
* In local mode: Connects to local daemon WebSocket.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
11
|
-
import { App } from '../../react-components/App';
|
|
12
|
-
import { CloudSessionProvider } from '../../react-components/CloudSessionProvider';
|
|
13
|
-
import { LogoIcon } from '../../react-components/Logo';
|
|
14
|
-
import { setActiveWorkspaceId } from '../../lib/api';
|
|
15
|
-
import { ProvisioningProgress } from '../../react-components/ProvisioningProgress';
|
|
16
|
-
import { ProviderConnectionList, type ProviderInfo } from '../../react-components/ProviderConnectionList';
|
|
17
|
-
|
|
18
|
-
interface Workspace {
|
|
19
|
-
id: string;
|
|
20
|
-
name: string;
|
|
21
|
-
status: 'provisioning' | 'running' | 'stopped' | 'error';
|
|
22
|
-
publicUrl?: string;
|
|
23
|
-
providers?: string[];
|
|
24
|
-
repositories?: string[];
|
|
25
|
-
createdAt: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface Repository {
|
|
29
|
-
id: string;
|
|
30
|
-
fullName: string;
|
|
31
|
-
isPrivate: boolean;
|
|
32
|
-
defaultBranch: string;
|
|
33
|
-
syncStatus: string;
|
|
34
|
-
hasNangoConnection: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type PageState = 'loading' | 'local' | 'select-workspace' | 'no-workspaces' | 'provisioning' | 'connect-provider' | 'connecting' | 'connected' | 'error';
|
|
38
|
-
|
|
39
|
-
interface ProvisioningInfo {
|
|
40
|
-
workspaceId: string;
|
|
41
|
-
workspaceName: string;
|
|
42
|
-
stage: string | null;
|
|
43
|
-
startedAt: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Available AI providers
|
|
47
|
-
const AI_PROVIDERS: ProviderInfo[] = [
|
|
48
|
-
{ id: 'anthropic', name: 'Anthropic', displayName: 'Claude', color: '#D97757', cliCommand: 'claude' },
|
|
49
|
-
{ id: 'codex', name: 'OpenAI', displayName: 'Codex', color: '#10A37F', cliCommand: 'codex login', supportsDeviceFlow: true, requiresUrlCopy: true },
|
|
50
|
-
{ id: 'google', name: 'Google', displayName: 'Gemini', color: '#4285F4', cliCommand: 'gemini' },
|
|
51
|
-
{ id: 'opencode', name: 'OpenCode', displayName: 'OpenCode', color: '#00D4AA', cliCommand: 'opencode' },
|
|
52
|
-
{ id: 'droid', name: 'Factory', displayName: 'Droid', color: '#6366F1', cliCommand: 'droid' },
|
|
53
|
-
{ id: 'cursor', name: 'Cursor', displayName: 'Cursor', color: '#7C3AED', cliCommand: 'agent' },
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
// Force cloud mode via env var - prevents silent fallback to local mode
|
|
57
|
-
const FORCE_CLOUD_MODE = process.env.NEXT_PUBLIC_FORCE_CLOUD_MODE === 'true';
|
|
58
|
-
|
|
59
|
-
export default function DashboardPage() {
|
|
60
|
-
const [state, setState] = useState<PageState>('loading');
|
|
61
|
-
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
|
|
62
|
-
const [repos, setRepos] = useState<Repository[]>([]);
|
|
63
|
-
const [selectedWorkspace, setSelectedWorkspace] = useState<Workspace | null>(null);
|
|
64
|
-
const [wsUrl, setWsUrl] = useState<string | undefined>(undefined);
|
|
65
|
-
const [error, setError] = useState<string | null>(null);
|
|
66
|
-
// Track cloud mode for potential future use
|
|
67
|
-
const [_isCloudMode, setIsCloudMode] = useState(FORCE_CLOUD_MODE);
|
|
68
|
-
const [csrfToken, setCsrfToken] = useState<string | null>(null);
|
|
69
|
-
const [provisioningInfo, setProvisioningInfo] = useState<ProvisioningInfo | null>(null);
|
|
70
|
-
const [connectedProviders, setConnectedProviders] = useState<string[]>([]);
|
|
71
|
-
|
|
72
|
-
// Check if we're in cloud mode and fetch data
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
const init = async () => {
|
|
75
|
-
try {
|
|
76
|
-
// Check session to determine if we're in cloud mode
|
|
77
|
-
const sessionRes = await fetch('/api/auth/session', { credentials: 'include' });
|
|
78
|
-
|
|
79
|
-
// If session endpoint doesn't exist (404), we're in local mode
|
|
80
|
-
if (sessionRes.status === 404) {
|
|
81
|
-
if (FORCE_CLOUD_MODE) {
|
|
82
|
-
throw new Error('Cloud mode enforced but session endpoint returned 404. Is the cloud server running?');
|
|
83
|
-
}
|
|
84
|
-
setIsCloudMode(false);
|
|
85
|
-
setState('local');
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Capture CSRF token from response header
|
|
90
|
-
const token = sessionRes.headers.get('X-CSRF-Token');
|
|
91
|
-
if (token) {
|
|
92
|
-
setCsrfToken(token);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const session = await sessionRes.json();
|
|
96
|
-
|
|
97
|
-
if (!session.authenticated) {
|
|
98
|
-
// Cloud mode but not authenticated - redirect to login
|
|
99
|
-
window.location.href = '/login';
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Cloud mode - fetch workspaces and repos
|
|
104
|
-
setIsCloudMode(true);
|
|
105
|
-
|
|
106
|
-
// Track which providers are already connected
|
|
107
|
-
// Map backend IDs to frontend IDs for consistency
|
|
108
|
-
const BACKEND_TO_FRONTEND_MAP: Record<string, string> = {
|
|
109
|
-
openai: 'codex', // Backend stores 'openai', frontend uses 'codex'
|
|
110
|
-
};
|
|
111
|
-
if (session.connectedProviders) {
|
|
112
|
-
const providers: string[] = [];
|
|
113
|
-
session.connectedProviders.forEach((p: { provider: string }) => {
|
|
114
|
-
providers.push(p.provider);
|
|
115
|
-
// Also add the frontend ID if there's a mapping
|
|
116
|
-
const frontendId = BACKEND_TO_FRONTEND_MAP[p.provider];
|
|
117
|
-
if (frontendId) {
|
|
118
|
-
providers.push(frontendId);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
setConnectedProviders(providers);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const [workspacesRes, reposRes] = await Promise.all([
|
|
125
|
-
// Use /accessible to include workspaces user can access via GitHub repo permissions
|
|
126
|
-
fetch('/api/workspaces/accessible', { credentials: 'include' }),
|
|
127
|
-
fetch('/api/github-app/repos', { credentials: 'include' }),
|
|
128
|
-
]);
|
|
129
|
-
|
|
130
|
-
if (!workspacesRes.ok) {
|
|
131
|
-
if (workspacesRes.status === 401) {
|
|
132
|
-
window.location.href = '/login';
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
throw new Error('Failed to fetch workspaces');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const workspacesData = await workspacesRes.json();
|
|
139
|
-
const reposData = reposRes.ok ? await reposRes.json() : { repositories: [] };
|
|
140
|
-
|
|
141
|
-
setWorkspaces(workspacesData.workspaces || []);
|
|
142
|
-
setRepos(reposData.repositories || []);
|
|
143
|
-
|
|
144
|
-
// Determine next state based on workspace availability
|
|
145
|
-
const runningWorkspaces = (workspacesData.workspaces || []).filter(
|
|
146
|
-
(w: Workspace) => w.status === 'running' && w.publicUrl
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
if (runningWorkspaces.length === 1) {
|
|
150
|
-
// Auto-connect to the only running workspace
|
|
151
|
-
connectToWorkspace(runningWorkspaces[0]);
|
|
152
|
-
} else if (runningWorkspaces.length > 1) {
|
|
153
|
-
setState('select-workspace');
|
|
154
|
-
} else if ((workspacesData.workspaces || []).length > 0) {
|
|
155
|
-
// Has workspaces but none running
|
|
156
|
-
setState('select-workspace');
|
|
157
|
-
} else if ((reposData.repositories || []).length > 0) {
|
|
158
|
-
// Has repos but no workspaces - show create workspace
|
|
159
|
-
setState('no-workspaces');
|
|
160
|
-
} else {
|
|
161
|
-
// No repos, no workspaces - redirect to connect repos
|
|
162
|
-
window.location.href = '/connect-repos';
|
|
163
|
-
}
|
|
164
|
-
} catch (err) {
|
|
165
|
-
// If session check fails with network error, assume local mode (unless forced cloud)
|
|
166
|
-
if (err instanceof TypeError && err.message.includes('Failed to fetch')) {
|
|
167
|
-
if (FORCE_CLOUD_MODE) {
|
|
168
|
-
console.error('Cloud mode enforced but network request failed:', err);
|
|
169
|
-
setError('Cloud mode enforced but failed to connect to server. Is the cloud server running?');
|
|
170
|
-
setState('error');
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
setIsCloudMode(false);
|
|
174
|
-
setState('local');
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
console.error('Init error:', err);
|
|
178
|
-
setError(err instanceof Error ? err.message : 'Failed to initialize');
|
|
179
|
-
setState('error');
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
init();
|
|
184
|
-
}, []);
|
|
185
|
-
|
|
186
|
-
const connectToWorkspace = useCallback((workspace: Workspace) => {
|
|
187
|
-
if (!workspace.publicUrl) {
|
|
188
|
-
setError('Workspace has no public URL');
|
|
189
|
-
setState('error');
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
setSelectedWorkspace(workspace);
|
|
194
|
-
setState('connecting');
|
|
195
|
-
|
|
196
|
-
// Set the active workspace ID for API proxying
|
|
197
|
-
setActiveWorkspaceId(workspace.id);
|
|
198
|
-
|
|
199
|
-
// Derive WebSocket URL from public URL
|
|
200
|
-
// e.g., https://workspace-abc.agentrelay.dev -> wss://workspace-abc.agentrelay.dev/ws
|
|
201
|
-
const url = new URL(workspace.publicUrl);
|
|
202
|
-
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
203
|
-
const derivedWsUrl = `${wsProtocol}//${url.host}/ws`;
|
|
204
|
-
|
|
205
|
-
setWsUrl(derivedWsUrl);
|
|
206
|
-
setState('connected');
|
|
207
|
-
}, []);
|
|
208
|
-
|
|
209
|
-
const handleCreateWorkspace = useCallback(async (repoFullName: string) => {
|
|
210
|
-
setError(null);
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
214
|
-
if (csrfToken) {
|
|
215
|
-
headers['X-CSRF-Token'] = csrfToken;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const res = await fetch('/api/workspaces/quick', {
|
|
219
|
-
method: 'POST',
|
|
220
|
-
credentials: 'include',
|
|
221
|
-
headers,
|
|
222
|
-
body: JSON.stringify({ repositoryFullName: repoFullName }),
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const data = await res.json();
|
|
226
|
-
|
|
227
|
-
if (!res.ok) {
|
|
228
|
-
throw new Error(data.error || 'Failed to create workspace');
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Set provisioning state with workspace info
|
|
232
|
-
const startedAt = Date.now();
|
|
233
|
-
setProvisioningInfo({
|
|
234
|
-
workspaceId: data.workspaceId,
|
|
235
|
-
workspaceName: repoFullName.split('/')[1] || repoFullName,
|
|
236
|
-
stage: null,
|
|
237
|
-
startedAt,
|
|
238
|
-
});
|
|
239
|
-
setState('provisioning');
|
|
240
|
-
|
|
241
|
-
// Poll for workspace to be ready
|
|
242
|
-
// Cloud deployments (Fly.io) can take 3-5 minutes for cold starts
|
|
243
|
-
const pollForReady = async (workspaceId: string) => {
|
|
244
|
-
const maxAttempts = 150; // 5 minutes with 2s interval
|
|
245
|
-
const pollIntervalMs = 2000;
|
|
246
|
-
let attempts = 0;
|
|
247
|
-
|
|
248
|
-
while (attempts < maxAttempts) {
|
|
249
|
-
const statusRes = await fetch(`/api/workspaces/${workspaceId}/status`, {
|
|
250
|
-
credentials: 'include',
|
|
251
|
-
});
|
|
252
|
-
const statusData = await statusRes.json();
|
|
253
|
-
|
|
254
|
-
// Update provisioning stage if available
|
|
255
|
-
if (statusData.provisioning?.stage) {
|
|
256
|
-
setProvisioningInfo(prev => prev ? {
|
|
257
|
-
...prev,
|
|
258
|
-
stage: statusData.provisioning.stage,
|
|
259
|
-
} : null);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (statusData.status === 'running') {
|
|
263
|
-
// Fetch updated workspace info
|
|
264
|
-
const wsRes = await fetch(`/api/workspaces/${workspaceId}`, {
|
|
265
|
-
credentials: 'include',
|
|
266
|
-
});
|
|
267
|
-
const wsData = await wsRes.json();
|
|
268
|
-
if (wsData.publicUrl) {
|
|
269
|
-
// Clear provisioning info and show provider connection screen
|
|
270
|
-
setProvisioningInfo(null);
|
|
271
|
-
setSelectedWorkspace(wsData);
|
|
272
|
-
setState('connect-provider');
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
} else if (statusData.status === 'error') {
|
|
276
|
-
const errorMsg = statusData.errorMessage || 'Workspace provisioning failed';
|
|
277
|
-
throw new Error(errorMsg);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
281
|
-
attempts++;
|
|
282
|
-
|
|
283
|
-
// Log progress every 30 seconds
|
|
284
|
-
if (attempts % 15 === 0) {
|
|
285
|
-
console.log(`[workspace] Still provisioning... (${Math.floor(attempts * pollIntervalMs / 1000)}s elapsed)`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
throw new Error('Workspace provisioning timed out after 5 minutes. Please try again or contact support.');
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
await pollForReady(data.workspaceId);
|
|
293
|
-
} catch (err) {
|
|
294
|
-
console.error('Create workspace error:', err);
|
|
295
|
-
setProvisioningInfo(null);
|
|
296
|
-
setError(err instanceof Error ? err.message : 'Failed to create workspace');
|
|
297
|
-
setState('no-workspaces');
|
|
298
|
-
}
|
|
299
|
-
}, [connectToWorkspace, csrfToken]);
|
|
300
|
-
|
|
301
|
-
// Handle provider connection success
|
|
302
|
-
const handleProviderConnected = useCallback((providerId: string) => {
|
|
303
|
-
setConnectedProviders(prev => [...new Set([...prev, providerId])]);
|
|
304
|
-
}, []);
|
|
305
|
-
|
|
306
|
-
// Skip provider connection and continue to workspace
|
|
307
|
-
const handleSkipProvider = useCallback(() => {
|
|
308
|
-
if (selectedWorkspace) {
|
|
309
|
-
connectToWorkspace(selectedWorkspace);
|
|
310
|
-
}
|
|
311
|
-
}, [selectedWorkspace, connectToWorkspace]);
|
|
312
|
-
|
|
313
|
-
const handleStartWorkspace = useCallback(async (workspace: Workspace) => {
|
|
314
|
-
setState('loading');
|
|
315
|
-
setError(null);
|
|
316
|
-
|
|
317
|
-
try {
|
|
318
|
-
const headers: Record<string, string> = {};
|
|
319
|
-
if (csrfToken) {
|
|
320
|
-
headers['X-CSRF-Token'] = csrfToken;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const res = await fetch(`/api/workspaces/${workspace.id}/restart`, {
|
|
324
|
-
method: 'POST',
|
|
325
|
-
credentials: 'include',
|
|
326
|
-
headers,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
if (!res.ok) {
|
|
330
|
-
const data = await res.json();
|
|
331
|
-
throw new Error(data.error || 'Failed to start workspace');
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Poll for workspace to be ready
|
|
335
|
-
const maxAttempts = 60;
|
|
336
|
-
let attempts = 0;
|
|
337
|
-
|
|
338
|
-
while (attempts < maxAttempts) {
|
|
339
|
-
const statusRes = await fetch(`/api/workspaces/${workspace.id}/status`, {
|
|
340
|
-
credentials: 'include',
|
|
341
|
-
});
|
|
342
|
-
const statusData = await statusRes.json();
|
|
343
|
-
|
|
344
|
-
if (statusData.status === 'running') {
|
|
345
|
-
const wsRes = await fetch(`/api/workspaces/${workspace.id}`, {
|
|
346
|
-
credentials: 'include',
|
|
347
|
-
});
|
|
348
|
-
const wsData = await wsRes.json();
|
|
349
|
-
if (wsData.publicUrl) {
|
|
350
|
-
connectToWorkspace({ ...workspace, ...wsData });
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
356
|
-
attempts++;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
throw new Error('Workspace start timed out');
|
|
360
|
-
} catch (err) {
|
|
361
|
-
console.error('Start workspace error:', err);
|
|
362
|
-
setError(err instanceof Error ? err.message : 'Failed to start workspace');
|
|
363
|
-
setState('select-workspace');
|
|
364
|
-
}
|
|
365
|
-
}, [connectToWorkspace, csrfToken]);
|
|
366
|
-
|
|
367
|
-
// Loading state
|
|
368
|
-
if (state === 'loading') {
|
|
369
|
-
return (
|
|
370
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center">
|
|
371
|
-
<div className="text-center">
|
|
372
|
-
<svg className="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24">
|
|
373
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
374
|
-
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
375
|
-
</svg>
|
|
376
|
-
<p className="mt-4 text-text-muted">Loading...</p>
|
|
377
|
-
</div>
|
|
378
|
-
</div>
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Local mode - just render the App component
|
|
383
|
-
if (state === 'local') {
|
|
384
|
-
return <App />;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Connected to workspace - render App with workspace's WebSocket
|
|
388
|
-
// Wrap in CloudSessionProvider so App has access to cloud session context
|
|
389
|
-
if (state === 'connected' && wsUrl) {
|
|
390
|
-
return (
|
|
391
|
-
<CloudSessionProvider cloudMode={true}>
|
|
392
|
-
<App wsUrl={wsUrl} />
|
|
393
|
-
</CloudSessionProvider>
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Connecting state
|
|
398
|
-
if (state === 'connecting') {
|
|
399
|
-
return (
|
|
400
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center">
|
|
401
|
-
<div className="text-center">
|
|
402
|
-
<svg className="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24">
|
|
403
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
404
|
-
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
405
|
-
</svg>
|
|
406
|
-
<p className="mt-4 text-white font-medium">Connecting to {selectedWorkspace?.name}...</p>
|
|
407
|
-
<p className="mt-2 text-text-muted text-sm">{selectedWorkspace?.publicUrl}</p>
|
|
408
|
-
</div>
|
|
409
|
-
</div>
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Provisioning state - show progress UI
|
|
414
|
-
if (state === 'provisioning' && provisioningInfo) {
|
|
415
|
-
return (
|
|
416
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center">
|
|
417
|
-
<div className="w-full max-w-xl">
|
|
418
|
-
<ProvisioningProgress
|
|
419
|
-
isProvisioning={true}
|
|
420
|
-
currentStage={provisioningInfo.stage}
|
|
421
|
-
workspaceName={provisioningInfo.workspaceName}
|
|
422
|
-
error={error}
|
|
423
|
-
onCancel={() => {
|
|
424
|
-
setProvisioningInfo(null);
|
|
425
|
-
setState('no-workspaces');
|
|
426
|
-
}}
|
|
427
|
-
/>
|
|
428
|
-
</div>
|
|
429
|
-
</div>
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Error state
|
|
434
|
-
if (state === 'error') {
|
|
435
|
-
return (
|
|
436
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center p-4">
|
|
437
|
-
<div className="bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-2xl p-8 max-w-md w-full text-center">
|
|
438
|
-
<div className="w-16 h-16 mx-auto mb-4 bg-error/20 rounded-full flex items-center justify-center">
|
|
439
|
-
<svg className="w-8 h-8 text-error" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
440
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
441
|
-
</svg>
|
|
442
|
-
</div>
|
|
443
|
-
<h2 className="text-xl font-semibold text-white mb-2">Something went wrong</h2>
|
|
444
|
-
<p className="text-text-muted mb-6">{error}</p>
|
|
445
|
-
<button
|
|
446
|
-
onClick={() => window.location.reload()}
|
|
447
|
-
className="w-full py-3 px-4 bg-bg-tertiary border border-border-subtle rounded-xl text-white font-medium hover:bg-bg-hover transition-colors"
|
|
448
|
-
>
|
|
449
|
-
Try Again
|
|
450
|
-
</button>
|
|
451
|
-
</div>
|
|
452
|
-
</div>
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Connect provider state - show after workspace is ready
|
|
457
|
-
if (state === 'connect-provider' && selectedWorkspace) {
|
|
458
|
-
return (
|
|
459
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex flex-col items-center justify-center p-4">
|
|
460
|
-
{/* Background grid */}
|
|
461
|
-
<div className="fixed inset-0 opacity-10 pointer-events-none">
|
|
462
|
-
<div
|
|
463
|
-
className="absolute inset-0"
|
|
464
|
-
style={{
|
|
465
|
-
backgroundImage: `linear-gradient(rgba(0, 217, 255, 0.1) 1px, transparent 1px),
|
|
466
|
-
linear-gradient(90deg, rgba(0, 217, 255, 0.1) 1px, transparent 1px)`,
|
|
467
|
-
backgroundSize: '50px 50px',
|
|
468
|
-
}}
|
|
469
|
-
/>
|
|
470
|
-
</div>
|
|
471
|
-
|
|
472
|
-
<div className="relative z-10 w-full max-w-xl">
|
|
473
|
-
{/* Logo */}
|
|
474
|
-
<div className="flex flex-col items-center mb-8">
|
|
475
|
-
<LogoIcon size={48} withGlow={true} />
|
|
476
|
-
<h1 className="mt-4 text-2xl font-bold text-white">Connect AI Provider</h1>
|
|
477
|
-
<p className="mt-2 text-text-muted text-center">
|
|
478
|
-
Your workspace <span className="text-white">{selectedWorkspace.name}</span> is ready!
|
|
479
|
-
<br />Connect an AI provider to start using agents.
|
|
480
|
-
</p>
|
|
481
|
-
</div>
|
|
482
|
-
|
|
483
|
-
{/* Shared provider connection component */}
|
|
484
|
-
<ProviderConnectionList
|
|
485
|
-
providers={AI_PROVIDERS}
|
|
486
|
-
connectedProviders={connectedProviders}
|
|
487
|
-
workspaceId={selectedWorkspace.id}
|
|
488
|
-
csrfToken={csrfToken || undefined}
|
|
489
|
-
onProviderConnected={handleProviderConnected}
|
|
490
|
-
onContinue={handleSkipProvider}
|
|
491
|
-
showDetailedInfo={true}
|
|
492
|
-
/>
|
|
493
|
-
</div>
|
|
494
|
-
</div>
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Workspace selection / no workspaces UI
|
|
499
|
-
return (
|
|
500
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex flex-col items-center justify-center p-4">
|
|
501
|
-
{/* Background grid */}
|
|
502
|
-
<div className="fixed inset-0 opacity-10 pointer-events-none">
|
|
503
|
-
<div
|
|
504
|
-
className="absolute inset-0"
|
|
505
|
-
style={{
|
|
506
|
-
backgroundImage: `linear-gradient(rgba(0, 217, 255, 0.1) 1px, transparent 1px),
|
|
507
|
-
linear-gradient(90deg, rgba(0, 217, 255, 0.1) 1px, transparent 1px)`,
|
|
508
|
-
backgroundSize: '50px 50px',
|
|
509
|
-
}}
|
|
510
|
-
/>
|
|
511
|
-
</div>
|
|
512
|
-
|
|
513
|
-
<div className="relative z-10 w-full max-w-2xl">
|
|
514
|
-
{/* Logo */}
|
|
515
|
-
<div className="flex flex-col items-center mb-8">
|
|
516
|
-
<LogoIcon size={48} withGlow={true} />
|
|
517
|
-
<h1 className="mt-4 text-2xl font-bold text-white">Agent Relay</h1>
|
|
518
|
-
<p className="mt-2 text-text-muted">
|
|
519
|
-
{state === 'no-workspaces' ? 'Create a workspace to get started' : 'Select a workspace'}
|
|
520
|
-
</p>
|
|
521
|
-
</div>
|
|
522
|
-
|
|
523
|
-
{error && (
|
|
524
|
-
<div className="mb-4 p-4 bg-error/10 border border-error/20 rounded-xl">
|
|
525
|
-
<p className="text-error">{error}</p>
|
|
526
|
-
</div>
|
|
527
|
-
)}
|
|
528
|
-
|
|
529
|
-
{/* Workspaces list */}
|
|
530
|
-
{state === 'select-workspace' && workspaces.length > 0 && (
|
|
531
|
-
<div className="bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-2xl p-6">
|
|
532
|
-
<h2 className="text-lg font-semibold text-white mb-4">Your Workspaces</h2>
|
|
533
|
-
<div className="space-y-3">
|
|
534
|
-
{workspaces.map((workspace) => (
|
|
535
|
-
<div
|
|
536
|
-
key={workspace.id}
|
|
537
|
-
className="flex items-center justify-between p-4 bg-bg-tertiary rounded-xl border border-border-subtle hover:border-accent-cyan/50 transition-colors"
|
|
538
|
-
>
|
|
539
|
-
<div className="flex items-center gap-3">
|
|
540
|
-
<div className={`w-3 h-3 rounded-full ${
|
|
541
|
-
workspace.status === 'running' ? 'bg-success' :
|
|
542
|
-
workspace.status === 'provisioning' ? 'bg-warning animate-pulse' :
|
|
543
|
-
workspace.status === 'error' ? 'bg-error' : 'bg-gray-500'
|
|
544
|
-
}`} />
|
|
545
|
-
<div>
|
|
546
|
-
<h3 className="font-medium text-white">{workspace.name}</h3>
|
|
547
|
-
<p className="text-sm text-text-muted">
|
|
548
|
-
{workspace.status === 'running' ? 'Running' :
|
|
549
|
-
workspace.status === 'provisioning' ? 'Starting...' :
|
|
550
|
-
workspace.status === 'stopped' ? 'Stopped' : 'Error'}
|
|
551
|
-
</p>
|
|
552
|
-
</div>
|
|
553
|
-
</div>
|
|
554
|
-
<div>
|
|
555
|
-
{workspace.status === 'running' && workspace.publicUrl ? (
|
|
556
|
-
<button
|
|
557
|
-
onClick={() => connectToWorkspace(workspace)}
|
|
558
|
-
className="py-2 px-4 bg-gradient-to-r from-accent-cyan to-[#00b8d9] text-bg-deep font-semibold rounded-lg hover:shadow-glow-cyan transition-all"
|
|
559
|
-
>
|
|
560
|
-
Connect
|
|
561
|
-
</button>
|
|
562
|
-
) : workspace.status === 'stopped' ? (
|
|
563
|
-
<button
|
|
564
|
-
onClick={() => handleStartWorkspace(workspace)}
|
|
565
|
-
className="py-2 px-4 bg-bg-card border border-border-subtle rounded-lg text-white hover:border-accent-cyan/50 transition-colors"
|
|
566
|
-
>
|
|
567
|
-
Start
|
|
568
|
-
</button>
|
|
569
|
-
) : workspace.status === 'provisioning' ? (
|
|
570
|
-
<span className="text-text-muted text-sm">Starting...</span>
|
|
571
|
-
) : (
|
|
572
|
-
<span className="text-error text-sm">Failed</span>
|
|
573
|
-
)}
|
|
574
|
-
</div>
|
|
575
|
-
</div>
|
|
576
|
-
))}
|
|
577
|
-
</div>
|
|
578
|
-
|
|
579
|
-
{repos.length > 0 && (
|
|
580
|
-
<div className="mt-6 pt-6 border-t border-border-subtle">
|
|
581
|
-
<p className="text-text-muted text-sm mb-3">Or create a new workspace:</p>
|
|
582
|
-
<div className="flex gap-2 flex-wrap">
|
|
583
|
-
{repos.slice(0, 3).map((repo) => (
|
|
584
|
-
<button
|
|
585
|
-
key={repo.id}
|
|
586
|
-
onClick={() => handleCreateWorkspace(repo.fullName)}
|
|
587
|
-
className="py-2 px-3 bg-bg-card border border-border-subtle rounded-lg text-sm text-text-muted hover:text-white hover:border-accent-cyan/50 transition-colors"
|
|
588
|
-
>
|
|
589
|
-
+ {repo.fullName.split('/')[1]}
|
|
590
|
-
</button>
|
|
591
|
-
))}
|
|
592
|
-
</div>
|
|
593
|
-
</div>
|
|
594
|
-
)}
|
|
595
|
-
</div>
|
|
596
|
-
)}
|
|
597
|
-
|
|
598
|
-
{/* No workspaces - create first one */}
|
|
599
|
-
{state === 'no-workspaces' && (
|
|
600
|
-
<div className="bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-2xl p-6">
|
|
601
|
-
<h2 className="text-lg font-semibold text-white mb-4">Create Your First Workspace</h2>
|
|
602
|
-
<p className="text-text-muted mb-6">
|
|
603
|
-
Select a repository to create a workspace where agents can work on your code.
|
|
604
|
-
</p>
|
|
605
|
-
|
|
606
|
-
{repos.length > 0 ? (
|
|
607
|
-
<div className="space-y-3">
|
|
608
|
-
{repos.map((repo) => (
|
|
609
|
-
<button
|
|
610
|
-
key={repo.id}
|
|
611
|
-
onClick={() => handleCreateWorkspace(repo.fullName)}
|
|
612
|
-
className="w-full flex items-center gap-3 p-4 bg-bg-tertiary rounded-xl border border-border-subtle hover:border-accent-cyan/50 transition-colors text-left"
|
|
613
|
-
>
|
|
614
|
-
<svg className="w-5 h-5 text-text-muted flex-shrink-0" fill="currentColor" viewBox="0 0 16 16">
|
|
615
|
-
<path d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8z" />
|
|
616
|
-
</svg>
|
|
617
|
-
<div className="flex-1 min-w-0">
|
|
618
|
-
<p className="text-white font-medium truncate">{repo.fullName}</p>
|
|
619
|
-
<p className="text-text-muted text-sm">{repo.isPrivate ? 'Private' : 'Public'}</p>
|
|
620
|
-
</div>
|
|
621
|
-
<svg className="w-5 h-5 text-text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
622
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
623
|
-
</svg>
|
|
624
|
-
</button>
|
|
625
|
-
))}
|
|
626
|
-
</div>
|
|
627
|
-
) : (
|
|
628
|
-
<div className="text-center py-8">
|
|
629
|
-
<p className="text-text-muted mb-4">No repositories connected yet.</p>
|
|
630
|
-
<a
|
|
631
|
-
href="/connect-repos"
|
|
632
|
-
className="inline-flex items-center gap-2 py-3 px-6 bg-gradient-to-r from-accent-cyan to-[#00b8d9] text-bg-deep font-semibold rounded-xl hover:shadow-glow-cyan transition-all"
|
|
633
|
-
>
|
|
634
|
-
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
|
635
|
-
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
636
|
-
</svg>
|
|
637
|
-
Connect GitHub
|
|
638
|
-
</a>
|
|
639
|
-
</div>
|
|
640
|
-
)}
|
|
641
|
-
</div>
|
|
642
|
-
)}
|
|
643
|
-
|
|
644
|
-
{/* Navigation */}
|
|
645
|
-
<div className="mt-6 flex justify-center gap-4 text-sm">
|
|
646
|
-
<a href="/connect-repos" className="text-text-muted hover:text-white transition-colors">
|
|
647
|
-
Manage Repositories
|
|
648
|
-
</a>
|
|
649
|
-
<span className="text-text-muted">·</span>
|
|
650
|
-
<button
|
|
651
|
-
onClick={async () => {
|
|
652
|
-
const headers: Record<string, string> = {};
|
|
653
|
-
if (csrfToken) {
|
|
654
|
-
headers['X-CSRF-Token'] = csrfToken;
|
|
655
|
-
}
|
|
656
|
-
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include', headers });
|
|
657
|
-
window.location.href = '/login';
|
|
658
|
-
}}
|
|
659
|
-
className="text-text-muted hover:text-white transition-colors"
|
|
660
|
-
>
|
|
661
|
-
Sign Out
|
|
662
|
-
</button>
|
|
663
|
-
</div>
|
|
664
|
-
</div>
|
|
665
|
-
</div>
|
|
666
|
-
);
|
|
667
|
-
}
|
|
Binary file
|