@agent-relay/dashboard 2.0.81 → 2.0.82

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 (244) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
  3. package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
  4. package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
  5. package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
  6. package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
  7. package/out/about.html +2 -2
  8. package/out/about.txt +1 -1
  9. package/out/app/onboarding.html +1 -1
  10. package/out/app/onboarding.txt +1 -1
  11. package/out/app.html +1 -1
  12. package/out/app.txt +2 -2
  13. package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +2 -2
  14. package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
  15. package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
  16. package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
  17. package/out/blog.html +2 -2
  18. package/out/blog.txt +1 -1
  19. package/out/careers.html +2 -2
  20. package/out/careers.txt +1 -1
  21. package/out/changelog.html +2 -2
  22. package/out/changelog.txt +1 -1
  23. package/out/cloud/link.html +1 -1
  24. package/out/cloud/link.txt +2 -2
  25. package/out/complete-profile.html +2 -2
  26. package/out/complete-profile.txt +1 -1
  27. package/out/connect-repos.html +1 -1
  28. package/out/connect-repos.txt +1 -1
  29. package/out/contact.html +2 -2
  30. package/out/contact.txt +1 -1
  31. package/out/docs.html +2 -2
  32. package/out/docs.txt +1 -1
  33. package/out/history.html +1 -1
  34. package/out/history.txt +2 -2
  35. package/out/index.html +1 -1
  36. package/out/index.txt +2 -2
  37. package/out/login.html +2 -2
  38. package/out/login.txt +1 -1
  39. package/out/metrics.html +1 -1
  40. package/out/metrics.txt +2 -2
  41. package/out/pricing.html +2 -2
  42. package/out/pricing.txt +1 -1
  43. package/out/privacy.html +2 -2
  44. package/out/privacy.txt +1 -1
  45. package/out/providers/setup/claude.html +1 -1
  46. package/out/providers/setup/claude.txt +1 -1
  47. package/out/providers/setup/codex.html +1 -1
  48. package/out/providers/setup/codex.txt +1 -1
  49. package/out/providers/setup/cursor.html +1 -1
  50. package/out/providers/setup/cursor.txt +1 -1
  51. package/out/providers.html +1 -1
  52. package/out/providers.txt +1 -1
  53. package/out/security.html +2 -2
  54. package/out/security.txt +1 -1
  55. package/out/signup.html +2 -2
  56. package/out/signup.txt +1 -1
  57. package/out/terms.html +2 -2
  58. package/out/terms.txt +1 -1
  59. package/package.json +7 -1
  60. package/src/app/about/page.tsx +7 -0
  61. package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
  62. package/src/app/app/[[...slug]]/page.tsx +23 -0
  63. package/src/app/app/onboarding/page.tsx +394 -0
  64. package/src/app/apple-icon.png +0 -0
  65. package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
  66. package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
  67. package/src/app/blog/page.tsx +15 -0
  68. package/src/app/careers/page.tsx +7 -0
  69. package/src/app/changelog/page.tsx +7 -0
  70. package/src/app/cloud/link/page.tsx +464 -0
  71. package/src/app/complete-profile/page.tsx +204 -0
  72. package/src/app/connect-repos/page.tsx +410 -0
  73. package/src/app/contact/page.tsx +7 -0
  74. package/src/app/docs/page.tsx +7 -0
  75. package/src/app/favicon.png +0 -0
  76. package/src/app/globals.css +200 -0
  77. package/src/app/history/page.tsx +658 -0
  78. package/src/app/layout.tsx +25 -0
  79. package/src/app/login/page.tsx +424 -0
  80. package/src/app/metrics/page.tsx +781 -0
  81. package/src/app/page.tsx +59 -0
  82. package/src/app/pricing/page.tsx +7 -0
  83. package/src/app/privacy/page.tsx +7 -0
  84. package/src/app/providers/page.tsx +193 -0
  85. package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
  86. package/src/app/providers/setup/[provider]/constants.ts +35 -0
  87. package/src/app/providers/setup/[provider]/page.tsx +42 -0
  88. package/src/app/security/page.tsx +7 -0
  89. package/src/app/signup/page.tsx +533 -0
  90. package/src/app/terms/page.tsx +7 -0
  91. package/src/components/ActivityFeed.tsx +216 -0
  92. package/src/components/AddWorkspaceModal.tsx +170 -0
  93. package/src/components/AgentCard.test.tsx +134 -0
  94. package/src/components/AgentCard.tsx +585 -0
  95. package/src/components/AgentList.test.tsx +147 -0
  96. package/src/components/AgentList.tsx +419 -0
  97. package/src/components/AgentLogPreview.tsx +173 -0
  98. package/src/components/AgentProfilePanel.tsx +569 -0
  99. package/src/components/App.tsx +3424 -0
  100. package/src/components/BillingPanel.tsx +922 -0
  101. package/src/components/BillingResult.tsx +447 -0
  102. package/src/components/BroadcastComposer.tsx +690 -0
  103. package/src/components/ChannelAdminPanel.tsx +773 -0
  104. package/src/components/ChannelBrowser.tsx +385 -0
  105. package/src/components/ChannelChat.tsx +261 -0
  106. package/src/components/ChannelSidebar.tsx +399 -0
  107. package/src/components/CloudSessionProvider.tsx +130 -0
  108. package/src/components/CommandPalette.tsx +815 -0
  109. package/src/components/ConfirmationDialog.tsx +133 -0
  110. package/src/components/ConversationHistory.tsx +518 -0
  111. package/src/components/CoordinatorPanel.tsx +956 -0
  112. package/src/components/DecisionQueue.tsx +717 -0
  113. package/src/components/DirectMessageView.tsx +164 -0
  114. package/src/components/FileAutocomplete.tsx +368 -0
  115. package/src/components/FleetOverview.tsx +278 -0
  116. package/src/components/LogViewer.tsx +310 -0
  117. package/src/components/LogViewerPanel.tsx +482 -0
  118. package/src/components/Logo.tsx +284 -0
  119. package/src/components/MentionAutocomplete.tsx +384 -0
  120. package/src/components/MessageComposer.tsx +473 -0
  121. package/src/components/MessageList.tsx +725 -0
  122. package/src/components/MessageSenderName.tsx +91 -0
  123. package/src/components/MessageStatusIndicator.tsx +142 -0
  124. package/src/components/NewConversationModal.tsx +400 -0
  125. package/src/components/NotificationToast.tsx +488 -0
  126. package/src/components/OnlineUsersIndicator.tsx +164 -0
  127. package/src/components/Pagination.tsx +124 -0
  128. package/src/components/PricingPlans.tsx +386 -0
  129. package/src/components/ProjectList.tsx +711 -0
  130. package/src/components/ProviderAuthFlow.tsx +343 -0
  131. package/src/components/ProviderConnectionList.tsx +375 -0
  132. package/src/components/ProvisioningProgress.tsx +730 -0
  133. package/src/components/ReactionChips.tsx +70 -0
  134. package/src/components/ReactionPicker.tsx +121 -0
  135. package/src/components/RepoAccessPanel.tsx +787 -0
  136. package/src/components/RepositoriesPanel.tsx +901 -0
  137. package/src/components/ServerCard.tsx +202 -0
  138. package/src/components/SessionExpiredModal.tsx +128 -0
  139. package/src/components/SpawnModal.test.tsx +190 -0
  140. package/src/components/SpawnModal.tsx +1001 -0
  141. package/src/components/TaskAssignmentUI.tsx +375 -0
  142. package/src/components/TerminalProviderSetup.tsx +517 -0
  143. package/src/components/ThemeProvider.tsx +159 -0
  144. package/src/components/ThinkingIndicator.tsx +231 -0
  145. package/src/components/ThreadList.tsx +198 -0
  146. package/src/components/ThreadPanel.tsx +405 -0
  147. package/src/components/TrajectoryViewer.tsx +698 -0
  148. package/src/components/TypingIndicator.tsx +69 -0
  149. package/src/components/UsageBanner.tsx +231 -0
  150. package/src/components/UserProfilePanel.tsx +233 -0
  151. package/src/components/WorkspaceContext.tsx +95 -0
  152. package/src/components/WorkspaceSelector.tsx +234 -0
  153. package/src/components/WorkspaceStatusIndicator.tsx +396 -0
  154. package/src/components/XTermInteractive.tsx +516 -0
  155. package/src/components/XTermLogViewer.tsx +719 -0
  156. package/src/components/channels/ChannelDialogs.tsx +1411 -0
  157. package/src/components/channels/ChannelHeader.tsx +317 -0
  158. package/src/components/channels/ChannelMessageList.tsx +463 -0
  159. package/src/components/channels/ChannelViewV1.tsx +146 -0
  160. package/src/components/channels/MessageInput.tsx +302 -0
  161. package/src/components/channels/SearchInput.tsx +172 -0
  162. package/src/components/channels/SearchResults.tsx +336 -0
  163. package/src/components/channels/api.test.ts +1527 -0
  164. package/src/components/channels/api.ts +703 -0
  165. package/src/components/channels/index.ts +76 -0
  166. package/src/components/channels/mockApi.ts +344 -0
  167. package/src/components/channels/types.ts +566 -0
  168. package/src/components/hooks/index.ts +58 -0
  169. package/src/components/hooks/useAgentLogs.ts +504 -0
  170. package/src/components/hooks/useAgents.ts +127 -0
  171. package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
  172. package/src/components/hooks/useBroadcastDedup.ts +86 -0
  173. package/src/components/hooks/useChannelAdmin.ts +329 -0
  174. package/src/components/hooks/useChannelBrowser.ts +239 -0
  175. package/src/components/hooks/useChannelCommands.ts +138 -0
  176. package/src/components/hooks/useChannels.ts +367 -0
  177. package/src/components/hooks/useDebounce.ts +29 -0
  178. package/src/components/hooks/useDirectMessage.test.ts +952 -0
  179. package/src/components/hooks/useDirectMessage.ts +141 -0
  180. package/src/components/hooks/useMessages.ts +310 -0
  181. package/src/components/hooks/useOrchestrator.test.ts +165 -0
  182. package/src/components/hooks/useOrchestrator.ts +424 -0
  183. package/src/components/hooks/usePinnedAgents.test.ts +356 -0
  184. package/src/components/hooks/usePinnedAgents.ts +140 -0
  185. package/src/components/hooks/usePresence.test.ts +245 -0
  186. package/src/components/hooks/usePresence.ts +377 -0
  187. package/src/components/hooks/useRecentRepos.ts +130 -0
  188. package/src/components/hooks/useSession.ts +209 -0
  189. package/src/components/hooks/useThread.ts +138 -0
  190. package/src/components/hooks/useTrajectory.ts +265 -0
  191. package/src/components/hooks/useWebSocket.ts +290 -0
  192. package/src/components/hooks/useWorkspaceMembers.ts +132 -0
  193. package/src/components/hooks/useWorkspaceRepos.ts +73 -0
  194. package/src/components/hooks/useWorkspaceStatus.ts +237 -0
  195. package/src/components/index.ts +81 -0
  196. package/src/components/layout/Header.tsx +311 -0
  197. package/src/components/layout/RepoContextHeader.tsx +361 -0
  198. package/src/components/layout/Sidebar.archive.test.tsx +126 -0
  199. package/src/components/layout/Sidebar.test.tsx +691 -0
  200. package/src/components/layout/Sidebar.tsx +900 -0
  201. package/src/components/layout/index.ts +7 -0
  202. package/src/components/settings/BillingSettingsPanel.tsx +564 -0
  203. package/src/components/settings/SettingsPage.tsx +683 -0
  204. package/src/components/settings/TeamSettingsPanel.tsx +560 -0
  205. package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
  206. package/src/components/settings/index.ts +11 -0
  207. package/src/components/settings/types.ts +79 -0
  208. package/src/components/utils/messageFormatting.test.tsx +331 -0
  209. package/src/components/utils/messageFormatting.tsx +597 -0
  210. package/src/index.ts +63 -0
  211. package/src/landing/AboutPage.tsx +77 -0
  212. package/src/landing/BlogContent.tsx +187 -0
  213. package/src/landing/BlogPage.tsx +47 -0
  214. package/src/landing/CareersPage.tsx +53 -0
  215. package/src/landing/ChangelogPage.tsx +33 -0
  216. package/src/landing/ContactPage.tsx +41 -0
  217. package/src/landing/DocsPage.tsx +43 -0
  218. package/src/landing/LandingPage.tsx +702 -0
  219. package/src/landing/PricingPage.tsx +549 -0
  220. package/src/landing/PrivacyPage.tsx +117 -0
  221. package/src/landing/SecurityPage.tsx +42 -0
  222. package/src/landing/StaticPage.tsx +165 -0
  223. package/src/landing/TermsPage.tsx +125 -0
  224. package/src/landing/blogData.ts +312 -0
  225. package/src/landing/index.ts +18 -0
  226. package/src/landing/styles.css +3673 -0
  227. package/src/lib/agent-merge.test.ts +43 -0
  228. package/src/lib/agent-merge.ts +35 -0
  229. package/src/lib/api.ts +1294 -0
  230. package/src/lib/cloudApi.ts +893 -0
  231. package/src/lib/colors.test.ts +175 -0
  232. package/src/lib/colors.ts +218 -0
  233. package/src/lib/config.ts +109 -0
  234. package/src/lib/hierarchy.ts +242 -0
  235. package/src/lib/stuckDetection.ts +142 -0
  236. package/src/lib/useUrlRouting.ts +190 -0
  237. package/src/types/index.ts +317 -0
  238. package/src/types/threading.ts +7 -0
  239. package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
  240. package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
  241. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
  242. /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
  243. /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
  244. /package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/{page-a58308f43557b908.js → page-b194f207fbd91862.js} +0 -0
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Provider Auth Flow Component
3
+ *
4
+ * Shared component for AI provider authentication via SSH tunnel.
5
+ * Used by both the onboarding page and workspace settings.
6
+ *
7
+ * Flow:
8
+ * 1. Calls /api/auth/ssh/init to get a CLI command with one-time token
9
+ * 2. User copies and runs the command in their local terminal
10
+ * 3. CLI establishes SSH to workspace and runs the provider's auth command
11
+ * 4. User completes interactive auth (OAuth in browser, etc.)
12
+ * 5. CLI calls /api/auth/ssh/complete to mark provider as connected
13
+ * 6. Dashboard polls /api/auth/ssh/status/:workspaceId to detect completion
14
+ */
15
+
16
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
17
+
18
+ export interface ProviderInfo {
19
+ id: string;
20
+ name: string;
21
+ displayName: string;
22
+ color: string;
23
+ cliCommand?: string;
24
+ /** Whether this provider's OAuth redirects to localhost (shows "site can't be reached") */
25
+ requiresUrlCopy?: boolean;
26
+ }
27
+
28
+ export interface ProviderAuthFlowProps {
29
+ provider: ProviderInfo;
30
+ workspaceId: string;
31
+ csrfToken?: string;
32
+ onSuccess: () => void;
33
+ onCancel: () => void;
34
+ onError: (error: string) => void;
35
+ }
36
+
37
+ type AuthStatus = 'idle' | 'starting' | 'waiting' | 'success' | 'error';
38
+
39
+ /**
40
+ * Map dashboard provider IDs to SSH backend provider names.
41
+ * The SSH status endpoint uses PROVIDER_COMMANDS keys (anthropic, openai, google, etc.)
42
+ * while the dashboard uses its own IDs (anthropic, codex, google, etc.)
43
+ */
44
+ const PROVIDER_STATUS_MAP: Record<string, string> = {
45
+ codex: 'openai',
46
+ };
47
+
48
+ function getStatusProviderName(providerId: string): string {
49
+ return PROVIDER_STATUS_MAP[providerId] || providerId;
50
+ }
51
+
52
+ export function ProviderAuthFlow({
53
+ provider,
54
+ workspaceId,
55
+ csrfToken,
56
+ onSuccess,
57
+ onCancel,
58
+ onError,
59
+ }: ProviderAuthFlowProps) {
60
+ const [status, setStatus] = useState<AuthStatus>('idle');
61
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
62
+ const [cliCommand, setCliCommand] = useState<string | null>(null);
63
+ const [copied, setCopied] = useState(false);
64
+ const pollingRef = useRef(false);
65
+ const completingRef = useRef(false);
66
+
67
+ const backendProviderId = provider.id;
68
+ const statusProviderId = getStatusProviderName(backendProviderId);
69
+
70
+ // Start the SSH auth flow
71
+ const startAuth = useCallback(async () => {
72
+ setStatus('starting');
73
+ setErrorMessage(null);
74
+ completingRef.current = false;
75
+
76
+ try {
77
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
78
+ if (csrfToken) headers['X-CSRF-Token'] = csrfToken;
79
+
80
+ const res = await fetch('/api/auth/ssh/init', {
81
+ method: 'POST',
82
+ credentials: 'include',
83
+ headers,
84
+ body: JSON.stringify({
85
+ provider: backendProviderId,
86
+ workspaceId,
87
+ }),
88
+ });
89
+
90
+ const data = await res.json();
91
+
92
+ if (!res.ok) {
93
+ throw new Error(data.error || 'Failed to start authentication');
94
+ }
95
+
96
+ const rawCommand = data.commandWithUrl || data.command;
97
+ // Prepend npx if not already present
98
+ const commandWithNpx = rawCommand && !rawCommand.trim().startsWith('npx ')
99
+ ? `npx ${rawCommand}`
100
+ : rawCommand;
101
+ setCliCommand(commandWithNpx);
102
+ setStatus('waiting');
103
+
104
+ // Start polling for completion
105
+ if (data.workspaceId) {
106
+ startPolling(data.workspaceId);
107
+ }
108
+ } catch (err) {
109
+ const msg = err instanceof Error ? err.message : 'Failed to start authentication';
110
+ setErrorMessage(msg);
111
+ setStatus('error');
112
+ onError(msg);
113
+ }
114
+ }, [backendProviderId, workspaceId, csrfToken, onError]);
115
+
116
+ // Poll SSH auth status endpoint to detect when provider is connected
117
+ const startPolling = useCallback((wsId: string) => {
118
+ if (pollingRef.current) return;
119
+ pollingRef.current = true;
120
+
121
+ const maxAttempts = 120; // 10 minutes at 5s intervals
122
+ let attempts = 0;
123
+
124
+ const poll = async () => {
125
+ if (attempts >= maxAttempts || !pollingRef.current) {
126
+ pollingRef.current = false;
127
+ if (attempts >= maxAttempts) {
128
+ setErrorMessage('Authentication timed out. Please try again.');
129
+ setStatus('error');
130
+ onError('Authentication timed out');
131
+ }
132
+ return;
133
+ }
134
+
135
+ try {
136
+ const res = await fetch(`/api/auth/ssh/status/${wsId}`, {
137
+ credentials: 'include',
138
+ });
139
+
140
+ if (res.ok) {
141
+ const data = await res.json() as {
142
+ providers: Array<{ name: string; status: string }>;
143
+ };
144
+
145
+ const providerStatus = data.providers?.find(
146
+ (p) => p.name === statusProviderId
147
+ );
148
+
149
+ if (providerStatus?.status === 'connected') {
150
+ pollingRef.current = false;
151
+ if (!completingRef.current) {
152
+ completingRef.current = true;
153
+ setStatus('success');
154
+ setTimeout(() => onSuccess(), 1500);
155
+ }
156
+ return;
157
+ }
158
+ }
159
+
160
+ attempts++;
161
+ setTimeout(poll, 5000);
162
+ } catch (err) {
163
+ console.error('Poll error:', err);
164
+ attempts++;
165
+ setTimeout(poll, 5000);
166
+ }
167
+ };
168
+
169
+ poll();
170
+ }, [statusProviderId, onError, onSuccess]);
171
+
172
+ // Cancel auth flow
173
+ const handleCancel = useCallback(() => {
174
+ pollingRef.current = false;
175
+ setStatus('idle');
176
+ setCliCommand(null);
177
+ setErrorMessage(null);
178
+ setCopied(false);
179
+ onCancel();
180
+ }, [onCancel]);
181
+
182
+ // Copy command to clipboard
183
+ const handleCopy = useCallback(() => {
184
+ if (cliCommand) {
185
+ navigator.clipboard.writeText(cliCommand);
186
+ setCopied(true);
187
+ setTimeout(() => setCopied(false), 2000);
188
+ }
189
+ }, [cliCommand]);
190
+
191
+ // Start auth when component mounts
192
+ useEffect(() => {
193
+ if (status === 'idle') {
194
+ startAuth();
195
+ }
196
+ }, [startAuth, status]);
197
+
198
+ // Clean up polling only on unmount (not on status changes, which would kill active polling)
199
+ useEffect(() => {
200
+ return () => {
201
+ pollingRef.current = false;
202
+ };
203
+ }, []);
204
+
205
+ return (
206
+ <div className="space-y-4">
207
+ {/* Header */}
208
+ <div className="flex items-center gap-3">
209
+ <div
210
+ className="w-10 h-10 rounded-lg flex items-center justify-center text-white font-bold"
211
+ style={{ backgroundColor: provider.color }}
212
+ >
213
+ {provider.displayName[0]}
214
+ </div>
215
+ <div>
216
+ <h3 className="font-medium text-white">{provider.displayName}</h3>
217
+ <p className="text-sm text-text-muted">
218
+ {status === 'starting' && 'Starting authentication...'}
219
+ {status === 'waiting' && 'Complete authentication below'}
220
+ {status === 'success' && 'Connected!'}
221
+ {status === 'error' && (errorMessage || 'Authentication failed')}
222
+ </p>
223
+ </div>
224
+ </div>
225
+
226
+ {/* Starting state */}
227
+ {status === 'starting' && (
228
+ <div className="flex items-center justify-center gap-3 py-4">
229
+ <svg className="w-5 h-5 text-accent-cyan animate-spin" fill="none" viewBox="0 0 24 24">
230
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
231
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
232
+ </svg>
233
+ <span className="text-text-muted">Preparing authentication...</span>
234
+ </div>
235
+ )}
236
+
237
+ {/* Waiting state - SSH CLI flow */}
238
+ {status === 'waiting' && cliCommand && (
239
+ <div className="space-y-4">
240
+ {/* Step 1: Copy and run the command */}
241
+ <div className="p-3 bg-accent-cyan/10 border border-accent-cyan/30 rounded-lg">
242
+ <p className="text-sm text-accent-cyan mb-2">
243
+ <strong>Step 1:</strong> Copy and run this command in your terminal
244
+ </p>
245
+ <div className="flex items-center gap-2">
246
+ <code className="flex-1 px-3 py-2 bg-bg-deep rounded-lg text-xs font-mono text-white overflow-x-auto">
247
+ {cliCommand}
248
+ </code>
249
+ <button
250
+ onClick={handleCopy}
251
+ className="px-3 py-2 bg-bg-tertiary border border-border-subtle rounded-lg text-text-muted hover:text-white hover:border-accent-cyan/50 transition-colors text-xs whitespace-nowrap"
252
+ >
253
+ {copied ? 'Copied!' : 'Copy'}
254
+ </button>
255
+ </div>
256
+ </div>
257
+
258
+ {/* Step 2: Accept SSH warnings */}
259
+ <div className="p-3 bg-bg-tertiary border border-border-subtle rounded-lg">
260
+ <p className="text-sm text-white mb-1">
261
+ <strong>Step 2:</strong> Accept any SSH host key warnings
262
+ </p>
263
+ <p className="text-xs text-text-muted">
264
+ If prompted with &quot;Are you sure you want to continue connecting?&quot;, type <code className="px-1 py-0.5 bg-bg-deep rounded text-accent-cyan">yes</code> and press Enter.
265
+ </p>
266
+ </div>
267
+
268
+ {/* Step 3: Complete sign-in */}
269
+ <div className="p-3 bg-bg-tertiary border border-border-subtle rounded-lg">
270
+ <p className="text-sm text-white mb-1">
271
+ <strong>Step 3:</strong> Complete the sign-in
272
+ </p>
273
+ <p className="text-xs text-text-muted">
274
+ A browser window will open for {provider.displayName} authentication. Sign in with your account and authorize access.
275
+ </p>
276
+ </div>
277
+
278
+ {/* Step 4: Wait for the input prompt, then exit */}
279
+ <div className="p-3 bg-amber-500/10 border border-amber-500/30 rounded-lg">
280
+ <p className="text-sm text-amber-400 mb-1">
281
+ <strong>Step 4:</strong> Wait for the {provider.displayName} input prompt, then type <code className="px-1 py-0.5 bg-bg-deep rounded">exit</code>
282
+ </p>
283
+ <p className="text-xs text-amber-400/80">
284
+ Do NOT close the terminal early. After sign-in completes, wait until you see the {provider.displayName} input screen (e.g. the <code className="px-1 py-0.5 bg-bg-deep rounded">&gt;</code> prompt). Then type <code className="px-1 py-0.5 bg-bg-deep rounded">exit</code> and press Enter. This page will update automatically.
285
+ </p>
286
+ </div>
287
+
288
+ {/* Polling indicator */}
289
+ <div className="flex items-center gap-2 p-3 bg-success/10 border border-success/30 rounded-lg text-sm text-success">
290
+ <svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
291
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
292
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
293
+ </svg>
294
+ <span>Waiting for authentication to complete...</span>
295
+ </div>
296
+
297
+ {/* Cancel button */}
298
+ <button
299
+ onClick={handleCancel}
300
+ className="w-full py-2 text-text-muted hover:text-white transition-colors text-sm"
301
+ >
302
+ Cancel
303
+ </button>
304
+ </div>
305
+ )}
306
+
307
+ {/* Success state */}
308
+ {status === 'success' && (
309
+ <div className="flex items-center justify-center gap-3 py-4">
310
+ <div className="w-10 h-10 bg-success/20 rounded-full flex items-center justify-center">
311
+ <svg className="w-6 h-6 text-success" fill="none" viewBox="0 0 24 24" stroke="currentColor">
312
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
313
+ </svg>
314
+ </div>
315
+ <span className="text-white font-medium">{provider.displayName} connected!</span>
316
+ </div>
317
+ )}
318
+
319
+ {/* Error state */}
320
+ {status === 'error' && (
321
+ <div className="space-y-3">
322
+ <div className="p-4 bg-error/10 border border-error/30 rounded-lg text-error text-sm">
323
+ {errorMessage || 'Authentication failed. Please try again.'}
324
+ </div>
325
+ <div className="flex gap-3">
326
+ <button
327
+ onClick={startAuth}
328
+ className="flex-1 py-2 px-4 bg-bg-tertiary border border-border-subtle text-white rounded-lg hover:border-accent-cyan/50 transition-colors"
329
+ >
330
+ Try Again
331
+ </button>
332
+ <button
333
+ onClick={handleCancel}
334
+ className="py-2 px-4 text-text-muted hover:text-white transition-colors"
335
+ >
336
+ Cancel
337
+ </button>
338
+ </div>
339
+ </div>
340
+ )}
341
+ </div>
342
+ );
343
+ }