@agent-relay/dashboard 2.0.82 → 2.0.84
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/1028-da5d75e35d1420f1.js +1 -0
- package/out/_next/static/chunks/1528-78b17000a7e10bc6.js +2 -0
- package/out/_next/static/chunks/1695-4a5d33ba715e09b4.js +1 -0
- package/out/_next/static/chunks/1705-36c2180d00a4a569.js +1 -0
- package/out/_next/static/chunks/1dd3208c-e1f87c7b3dc1a820.js +1 -0
- package/out/_next/static/chunks/3663-47290254b8f6f5dd.js +1 -0
- package/out/_next/static/chunks/3677-4b225baf4801d9b9.js +73 -0
- package/out/_next/static/chunks/5118-7e8ada2df38eef07.js +1 -0
- package/out/_next/static/chunks/5888-15cbe97c90ed5fae.js +1 -0
- package/out/_next/static/chunks/6773-a45343a98df3abb5.js +1 -0
- package/out/_next/static/chunks/6940-b824612b605e79b3.js +9 -0
- package/out/_next/static/chunks/7894-f4a15249082a680d.js +1 -0
- package/out/_next/static/chunks/9175-b3617c1e5cbfed0e.js +1 -0
- package/out/_next/static/chunks/9372-1a804b8d08c7a236.js +1 -0
- package/out/_next/static/chunks/{ab6c8a12-0a58072fbb505134.js → ab6c8a12-91438a812d94ecf0.js} +1 -1
- package/out/_next/static/chunks/app/_not-found/page-8e8842f82d204726.js +1 -0
- package/out/_next/static/chunks/app/about/page-b78577a7da8fa459.js +1 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/page-3dffd65b6344f53e.js +1 -0
- package/out/_next/static/chunks/app/app/onboarding/page-b89be9aa6264a5e1.js +1 -0
- package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-fbd00893ef69e499.js +1 -0
- package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-de2ea13649d0b6d3.js +1 -0
- package/out/_next/static/chunks/app/blog/page-a08e263c57a156fa.js +1 -0
- package/out/_next/static/chunks/app/careers/page-02228e1d6969b232.js +1 -0
- package/out/_next/static/chunks/app/changelog/page-1b5c1d79efc6e53a.js +1 -0
- package/out/_next/static/chunks/app/cloud/link/page-99654edffffb3af2.js +1 -0
- package/out/_next/static/chunks/app/complete-profile/page-59d146e5ddeafc5c.js +1 -0
- package/out/_next/static/chunks/app/connect-repos/page-995e16a976a6632c.js +1 -0
- package/out/_next/static/chunks/app/contact/page-273396a5ad57bcee.js +1 -0
- package/out/_next/static/chunks/app/dev/cli-tools/page-a71b80dcb2d5fc8d.js +1 -0
- package/out/_next/static/chunks/app/dev/log-viewer/page-46a6151ae1be0796.js +1 -0
- package/out/_next/static/chunks/app/docs/page-7c7cb603b24b7c40.js +1 -0
- package/out/_next/static/chunks/app/history/page-0c5cab1dab4e8886.js +1 -0
- package/out/_next/static/chunks/app/layout-96d72ba8ef8a43a0.js +1 -0
- package/out/_next/static/chunks/app/login/page-0ccbab34213df842.js +1 -0
- package/out/_next/static/chunks/app/metrics/page-8616272aeab9c8b0.js +1 -0
- package/out/_next/static/chunks/app/page-09ce10603ad9a251.js +1 -0
- package/out/_next/static/chunks/app/pricing/page-91c975079120c941.js +1 -0
- package/out/_next/static/chunks/app/privacy/{page-c21d51ac2dee3a88.js → page-a49ab271cc686644.js} +1 -1
- package/out/_next/static/chunks/app/providers/{page-59114505f4353512.js → page-d775d6eb5bc29e96.js} +1 -1
- package/out/_next/static/chunks/app/providers/setup/[provider]/page-ec4ef3cd80de807e.js +1 -0
- package/out/_next/static/chunks/app/security/page-d9da9bd9191e8f95.js +1 -0
- package/out/_next/static/chunks/app/signup/page-930eca0bf5fd299d.js +1 -0
- package/out/_next/static/chunks/app/terms/page-3e4827620b98613c.js +1 -0
- package/out/_next/static/chunks/framework-648e1ae7da590300.js +1 -0
- package/out/_next/static/chunks/{main-acb1b24265295d6a.js → main-2b1990080c292d92.js} +1 -1
- package/out/_next/static/chunks/main-app-9f6b7ff9e754a8f5.js +1 -0
- package/out/_next/static/chunks/pages/_app-a077b72e02273ab1.js +1 -0
- package/out/_next/static/chunks/pages/_error-84001666436a04e4.js +1 -0
- package/out/_next/static/chunks/{webpack-dd93b81e2659669c.js → webpack-7586035f1585f2db.js} +1 -1
- package/out/_next/static/css/eb9fc69d1e3d2bed.css +1 -0
- package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_buildManifest.js +1 -1
- package/out/about.html +2 -2
- package/out/about.txt +2 -2
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +2 -2
- 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 +3 -3
- 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 +2 -2
- package/out/changelog.html +2 -2
- package/out/changelog.txt +2 -2
- 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 +2 -2
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +2 -2
- package/out/contact.html +2 -2
- package/out/contact.txt +2 -2
- package/out/dev/cli-tools.html +1 -0
- package/out/dev/cli-tools.txt +7 -0
- package/out/dev/log-viewer.html +23 -0
- package/out/dev/log-viewer.txt +7 -0
- package/out/docs.html +2 -2
- package/out/docs.txt +2 -2
- 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 +2 -2
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +2 -2
- package/out/pricing.txt +2 -2
- package/out/privacy.html +2 -2
- package/out/privacy.txt +2 -2
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +2 -2
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +2 -2
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +2 -2
- package/out/providers.html +1 -1
- package/out/providers.txt +2 -2
- package/out/security.html +2 -2
- package/out/security.txt +2 -2
- package/out/signup.html +2 -2
- package/out/signup.txt +2 -2
- package/out/terms.html +2 -2
- package/out/terms.txt +2 -2
- package/package.json +5 -1
- package/src/adapters/DashboardConfigProvider.tsx +56 -0
- package/src/adapters/cloudFetchAdapter.ts +278 -0
- package/src/adapters/index.ts +3 -0
- package/src/adapters/types.ts +508 -0
- package/src/app/app/[[...slug]]/DashboardPageClient.tsx +67 -18
- package/src/app/app/onboarding/page.tsx +870 -170
- package/src/app/cloud/link/page.tsx +14 -6
- package/src/app/connect-repos/page.tsx +9 -3
- package/src/app/dev/cli-tools/page.tsx +130 -0
- package/src/app/dev/log-viewer/MockLogViewer.tsx +132 -0
- package/src/app/dev/log-viewer/fixtures.ts +110 -0
- package/src/app/dev/log-viewer/page.tsx +288 -0
- package/src/app/history/page.tsx +28 -12
- package/src/app/page.tsx +1 -1
- package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +209 -59
- package/src/components/AgentCard.tsx +4 -4
- package/src/components/AgentLogPreview.tsx +2 -38
- package/src/components/App.tsx +441 -2624
- package/src/components/CliToolHarness.test.tsx +83 -0
- package/src/components/CliToolHarness.tsx +292 -0
- package/src/components/CoordinatorPanel.tsx +13 -6
- package/src/components/LogViewer.tsx +2 -42
- package/src/components/ProviderAuthFlow.tsx +201 -81
- package/src/components/ProvisioningProgress.tsx +1 -1
- package/src/components/ReactionChips.tsx +2 -1
- package/src/components/SpawnModal.test.tsx +51 -18
- package/src/components/SpawnModal.tsx +175 -207
- package/src/components/TerminalProviderSetup.tsx +1 -1
- package/src/components/ThreadPanel.tsx +2 -0
- package/src/components/WorkspaceContext.tsx +7 -19
- package/src/components/XTermLogViewer.tsx +190 -27
- package/src/components/channels/ChannelMessageList.tsx +94 -4
- package/src/components/channels/ChannelViewV1.tsx +35 -11
- package/src/components/channels/api.ts +21 -20
- package/src/components/channels/types.ts +16 -0
- package/src/components/hooks/index.ts +0 -19
- package/src/components/hooks/useMessages.test.ts +80 -0
- package/src/components/hooks/useMessages.ts +13 -4
- package/src/components/hooks/useOrchestrator.ts +1 -1
- package/src/components/hooks/usePresence.ts +45 -6
- package/src/components/hooks/useThread.ts +83 -46
- package/src/components/hooks/useTrajectory.ts +62 -5
- package/src/components/hooks/useWebSocket.test.ts +358 -0
- package/src/components/hooks/useWebSocket.ts +243 -5
- package/src/components/index.ts +2 -14
- package/src/components/layout/Header.tsx +9 -15
- package/src/components/layout/Sidebar.tsx +1 -8
- package/src/components/settings/SettingsPage.tsx +108 -47
- package/src/components/settings/index.ts +0 -3
- package/src/landing/blogData.ts +1 -1
- package/src/lib/agent-merge.test.ts +2 -2
- package/src/lib/api.ts +8 -38
- package/src/lib/identity.test.ts +139 -0
- package/src/lib/identity.ts +48 -0
- package/src/lib/relaycastMessageAdapters.test.ts +182 -0
- package/src/lib/relaycastMessageAdapters.ts +105 -0
- package/src/lib/sanitize-logs.test.ts +227 -0
- package/src/lib/sanitize-logs.ts +202 -0
- package/src/providers/AgentProvider.tsx +799 -0
- package/src/providers/ChannelProvider.tsx +528 -0
- package/src/providers/CloudWorkspaceProvider.tsx +402 -0
- package/src/providers/MessageProvider.tsx +875 -0
- package/src/providers/RelayConfigProvider.tsx +94 -0
- package/src/providers/SendProvider.tsx +497 -0
- package/src/providers/SettingsProvider.tsx +247 -0
- package/src/providers/index.ts +26 -0
- package/src/types/index.ts +10 -10
- package/out/_next/static/chunks/11-9a2993a37266dcb3.js +0 -9
- package/out/_next/static/chunks/118-ae2b650136a5a5fc.js +0 -1
- package/out/_next/static/chunks/1dd3208c-40ab0fc0f60392b8.js +0 -1
- package/out/_next/static/chunks/202-fc0763dd7488e58f.js +0 -1
- package/out/_next/static/chunks/259-83b77fa1b91ba5aa.js +0 -1
- package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +0 -1
- package/out/_next/static/chunks/528-f5f676996d613c25.js +0 -2
- package/out/_next/static/chunks/663-ddb04081febc3678.js +0 -1
- package/out/_next/static/chunks/687-88b6b139a6bb0e2e.js +0 -1
- package/out/_next/static/chunks/695-51d25b1988644374.js +0 -1
- package/out/_next/static/chunks/773-54a2641043c81e55.js +0 -1
- package/out/_next/static/chunks/app/_not-found/page-6da9b72091e5b511.js +0 -1
- package/out/_next/static/chunks/app/about/page-fff7c6457683f243.js +0 -1
- package/out/_next/static/chunks/app/app/[[...slug]]/page-f7eca1b66fb4249b.js +0 -1
- package/out/_next/static/chunks/app/app/onboarding/page-129abc5da2e67971.js +0 -1
- package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-5d5f28fd126b692f.js +0 -1
- package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-b194f207fbd91862.js +0 -1
- package/out/_next/static/chunks/app/blog/page-b9bd9d8703fca76a.js +0 -1
- package/out/_next/static/chunks/app/careers/page-a4bd8d5f4de8f4eb.js +0 -1
- package/out/_next/static/chunks/app/changelog/page-9a1f6ad1743d63c5.js +0 -1
- package/out/_next/static/chunks/app/cloud/link/page-0844c5699b027c3b.js +0 -1
- package/out/_next/static/chunks/app/complete-profile/page-39ed5a67916beb87.js +0 -1
- package/out/_next/static/chunks/app/connect-repos/page-297eddee0c39f2a3.js +0 -1
- package/out/_next/static/chunks/app/contact/page-3c1dd8690217fade.js +0 -1
- package/out/_next/static/chunks/app/docs/page-1875e981f2c3fd13.js +0 -1
- package/out/_next/static/chunks/app/history/page-2d5c5695c9e8b40c.js +0 -1
- package/out/_next/static/chunks/app/layout-0a4b99656da25511.js +0 -1
- package/out/_next/static/chunks/app/login/page-f69c076f5a6fc520.js +0 -1
- package/out/_next/static/chunks/app/metrics/page-bebbee055669a17e.js +0 -1
- package/out/_next/static/chunks/app/page-0ee604f7070d14c0.js +0 -1
- package/out/_next/static/chunks/app/pricing/page-eeae7d594af333b6.js +0 -1
- package/out/_next/static/chunks/app/providers/setup/[provider]/page-daf9b3e05e77ae19.js +0 -1
- package/out/_next/static/chunks/app/security/page-cd562730fe84a0a2.js +0 -1
- package/out/_next/static/chunks/app/signup/page-c242ca08101a84ff.js +0 -1
- package/out/_next/static/chunks/app/terms/page-c7001720e7941dc6.js +0 -1
- package/out/_next/static/chunks/framework-3664cab31236a9fa.js +0 -1
- package/out/_next/static/chunks/main-app-7f73a939a312a228.js +0 -1
- package/out/_next/static/chunks/pages/_app-10a93ab5b7c32eb3.js +0 -1
- package/out/_next/static/chunks/pages/_error-2d792b2a41857be4.js +0 -1
- package/out/_next/static/css/8968d98ed4c4d33f.css +0 -1
- package/src/components/BillingResult.tsx +0 -447
- package/src/components/CloudSessionProvider.tsx +0 -130
- package/src/components/SessionExpiredModal.tsx +0 -128
- package/src/components/WorkspaceStatusIndicator.tsx +0 -396
- package/src/components/hooks/useSession.ts +0 -209
- package/src/components/hooks/useWorkspaceMembers.ts +0 -132
- package/src/components/hooks/useWorkspaceStatus.ts +0 -237
- package/src/components/settings/BillingSettingsPanel.tsx +0 -564
- package/src/components/settings/TeamSettingsPanel.tsx +0 -560
- package/src/components/settings/WorkspaceSettingsPanel.tsx +0 -1368
- package/src/lib/cloudApi.ts +0 -893
- /package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_ssgManifest.js +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider Setup Client Component
|
|
3
3
|
*
|
|
4
|
-
* Full-page
|
|
5
|
-
*
|
|
4
|
+
* Full-page provider setup that supports both workspace mode and onboarding mode.
|
|
5
|
+
* - Workspace mode: optionally route back to a specific workspace
|
|
6
|
+
* - Onboarding mode: no workspaceId required; use auth mode "onboarding"
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
'use client';
|
|
9
10
|
|
|
10
|
-
import React from 'react';
|
|
11
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
11
12
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
12
13
|
import { LogoIcon } from '../../../../components/Logo';
|
|
13
14
|
import { TerminalProviderSetup } from '../../../../components/TerminalProviderSetup';
|
|
@@ -23,12 +24,13 @@ const PROVIDER_AUTH_CONFIG: Record<string, {
|
|
|
23
24
|
codex: { authMethod: 'oauth', requiresUrlCopy: true },
|
|
24
25
|
openai: { authMethod: 'oauth', requiresUrlCopy: true },
|
|
25
26
|
cursor: { authMethod: 'oauth', requiresUrlCopy: true },
|
|
26
|
-
// Gemini uses terminal - CLI shows interactive menu for OAuth vs API key
|
|
27
27
|
google: { authMethod: 'terminal' },
|
|
28
28
|
opencode: { authMethod: 'terminal' },
|
|
29
29
|
droid: { authMethod: 'terminal' },
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
type SetupMode = 'api_key' | 'cli';
|
|
33
|
+
|
|
32
34
|
export interface ProviderSetupClientProps {
|
|
33
35
|
provider: string;
|
|
34
36
|
}
|
|
@@ -36,10 +38,122 @@ export interface ProviderSetupClientProps {
|
|
|
36
38
|
export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
37
39
|
const router = useRouter();
|
|
38
40
|
const searchParams = useSearchParams();
|
|
39
|
-
const workspaceId = searchParams.get('workspace');
|
|
41
|
+
const workspaceId = searchParams.get('workspace') || undefined;
|
|
42
|
+
|
|
43
|
+
const [csrfToken, setCsrfToken] = useState<string | null>(null);
|
|
44
|
+
const [setupMode, setSetupMode] = useState<SetupMode>(workspaceId ? 'cli' : 'api_key');
|
|
45
|
+
const [apiKey, setApiKey] = useState('');
|
|
46
|
+
const [apiKeyError, setApiKeyError] = useState<string | null>(null);
|
|
47
|
+
const [apiKeySuccess, setApiKeySuccess] = useState<string | null>(null);
|
|
48
|
+
const [isSubmittingApiKey, setIsSubmittingApiKey] = useState(false);
|
|
40
49
|
|
|
41
50
|
const config = PROVIDER_CONFIGS[provider];
|
|
42
51
|
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
fetch('/api/auth/session', { credentials: 'include' })
|
|
54
|
+
.then((res) => {
|
|
55
|
+
const token = res.headers.get('X-CSRF-Token');
|
|
56
|
+
if (token) {
|
|
57
|
+
setCsrfToken(token);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.catch(() => {
|
|
61
|
+
// Ignore CSRF bootstrap errors here; requests still run without token if server allows.
|
|
62
|
+
});
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
65
|
+
const returnPath = workspaceId ? `/app?workspace=${workspaceId}` : '/app/onboarding';
|
|
66
|
+
|
|
67
|
+
const handleSuccess = useCallback(() => {
|
|
68
|
+
router.push(returnPath);
|
|
69
|
+
}, [router, returnPath]);
|
|
70
|
+
|
|
71
|
+
const handleCancel = useCallback(() => {
|
|
72
|
+
router.push(returnPath);
|
|
73
|
+
}, [router, returnPath]);
|
|
74
|
+
|
|
75
|
+
const handleConnectAnother = useCallback(() => {
|
|
76
|
+
if (workspaceId) {
|
|
77
|
+
router.push(`/providers?workspace=${workspaceId}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
router.push('/app/onboarding');
|
|
82
|
+
}, [router, workspaceId]);
|
|
83
|
+
|
|
84
|
+
const supportsApiKey = useMemo(
|
|
85
|
+
() => ['anthropic', 'codex', 'openai', 'google'].includes(config?.name || ''),
|
|
86
|
+
[config?.name]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const authConfig = config ? PROVIDER_AUTH_CONFIG[config.name] : undefined;
|
|
90
|
+
const isOAuthProvider = authConfig?.authMethod === 'oauth';
|
|
91
|
+
|
|
92
|
+
const submitApiKey = useCallback(async () => {
|
|
93
|
+
if (!config) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!apiKey.trim()) {
|
|
98
|
+
setApiKeyError('Please enter an API key');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setIsSubmittingApiKey(true);
|
|
103
|
+
setApiKeyError(null);
|
|
104
|
+
setApiKeySuccess(null);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
108
|
+
if (csrfToken) {
|
|
109
|
+
headers['X-CSRF-Token'] = csrfToken;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const providerCandidates = config.name === 'codex'
|
|
113
|
+
? ['openai', 'codex']
|
|
114
|
+
: [config.name];
|
|
115
|
+
|
|
116
|
+
let connected = false;
|
|
117
|
+
let lastError = 'Failed to connect API key';
|
|
118
|
+
|
|
119
|
+
for (const providerName of providerCandidates) {
|
|
120
|
+
const body: Record<string, string> = { apiKey: apiKey.trim() };
|
|
121
|
+
if (workspaceId) {
|
|
122
|
+
body.workspaceId = workspaceId;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const res = await fetch(`/api/providers/${providerName}/api-key`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
credentials: 'include',
|
|
128
|
+
headers,
|
|
129
|
+
body: JSON.stringify(body),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (res.ok) {
|
|
133
|
+
connected = true;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const data = await res.json().catch(() => ({}));
|
|
138
|
+
lastError = data.error || data.message || 'Failed to connect API key';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!connected) {
|
|
142
|
+
throw new Error(lastError);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setApiKey('');
|
|
146
|
+
setApiKeySuccess(`${config.displayName} connected successfully.`);
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
handleSuccess();
|
|
149
|
+
}, 800);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
setApiKeyError(err instanceof Error ? err.message : 'Failed to connect API key');
|
|
152
|
+
} finally {
|
|
153
|
+
setIsSubmittingApiKey(false);
|
|
154
|
+
}
|
|
155
|
+
}, [apiKey, config, csrfToken, handleSuccess, workspaceId]);
|
|
156
|
+
|
|
43
157
|
if (!config) {
|
|
44
158
|
return (
|
|
45
159
|
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center">
|
|
@@ -53,36 +167,6 @@ export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
|
53
167
|
);
|
|
54
168
|
}
|
|
55
169
|
|
|
56
|
-
if (!workspaceId) {
|
|
57
|
-
return (
|
|
58
|
-
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center">
|
|
59
|
-
<div className="text-center">
|
|
60
|
-
<p className="text-error">No workspace specified</p>
|
|
61
|
-
<a href="/app" className="mt-4 text-accent-cyan hover:underline">
|
|
62
|
-
Back to dashboard
|
|
63
|
-
</a>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const handleSuccess = () => {
|
|
70
|
-
router.push(`/app?workspace=${workspaceId}`);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const handleCancel = () => {
|
|
74
|
-
router.push(`/app?workspace=${workspaceId}`);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const handleConnectAnother = () => {
|
|
78
|
-
// Navigate to providers page to select another provider
|
|
79
|
-
router.push(`/providers?workspace=${workspaceId}`);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Get auth configuration for this provider
|
|
83
|
-
const authConfig = PROVIDER_AUTH_CONFIG[config.name];
|
|
84
|
-
const isOAuthProvider = authConfig?.authMethod === 'oauth';
|
|
85
|
-
|
|
86
170
|
return (
|
|
87
171
|
<div className="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] p-8">
|
|
88
172
|
<div className="max-w-4xl mx-auto">
|
|
@@ -93,7 +177,7 @@ export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
|
93
177
|
<span className="text-lg font-bold text-white">Agent Relay</span>
|
|
94
178
|
</a>
|
|
95
179
|
<a
|
|
96
|
-
href={
|
|
180
|
+
href={returnPath}
|
|
97
181
|
className="px-4 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors"
|
|
98
182
|
>
|
|
99
183
|
Skip for now →
|
|
@@ -117,29 +201,93 @@ export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
|
117
201
|
</h1>
|
|
118
202
|
</div>
|
|
119
203
|
<p className="text-text-muted">
|
|
120
|
-
{
|
|
121
|
-
? '
|
|
122
|
-
: '
|
|
123
|
-
}
|
|
204
|
+
{workspaceId
|
|
205
|
+
? 'Authenticate provider access for this workspace.'
|
|
206
|
+
: 'Authenticate provider access during onboarding before workspace provisioning.'}
|
|
124
207
|
</p>
|
|
125
208
|
</div>
|
|
126
209
|
|
|
127
|
-
{/*
|
|
128
|
-
{
|
|
210
|
+
{/* Setup mode selector */}
|
|
211
|
+
{supportsApiKey && (
|
|
212
|
+
<div className="mb-6 flex gap-2 p-1 bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-xl">
|
|
213
|
+
<button
|
|
214
|
+
onClick={() => setSetupMode('api_key')}
|
|
215
|
+
className={`flex-1 py-2.5 px-4 rounded-lg text-sm transition-colors ${
|
|
216
|
+
setupMode === 'api_key'
|
|
217
|
+
? 'bg-accent-cyan text-bg-deep font-semibold'
|
|
218
|
+
: 'text-text-muted hover:text-white'
|
|
219
|
+
}`}
|
|
220
|
+
>
|
|
221
|
+
API Key Input
|
|
222
|
+
</button>
|
|
223
|
+
<button
|
|
224
|
+
onClick={() => setSetupMode('cli')}
|
|
225
|
+
className={`flex-1 py-2.5 px-4 rounded-lg text-sm transition-colors ${
|
|
226
|
+
setupMode === 'cli'
|
|
227
|
+
? 'bg-accent-cyan text-bg-deep font-semibold'
|
|
228
|
+
: 'text-text-muted hover:text-white'
|
|
229
|
+
}`}
|
|
230
|
+
>
|
|
231
|
+
Authenticate via CLI
|
|
232
|
+
</button>
|
|
233
|
+
</div>
|
|
234
|
+
)}
|
|
235
|
+
|
|
236
|
+
{/* API key setup */}
|
|
237
|
+
{supportsApiKey && setupMode === 'api_key' ? (
|
|
238
|
+
<div className="bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-2xl p-6 shadow-2xl space-y-4">
|
|
239
|
+
<p className="text-sm text-text-muted">
|
|
240
|
+
Enter your {config.displayName} API key. In onboarding mode, this is stored at the user level and reused during workspace provisioning.
|
|
241
|
+
</p>
|
|
242
|
+
<div className="flex gap-3">
|
|
243
|
+
<input
|
|
244
|
+
type="password"
|
|
245
|
+
value={apiKey}
|
|
246
|
+
onChange={(event) => setApiKey(event.target.value)}
|
|
247
|
+
placeholder={`Enter ${config.displayName} API key`}
|
|
248
|
+
className="flex-1 px-4 py-3 bg-bg-tertiary border border-border-subtle rounded-lg text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:border-accent-cyan focus:ring-1 focus:ring-accent-cyan/30 transition-all"
|
|
249
|
+
/>
|
|
250
|
+
<button
|
|
251
|
+
onClick={submitApiKey}
|
|
252
|
+
disabled={isSubmittingApiKey || !apiKey.trim()}
|
|
253
|
+
className="px-5 py-3 bg-accent-cyan text-bg-deep font-semibold rounded-lg text-sm hover:bg-accent-cyan/90 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
|
254
|
+
>
|
|
255
|
+
{isSubmittingApiKey ? 'Connecting...' : 'Connect'}
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
{apiKeyError && (
|
|
260
|
+
<div className="p-3 bg-error/10 border border-error/30 rounded-lg text-sm text-error">
|
|
261
|
+
{apiKeyError}
|
|
262
|
+
</div>
|
|
263
|
+
)}
|
|
264
|
+
|
|
265
|
+
{apiKeySuccess && (
|
|
266
|
+
<div className="p-3 bg-success/10 border border-success/30 rounded-lg text-sm text-success">
|
|
267
|
+
{apiKeySuccess}
|
|
268
|
+
</div>
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
) : isOAuthProvider ? (
|
|
129
272
|
<ProviderAuthFlow
|
|
130
273
|
provider={{
|
|
131
274
|
id: config.name,
|
|
132
275
|
name: config.name,
|
|
133
276
|
displayName: config.displayName,
|
|
134
277
|
color: config.color,
|
|
135
|
-
requiresUrlCopy: authConfig
|
|
278
|
+
requiresUrlCopy: authConfig?.requiresUrlCopy,
|
|
136
279
|
}}
|
|
137
280
|
workspaceId={workspaceId}
|
|
281
|
+
mode={workspaceId ? 'workspace' : 'onboarding'}
|
|
282
|
+
csrfToken={csrfToken || undefined}
|
|
283
|
+
showManualDone={!workspaceId}
|
|
138
284
|
onSuccess={handleSuccess}
|
|
139
285
|
onCancel={handleCancel}
|
|
140
|
-
onError={(err) =>
|
|
286
|
+
onError={(err) => {
|
|
287
|
+
setApiKeyError(err);
|
|
288
|
+
}}
|
|
141
289
|
/>
|
|
142
|
-
) : (
|
|
290
|
+
) : workspaceId ? (
|
|
143
291
|
<TerminalProviderSetup
|
|
144
292
|
provider={{
|
|
145
293
|
id: config.id,
|
|
@@ -153,29 +301,31 @@ export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
|
153
301
|
onSuccess={handleSuccess}
|
|
154
302
|
onCancel={handleCancel}
|
|
155
303
|
onConnectAnother={handleConnectAnother}
|
|
156
|
-
onError={(err) =>
|
|
304
|
+
onError={(err) => setApiKeyError(err)}
|
|
157
305
|
className="shadow-2xl"
|
|
158
306
|
/>
|
|
307
|
+
) : (
|
|
308
|
+
<div className="bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-2xl p-6 text-center">
|
|
309
|
+
<p className="text-text-muted text-sm">
|
|
310
|
+
Interactive terminal setup requires a workspace. Use API key input or CLI authentication for onboarding mode.
|
|
311
|
+
</p>
|
|
312
|
+
</div>
|
|
159
313
|
)}
|
|
160
314
|
|
|
161
315
|
{/* Help text */}
|
|
162
316
|
<div className="mt-6 p-4 bg-bg-primary/80 backdrop-blur-sm border border-border-subtle rounded-xl">
|
|
163
317
|
<h3 className="text-white font-medium mb-2">How this works:</h3>
|
|
164
|
-
{
|
|
318
|
+
{setupMode === 'api_key' && supportsApiKey ? (
|
|
165
319
|
<ol className="text-sm text-text-muted space-y-1 list-decimal list-inside">
|
|
166
|
-
<li>
|
|
167
|
-
<li>
|
|
168
|
-
<li>
|
|
169
|
-
<li>The CLI will capture the callback and complete the setup</li>
|
|
170
|
-
<li>Return here to continue to the dashboard</li>
|
|
320
|
+
<li>Paste your provider API key</li>
|
|
321
|
+
<li>Click connect to store credentials securely</li>
|
|
322
|
+
<li>Continue back to onboarding or dashboard</li>
|
|
171
323
|
</ol>
|
|
172
324
|
) : (
|
|
173
325
|
<ol className="text-sm text-text-muted space-y-1 list-decimal list-inside">
|
|
174
|
-
<li>
|
|
175
|
-
<li>
|
|
176
|
-
<li>
|
|
177
|
-
<li>Answer any remaining prompts (skills, permissions, etc.) in the terminal</li>
|
|
178
|
-
<li>Once connected, click "Done - Continue" to go to the dashboard</li>
|
|
326
|
+
<li>Copy the command shown and run it in your terminal</li>
|
|
327
|
+
<li>Complete the authentication flow in browser/CLI</li>
|
|
328
|
+
<li>Return here and continue once connected</li>
|
|
179
329
|
</ol>
|
|
180
330
|
)}
|
|
181
331
|
</div>
|
|
@@ -183,10 +333,10 @@ export function ProviderSetupClient({ provider }: ProviderSetupClientProps) {
|
|
|
183
333
|
{/* Fallback link */}
|
|
184
334
|
<div className="mt-4 text-center">
|
|
185
335
|
<a
|
|
186
|
-
href={`/providers?connect=${config.id}&workspace=${workspaceId}`}
|
|
336
|
+
href={workspaceId ? `/providers?connect=${config.id}&workspace=${workspaceId}` : '/app/onboarding'}
|
|
187
337
|
className="text-sm text-text-muted hover:text-accent-cyan transition-colors"
|
|
188
338
|
>
|
|
189
|
-
Having trouble? Try
|
|
339
|
+
Having trouble? Try another connection method →
|
|
190
340
|
</a>
|
|
191
341
|
</div>
|
|
192
342
|
</div>
|
|
@@ -185,7 +185,7 @@ export function AgentCard({
|
|
|
185
185
|
<span
|
|
186
186
|
className="inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-medium uppercase tracking-wider
|
|
187
187
|
bg-warning-light text-warning border border-warning/30"
|
|
188
|
-
title={`Local agent from ${agent.
|
|
188
|
+
title={`Local agent from ${agent.brokerName || 'linked broker'}`}
|
|
189
189
|
>
|
|
190
190
|
Local
|
|
191
191
|
</span>
|
|
@@ -329,7 +329,7 @@ export function AgentCard({
|
|
|
329
329
|
<span
|
|
330
330
|
className="inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-medium uppercase tracking-wider
|
|
331
331
|
bg-warning-light text-warning border border-warning/30"
|
|
332
|
-
title={`Local agent from ${agent.
|
|
332
|
+
title={`Local agent from ${agent.brokerName || 'linked broker'}`}
|
|
333
333
|
>
|
|
334
334
|
Local
|
|
335
335
|
</span>
|
|
@@ -352,9 +352,9 @@ export function AgentCard({
|
|
|
352
352
|
)}
|
|
353
353
|
</div>
|
|
354
354
|
{showBreadcrumb ? (
|
|
355
|
-
<span className="text-xs text-text-muted truncate block">{agent.isLocal ? agent.
|
|
355
|
+
<span className="text-xs text-text-muted truncate block">{agent.isLocal ? agent.brokerName || agent.machineId : getAgentBreadcrumb(agent.name)}</span>
|
|
356
356
|
) : (
|
|
357
|
-
<span className="text-xs text-text-muted truncate block">{agent.isLocal ? agent.
|
|
357
|
+
<span className="text-xs text-text-muted truncate block">{agent.isLocal ? agent.brokerName || agent.machineId : agent.name}</span>
|
|
358
358
|
)}
|
|
359
359
|
{agent.agentId && (
|
|
360
360
|
<span className="text-[10px] text-text-muted font-mono opacity-70" title="Agent ID (use to resume)">
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import React, { useMemo } from 'react';
|
|
9
9
|
import { useAgentLogs } from './hooks/useAgentLogs';
|
|
10
10
|
import { getAgentColor } from '../lib/colors';
|
|
11
|
+
import { sanitizeLogContent, isSpinnerFragment } from '../lib/sanitize-logs';
|
|
11
12
|
|
|
12
13
|
export interface AgentLogPreviewProps {
|
|
13
14
|
agentName: string;
|
|
@@ -33,8 +34,6 @@ export function AgentLogPreview({
|
|
|
33
34
|
const colors = getAgentColor(agentName);
|
|
34
35
|
|
|
35
36
|
const previewLines = useMemo(() => {
|
|
36
|
-
const spinnerPattern = /^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷◐◓◑◒●○◉◎|\\\/\-*.\u2800-\u28FF]+$/;
|
|
37
|
-
|
|
38
37
|
const allLines: string[] = [];
|
|
39
38
|
for (const log of logs) {
|
|
40
39
|
if (log.type === 'system') continue;
|
|
@@ -42,7 +41,7 @@ export function AgentLogPreview({
|
|
|
42
41
|
for (const rawLine of sanitized.split('\n')) {
|
|
43
42
|
const trimmed = rawLine.trim();
|
|
44
43
|
if (trimmed.length === 0) continue;
|
|
45
|
-
if (
|
|
44
|
+
if (isSpinnerFragment(trimmed)) continue;
|
|
46
45
|
allLines.push(rawLine.replace(/\s+$/g, ''));
|
|
47
46
|
}
|
|
48
47
|
}
|
|
@@ -136,38 +135,3 @@ function ExpandIcon() {
|
|
|
136
135
|
);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
/**
|
|
140
|
-
* Strip ANSI escape codes (including degraded sequences like "[38;5;216m")
|
|
141
|
-
* and control characters so logs render as clean text.
|
|
142
|
-
*/
|
|
143
|
-
function sanitizeLogContent(text: string): string {
|
|
144
|
-
if (!text) return '';
|
|
145
|
-
|
|
146
|
-
let result = text;
|
|
147
|
-
|
|
148
|
-
// Remove OSC sequences (like window title): \x1b]...(\x07|\x1b\\)
|
|
149
|
-
result = result.replace(/\x1b\].*?(?:\x07|\x1b\\)/gs, '');
|
|
150
|
-
|
|
151
|
-
// Remove DCS (Device Control String) sequences: \x1bP...\x1b\\
|
|
152
|
-
result = result.replace(/\x1bP.*?\x1b\\/gs, '');
|
|
153
|
-
|
|
154
|
-
// Remove standard ANSI escape sequences (CSI, SGR, etc.)
|
|
155
|
-
result = result.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
156
|
-
|
|
157
|
-
// Remove single-character escapes
|
|
158
|
-
result = result.replace(/\x1b[@-Z\\-_]/g, '');
|
|
159
|
-
|
|
160
|
-
// Remove orphaned CSI sequences that lost their escape byte
|
|
161
|
-
result = result.replace(/^\[\??\d+[hlKJHfABCDGPXsu]/gm, '');
|
|
162
|
-
|
|
163
|
-
// Remove literal SGR sequences that show up without ESC (e.g. "[38;5;216m")
|
|
164
|
-
result = result.replace(/\[\d+(?:;\d+)*m/g, '');
|
|
165
|
-
|
|
166
|
-
// Remove carriage returns/backspaces and other control chars (except newline/tab)
|
|
167
|
-
result = result.replace(/\r/g, '');
|
|
168
|
-
result = result.replace(/.\x08/g, '');
|
|
169
|
-
result = result.replace(/\x08+/g, '');
|
|
170
|
-
result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
|
171
|
-
|
|
172
|
-
return result;
|
|
173
|
-
}
|