@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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/README.md +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +44 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +994 -130
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +189 -10
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +62 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +31 -964
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +96 -34
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +95 -25
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/heartbeat-defaults'
|
|
4
|
+
import { useCallback, useEffect, useState, type ReactNode } from 'react'
|
|
4
5
|
import type { Agent } from '@/types'
|
|
5
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
7
|
+
import { AgentAvatar } from './agent-avatar'
|
|
6
8
|
import { AgentFilesEditor } from './agent-files-editor'
|
|
7
9
|
import { OpenClawSkillsPanel } from './openclaw-skills-panel'
|
|
8
10
|
import { PermissionPresetSelector } from './permission-preset-selector'
|
|
@@ -29,6 +31,24 @@ const TABS: { id: InspectorTab; label: string; openclawOnly?: boolean }[] = [
|
|
|
29
31
|
{ id: 'advanced', label: 'Advanced' },
|
|
30
32
|
]
|
|
31
33
|
|
|
34
|
+
const PROVIDER_LABELS: Record<string, string> = {
|
|
35
|
+
'claude-cli': 'Claude CLI',
|
|
36
|
+
'codex-cli': 'Codex CLI',
|
|
37
|
+
'opencode-cli': 'OpenCode CLI',
|
|
38
|
+
openai: 'OpenAI',
|
|
39
|
+
anthropic: 'Anthropic',
|
|
40
|
+
openclaw: 'OpenClaw',
|
|
41
|
+
ollama: 'Ollama',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function panelCardClass(className = '') {
|
|
45
|
+
return `rounded-[16px] border border-white/[0.06] bg-white/[0.03] shadow-[inset_0_1px_0_rgba(255,255,255,0.04)] ${className}`.trim()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function SectionLabel({ children }: { children: ReactNode }) {
|
|
49
|
+
return <label className="block text-[11px] font-700 uppercase tracking-[0.16em] text-text-3/45 mb-2">{children}</label>
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDeleteChat, isMainChat }: Props) {
|
|
33
53
|
const inspectorTab = useAppStore((s) => s.inspectorTab)
|
|
34
54
|
const setInspectorTab = useAppStore((s) => s.setInspectorTab)
|
|
@@ -55,15 +75,40 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
55
75
|
}, [setInspectorOpen])
|
|
56
76
|
|
|
57
77
|
const agentSchedules = Object.values(schedules).filter((s) => s.agentId === agent.id)
|
|
78
|
+
const providerLabel = PROVIDER_LABELS[agent.provider] || agent.provider.replace(/-/g, ' ')
|
|
58
79
|
|
|
59
80
|
return (
|
|
60
|
-
<div className="w-[
|
|
81
|
+
<div className="w-[420px] shrink-0 border-l border-white/[0.06] bg-bg flex flex-col h-full overflow-hidden fade-up-delay"
|
|
82
|
+
style={{ background: 'radial-gradient(circle at top right, rgba(66, 211, 255, 0.06), transparent 30%), linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0))' }}>
|
|
61
83
|
{/* Header */}
|
|
62
|
-
<div className="
|
|
63
|
-
<
|
|
84
|
+
<div className="px-4 pt-4 pb-3 border-b border-white/[0.06] shrink-0 bg-black/[0.12]">
|
|
85
|
+
<div className="flex items-start gap-3">
|
|
86
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={40} />
|
|
87
|
+
<div className="min-w-0 flex-1">
|
|
88
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
89
|
+
<h3 className="font-display text-[16px] font-700 text-text truncate tracking-[-0.02em]">{agent.name}</h3>
|
|
90
|
+
{agent.heartbeatEnabled && (
|
|
91
|
+
<span className="inline-flex items-center gap-1 rounded-[7px] border border-emerald-400/15 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-emerald-300">
|
|
92
|
+
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
|
93
|
+
Heartbeat
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
<div className="mt-1 flex flex-wrap items-center gap-1.5">
|
|
98
|
+
<span className="inline-flex items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-600 text-text-3/70">
|
|
99
|
+
{providerLabel}
|
|
100
|
+
</span>
|
|
101
|
+
<span className="inline-flex max-w-[180px] items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-mono text-text-3/70 truncate">
|
|
102
|
+
{agent.model || 'Default model'}
|
|
103
|
+
</span>
|
|
104
|
+
<span className="inline-flex items-center rounded-[8px] border border-white/[0.06] bg-white/[0.03] px-2 py-1 text-[10px] font-600 text-text-3/70">
|
|
105
|
+
{(agent.plugins?.length ?? 0)} plugins
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
64
109
|
<button
|
|
65
110
|
onClick={() => setInspectorOpen(false)}
|
|
66
|
-
className="p-1 rounded-[
|
|
111
|
+
className="p-1.5 rounded-[8px] text-text-3/50 hover:text-text-3 bg-transparent border-none cursor-pointer transition-all hover:bg-white/[0.04]"
|
|
67
112
|
aria-label="Close inspector"
|
|
68
113
|
>
|
|
69
114
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
@@ -71,26 +116,29 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
71
116
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
72
117
|
</svg>
|
|
73
118
|
</button>
|
|
119
|
+
</div>
|
|
74
120
|
</div>
|
|
75
121
|
|
|
76
122
|
{/* Tab bar */}
|
|
77
|
-
<div className="
|
|
123
|
+
<div className="px-4 py-3 shrink-0">
|
|
124
|
+
<div className="flex gap-1 rounded-[12px] border border-white/[0.06] bg-black/[0.12] p-1 overflow-x-auto" role="tablist">
|
|
78
125
|
{visibleTabs.map((tab) => (
|
|
79
126
|
<button
|
|
80
127
|
key={tab.id}
|
|
81
128
|
role="tab"
|
|
82
129
|
onClick={() => setInspectorTab(tab.id)}
|
|
83
130
|
aria-selected={inspectorTab === tab.id}
|
|
84
|
-
className={`px-
|
|
131
|
+
className={`px-3 py-1.5 rounded-[9px] text-[11px] font-700 cursor-pointer transition-all whitespace-nowrap focus-visible:ring-1 focus-visible:ring-accent-bright/50
|
|
85
132
|
${inspectorTab === tab.id
|
|
86
|
-
? 'bg-
|
|
87
|
-
: 'bg-transparent text-text-3 hover:text-text-2'}`}
|
|
133
|
+
? 'bg-white/[0.08] text-text'
|
|
134
|
+
: 'bg-transparent text-text-3/65 hover:text-text-2'}`}
|
|
88
135
|
style={{ fontFamily: 'inherit' }}
|
|
89
136
|
>
|
|
90
137
|
{tab.label}
|
|
91
138
|
</button>
|
|
92
139
|
))}
|
|
93
140
|
</div>
|
|
141
|
+
</div>
|
|
94
142
|
|
|
95
143
|
{/* Tab content */}
|
|
96
144
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
@@ -141,30 +189,43 @@ interface OverviewTabProps {
|
|
|
141
189
|
}
|
|
142
190
|
|
|
143
191
|
function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDeleteChat, isMainChat }: OverviewTabProps) {
|
|
192
|
+
const summaryStats = [
|
|
193
|
+
{ label: 'Provider', value: PROVIDER_LABELS[agent.provider] || agent.provider.replace(/-/g, ' ') },
|
|
194
|
+
{ label: 'Model', value: agent.model || 'Default' },
|
|
195
|
+
{ label: 'Plugins', value: String(agent.plugins?.length ?? 0) },
|
|
196
|
+
{ label: 'Heartbeat', value: agent.heartbeatEnabled ? `Every ${agent.heartbeatIntervalSec ?? DEFAULT_HEARTBEAT_INTERVAL_SEC}s` : 'Off' },
|
|
197
|
+
]
|
|
198
|
+
|
|
144
199
|
return (
|
|
145
200
|
<div className="p-4 flex flex-col gap-4">
|
|
146
|
-
<div>
|
|
147
|
-
<
|
|
148
|
-
<p className="text-[
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
201
|
+
<div className={panelCardClass('p-4 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.02))]')}>
|
|
202
|
+
<SectionLabel>Overview</SectionLabel>
|
|
203
|
+
<p className="text-[14px] text-text-2 leading-relaxed">
|
|
204
|
+
{agent.description || 'No description yet. Use the agent editor to define what this agent is for.'}
|
|
205
|
+
</p>
|
|
206
|
+
<div className="mt-4 grid grid-cols-2 gap-2">
|
|
207
|
+
{summaryStats.map((item) => (
|
|
208
|
+
<div key={item.label} className="rounded-[12px] border border-white/[0.06] bg-black/[0.14] px-3 py-2.5">
|
|
209
|
+
<div className="text-[10px] font-700 uppercase tracking-[0.14em] text-text-3/45">{item.label}</div>
|
|
210
|
+
<div className="mt-1 text-[12px] text-text-2 font-medium break-words">{item.value}</div>
|
|
211
|
+
</div>
|
|
212
|
+
))}
|
|
213
|
+
</div>
|
|
153
214
|
</div>
|
|
154
215
|
{agent.systemPrompt && (
|
|
155
|
-
<div>
|
|
156
|
-
<
|
|
157
|
-
<p className="text-[12px] text-text-3 bg-
|
|
216
|
+
<div className={panelCardClass('p-4')}>
|
|
217
|
+
<SectionLabel>System Prompt</SectionLabel>
|
|
218
|
+
<p className="text-[12px] text-text-3 bg-black/[0.14] rounded-[12px] p-3 border border-white/[0.04] max-h-[220px] overflow-y-auto whitespace-pre-wrap font-mono leading-relaxed">
|
|
158
219
|
{agent.systemPrompt}
|
|
159
220
|
</p>
|
|
160
221
|
</div>
|
|
161
222
|
)}
|
|
162
223
|
{agent.capabilities && agent.capabilities.length > 0 && (
|
|
163
|
-
<div>
|
|
164
|
-
<
|
|
165
|
-
<div className="flex flex-wrap gap-1">
|
|
224
|
+
<div className={panelCardClass('p-4')}>
|
|
225
|
+
<SectionLabel>Capabilities</SectionLabel>
|
|
226
|
+
<div className="flex flex-wrap gap-1.5">
|
|
166
227
|
{agent.capabilities.map((cap) => (
|
|
167
|
-
<span key={cap} className="px-2 py-
|
|
228
|
+
<span key={cap} className="px-2.5 py-1 rounded-[8px] text-[11px] font-700 bg-accent-soft/70 text-accent-bright border border-accent-bright/10">
|
|
168
229
|
{cap}
|
|
169
230
|
</span>
|
|
170
231
|
))}
|
|
@@ -172,11 +233,11 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
172
233
|
</div>
|
|
173
234
|
)}
|
|
174
235
|
{agent.plugins && agent.plugins.length > 0 && (
|
|
175
|
-
<div>
|
|
176
|
-
<
|
|
177
|
-
<div className="flex flex-wrap gap-1">
|
|
236
|
+
<div className={panelCardClass('p-4')}>
|
|
237
|
+
<SectionLabel>Plugins</SectionLabel>
|
|
238
|
+
<div className="flex flex-wrap gap-1.5">
|
|
178
239
|
{agent.plugins.map((tool) => (
|
|
179
|
-
<span key={tool} className="px-2 py-
|
|
240
|
+
<span key={tool} className="px-2.5 py-1 rounded-[8px] text-[11px] font-700 bg-sky-400/[0.08] text-sky-300 border border-sky-400/[0.08]">
|
|
180
241
|
{tool}
|
|
181
242
|
</span>
|
|
182
243
|
))}
|
|
@@ -186,13 +247,13 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
186
247
|
|
|
187
248
|
{/* Actions */}
|
|
188
249
|
{(onEditAgent || onClearHistory || onDeleteAgent || onDeleteChat) && (
|
|
189
|
-
|
|
190
|
-
<
|
|
250
|
+
<div className={panelCardClass('p-4')}>
|
|
251
|
+
<SectionLabel>Actions</SectionLabel>
|
|
191
252
|
<div className="flex flex-col gap-2">
|
|
192
253
|
{onEditAgent && (
|
|
193
254
|
<button
|
|
194
255
|
onClick={onEditAgent}
|
|
195
|
-
className="w-full px-3 py-2 rounded-[
|
|
256
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-accent-bright bg-accent-soft/50 border border-accent-bright/10 cursor-pointer transition-all hover:bg-accent-soft text-left"
|
|
196
257
|
style={{ fontFamily: 'inherit' }}
|
|
197
258
|
>
|
|
198
259
|
Edit Agent
|
|
@@ -200,12 +261,12 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
200
261
|
)}
|
|
201
262
|
{(onClearHistory || onDeleteAgent || onDeleteChat) && (
|
|
202
263
|
<>
|
|
203
|
-
<
|
|
264
|
+
<SectionLabel>Danger Zone</SectionLabel>
|
|
204
265
|
<div className="flex flex-col gap-1.5">
|
|
205
266
|
{onClearHistory && (
|
|
206
267
|
<button
|
|
207
268
|
onClick={onClearHistory}
|
|
208
|
-
className="w-full px-3 py-2 rounded-[
|
|
269
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
209
270
|
style={{ fontFamily: 'inherit' }}
|
|
210
271
|
>
|
|
211
272
|
Clear History
|
|
@@ -214,7 +275,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
214
275
|
{onDeleteAgent && !isMainChat && (
|
|
215
276
|
<button
|
|
216
277
|
onClick={onDeleteAgent}
|
|
217
|
-
className="w-full px-3 py-2 rounded-[
|
|
278
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
218
279
|
style={{ fontFamily: 'inherit' }}
|
|
219
280
|
>
|
|
220
281
|
Delete Agent
|
|
@@ -223,7 +284,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
223
284
|
{onDeleteChat && !isMainChat && (
|
|
224
285
|
<button
|
|
225
286
|
onClick={onDeleteChat}
|
|
226
|
-
className="w-full px-3 py-2 rounded-[
|
|
287
|
+
className="w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 text-red-400/80 bg-red-400/[0.04] border border-red-400/[0.08] cursor-pointer transition-all hover:bg-red-400/[0.08] hover:text-red-400 text-left"
|
|
227
288
|
style={{ fontFamily: 'inherit' }}
|
|
228
289
|
>
|
|
229
290
|
Delete Chat
|
|
@@ -233,7 +294,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
233
294
|
</>
|
|
234
295
|
)}
|
|
235
296
|
</div>
|
|
236
|
-
|
|
297
|
+
</div>
|
|
237
298
|
)}
|
|
238
299
|
</div>
|
|
239
300
|
)
|
|
@@ -277,7 +338,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
277
338
|
<div className="p-4 flex flex-col gap-3">
|
|
278
339
|
{/* Local schedules */}
|
|
279
340
|
{schedules.map((s) => (
|
|
280
|
-
<div key={s.id} className=
|
|
341
|
+
<div key={s.id} className={panelCardClass('py-2.5 px-3.5')}>
|
|
281
342
|
<div className="flex items-center gap-2">
|
|
282
343
|
<span className="text-[13px] font-600 text-text truncate flex-1">{s.name}</span>
|
|
283
344
|
<span className={`text-[10px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[4px]
|
|
@@ -296,7 +357,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
296
357
|
<>
|
|
297
358
|
{cronLoading && <div className="text-[12px] text-text-3/50">Loading gateway crons...</div>}
|
|
298
359
|
{gatewayCrons.map((c) => (
|
|
299
|
-
<div key={c.id} className=
|
|
360
|
+
<div key={c.id} className={panelCardClass('py-2.5 px-3.5')}>
|
|
300
361
|
<div className="flex items-center gap-2">
|
|
301
362
|
<span className="text-[13px] font-600 text-text truncate flex-1">{c.name}</span>
|
|
302
363
|
<span className={`text-[10px] font-600 uppercase tracking-wider px-1.5 py-0.5 rounded-[4px]
|
|
@@ -329,7 +390,7 @@ function AutomationsTab({ schedules, agent }: { schedules: Array<{ id: string; n
|
|
|
329
390
|
)}
|
|
330
391
|
|
|
331
392
|
{!schedules.length && !gatewayCrons.length && !cronLoading && !showCronForm && (
|
|
332
|
-
<div className=
|
|
393
|
+
<div className={panelCardClass('p-4 text-[13px] text-text-3/50')}>No automations linked to this agent.</div>
|
|
333
394
|
)}
|
|
334
395
|
</div>
|
|
335
396
|
)
|
|
@@ -354,30 +415,30 @@ function AdvancedTab({ agent }: { agent: Agent }) {
|
|
|
354
415
|
)}
|
|
355
416
|
|
|
356
417
|
{agent.heartbeatEnabled && (
|
|
357
|
-
<div>
|
|
358
|
-
<
|
|
418
|
+
<div className={panelCardClass('p-4')}>
|
|
419
|
+
<SectionLabel>Heartbeat</SectionLabel>
|
|
359
420
|
<p className="text-[13px] text-text-2">
|
|
360
|
-
Every {agent.heartbeatIntervalSec ??
|
|
421
|
+
Every {agent.heartbeatIntervalSec ?? DEFAULT_HEARTBEAT_INTERVAL_SEC}s
|
|
361
422
|
{agent.heartbeatModel && ` (${agent.heartbeatModel})`}
|
|
362
423
|
</p>
|
|
363
424
|
</div>
|
|
364
425
|
)}
|
|
365
426
|
{agent.thinkingLevel && (
|
|
366
|
-
<div>
|
|
367
|
-
<
|
|
427
|
+
<div className={panelCardClass('p-4')}>
|
|
428
|
+
<SectionLabel>Thinking Level</SectionLabel>
|
|
368
429
|
<p className="text-[13px] text-text-2 capitalize">{agent.thinkingLevel}</p>
|
|
369
430
|
</div>
|
|
370
431
|
)}
|
|
371
|
-
<div>
|
|
372
|
-
<
|
|
432
|
+
<div className={panelCardClass('p-4')}>
|
|
433
|
+
<SectionLabel>Agent ID</SectionLabel>
|
|
373
434
|
<p className="text-[12px] text-text-3 font-mono select-all">{agent.id}</p>
|
|
374
435
|
</div>
|
|
375
|
-
<div>
|
|
376
|
-
<
|
|
436
|
+
<div className={panelCardClass('p-4')}>
|
|
437
|
+
<SectionLabel>Created</SectionLabel>
|
|
377
438
|
<p className="text-[12px] text-text-3">{new Date(agent.createdAt).toLocaleString()}</p>
|
|
378
439
|
</div>
|
|
379
|
-
<div>
|
|
380
|
-
<
|
|
440
|
+
<div className={panelCardClass('p-4')}>
|
|
441
|
+
<SectionLabel>Updated</SectionLabel>
|
|
381
442
|
<p className="text-[12px] text-text-3">{new Date(agent.updatedAt).toLocaleString()}</p>
|
|
382
443
|
</div>
|
|
383
444
|
</div>
|
|
@@ -51,7 +51,10 @@ export function SandboxEnvPanel() {
|
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
53
|
<div className="flex flex-col gap-2">
|
|
54
|
-
<label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50">Sandbox Env Allowlist</label>
|
|
54
|
+
<label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50">OpenClaw Sandbox Env Allowlist</label>
|
|
55
|
+
<p className="text-[12px] text-text-3/50">
|
|
56
|
+
Applies to OpenClaw gateway Docker sandboxes only. It does not affect SwarmClaw's local <code className="font-mono">sandbox_exec</code> tool.
|
|
57
|
+
</p>
|
|
55
58
|
<div className="flex flex-col gap-1">
|
|
56
59
|
{available.map((key) => (
|
|
57
60
|
<label key={key} className="flex items-center gap-2 py-1 px-2 rounded-[8px] hover:bg-white/[0.02] cursor-pointer transition-colors">
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { useState, useEffect } from 'react'
|
|
4
4
|
import { setStoredAccessKey } from '@/lib/api-client'
|
|
5
5
|
import { fetchWithTimeout } from '@/lib/fetch-timeout'
|
|
6
|
-
import { copyTextToClipboard } from '@/lib/clipboard'
|
|
7
6
|
|
|
8
7
|
interface AccessKeyGateProps {
|
|
9
8
|
onAuthenticated: () => void
|
|
@@ -19,8 +18,6 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
19
18
|
|
|
20
19
|
// First-time setup state
|
|
21
20
|
const [firstTime, setFirstTime] = useState(false)
|
|
22
|
-
const [generatedKey, setGeneratedKey] = useState('')
|
|
23
|
-
const [copied, setCopied] = useState(false)
|
|
24
21
|
|
|
25
22
|
useEffect(() => {
|
|
26
23
|
let cancelled = false
|
|
@@ -28,9 +25,8 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
28
25
|
try {
|
|
29
26
|
const res = await fetchWithTimeout('/api/auth', {}, AUTH_CHECK_TIMEOUT_MS)
|
|
30
27
|
const data = await res.json().catch(() => ({}))
|
|
31
|
-
if (!cancelled && data.firstTime
|
|
28
|
+
if (!cancelled && data.firstTime) {
|
|
32
29
|
setFirstTime(true)
|
|
33
|
-
setGeneratedKey(data.key)
|
|
34
30
|
}
|
|
35
31
|
} catch (err) {
|
|
36
32
|
console.error('Auth check failed:', err)
|
|
@@ -41,38 +37,6 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
41
37
|
return () => { cancelled = true }
|
|
42
38
|
}, [])
|
|
43
39
|
|
|
44
|
-
const handleCopyKey = async () => {
|
|
45
|
-
try {
|
|
46
|
-
const copiedKey = await copyTextToClipboard(generatedKey)
|
|
47
|
-
if (!copiedKey) return
|
|
48
|
-
setCopied(true)
|
|
49
|
-
setTimeout(() => setCopied(false), 2000)
|
|
50
|
-
} catch {
|
|
51
|
-
// Fallback: select the text
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const handleClaimKey = async () => {
|
|
56
|
-
setLoading(true)
|
|
57
|
-
try {
|
|
58
|
-
const res = await fetchWithTimeout('/api/auth', {
|
|
59
|
-
method: 'POST',
|
|
60
|
-
headers: { 'Content-Type': 'application/json' },
|
|
61
|
-
body: JSON.stringify({ key: generatedKey }),
|
|
62
|
-
}, AUTH_CHECK_TIMEOUT_MS)
|
|
63
|
-
if (res.ok) {
|
|
64
|
-
setStoredAccessKey(generatedKey)
|
|
65
|
-
onAuthenticated()
|
|
66
|
-
} else {
|
|
67
|
-
setError('Invalid access key')
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
setError('Connection failed')
|
|
71
|
-
} finally {
|
|
72
|
-
setLoading(false)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
40
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
77
41
|
e.preventDefault()
|
|
78
42
|
const trimmed = key.trim()
|
|
@@ -148,77 +112,52 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
148
112
|
</div>
|
|
149
113
|
|
|
150
114
|
{firstTime ? (
|
|
151
|
-
/* ── First-time setup:
|
|
115
|
+
/* ── First-time setup: prompt for the key without disclosing it over HTTP ── */
|
|
152
116
|
<>
|
|
153
117
|
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.1s both' }}>
|
|
154
118
|
<h1 className="font-display text-[36px] font-800 leading-[1.05] tracking-[-0.04em] mb-3">
|
|
155
|
-
|
|
119
|
+
First-Time Setup
|
|
156
120
|
</h1>
|
|
157
121
|
<p className="text-[14px] text-text-2 mb-8">
|
|
158
|
-
|
|
122
|
+
Enter the access key generated for this server. It is shown in the terminal on first launch and stored in <code className="text-text-2">.env.local</code>.
|
|
159
123
|
</p>
|
|
160
124
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
className="text-text-3 shrink-0"
|
|
176
|
-
>
|
|
177
|
-
{copied ? (
|
|
178
|
-
<path d="M20 6L9 17l-5-5" />
|
|
179
|
-
) : (
|
|
180
|
-
<>
|
|
181
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
182
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
183
|
-
</>
|
|
184
|
-
)}
|
|
185
|
-
</svg>
|
|
125
|
+
<form onSubmit={handleSubmit} className="flex flex-col items-center gap-4">
|
|
126
|
+
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.2s both', width: '100%', display: 'flex', justifyContent: 'center' }}>
|
|
127
|
+
<input
|
|
128
|
+
type="password"
|
|
129
|
+
value={key}
|
|
130
|
+
onChange={(e) => { setKey(e.target.value); setError('') }}
|
|
131
|
+
placeholder="Paste access key"
|
|
132
|
+
autoFocus
|
|
133
|
+
autoComplete="off"
|
|
134
|
+
className="w-full max-w-[320px] px-6 py-4 rounded-[16px] border border-white/[0.08] bg-surface
|
|
135
|
+
text-text text-[16px] text-center font-mono outline-none
|
|
136
|
+
transition-all duration-200 placeholder:text-text-3/70
|
|
137
|
+
focus:border-accent-bright/30 focus:shadow-[0_0_30px_rgba(99,102,241,0.1)]"
|
|
138
|
+
/>
|
|
186
139
|
</div>
|
|
187
|
-
</div>
|
|
188
140
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}}
|
|
196
|
-
>
|
|
197
|
-
<span className="text-text-3">Click to copy · Also saved in </span>
|
|
198
|
-
<code className="text-text-2">.env.local</code>
|
|
199
|
-
</p>
|
|
200
|
-
<p
|
|
201
|
-
className="absolute inset-x-0 text-[12px] text-emerald-400 font-medium transition-all duration-300"
|
|
202
|
-
style={{
|
|
203
|
-
opacity: copied ? 1 : 0,
|
|
204
|
-
transform: copied ? 'translateY(0)' : 'translateY(4px)',
|
|
205
|
-
}}
|
|
206
|
-
>
|
|
207
|
-
Key copied to clipboard
|
|
141
|
+
{error && (
|
|
142
|
+
<p className="text-[13px] text-red-400" style={{ animation: 'ai-shake 0.5s' }}>{error}</p>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
<p className="text-[12px] text-text-3 max-w-[340px]" style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.3s both' }}>
|
|
146
|
+
The key is never sent back by the server over unauthenticated HTTP anymore. Read it from the launch terminal, or open <code className="text-text-2">.env.local</code> locally.
|
|
208
147
|
</p>
|
|
209
|
-
</div>
|
|
210
148
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
149
|
+
<div style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.4s both' }}>
|
|
150
|
+
<button
|
|
151
|
+
type="submit"
|
|
152
|
+
disabled={loading || !key.trim()}
|
|
153
|
+
className="px-12 py-4 rounded-[16px] border-none bg-accent-bright text-white text-[16px] font-display font-600
|
|
154
|
+
cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
|
|
155
|
+
shadow-[0_6px_28px_rgba(99,102,241,0.3)] disabled:opacity-30"
|
|
156
|
+
>
|
|
157
|
+
{loading ? 'Connecting...' : 'Connect'}
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
</form>
|
|
222
161
|
</>
|
|
223
162
|
) : (
|
|
224
163
|
/* ── Returning user: enter key ── */
|