@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.
- package/out/404.html +1 -1
- package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
- package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
- package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
- package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
- package/out/about.html +2 -2
- package/out/about.txt +1 -1
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +1 -1
- package/out/app.html +1 -1
- package/out/app.txt +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
- package/out/blog.html +2 -2
- package/out/blog.txt +1 -1
- package/out/careers.html +2 -2
- package/out/careers.txt +1 -1
- package/out/changelog.html +2 -2
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +2 -2
- package/out/complete-profile.html +2 -2
- package/out/complete-profile.txt +1 -1
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +1 -1
- package/out/contact.html +2 -2
- package/out/contact.txt +1 -1
- package/out/docs.html +2 -2
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +2 -2
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +2 -2
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +2 -2
- package/out/pricing.txt +1 -1
- package/out/privacy.html +2 -2
- package/out/privacy.txt +1 -1
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +1 -1
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +1 -1
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +1 -1
- package/out/providers.html +1 -1
- package/out/providers.txt +1 -1
- package/out/security.html +2 -2
- package/out/security.txt +1 -1
- package/out/signup.html +2 -2
- package/out/signup.txt +1 -1
- package/out/terms.html +2 -2
- package/out/terms.txt +1 -1
- package/package.json +7 -1
- package/src/app/about/page.tsx +7 -0
- package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
- package/src/app/app/[[...slug]]/page.tsx +23 -0
- package/src/app/app/onboarding/page.tsx +394 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
- package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
- package/src/app/blog/page.tsx +15 -0
- package/src/app/careers/page.tsx +7 -0
- package/src/app/changelog/page.tsx +7 -0
- package/src/app/cloud/link/page.tsx +464 -0
- package/src/app/complete-profile/page.tsx +204 -0
- package/src/app/connect-repos/page.tsx +410 -0
- package/src/app/contact/page.tsx +7 -0
- package/src/app/docs/page.tsx +7 -0
- package/src/app/favicon.png +0 -0
- package/src/app/globals.css +200 -0
- package/src/app/history/page.tsx +658 -0
- package/src/app/layout.tsx +25 -0
- package/src/app/login/page.tsx +424 -0
- package/src/app/metrics/page.tsx +781 -0
- package/src/app/page.tsx +59 -0
- package/src/app/pricing/page.tsx +7 -0
- package/src/app/privacy/page.tsx +7 -0
- package/src/app/providers/page.tsx +193 -0
- package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
- package/src/app/providers/setup/[provider]/constants.ts +35 -0
- package/src/app/providers/setup/[provider]/page.tsx +42 -0
- package/src/app/security/page.tsx +7 -0
- package/src/app/signup/page.tsx +533 -0
- package/src/app/terms/page.tsx +7 -0
- package/src/components/ActivityFeed.tsx +216 -0
- package/src/components/AddWorkspaceModal.tsx +170 -0
- package/src/components/AgentCard.test.tsx +134 -0
- package/src/components/AgentCard.tsx +585 -0
- package/src/components/AgentList.test.tsx +147 -0
- package/src/components/AgentList.tsx +419 -0
- package/src/components/AgentLogPreview.tsx +173 -0
- package/src/components/AgentProfilePanel.tsx +569 -0
- package/src/components/App.tsx +3424 -0
- package/src/components/BillingPanel.tsx +922 -0
- package/src/components/BillingResult.tsx +447 -0
- package/src/components/BroadcastComposer.tsx +690 -0
- package/src/components/ChannelAdminPanel.tsx +773 -0
- package/src/components/ChannelBrowser.tsx +385 -0
- package/src/components/ChannelChat.tsx +261 -0
- package/src/components/ChannelSidebar.tsx +399 -0
- package/src/components/CloudSessionProvider.tsx +130 -0
- package/src/components/CommandPalette.tsx +815 -0
- package/src/components/ConfirmationDialog.tsx +133 -0
- package/src/components/ConversationHistory.tsx +518 -0
- package/src/components/CoordinatorPanel.tsx +956 -0
- package/src/components/DecisionQueue.tsx +717 -0
- package/src/components/DirectMessageView.tsx +164 -0
- package/src/components/FileAutocomplete.tsx +368 -0
- package/src/components/FleetOverview.tsx +278 -0
- package/src/components/LogViewer.tsx +310 -0
- package/src/components/LogViewerPanel.tsx +482 -0
- package/src/components/Logo.tsx +284 -0
- package/src/components/MentionAutocomplete.tsx +384 -0
- package/src/components/MessageComposer.tsx +473 -0
- package/src/components/MessageList.tsx +725 -0
- package/src/components/MessageSenderName.tsx +91 -0
- package/src/components/MessageStatusIndicator.tsx +142 -0
- package/src/components/NewConversationModal.tsx +400 -0
- package/src/components/NotificationToast.tsx +488 -0
- package/src/components/OnlineUsersIndicator.tsx +164 -0
- package/src/components/Pagination.tsx +124 -0
- package/src/components/PricingPlans.tsx +386 -0
- package/src/components/ProjectList.tsx +711 -0
- package/src/components/ProviderAuthFlow.tsx +343 -0
- package/src/components/ProviderConnectionList.tsx +375 -0
- package/src/components/ProvisioningProgress.tsx +730 -0
- package/src/components/ReactionChips.tsx +70 -0
- package/src/components/ReactionPicker.tsx +121 -0
- package/src/components/RepoAccessPanel.tsx +787 -0
- package/src/components/RepositoriesPanel.tsx +901 -0
- package/src/components/ServerCard.tsx +202 -0
- package/src/components/SessionExpiredModal.tsx +128 -0
- package/src/components/SpawnModal.test.tsx +190 -0
- package/src/components/SpawnModal.tsx +1001 -0
- package/src/components/TaskAssignmentUI.tsx +375 -0
- package/src/components/TerminalProviderSetup.tsx +517 -0
- package/src/components/ThemeProvider.tsx +159 -0
- package/src/components/ThinkingIndicator.tsx +231 -0
- package/src/components/ThreadList.tsx +198 -0
- package/src/components/ThreadPanel.tsx +405 -0
- package/src/components/TrajectoryViewer.tsx +698 -0
- package/src/components/TypingIndicator.tsx +69 -0
- package/src/components/UsageBanner.tsx +231 -0
- package/src/components/UserProfilePanel.tsx +233 -0
- package/src/components/WorkspaceContext.tsx +95 -0
- package/src/components/WorkspaceSelector.tsx +234 -0
- package/src/components/WorkspaceStatusIndicator.tsx +396 -0
- package/src/components/XTermInteractive.tsx +516 -0
- package/src/components/XTermLogViewer.tsx +719 -0
- package/src/components/channels/ChannelDialogs.tsx +1411 -0
- package/src/components/channels/ChannelHeader.tsx +317 -0
- package/src/components/channels/ChannelMessageList.tsx +463 -0
- package/src/components/channels/ChannelViewV1.tsx +146 -0
- package/src/components/channels/MessageInput.tsx +302 -0
- package/src/components/channels/SearchInput.tsx +172 -0
- package/src/components/channels/SearchResults.tsx +336 -0
- package/src/components/channels/api.test.ts +1527 -0
- package/src/components/channels/api.ts +703 -0
- package/src/components/channels/index.ts +76 -0
- package/src/components/channels/mockApi.ts +344 -0
- package/src/components/channels/types.ts +566 -0
- package/src/components/hooks/index.ts +58 -0
- package/src/components/hooks/useAgentLogs.ts +504 -0
- package/src/components/hooks/useAgents.ts +127 -0
- package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
- package/src/components/hooks/useBroadcastDedup.ts +86 -0
- package/src/components/hooks/useChannelAdmin.ts +329 -0
- package/src/components/hooks/useChannelBrowser.ts +239 -0
- package/src/components/hooks/useChannelCommands.ts +138 -0
- package/src/components/hooks/useChannels.ts +367 -0
- package/src/components/hooks/useDebounce.ts +29 -0
- package/src/components/hooks/useDirectMessage.test.ts +952 -0
- package/src/components/hooks/useDirectMessage.ts +141 -0
- package/src/components/hooks/useMessages.ts +310 -0
- package/src/components/hooks/useOrchestrator.test.ts +165 -0
- package/src/components/hooks/useOrchestrator.ts +424 -0
- package/src/components/hooks/usePinnedAgents.test.ts +356 -0
- package/src/components/hooks/usePinnedAgents.ts +140 -0
- package/src/components/hooks/usePresence.test.ts +245 -0
- package/src/components/hooks/usePresence.ts +377 -0
- package/src/components/hooks/useRecentRepos.ts +130 -0
- package/src/components/hooks/useSession.ts +209 -0
- package/src/components/hooks/useThread.ts +138 -0
- package/src/components/hooks/useTrajectory.ts +265 -0
- package/src/components/hooks/useWebSocket.ts +290 -0
- package/src/components/hooks/useWorkspaceMembers.ts +132 -0
- package/src/components/hooks/useWorkspaceRepos.ts +73 -0
- package/src/components/hooks/useWorkspaceStatus.ts +237 -0
- package/src/components/index.ts +81 -0
- package/src/components/layout/Header.tsx +311 -0
- package/src/components/layout/RepoContextHeader.tsx +361 -0
- package/src/components/layout/Sidebar.archive.test.tsx +126 -0
- package/src/components/layout/Sidebar.test.tsx +691 -0
- package/src/components/layout/Sidebar.tsx +900 -0
- package/src/components/layout/index.ts +7 -0
- package/src/components/settings/BillingSettingsPanel.tsx +564 -0
- package/src/components/settings/SettingsPage.tsx +683 -0
- package/src/components/settings/TeamSettingsPanel.tsx +560 -0
- package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
- package/src/components/settings/index.ts +11 -0
- package/src/components/settings/types.ts +79 -0
- package/src/components/utils/messageFormatting.test.tsx +331 -0
- package/src/components/utils/messageFormatting.tsx +597 -0
- package/src/index.ts +63 -0
- package/src/landing/AboutPage.tsx +77 -0
- package/src/landing/BlogContent.tsx +187 -0
- package/src/landing/BlogPage.tsx +47 -0
- package/src/landing/CareersPage.tsx +53 -0
- package/src/landing/ChangelogPage.tsx +33 -0
- package/src/landing/ContactPage.tsx +41 -0
- package/src/landing/DocsPage.tsx +43 -0
- package/src/landing/LandingPage.tsx +702 -0
- package/src/landing/PricingPage.tsx +549 -0
- package/src/landing/PrivacyPage.tsx +117 -0
- package/src/landing/SecurityPage.tsx +42 -0
- package/src/landing/StaticPage.tsx +165 -0
- package/src/landing/TermsPage.tsx +125 -0
- package/src/landing/blogData.ts +312 -0
- package/src/landing/index.ts +18 -0
- package/src/landing/styles.css +3673 -0
- package/src/lib/agent-merge.test.ts +43 -0
- package/src/lib/agent-merge.ts +35 -0
- package/src/lib/api.ts +1294 -0
- package/src/lib/cloudApi.ts +893 -0
- package/src/lib/colors.test.ts +175 -0
- package/src/lib/colors.ts +218 -0
- package/src/lib/config.ts +109 -0
- package/src/lib/hierarchy.ts +242 -0
- package/src/lib/stuckDetection.ts +142 -0
- package/src/lib/useUrlRouting.ts +190 -0
- package/src/types/index.ts +317 -0
- package/src/types/threading.ts +7 -0
- package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
- package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
- /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
- /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
- /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
- /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 "Are you sure you want to continue connecting?", 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">></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
|
+
}
|