@swarmclawai/swarmclaw 0.3.1 → 0.4.5
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 +33 -13
- package/bin/server-cmd.js +14 -7
- package/bin/swarmclaw.js +3 -1
- package/bin/update-cmd.js +120 -0
- package/next.config.ts +10 -0
- package/package.json +4 -1
- package/src/app/api/agents/[id]/route.ts +20 -18
- package/src/app/api/agents/[id]/thread/route.ts +4 -3
- package/src/app/api/agents/route.ts +8 -3
- package/src/app/api/auth/route.ts +3 -1
- package/src/app/api/claude-skills/route.ts +3 -1
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/connectors/[id]/route.ts +14 -3
- package/src/app/api/connectors/[id]/webhook/route.ts +99 -0
- package/src/app/api/connectors/route.ts +12 -4
- package/src/app/api/credentials/[id]/route.ts +2 -1
- package/src/app/api/credentials/route.ts +5 -3
- package/src/app/api/daemon/route.ts +6 -1
- package/src/app/api/documents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +8 -0
- package/src/app/api/ip/route.ts +3 -1
- package/src/app/api/knowledge/[id]/route.ts +5 -4
- package/src/app/api/knowledge/upload/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/route.ts +11 -14
- package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
- package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
- package/src/app/api/mcp-servers/route.ts +5 -3
- package/src/app/api/memory/[id]/route.ts +9 -8
- package/src/app/api/memory/route.ts +2 -2
- package/src/app/api/memory-images/[filename]/route.ts +2 -1
- package/src/app/api/openclaw/directory/route.ts +26 -0
- package/src/app/api/openclaw/discover/route.ts +61 -0
- package/src/app/api/openclaw/sync/route.ts +30 -0
- package/src/app/api/orchestrator/graph/route.ts +25 -0
- package/src/app/api/orchestrator/run/route.ts +2 -2
- package/src/app/api/plugins/marketplace/route.ts +3 -1
- package/src/app/api/plugins/route.ts +3 -1
- package/src/app/api/projects/[id]/route.ts +55 -0
- package/src/app/api/projects/route.ts +27 -0
- package/src/app/api/providers/[id]/models/route.ts +2 -1
- package/src/app/api/providers/[id]/route.ts +13 -12
- package/src/app/api/providers/configs/route.ts +3 -1
- package/src/app/api/providers/route.ts +7 -3
- package/src/app/api/schedules/[id]/route.ts +16 -15
- package/src/app/api/schedules/[id]/run/route.ts +4 -3
- package/src/app/api/schedules/route.ts +8 -3
- package/src/app/api/secrets/[id]/route.ts +16 -17
- package/src/app/api/secrets/route.ts +5 -3
- package/src/app/api/sessions/[id]/chat/route.ts +5 -2
- package/src/app/api/sessions/[id]/clear/route.ts +2 -1
- package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
- package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
- package/src/app/api/sessions/[id]/messages/route.ts +2 -1
- package/src/app/api/sessions/[id]/retry/route.ts +2 -1
- package/src/app/api/sessions/[id]/route.ts +2 -1
- package/src/app/api/sessions/route.ts +11 -4
- package/src/app/api/settings/route.ts +3 -1
- package/src/app/api/setup/doctor/route.ts +1 -0
- package/src/app/api/setup/openclaw-device/route.ts +3 -1
- package/src/app/api/skills/[id]/route.ts +23 -21
- package/src/app/api/skills/import/route.ts +2 -2
- package/src/app/api/skills/route.ts +5 -3
- package/src/app/api/tasks/[id]/approve/route.ts +74 -0
- package/src/app/api/tasks/[id]/route.ts +9 -5
- package/src/app/api/tasks/route.ts +5 -2
- package/src/app/api/tts/stream/route.ts +48 -0
- package/src/app/api/upload/route.ts +2 -2
- package/src/app/api/uploads/[filename]/route.ts +4 -1
- package/src/app/api/usage/route.ts +3 -1
- package/src/app/api/version/route.ts +3 -1
- package/src/app/api/webhooks/[id]/route.ts +31 -32
- package/src/app/api/webhooks/route.ts +5 -3
- package/src/app/icon.svg +58 -0
- package/src/app/page.tsx +11 -26
- package/src/cli/index.js +28 -9
- package/src/cli/index.ts +45 -2
- package/src/cli/spec.js +2 -8
- package/src/components/agents/agent-card.tsx +1 -1
- package/src/components/agents/agent-list.tsx +3 -1
- package/src/components/agents/agent-sheet.tsx +166 -81
- package/src/components/chat/chat-area.tsx +71 -34
- package/src/components/chat/chat-header.tsx +141 -29
- package/src/components/chat/chat-tool-toggles.tsx +12 -53
- package/src/components/chat/message-bubble.tsx +110 -42
- package/src/components/chat/tool-call-bubble.tsx +50 -6
- package/src/components/chat/tool-request-banner.tsx +1 -9
- package/src/components/chat/voice-overlay.tsx +80 -0
- package/src/components/connectors/connector-list.tsx +9 -10
- package/src/components/connectors/connector-sheet.tsx +55 -36
- package/src/components/input/chat-input.tsx +72 -56
- package/src/components/knowledge/knowledge-list.tsx +27 -31
- package/src/components/layout/app-layout.tsx +133 -90
- package/src/components/layout/daemon-indicator.tsx +3 -5
- package/src/components/logs/log-list.tsx +5 -9
- package/src/components/mcp-servers/mcp-server-list.tsx +24 -2
- package/src/components/memory/memory-detail.tsx +1 -1
- package/src/components/plugins/plugin-list.tsx +227 -27
- package/src/components/projects/project-list.tsx +122 -0
- package/src/components/projects/project-sheet.tsx +135 -0
- package/src/components/providers/provider-list.tsx +46 -13
- package/src/components/providers/provider-sheet.tsx +0 -45
- package/src/components/runs/run-list.tsx +6 -15
- package/src/components/schedules/schedule-card.tsx +54 -4
- package/src/components/schedules/schedule-list.tsx +9 -4
- package/src/components/schedules/schedule-sheet.tsx +0 -47
- package/src/components/secrets/secrets-list.tsx +20 -2
- package/src/components/sessions/new-session-sheet.tsx +14 -15
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/sessions/session-list.tsx +7 -7
- package/src/components/shared/connector-platform-icon.tsx +26 -20
- package/src/components/shared/model-combobox.tsx +148 -0
- package/src/components/shared/settings/section-heartbeat.tsx +8 -40
- package/src/components/shared/settings/section-orchestrator.tsx +9 -11
- package/src/components/shared/settings/section-web-search.tsx +56 -0
- package/src/components/shared/settings/settings-page.tsx +73 -0
- package/src/components/skills/skill-list.tsx +262 -35
- package/src/components/skills/skill-sheet.tsx +0 -45
- package/src/components/tasks/task-board.tsx +3 -6
- package/src/components/tasks/task-card.tsx +43 -1
- package/src/components/tasks/task-list.tsx +8 -7
- package/src/components/tasks/task-sheet.tsx +0 -44
- package/src/components/usage/usage-list.tsx +12 -4
- package/src/hooks/use-continuous-speech.ts +144 -0
- package/src/hooks/use-view-router.ts +52 -0
- package/src/hooks/use-voice-conversation.ts +80 -0
- package/src/hooks/use-ws.ts +66 -0
- package/src/instrumentation.ts +2 -0
- package/src/lib/chat.ts +14 -2
- package/src/lib/id.ts +6 -0
- package/src/lib/projects.ts +13 -0
- package/src/lib/provider-sets.ts +5 -0
- package/src/lib/providers/anthropic.ts +15 -2
- package/src/lib/providers/index.ts +8 -0
- package/src/lib/providers/ollama.ts +10 -2
- package/src/lib/providers/openai.ts +42 -13
- package/src/lib/providers/openclaw.ts +11 -0
- package/src/lib/server/api-routes.test.ts +5 -6
- package/src/lib/server/build-llm.ts +17 -4
- package/src/lib/server/chat-execution.ts +57 -8
- package/src/lib/server/collection-helpers.ts +54 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +208 -0
- package/src/lib/server/connectors/bluebubbles.ts +357 -0
- package/src/lib/server/connectors/connector-routing.test.ts +1 -1
- package/src/lib/server/connectors/googlechat.ts +46 -7
- package/src/lib/server/connectors/manager.ts +401 -6
- package/src/lib/server/connectors/media.ts +2 -2
- package/src/lib/server/connectors/openclaw.ts +64 -0
- package/src/lib/server/connectors/pairing.test.ts +99 -0
- package/src/lib/server/connectors/pairing.ts +256 -0
- package/src/lib/server/connectors/signal.ts +1 -0
- package/src/lib/server/connectors/teams.ts +5 -5
- package/src/lib/server/connectors/types.ts +10 -0
- package/src/lib/server/context-manager.ts +1 -1
- package/src/lib/server/daemon-state.ts +3 -0
- package/src/lib/server/data-dir.ts +1 -0
- package/src/lib/server/execution-log.ts +3 -3
- package/src/lib/server/heartbeat-service.ts +67 -3
- package/src/lib/server/knowledge-db.test.ts +2 -33
- package/src/lib/server/langgraph-checkpoint.ts +274 -0
- package/src/lib/server/main-agent-loop.ts +67 -8
- package/src/lib/server/memory-db.ts +6 -6
- package/src/lib/server/openclaw-approvals.ts +105 -0
- package/src/lib/server/openclaw-sync.ts +496 -0
- package/src/lib/server/orchestrator-lg.ts +422 -20
- package/src/lib/server/orchestrator.ts +29 -9
- package/src/lib/server/process-manager.ts +2 -2
- package/src/lib/server/queue.ts +39 -13
- package/src/lib/server/scheduler.ts +2 -2
- package/src/lib/server/session-mailbox.ts +2 -2
- package/src/lib/server/session-run-manager.ts +8 -3
- package/src/lib/server/session-tools/connector.ts +51 -4
- package/src/lib/server/session-tools/crud.ts +3 -3
- package/src/lib/server/session-tools/delegate.ts +5 -5
- package/src/lib/server/session-tools/file.ts +176 -3
- package/src/lib/server/session-tools/index.ts +4 -0
- package/src/lib/server/session-tools/memory.ts +2 -2
- package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
- package/src/lib/server/session-tools/sandbox.ts +197 -0
- package/src/lib/server/session-tools/search-providers.ts +270 -0
- package/src/lib/server/session-tools/session-info.ts +2 -2
- package/src/lib/server/session-tools/web.ts +47 -66
- package/src/lib/server/storage-mcp.test.ts +25 -2
- package/src/lib/server/storage.ts +36 -7
- package/src/lib/server/stream-agent-chat.ts +106 -22
- package/src/lib/server/task-result.test.ts +44 -0
- package/src/lib/server/task-result.ts +14 -0
- package/src/lib/server/task-validation.test.ts +23 -0
- package/src/lib/server/task-validation.ts +5 -3
- package/src/lib/server/ws-hub.ts +85 -0
- package/src/lib/tool-definitions.ts +44 -0
- package/src/lib/tts-stream.ts +130 -0
- package/src/lib/upload.ts +7 -1
- package/src/lib/view-routes.ts +28 -0
- package/src/lib/ws-client.ts +124 -0
- package/src/proxy.ts +3 -0
- package/src/stores/use-app-store.ts +28 -1
- package/src/stores/use-chat-store.ts +42 -14
- package/src/types/index.ts +34 -2
- package/src/app/api/agents/generate/route.ts +0 -42
- package/src/app/api/generate/info/route.ts +0 -12
- package/src/app/api/generate/route.ts +0 -106
- package/src/app/favicon.ico +0 -0
- package/src/components/shared/ai-gen-block.tsx +0 -77
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { Connector, ConnectorPlatform, Session } from '@/types'
|
|
2
2
|
import { cn } from '@/lib/utils'
|
|
3
|
+
import { BsMicrosoftTeams } from 'react-icons/bs'
|
|
4
|
+
import {
|
|
5
|
+
SiApple,
|
|
6
|
+
SiDiscord,
|
|
7
|
+
SiGooglechat,
|
|
8
|
+
SiMatrix,
|
|
9
|
+
SiSignal,
|
|
10
|
+
SiSlack,
|
|
11
|
+
SiTelegram,
|
|
12
|
+
SiWhatsapp,
|
|
13
|
+
} from 'react-icons/si'
|
|
3
14
|
|
|
4
15
|
export const CONNECTOR_PLATFORM_META: Record<ConnectorPlatform, { label: string; color: string }> = {
|
|
5
16
|
discord: { label: 'Discord', color: '#5865F2' },
|
|
@@ -7,6 +18,7 @@ export const CONNECTOR_PLATFORM_META: Record<ConnectorPlatform, { label: string;
|
|
|
7
18
|
slack: { label: 'Slack', color: '#4A154B' },
|
|
8
19
|
whatsapp: { label: 'WhatsApp', color: '#25D366' },
|
|
9
20
|
openclaw: { label: 'OpenClaw', color: '#F97316' },
|
|
21
|
+
bluebubbles: { label: 'BlueBubbles', color: '#2E89FF' },
|
|
10
22
|
signal: { label: 'Signal', color: '#3A76F0' },
|
|
11
23
|
teams: { label: 'Teams', color: '#6264A7' },
|
|
12
24
|
googlechat: { label: 'Google Chat', color: '#00AC47' },
|
|
@@ -45,29 +57,23 @@ export function ConnectorPlatformIcon({
|
|
|
45
57
|
}: ConnectorPlatformIconProps) {
|
|
46
58
|
switch (platform) {
|
|
47
59
|
case 'discord':
|
|
48
|
-
return
|
|
49
|
-
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" className={className}>
|
|
50
|
-
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
|
|
51
|
-
</svg>
|
|
52
|
-
)
|
|
60
|
+
return <SiDiscord size={size} className={className} />
|
|
53
61
|
case 'telegram':
|
|
54
|
-
return
|
|
55
|
-
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" className={className}>
|
|
56
|
-
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
|
57
|
-
</svg>
|
|
58
|
-
)
|
|
62
|
+
return <SiTelegram size={size} className={className} />
|
|
59
63
|
case 'slack':
|
|
60
|
-
return
|
|
61
|
-
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" className={className}>
|
|
62
|
-
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
|
|
63
|
-
</svg>
|
|
64
|
-
)
|
|
64
|
+
return <SiSlack size={size} className={className} />
|
|
65
65
|
case 'whatsapp':
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
return <SiWhatsapp size={size} className={className} />
|
|
67
|
+
case 'bluebubbles':
|
|
68
|
+
return <SiApple size={size} className={className} />
|
|
69
|
+
case 'signal':
|
|
70
|
+
return <SiSignal size={size} className={className} />
|
|
71
|
+
case 'googlechat':
|
|
72
|
+
return <SiGooglechat size={size} className={className} />
|
|
73
|
+
case 'matrix':
|
|
74
|
+
return <SiMatrix size={size} className={className} />
|
|
75
|
+
case 'teams':
|
|
76
|
+
return <BsMicrosoftTeams size={size} className={className} />
|
|
71
77
|
case 'openclaw':
|
|
72
78
|
return (
|
|
73
79
|
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useRef, useEffect, useCallback } from 'react'
|
|
4
|
+
import { api } from '@/lib/api-client'
|
|
5
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
6
|
+
|
|
7
|
+
interface ModelComboboxProps {
|
|
8
|
+
providerId: string
|
|
9
|
+
value: string
|
|
10
|
+
onChange: (model: string) => void
|
|
11
|
+
models: string[]
|
|
12
|
+
defaultModels?: string[]
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ModelCombobox({
|
|
17
|
+
providerId,
|
|
18
|
+
value,
|
|
19
|
+
onChange,
|
|
20
|
+
models,
|
|
21
|
+
defaultModels = [],
|
|
22
|
+
className,
|
|
23
|
+
}: ModelComboboxProps) {
|
|
24
|
+
const [open, setOpen] = useState(false)
|
|
25
|
+
const [query, setQuery] = useState('')
|
|
26
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
27
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
28
|
+
const loadProviders = useAppStore((s) => s.loadProviders)
|
|
29
|
+
|
|
30
|
+
const filtered = query
|
|
31
|
+
? models.filter((m) => m.toLowerCase().includes(query.toLowerCase()))
|
|
32
|
+
: models
|
|
33
|
+
|
|
34
|
+
const isCustom = (m: string) => defaultModels.length > 0 && !defaultModels.includes(m)
|
|
35
|
+
const showAdd = query && !models.some((m) => m.toLowerCase() === query.toLowerCase())
|
|
36
|
+
|
|
37
|
+
const persistModels = useCallback(async (next: string[]) => {
|
|
38
|
+
await api('PUT', `/providers/${providerId}/models`, { models: next })
|
|
39
|
+
await loadProviders()
|
|
40
|
+
}, [providerId, loadProviders])
|
|
41
|
+
|
|
42
|
+
const addModel = useCallback(async (name: string) => {
|
|
43
|
+
const next = [...models, name]
|
|
44
|
+
await persistModels(next)
|
|
45
|
+
onChange(name)
|
|
46
|
+
setQuery('')
|
|
47
|
+
setOpen(false)
|
|
48
|
+
}, [models, persistModels, onChange])
|
|
49
|
+
|
|
50
|
+
const removeModel = useCallback(async (name: string, e: React.MouseEvent) => {
|
|
51
|
+
e.stopPropagation()
|
|
52
|
+
const next = models.filter((m) => m !== name)
|
|
53
|
+
if (value === name) onChange(next[0] || '')
|
|
54
|
+
if (next.length === defaultModels.length && next.every((m) => defaultModels.includes(m))) {
|
|
55
|
+
await api('DELETE', `/providers/${providerId}/models`)
|
|
56
|
+
} else {
|
|
57
|
+
await persistModels(next)
|
|
58
|
+
}
|
|
59
|
+
await loadProviders()
|
|
60
|
+
}, [models, defaultModels, value, onChange, providerId, persistModels, loadProviders])
|
|
61
|
+
|
|
62
|
+
const selectModel = useCallback((m: string) => {
|
|
63
|
+
onChange(m)
|
|
64
|
+
setQuery('')
|
|
65
|
+
setOpen(false)
|
|
66
|
+
}, [onChange])
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const handler = (e: MouseEvent) => {
|
|
70
|
+
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
|
71
|
+
setOpen(false)
|
|
72
|
+
setQuery('')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
document.addEventListener('mousedown', handler)
|
|
76
|
+
return () => document.removeEventListener('mousedown', handler)
|
|
77
|
+
}, [])
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div ref={containerRef} className="relative">
|
|
81
|
+
<div
|
|
82
|
+
className={`flex items-center ${className || ''}`}
|
|
83
|
+
onClick={() => {
|
|
84
|
+
setOpen(true)
|
|
85
|
+
setTimeout(() => inputRef.current?.focus(), 0)
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<input
|
|
89
|
+
ref={inputRef}
|
|
90
|
+
type="text"
|
|
91
|
+
value={open ? query : value}
|
|
92
|
+
onChange={(e) => {
|
|
93
|
+
setQuery(e.target.value)
|
|
94
|
+
if (!open) setOpen(true)
|
|
95
|
+
}}
|
|
96
|
+
onFocus={() => setOpen(true)}
|
|
97
|
+
placeholder={value || 'Select model…'}
|
|
98
|
+
className="w-full bg-transparent outline-none text-inherit placeholder:text-text-3/50"
|
|
99
|
+
style={{ fontFamily: 'inherit' }}
|
|
100
|
+
/>
|
|
101
|
+
<svg className="w-4 h-4 text-text-3 shrink-0 ml-2" viewBox="0 0 16 16" fill="none">
|
|
102
|
+
<path d="M4 6l4 4 4-4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
103
|
+
</svg>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
{open && (
|
|
107
|
+
<div className="absolute z-50 top-full left-0 right-0 mt-1 max-h-[240px] overflow-y-auto rounded-[12px] border border-white/[0.08] bg-surface-2 shadow-xl">
|
|
108
|
+
{filtered.map((m) => (
|
|
109
|
+
<div
|
|
110
|
+
key={m}
|
|
111
|
+
onClick={() => selectModel(m)}
|
|
112
|
+
className={`flex items-center justify-between px-3 py-2 text-[14px] cursor-pointer transition-colors hover:bg-white/[0.04] ${m === value ? 'text-accent-bright' : 'text-text'}`}
|
|
113
|
+
>
|
|
114
|
+
<span className="truncate">{m}</span>
|
|
115
|
+
{isCustom(m) && (
|
|
116
|
+
<button
|
|
117
|
+
onClick={(e) => removeModel(m, e)}
|
|
118
|
+
className="ml-2 p-0.5 rounded hover:bg-white/[0.08] text-text-3 hover:text-red-400 transition-colors shrink-0"
|
|
119
|
+
title="Remove custom model"
|
|
120
|
+
>
|
|
121
|
+
<svg className="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none">
|
|
122
|
+
<path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
123
|
+
</svg>
|
|
124
|
+
</button>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
))}
|
|
128
|
+
|
|
129
|
+
{showAdd && (
|
|
130
|
+
<div
|
|
131
|
+
onClick={() => addModel(query.trim())}
|
|
132
|
+
className="flex items-center gap-2 px-3 py-2 text-[14px] cursor-pointer transition-colors hover:bg-white/[0.04] text-accent-bright border-t border-white/[0.06]"
|
|
133
|
+
>
|
|
134
|
+
<svg className="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none">
|
|
135
|
+
<path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
136
|
+
</svg>
|
|
137
|
+
<span className="truncate">Add “{query.trim()}”</span>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{filtered.length === 0 && !showAdd && (
|
|
142
|
+
<div className="px-3 py-2 text-[14px] text-text-3">No models found</div>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
@@ -26,7 +26,7 @@ export function HeartbeatSection({ appSettings, patchSettings, inputClass }: Set
|
|
|
26
26
|
`Stopped heartbeat on ${result.updatedSessions} session(s); cancelled ${result.cancelledQueued} queued run(s), aborted ${result.abortedRunning} running run(s).`,
|
|
27
27
|
)
|
|
28
28
|
} catch (err: any) {
|
|
29
|
-
setHeartbeatBulkNotice(err?.message || 'Failed to disable heartbeat for all
|
|
29
|
+
setHeartbeatBulkNotice(err?.message || 'Failed to disable heartbeat for all agents.')
|
|
30
30
|
} finally {
|
|
31
31
|
setDisablingHeartbeats(false)
|
|
32
32
|
}
|
|
@@ -35,49 +35,20 @@ export function HeartbeatSection({ appSettings, patchSettings, inputClass }: Set
|
|
|
35
35
|
return (
|
|
36
36
|
<div className="mb-10">
|
|
37
37
|
<h3 className="font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
38
|
-
Heartbeat
|
|
38
|
+
Heartbeat Defaults
|
|
39
39
|
</h3>
|
|
40
40
|
<p className="text-[12px] text-text-3 mb-5">
|
|
41
|
-
|
|
41
|
+
Global defaults inherited by agents. Enable heartbeat and set interval/model per-agent in the agent editor.
|
|
42
42
|
</p>
|
|
43
43
|
<div className="p-6 rounded-[18px] bg-surface border border-white/[0.06]">
|
|
44
44
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-5">
|
|
45
45
|
<div>
|
|
46
|
-
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">
|
|
47
|
-
<input
|
|
48
|
-
type="text"
|
|
49
|
-
value={appSettings.heartbeatInterval ?? appSettings.heartbeatIntervalSec ?? '30m'}
|
|
50
|
-
onChange={(e) => {
|
|
51
|
-
const val = e.target.value.trim()
|
|
52
|
-
patchSettings({ heartbeatInterval: val || null })
|
|
53
|
-
}}
|
|
54
|
-
placeholder="30m"
|
|
55
|
-
className={inputClass}
|
|
56
|
-
style={{ fontFamily: 'inherit' }}
|
|
57
|
-
/>
|
|
58
|
-
<p className="text-[11px] text-text-3/60 mt-2">Duration string (e.g. 30m, 1h) or seconds. Set to 0 to disable.</p>
|
|
59
|
-
</div>
|
|
60
|
-
<div>
|
|
61
|
-
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Heartbeat Prompt</label>
|
|
46
|
+
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Default Prompt</label>
|
|
62
47
|
<input
|
|
63
48
|
type="text"
|
|
64
49
|
value={appSettings.heartbeatPrompt || ''}
|
|
65
50
|
onChange={(e) => patchSettings({ heartbeatPrompt: e.target.value || null })}
|
|
66
|
-
placeholder="Leave blank for default"
|
|
67
|
-
className={inputClass}
|
|
68
|
-
style={{ fontFamily: 'inherit' }}
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
|
|
73
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-5">
|
|
74
|
-
<div>
|
|
75
|
-
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Heartbeat Model</label>
|
|
76
|
-
<input
|
|
77
|
-
type="text"
|
|
78
|
-
value={appSettings.heartbeatModel || ''}
|
|
79
|
-
onChange={(e) => patchSettings({ heartbeatModel: e.target.value || null })}
|
|
80
|
-
placeholder="Leave blank for session default"
|
|
51
|
+
placeholder="Leave blank for built-in default"
|
|
81
52
|
className={inputClass}
|
|
82
53
|
style={{ fontFamily: 'inherit' }}
|
|
83
54
|
/>
|
|
@@ -142,20 +113,17 @@ export function HeartbeatSection({ appSettings, patchSettings, inputClass }: Set
|
|
|
142
113
|
</div>
|
|
143
114
|
|
|
144
115
|
<div>
|
|
145
|
-
<
|
|
146
|
-
Internal ping text used for ongoing sessions. Leave blank to use the default.
|
|
147
|
-
</p>
|
|
148
|
-
<div className="mt-4 flex items-center gap-2.5">
|
|
116
|
+
<div className="flex items-center gap-2.5">
|
|
149
117
|
<button
|
|
150
118
|
onClick={handleDisableAllHeartbeats}
|
|
151
119
|
disabled={disablingHeartbeats}
|
|
152
120
|
className="px-3.5 py-2 rounded-[10px] border border-rose-400/25 bg-rose-500/10 text-rose-300 hover:bg-rose-500/16 transition-colors cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed text-[12px] font-600"
|
|
153
121
|
style={{ fontFamily: 'inherit' }}
|
|
154
122
|
>
|
|
155
|
-
{disablingHeartbeats ? 'Stopping\u2026' : 'Stop All
|
|
123
|
+
{disablingHeartbeats ? 'Stopping\u2026' : 'Stop All Heartbeats'}
|
|
156
124
|
</button>
|
|
157
125
|
<span className="text-[11px] text-text-3/70">
|
|
158
|
-
Disables heartbeat on every
|
|
126
|
+
Disables heartbeat on every agent and cancels queued runs.
|
|
159
127
|
</span>
|
|
160
128
|
</div>
|
|
161
129
|
{heartbeatBulkNotice && (
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useAppStore } from '@/stores/use-app-store'
|
|
4
|
+
import { ModelCombobox } from '@/components/shared/model-combobox'
|
|
5
|
+
import { NON_LANGGRAPH_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
4
6
|
import type { SettingsSectionProps } from './types'
|
|
5
7
|
|
|
6
|
-
const NON_LANGGRAPH_PROVIDER_IDS = new Set(['claude-cli', 'codex-cli', 'opencode-cli'])
|
|
7
|
-
|
|
8
8
|
export function OrchestratorSection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) {
|
|
9
9
|
const providers = useAppStore((s) => s.providers)
|
|
10
10
|
const credentials = useAppStore((s) => s.credentials)
|
|
@@ -53,16 +53,14 @@ export function OrchestratorSection({ appSettings, patchSettings, inputClass }:
|
|
|
53
53
|
{lgProviderInfo && lgProviderInfo.models.length > 0 && (
|
|
54
54
|
<div className="mb-5">
|
|
55
55
|
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-3">Model</label>
|
|
56
|
-
<
|
|
56
|
+
<ModelCombobox
|
|
57
|
+
providerId={lgProviderInfo.id}
|
|
57
58
|
value={appSettings.langGraphModel || lgProviderInfo.models[0]}
|
|
58
|
-
onChange={(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<option key={m} value={m}>{m}</option>
|
|
64
|
-
))}
|
|
65
|
-
</select>
|
|
59
|
+
onChange={(m) => patchSettings({ langGraphModel: m })}
|
|
60
|
+
models={lgProviderInfo.models}
|
|
61
|
+
defaultModels={lgProviderInfo.defaultModels}
|
|
62
|
+
className={`${inputClass} cursor-pointer`}
|
|
63
|
+
/>
|
|
66
64
|
</div>
|
|
67
65
|
)}
|
|
68
66
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { SettingsSectionProps } from './types'
|
|
4
|
+
|
|
5
|
+
export function WebSearchSection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) {
|
|
6
|
+
const provider = appSettings.webSearchProvider || 'duckduckgo'
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="mb-10">
|
|
10
|
+
<h3 className="font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
11
|
+
Web Search
|
|
12
|
+
</h3>
|
|
13
|
+
<p className="text-[12px] text-text-3 mb-5">
|
|
14
|
+
Choose which search engine agents use for the <code className="text-[11px] font-mono text-text-2">web_search</code> tool.
|
|
15
|
+
</p>
|
|
16
|
+
<div className="p-6 rounded-[18px] bg-surface border border-white/[0.06]">
|
|
17
|
+
<div className="mb-5">
|
|
18
|
+
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Search Provider</label>
|
|
19
|
+
<select
|
|
20
|
+
value={provider}
|
|
21
|
+
onChange={(e) => patchSettings({ webSearchProvider: e.target.value as typeof provider })}
|
|
22
|
+
className={inputClass}
|
|
23
|
+
style={{ fontFamily: 'inherit' }}
|
|
24
|
+
>
|
|
25
|
+
<option value="duckduckgo">DuckDuckGo (default, no key required)</option>
|
|
26
|
+
<option value="google">Google (scraping, no key required)</option>
|
|
27
|
+
<option value="bing">Bing (scraping, no key required)</option>
|
|
28
|
+
<option value="searxng">SearXNG (self-hosted, no key required)</option>
|
|
29
|
+
<option value="tavily">Tavily (requires API key in Secrets)</option>
|
|
30
|
+
<option value="brave">Brave Search (requires API key in Secrets)</option>
|
|
31
|
+
</select>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{provider === 'searxng' && (
|
|
35
|
+
<div>
|
|
36
|
+
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">SearXNG URL</label>
|
|
37
|
+
<input
|
|
38
|
+
type="text"
|
|
39
|
+
value={appSettings.searxngUrl || ''}
|
|
40
|
+
onChange={(e) => patchSettings({ searxngUrl: e.target.value || undefined })}
|
|
41
|
+
placeholder="http://localhost:8080"
|
|
42
|
+
className={inputClass}
|
|
43
|
+
style={{ fontFamily: 'inherit' }}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
{(provider === 'tavily' || provider === 'brave') && (
|
|
49
|
+
<p className="text-[11px] text-text-3/70">
|
|
50
|
+
Add a secret named "{provider}" or "{provider}_api_key" in the Secrets section below.
|
|
51
|
+
</p>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { inputClass } from './utils'
|
|
6
|
+
import { UserPreferencesSection } from './section-user-preferences'
|
|
7
|
+
import { OrchestratorSection } from './section-orchestrator'
|
|
8
|
+
import { RuntimeLoopSection } from './section-runtime-loop'
|
|
9
|
+
import { CapabilityPolicySection } from './section-capability-policy'
|
|
10
|
+
import { VoiceSection } from './section-voice'
|
|
11
|
+
import { WebSearchSection } from './section-web-search'
|
|
12
|
+
import { HeartbeatSection } from './section-heartbeat'
|
|
13
|
+
import { EmbeddingSection } from './section-embedding'
|
|
14
|
+
import { MemorySection } from './section-memory'
|
|
15
|
+
import { SecretsSection } from './section-secrets'
|
|
16
|
+
import { ProvidersSection } from './section-providers'
|
|
17
|
+
import { PluginManager } from './plugin-manager'
|
|
18
|
+
|
|
19
|
+
export function SettingsPage() {
|
|
20
|
+
const loadProviders = useAppStore((s) => s.loadProviders)
|
|
21
|
+
const loadCredentials = useAppStore((s) => s.loadCredentials)
|
|
22
|
+
const appSettings = useAppStore((s) => s.appSettings)
|
|
23
|
+
const loadSettings = useAppStore((s) => s.loadSettings)
|
|
24
|
+
const updateSettings = useAppStore((s) => s.updateSettings)
|
|
25
|
+
const loadSecrets = useAppStore((s) => s.loadSecrets)
|
|
26
|
+
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
27
|
+
const credentials = useAppStore((s) => s.credentials)
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
loadProviders()
|
|
31
|
+
loadCredentials()
|
|
32
|
+
loadSettings()
|
|
33
|
+
loadSecrets()
|
|
34
|
+
loadAgents()
|
|
35
|
+
}, [])
|
|
36
|
+
|
|
37
|
+
const credList = Object.values(credentials)
|
|
38
|
+
const patchSettings = updateSettings
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="flex-1 flex flex-col h-full overflow-y-auto">
|
|
42
|
+
<div className="w-full max-w-3xl mx-auto px-6 py-8">
|
|
43
|
+
<div className="mb-10">
|
|
44
|
+
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">Settings</h2>
|
|
45
|
+
<p className="text-[14px] text-text-3">Manage providers, API keys & orchestrator engine</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<UserPreferencesSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
49
|
+
<OrchestratorSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
50
|
+
<RuntimeLoopSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
51
|
+
<CapabilityPolicySection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
52
|
+
<WebSearchSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
53
|
+
<VoiceSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
54
|
+
<HeartbeatSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
55
|
+
<EmbeddingSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} credList={credList} />
|
|
56
|
+
<MemorySection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
57
|
+
<SecretsSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
58
|
+
<ProvidersSection appSettings={appSettings} patchSettings={patchSettings} inputClass={inputClass} />
|
|
59
|
+
|
|
60
|
+
<div className="mb-10">
|
|
61
|
+
<h3 className="font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
62
|
+
Plugins
|
|
63
|
+
</h3>
|
|
64
|
+
<p className="text-[12px] text-text-3 mb-5">
|
|
65
|
+
Extend agent behavior with hooks. Install from the marketplace, a URL, or drop .js files into <code className="text-[11px] font-mono text-text-2">data/plugins/</code>.
|
|
66
|
+
<span className="text-text-3/70 ml-1">OpenClaw plugins are also supported.</span>
|
|
67
|
+
</p>
|
|
68
|
+
<PluginManager />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|