@swarmclawai/swarmclaw 0.7.7 → 0.8.0
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 +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -6,6 +6,7 @@ import { createProviderConfig, updateProviderConfig, deleteProviderConfig } from
|
|
|
6
6
|
import { api } from '@/lib/api-client'
|
|
7
7
|
import { fetchProviderModelDiscovery } from '@/lib/provider-model-discovery-client'
|
|
8
8
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
9
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
9
10
|
import { toast } from 'sonner'
|
|
10
11
|
|
|
11
12
|
export function ProviderSheet() {
|
|
@@ -40,6 +41,8 @@ export function ProviderSheet() {
|
|
|
40
41
|
const [liveLoading, setLiveLoading] = useState(false)
|
|
41
42
|
const [liveMessage, setLiveMessage] = useState('')
|
|
42
43
|
const [liveCached, setLiveCached] = useState(false)
|
|
44
|
+
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
45
|
+
const [deleting, setDeleting] = useState(false)
|
|
43
46
|
|
|
44
47
|
// Find editing provider in custom configs OR built-in list
|
|
45
48
|
const editingCustom = editingId ? providerConfigs.find((c) => c.id === editingId) : null
|
|
@@ -122,6 +125,8 @@ export function ProviderSheet() {
|
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
const onClose = () => {
|
|
128
|
+
setConfirmDelete(false)
|
|
129
|
+
setDeleting(false)
|
|
125
130
|
setOpen(false)
|
|
126
131
|
setEditingId(null)
|
|
127
132
|
}
|
|
@@ -162,14 +167,17 @@ export function ProviderSheet() {
|
|
|
162
167
|
|
|
163
168
|
const handleDelete = async () => {
|
|
164
169
|
if (editingCustom) {
|
|
165
|
-
|
|
170
|
+
setDeleting(true)
|
|
166
171
|
try {
|
|
167
172
|
await deleteProviderConfig(editingCustom.id)
|
|
168
173
|
toast.success('Provider deleted')
|
|
169
174
|
await loadProviderConfigs()
|
|
175
|
+
setConfirmDelete(false)
|
|
170
176
|
onClose()
|
|
171
177
|
} catch (err: unknown) {
|
|
172
178
|
toast.error(err instanceof Error ? err.message : 'Failed to delete provider')
|
|
179
|
+
} finally {
|
|
180
|
+
setDeleting(false)
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
}
|
|
@@ -493,7 +501,7 @@ export function ProviderSheet() {
|
|
|
493
501
|
|
|
494
502
|
<div className="flex gap-3 pt-2 border-t border-white/[0.04]">
|
|
495
503
|
{editingCustom && (
|
|
496
|
-
<button onClick={
|
|
504
|
+
<button onClick={() => setConfirmDelete(true)} className="py-3.5 px-6 rounded-[14px] border border-red-500/20 bg-transparent text-red-400 text-[15px] font-600 cursor-pointer hover:bg-red-500/10 transition-all" style={{ fontFamily: 'inherit' }}>
|
|
497
505
|
Delete
|
|
498
506
|
</button>
|
|
499
507
|
)}
|
|
@@ -520,6 +528,17 @@ export function ProviderSheet() {
|
|
|
520
528
|
</button>
|
|
521
529
|
)}
|
|
522
530
|
</div>
|
|
531
|
+
<ConfirmDialog
|
|
532
|
+
open={confirmDelete}
|
|
533
|
+
title="Delete Provider?"
|
|
534
|
+
message={editingCustom ? `Delete custom provider "${editingCustom.name}"?` : 'Delete this provider?'}
|
|
535
|
+
confirmLabel={deleting ? 'Deleting...' : 'Delete'}
|
|
536
|
+
confirmDisabled={deleting}
|
|
537
|
+
cancelDisabled={deleting}
|
|
538
|
+
danger
|
|
539
|
+
onConfirm={() => { void handleDelete() }}
|
|
540
|
+
onCancel={() => { if (!deleting) setConfirmDelete(false) }}
|
|
541
|
+
/>
|
|
523
542
|
</BottomSheet>
|
|
524
543
|
)
|
|
525
544
|
}
|
|
@@ -4,6 +4,8 @@ import type { Schedule } from '@/types'
|
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { api } from '@/lib/api-client'
|
|
6
6
|
import { cronToHuman } from '@/lib/cron-human'
|
|
7
|
+
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
8
|
+
import { isUserCreatedSchedule } from '@/lib/schedule-origin'
|
|
7
9
|
|
|
8
10
|
const STATUS_COLORS: Record<string, string> = {
|
|
9
11
|
active: 'text-emerald-400 bg-emerald-400/[0.08]',
|
|
@@ -55,6 +57,7 @@ export function ScheduleCard({ schedule, inSidebar, index = 0 }: Props) {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
const agent = agents[schedule.agentId]
|
|
60
|
+
const creatorAgent = schedule.createdByAgentId ? agents[schedule.createdByAgentId] : null
|
|
58
61
|
const statusClass = STATUS_COLORS[schedule.status] || STATUS_COLORS.paused
|
|
59
62
|
const canToggle = schedule.status === 'active' || schedule.status === 'paused'
|
|
60
63
|
|
|
@@ -101,7 +104,7 @@ export function ScheduleCard({ schedule, inSidebar, index = 0 }: Props) {
|
|
|
101
104
|
</div>
|
|
102
105
|
</div>
|
|
103
106
|
<div className="text-[12px] text-text-3/70 mt-1.5 truncate">
|
|
104
|
-
{agent?.name || 'Unknown agent'} · {schedule.scheduleType}
|
|
107
|
+
Runs on {agent?.name || 'Unknown agent'} · {schedule.scheduleType}
|
|
105
108
|
{!inSidebar && schedule.scheduleType === 'cron' && schedule.cron && (
|
|
106
109
|
<span className="text-text-3/50 ml-1" title={schedule.cron}>({cronToHuman(schedule.cron)})</span>
|
|
107
110
|
)}
|
|
@@ -113,6 +116,27 @@ export function ScheduleCard({ schedule, inSidebar, index = 0 }: Props) {
|
|
|
113
116
|
</span>
|
|
114
117
|
)}
|
|
115
118
|
</div>
|
|
119
|
+
<div className="flex flex-wrap items-center gap-1.5 mt-2">
|
|
120
|
+
{creatorAgent ? (
|
|
121
|
+
<span className="inline-flex max-w-full items-center gap-1.5 rounded-[7px] bg-white/[0.05] px-2 py-1 text-[10px] font-600 text-text-2">
|
|
122
|
+
<AgentAvatar
|
|
123
|
+
seed={creatorAgent.avatarSeed}
|
|
124
|
+
avatarUrl={creatorAgent.avatarUrl}
|
|
125
|
+
name={creatorAgent.name}
|
|
126
|
+
size={14}
|
|
127
|
+
/>
|
|
128
|
+
<span className="truncate">Created by {creatorAgent.name}</span>
|
|
129
|
+
</span>
|
|
130
|
+
) : (
|
|
131
|
+
<span className="inline-flex max-w-full items-center gap-1.5 rounded-[7px] bg-white/[0.04] px-2 py-1 text-[10px] font-600 text-text-3">
|
|
132
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
133
|
+
<circle cx="12" cy="8" r="4" />
|
|
134
|
+
<path d="M4 20c1.5-3.5 4.6-5 8-5s6.5 1.5 8 5" />
|
|
135
|
+
</svg>
|
|
136
|
+
<span className="truncate">{isUserCreatedSchedule(schedule) ? 'Created manually' : 'Creator unknown'}</span>
|
|
137
|
+
</span>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
116
140
|
<div className="text-[11px] text-text-3/60 mt-1">
|
|
117
141
|
Next: {formatNext(schedule.nextRunAt)}
|
|
118
142
|
</div>
|
|
@@ -5,12 +5,15 @@ import { useAppStore } from '@/stores/use-app-store'
|
|
|
5
5
|
import { createSchedule, updateSchedule, deleteSchedule } from '@/lib/schedules'
|
|
6
6
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
7
7
|
import { AgentPickerList } from '@/components/shared/agent-picker-list'
|
|
8
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
8
9
|
import { inputClass } from '@/components/shared/form-styles'
|
|
10
|
+
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
9
11
|
import type { ScheduleType, ScheduleStatus } from '@/types'
|
|
10
12
|
import cronstrue from 'cronstrue'
|
|
11
13
|
import { SectionLabel } from '@/components/shared/section-label'
|
|
12
14
|
import { SCHEDULE_TEMPLATES, type ScheduleTemplate } from '@/lib/schedule-templates'
|
|
13
15
|
import { HintTip } from '@/components/shared/hint-tip'
|
|
16
|
+
import { isUserCreatedSchedule } from '@/lib/schedule-origin'
|
|
14
17
|
import { toast } from 'sonner'
|
|
15
18
|
import {
|
|
16
19
|
Newspaper, BarChart3, HeartPulse, PenLine, Trash2,
|
|
@@ -102,6 +105,8 @@ export function ScheduleSheet() {
|
|
|
102
105
|
const [intervalMs, setIntervalMs] = useState(3600000)
|
|
103
106
|
const [status, setStatus] = useState<ScheduleStatus>('active')
|
|
104
107
|
const [customCron, setCustomCron] = useState(false)
|
|
108
|
+
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
109
|
+
const [deleting, setDeleting] = useState(false)
|
|
105
110
|
|
|
106
111
|
const editing = editingId ? schedules[editingId] : null
|
|
107
112
|
const isCreating = !editing
|
|
@@ -163,6 +168,8 @@ export function ScheduleSheet() {
|
|
|
163
168
|
}, [cron])
|
|
164
169
|
|
|
165
170
|
const onClose = () => {
|
|
171
|
+
setConfirmDelete(false)
|
|
172
|
+
setDeleting(false)
|
|
166
173
|
setOpen(false)
|
|
167
174
|
setEditingId(null)
|
|
168
175
|
}
|
|
@@ -195,14 +202,17 @@ export function ScheduleSheet() {
|
|
|
195
202
|
|
|
196
203
|
const handleDelete = async () => {
|
|
197
204
|
if (!editing) return
|
|
198
|
-
|
|
205
|
+
setDeleting(true)
|
|
199
206
|
try {
|
|
200
207
|
await deleteSchedule(editing.id)
|
|
201
208
|
toast.success('Schedule deleted')
|
|
202
209
|
await loadSchedules()
|
|
210
|
+
setConfirmDelete(false)
|
|
203
211
|
onClose()
|
|
204
212
|
} catch (err: unknown) {
|
|
205
213
|
toast.error(err instanceof Error ? err.message : 'Failed to delete schedule')
|
|
214
|
+
} finally {
|
|
215
|
+
setDeleting(false)
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
|
|
@@ -211,6 +221,7 @@ export function ScheduleSheet() {
|
|
|
211
221
|
const step1Valid = scheduleType === 'cron' ? cron.trim().length > 0 : intervalMs > 0
|
|
212
222
|
|
|
213
223
|
const selectedAgent = agentId ? agents[agentId] : null
|
|
224
|
+
const creatorAgent = editing?.createdByAgentId ? agents[editing.createdByAgentId] : null
|
|
214
225
|
|
|
215
226
|
return (
|
|
216
227
|
<BottomSheet open={open} onClose={onClose} wide>
|
|
@@ -466,6 +477,26 @@ export function ScheduleSheet() {
|
|
|
466
477
|
<span className="text-[11px] text-text-3/50 uppercase tracking-wider font-600">Agent</span>
|
|
467
478
|
<div className="text-[14px] text-text font-600 mt-0.5">{selectedAgent?.name || agentId}</div>
|
|
468
479
|
</div>
|
|
480
|
+
{editing && (
|
|
481
|
+
<div>
|
|
482
|
+
<span className="text-[11px] text-text-3/50 uppercase tracking-wider font-600">Created By</span>
|
|
483
|
+
{creatorAgent ? (
|
|
484
|
+
<div className="mt-1 inline-flex items-center gap-2 rounded-[10px] bg-white/[0.04] px-3 py-2 text-[13px] text-text-2">
|
|
485
|
+
<AgentAvatar
|
|
486
|
+
seed={creatorAgent.avatarSeed}
|
|
487
|
+
avatarUrl={creatorAgent.avatarUrl}
|
|
488
|
+
name={creatorAgent.name}
|
|
489
|
+
size={18}
|
|
490
|
+
/>
|
|
491
|
+
<span>{creatorAgent.name}</span>
|
|
492
|
+
</div>
|
|
493
|
+
) : (
|
|
494
|
+
<div className="text-[13px] text-text-2 mt-0.5">
|
|
495
|
+
{isUserCreatedSchedule(editing) ? 'Manual / user-created' : 'Unknown'}
|
|
496
|
+
</div>
|
|
497
|
+
)}
|
|
498
|
+
</div>
|
|
499
|
+
)}
|
|
469
500
|
<div>
|
|
470
501
|
<span className="text-[11px] text-text-3/50 uppercase tracking-wider font-600">Task</span>
|
|
471
502
|
<div className="text-[13px] text-text-2 mt-0.5 whitespace-pre-wrap">{taskPrompt}</div>
|
|
@@ -497,7 +528,7 @@ export function ScheduleSheet() {
|
|
|
497
528
|
{/* Footer */}
|
|
498
529
|
<div className="flex gap-3 pt-2 border-t border-white/[0.04]">
|
|
499
530
|
{editing && step === 0 && (
|
|
500
|
-
<button onClick={
|
|
531
|
+
<button onClick={() => setConfirmDelete(true)} className="py-3.5 px-6 rounded-[14px] border border-red-500/20 bg-transparent text-red-400 text-[15px] font-600 cursor-pointer hover:bg-red-500/10 transition-all" style={{ fontFamily: 'inherit' }}>
|
|
501
532
|
Delete
|
|
502
533
|
</button>
|
|
503
534
|
)}
|
|
@@ -547,6 +578,17 @@ export function ScheduleSheet() {
|
|
|
547
578
|
</button>
|
|
548
579
|
)}
|
|
549
580
|
</div>
|
|
581
|
+
<ConfirmDialog
|
|
582
|
+
open={confirmDelete}
|
|
583
|
+
title="Delete Schedule?"
|
|
584
|
+
message={editing ? `Delete "${editing.name}"? This will remove the schedule from the app.` : 'Delete this schedule?'}
|
|
585
|
+
confirmLabel={deleting ? 'Deleting...' : 'Delete'}
|
|
586
|
+
confirmDisabled={deleting}
|
|
587
|
+
cancelDisabled={deleting}
|
|
588
|
+
danger
|
|
589
|
+
onConfirm={() => { void handleDelete() }}
|
|
590
|
+
onCancel={() => { if (!deleting) setConfirmDelete(false) }}
|
|
591
|
+
/>
|
|
550
592
|
</BottomSheet>
|
|
551
593
|
)
|
|
552
594
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
6
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
6
7
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
7
8
|
import { api } from '@/lib/api-client'
|
|
8
9
|
import { toast } from 'sonner'
|
|
@@ -25,6 +26,8 @@ export function SecretSheet() {
|
|
|
25
26
|
const [scope, setScope] = useState<'global' | 'agent'>('global')
|
|
26
27
|
const [agentIds, setAgentIds] = useState<string[]>([])
|
|
27
28
|
const [saving, setSaving] = useState(false)
|
|
29
|
+
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
30
|
+
const [deleting, setDeleting] = useState(false)
|
|
28
31
|
|
|
29
32
|
const editing = editingId ? secrets[editingId] : null
|
|
30
33
|
const agentList = Object.values(agents)
|
|
@@ -50,6 +53,8 @@ export function SecretSheet() {
|
|
|
50
53
|
}, [editing, open])
|
|
51
54
|
|
|
52
55
|
const handleClose = () => {
|
|
56
|
+
setConfirmDelete(false)
|
|
57
|
+
setDeleting(false)
|
|
53
58
|
setOpen(false)
|
|
54
59
|
setEditingId(null)
|
|
55
60
|
}
|
|
@@ -87,14 +92,17 @@ export function SecretSheet() {
|
|
|
87
92
|
|
|
88
93
|
const handleDelete = async () => {
|
|
89
94
|
if (!editing) return
|
|
90
|
-
|
|
95
|
+
setDeleting(true)
|
|
91
96
|
try {
|
|
92
97
|
await api('DELETE', `/secrets/${editing.id}`)
|
|
93
98
|
toast.success('Secret deleted')
|
|
94
99
|
await loadSecrets()
|
|
100
|
+
setConfirmDelete(false)
|
|
95
101
|
handleClose()
|
|
96
102
|
} catch (err: unknown) {
|
|
97
103
|
toast.error(err instanceof Error ? err.message : 'Failed to delete secret')
|
|
104
|
+
} finally {
|
|
105
|
+
setDeleting(false)
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
@@ -184,7 +192,7 @@ export function SecretSheet() {
|
|
|
184
192
|
<div className="flex gap-3 pt-3">
|
|
185
193
|
{editing && (
|
|
186
194
|
<button
|
|
187
|
-
onClick={
|
|
195
|
+
onClick={() => setConfirmDelete(true)}
|
|
188
196
|
className="px-5 py-3 rounded-[14px] border border-danger/30 bg-transparent text-danger text-[14px] font-600 cursor-pointer hover:bg-danger/10 transition-colors"
|
|
189
197
|
style={{ fontFamily: 'inherit' }}
|
|
190
198
|
>
|
|
@@ -202,6 +210,17 @@ export function SecretSheet() {
|
|
|
202
210
|
{saving ? 'Saving...' : editing ? 'Update' : 'Save'}
|
|
203
211
|
</button>
|
|
204
212
|
</div>
|
|
213
|
+
<ConfirmDialog
|
|
214
|
+
open={confirmDelete}
|
|
215
|
+
title="Delete Secret?"
|
|
216
|
+
message={editing ? `Delete "${editing.name}"? This will remove the stored secret from the app.` : 'Delete this secret?'}
|
|
217
|
+
confirmLabel={deleting ? 'Deleting...' : 'Delete'}
|
|
218
|
+
confirmDisabled={deleting}
|
|
219
|
+
cancelDisabled={deleting}
|
|
220
|
+
danger
|
|
221
|
+
onConfirm={() => { void handleDelete() }}
|
|
222
|
+
onCancel={() => { if (!deleting) setConfirmDelete(false) }}
|
|
223
|
+
/>
|
|
205
224
|
</div>
|
|
206
225
|
</BottomSheet>
|
|
207
226
|
)
|
|
@@ -4,6 +4,7 @@ import { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
|
|
4
4
|
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
6
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
7
|
+
import { toast } from 'sonner'
|
|
7
8
|
|
|
8
9
|
export function AgentSwitchDialog() {
|
|
9
10
|
const [open, setOpen] = useState(false)
|
|
@@ -47,10 +48,15 @@ export function AgentSwitchDialog() {
|
|
|
47
48
|
}, [agents, query])
|
|
48
49
|
|
|
49
50
|
const handleSelect = useCallback((agentId: string) => {
|
|
51
|
+
const agent = agents[agentId]
|
|
52
|
+
if (agent?.disabled === true && !agent.threadSessionId) {
|
|
53
|
+
toast.error(`${agent.name} is disabled. Re-enable it to start a new chat.`)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
50
56
|
setOpen(false)
|
|
51
57
|
void setCurrentAgent(agentId)
|
|
52
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
-
}, [])
|
|
59
|
+
}, [agents, setCurrentAgent])
|
|
54
60
|
|
|
55
61
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
56
62
|
if (e.key === 'ArrowDown') {
|
|
@@ -119,6 +125,11 @@ export function AgentSwitchDialog() {
|
|
|
119
125
|
<div className="flex-1 min-w-0">
|
|
120
126
|
<div className="flex items-center gap-2">
|
|
121
127
|
<span className="text-[13px] font-500 text-text truncate">{agent.name}</span>
|
|
128
|
+
{agent.disabled === true && (
|
|
129
|
+
<span className="px-1.5 py-0.5 rounded-[4px] bg-amber-400/[0.08] text-[10px] font-500 text-amber-300 shrink-0">
|
|
130
|
+
disabled
|
|
131
|
+
</span>
|
|
132
|
+
)}
|
|
122
133
|
{agent.id === currentAgentId && (
|
|
123
134
|
<span className="px-1.5 py-0.5 rounded-[4px] bg-accent-bright/15 text-[10px] font-500 text-accent-bright shrink-0">
|
|
124
135
|
current
|
|
@@ -9,9 +9,11 @@ interface Props {
|
|
|
9
9
|
onClose: () => void
|
|
10
10
|
children: ReactNode
|
|
11
11
|
wide?: boolean
|
|
12
|
+
title?: string
|
|
13
|
+
description?: string
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
export function BottomSheet({ open, onClose, children, wide }: Props) {
|
|
16
|
+
export function BottomSheet({ open, onClose, children, wide, title, description }: Props) {
|
|
15
17
|
return (
|
|
16
18
|
<DialogPrimitive.Root open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onClose() }}>
|
|
17
19
|
<DialogPrimitive.Portal>
|
|
@@ -27,10 +29,18 @@ export function BottomSheet({ open, onClose, children, wide }: Props) {
|
|
|
27
29
|
${wide ? 'sm:max-w-[760px]' : 'sm:max-w-[560px]'}`}
|
|
28
30
|
style={{ animationDuration: '220ms' }}
|
|
29
31
|
>
|
|
30
|
-
<div className="relative shrink-0 px-4 pt-
|
|
32
|
+
<div className="relative shrink-0 px-4 pt-4 pr-14 sm:px-5 sm:pt-6 sm:pr-16">
|
|
31
33
|
<div className="mx-auto h-1 w-10 rounded-full bg-white/[0.08] sm:hidden" />
|
|
34
|
+
<DialogPrimitive.Title className="sr-only">
|
|
35
|
+
{title || 'Dialog'}
|
|
36
|
+
</DialogPrimitive.Title>
|
|
37
|
+
{description ? (
|
|
38
|
+
<DialogPrimitive.Description className="sr-only">
|
|
39
|
+
{description}
|
|
40
|
+
</DialogPrimitive.Description>
|
|
41
|
+
) : null}
|
|
32
42
|
<DialogPrimitive.Close
|
|
33
|
-
className="absolute right-
|
|
43
|
+
className="absolute right-4 top-3.5 inline-flex h-9 w-9 items-center justify-center rounded-[12px] border border-white/[0.06] bg-white/[0.03] text-text-3 transition-all hover:bg-white/[0.06] hover:text-text-2 focus:outline-none focus:ring-2 focus:ring-accent-bright/30 sm:right-5 sm:top-5"
|
|
34
44
|
>
|
|
35
45
|
<XIcon className="size-4" />
|
|
36
46
|
<span className="sr-only">Close</span>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useMemo, useRef } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { toast } from 'sonner'
|
|
5
6
|
|
|
6
7
|
interface CommandItem {
|
|
7
8
|
id: string
|
|
@@ -129,10 +130,16 @@ export function CommandPalette() {
|
|
|
129
130
|
result.push({
|
|
130
131
|
id: `agent:${agent.id}`,
|
|
131
132
|
label: agent.name,
|
|
132
|
-
description:
|
|
133
|
+
description: agent.disabled === true
|
|
134
|
+
? `${agent.name} is disabled`
|
|
135
|
+
: `Open ${agent.name}'s chat`,
|
|
133
136
|
keywords: [agent.provider, agent.model, agent.description || ''].filter(Boolean),
|
|
134
137
|
category: 'agent',
|
|
135
138
|
onSelect: async () => {
|
|
139
|
+
if (agent.disabled === true && !agent.threadSessionId) {
|
|
140
|
+
toast.error(`${agent.name} is disabled. Re-enable it to start a new chat.`)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
136
143
|
await setCurrentAgent(agent.id)
|
|
137
144
|
setActiveView('agents')
|
|
138
145
|
setOpen(false)
|
|
@@ -14,14 +14,26 @@ interface Props {
|
|
|
14
14
|
title: string
|
|
15
15
|
message: string
|
|
16
16
|
confirmLabel?: string
|
|
17
|
+
confirmDisabled?: boolean
|
|
18
|
+
cancelDisabled?: boolean
|
|
17
19
|
danger?: boolean
|
|
18
20
|
onConfirm: () => void
|
|
19
21
|
onCancel: () => void
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
export function ConfirmDialog({
|
|
24
|
+
export function ConfirmDialog({
|
|
25
|
+
open,
|
|
26
|
+
title,
|
|
27
|
+
message,
|
|
28
|
+
confirmLabel = 'Confirm',
|
|
29
|
+
confirmDisabled = false,
|
|
30
|
+
cancelDisabled = false,
|
|
31
|
+
danger,
|
|
32
|
+
onConfirm,
|
|
33
|
+
onCancel,
|
|
34
|
+
}: Props) {
|
|
23
35
|
return (
|
|
24
|
-
<Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen) onCancel() }}>
|
|
36
|
+
<Dialog open={open} onOpenChange={(nextOpen) => { if (!nextOpen && !cancelDisabled) onCancel() }}>
|
|
25
37
|
<DialogContent
|
|
26
38
|
className="sm:max-w-[400px] rounded-[20px] border-white/[0.06] bg-raised p-0 shadow-[0_24px_80px_rgba(0,0,0,0.55)]"
|
|
27
39
|
>
|
|
@@ -38,7 +50,8 @@ export function ConfirmDialog({ open, title, message, confirmLabel = 'Confirm',
|
|
|
38
50
|
<button
|
|
39
51
|
type="button"
|
|
40
52
|
onClick={onCancel}
|
|
41
|
-
|
|
53
|
+
disabled={cancelDisabled}
|
|
54
|
+
className="flex-1 rounded-[12px] border border-white/[0.06] bg-transparent px-4 py-2.5 text-[13px] font-600 text-text-2 transition-all duration-200 hover:bg-surface disabled:cursor-not-allowed disabled:opacity-50"
|
|
42
55
|
style={{ fontFamily: 'inherit' }}
|
|
43
56
|
>
|
|
44
57
|
Cancel
|
|
@@ -46,10 +59,12 @@ export function ConfirmDialog({ open, title, message, confirmLabel = 'Confirm',
|
|
|
46
59
|
<button
|
|
47
60
|
type="button"
|
|
48
61
|
onClick={onConfirm}
|
|
62
|
+
disabled={confirmDisabled}
|
|
49
63
|
className={`flex-1 rounded-[12px] border-none px-4 py-2.5 text-[13px] font-600 text-white transition-all duration-200 active:scale-[0.98]
|
|
50
64
|
${danger
|
|
51
65
|
? 'bg-danger shadow-[0_4px_20px_rgba(244,63,94,0.2)]'
|
|
52
|
-
: 'bg-accent-bright shadow-[0_4px_20px_rgba(99,102,241,0.2)]'}
|
|
66
|
+
: 'bg-accent-bright shadow-[0_4px_20px_rgba(99,102,241,0.2)]'}
|
|
67
|
+
disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100`}
|
|
53
68
|
style={{ fontFamily: 'inherit' }}
|
|
54
69
|
>
|
|
55
70
|
{confirmLabel}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getConnectorPlatformLabel,
|
|
6
|
+
resolveConnectorPlatformMeta,
|
|
7
|
+
} from './connector-platform-icon'
|
|
8
|
+
|
|
9
|
+
describe('connector platform metadata', () => {
|
|
10
|
+
it('resolves legacy connector platforms used by stored runtime data', () => {
|
|
11
|
+
assert.deepEqual(resolveConnectorPlatformMeta('webchat'), {
|
|
12
|
+
label: 'Web Chat',
|
|
13
|
+
color: '#0EA5E9',
|
|
14
|
+
})
|
|
15
|
+
assert.deepEqual(resolveConnectorPlatformMeta('mockmail'), {
|
|
16
|
+
label: 'MockMail',
|
|
17
|
+
color: '#7C3AED',
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('falls back safely for unknown connector platform strings', () => {
|
|
22
|
+
assert.deepEqual(resolveConnectorPlatformMeta('custom-bridge'), {
|
|
23
|
+
label: 'Custom Bridge',
|
|
24
|
+
color: '#64748B',
|
|
25
|
+
})
|
|
26
|
+
assert.equal(getConnectorPlatformLabel('custom-bridge'), 'Custom Bridge')
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -24,10 +24,35 @@ export const CONNECTOR_PLATFORM_META: Record<ConnectorPlatform, { label: string;
|
|
|
24
24
|
googlechat: { label: 'Google Chat', color: '#00AC47' },
|
|
25
25
|
matrix: { label: 'Matrix', color: '#0DBD8B' },
|
|
26
26
|
email: { label: 'Email', color: '#EA4335' },
|
|
27
|
+
webchat: { label: 'Web Chat', color: '#0EA5E9' },
|
|
28
|
+
mockmail: { label: 'MockMail', color: '#7C3AED' },
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const FALLBACK_CONNECTOR_PLATFORM_META = { label: 'Connector', color: '#64748B' } as const
|
|
32
|
+
|
|
33
|
+
function formatUnknownConnectorPlatformLabel(platform: string): string {
|
|
34
|
+
const trimmed = platform.trim()
|
|
35
|
+
if (!trimmed) return FALLBACK_CONNECTOR_PLATFORM_META.label
|
|
36
|
+
return trimmed
|
|
37
|
+
.replace(/[_-]+/g, ' ')
|
|
38
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
39
|
+
.split(/\s+/)
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
42
|
+
.join(' ')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveConnectorPlatformMeta(platform: string): { label: string; color: string } {
|
|
46
|
+
const known = CONNECTOR_PLATFORM_META[platform as ConnectorPlatform]
|
|
47
|
+
if (known) return known
|
|
48
|
+
return {
|
|
49
|
+
label: formatUnknownConnectorPlatformLabel(platform),
|
|
50
|
+
color: FALLBACK_CONNECTOR_PLATFORM_META.color,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getConnectorPlatformLabel(platform: string): string {
|
|
55
|
+
return resolveConnectorPlatformMeta(platform).label
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
export function getConnectorIdFromSessionName(sessionName?: string | null): string | null {
|
|
@@ -46,7 +71,7 @@ export function getSessionConnector(
|
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
interface ConnectorPlatformIconProps {
|
|
49
|
-
platform:
|
|
74
|
+
platform: string
|
|
50
75
|
size?: number
|
|
51
76
|
className?: string
|
|
52
77
|
}
|
|
@@ -131,12 +156,20 @@ export function ConnectorPlatformIcon({
|
|
|
131
156
|
</svg>
|
|
132
157
|
)
|
|
133
158
|
default:
|
|
134
|
-
return
|
|
159
|
+
return (
|
|
160
|
+
<span
|
|
161
|
+
aria-hidden
|
|
162
|
+
className={cn('inline-flex items-center justify-center rounded-full font-700 uppercase', className)}
|
|
163
|
+
style={{ width: size, height: size, fontSize: Math.max(8, Math.floor(size * 0.5)), lineHeight: 1 }}
|
|
164
|
+
>
|
|
165
|
+
{getConnectorPlatformLabel(platform).charAt(0)}
|
|
166
|
+
</span>
|
|
167
|
+
)
|
|
135
168
|
}
|
|
136
169
|
}
|
|
137
170
|
|
|
138
171
|
interface ConnectorPlatformBadgeProps {
|
|
139
|
-
platform:
|
|
172
|
+
platform: string
|
|
140
173
|
size?: number
|
|
141
174
|
iconSize?: number
|
|
142
175
|
className?: string
|
|
@@ -152,7 +185,7 @@ export function ConnectorPlatformBadge({
|
|
|
152
185
|
roundedClassName = 'rounded-[10px]',
|
|
153
186
|
title,
|
|
154
187
|
}: ConnectorPlatformBadgeProps) {
|
|
155
|
-
const meta =
|
|
188
|
+
const meta = resolveConnectorPlatformMeta(platform)
|
|
156
189
|
const glyphSize = iconSize ?? Math.max(12, Math.floor(size * 0.52))
|
|
157
190
|
|
|
158
191
|
return (
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useCallback, useMemo } from 'react'
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
|
+
import { getPluginSourceLabel } from '@/lib/plugin-sources'
|
|
5
6
|
import type { PluginMeta, MarketplacePlugin } from '@/types'
|
|
6
7
|
import { toast } from 'sonner'
|
|
7
8
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
@@ -93,9 +94,12 @@ export function PluginManager() {
|
|
|
93
94
|
|
|
94
95
|
const safeFilename = `${p.id.replace(/[^a-zA-Z0-9.-]/g, '_')}.js`
|
|
95
96
|
|
|
96
|
-
await api('POST', '/plugins/install', {
|
|
97
|
-
url: p.url,
|
|
98
|
-
filename: safeFilename
|
|
97
|
+
await api('POST', '/plugins/install', {
|
|
98
|
+
url: p.url,
|
|
99
|
+
filename: safeFilename,
|
|
100
|
+
installMethod: 'marketplace',
|
|
101
|
+
sourceLabel: p.source,
|
|
102
|
+
installSource: p.catalogSource || p.source,
|
|
99
103
|
})
|
|
100
104
|
|
|
101
105
|
await loadPlugins()
|
|
@@ -154,6 +158,18 @@ export function PluginManager() {
|
|
|
154
158
|
)}
|
|
155
159
|
</div>
|
|
156
160
|
<div className="text-[11px] font-mono text-text-3/40 truncate">{p.filename}</div>
|
|
161
|
+
<div className="flex items-center gap-1.5 mt-1.5 flex-wrap">
|
|
162
|
+
{p.sourceLabel && (
|
|
163
|
+
<span className="text-[9px] font-700 px-1.5 py-0.5 rounded uppercase tracking-wider bg-sky-500/10 text-sky-300">
|
|
164
|
+
{getPluginSourceLabel(p.sourceLabel)}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
{p.installSource && p.installSource !== p.sourceLabel && (
|
|
168
|
+
<span className="text-[9px] font-700 px-1.5 py-0.5 rounded uppercase tracking-wider bg-white/[0.04] text-text-3/65">
|
|
169
|
+
via {getPluginSourceLabel(p.installSource)}
|
|
170
|
+
</span>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
157
173
|
{p.description && <div className="text-[12px] text-text-3/70 mt-1 line-clamp-1">{p.description}</div>}
|
|
158
174
|
{p.hasDependencyManifest && (
|
|
159
175
|
<div className="mt-1.5 flex items-center gap-2 text-[10px] font-700 uppercase tracking-[0.08em]">
|
|
@@ -320,9 +336,16 @@ export function PluginManager() {
|
|
|
320
336
|
<div className="min-w-0 flex-1">
|
|
321
337
|
<div className="flex items-center gap-3 mb-1.5">
|
|
322
338
|
<span className="text-[16px] font-700 text-text tracking-tight">{p.name}</span>
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
339
|
+
{p.source && (
|
|
340
|
+
<span className="text-[9px] font-800 uppercase px-2 py-0.5 rounded-[6px] border bg-sky-500/10 text-sky-300 border-sky-500/20">
|
|
341
|
+
{getPluginSourceLabel(p.source)}
|
|
342
|
+
</span>
|
|
343
|
+
)}
|
|
344
|
+
{p.catalogSource && p.catalogSource !== p.source && (
|
|
345
|
+
<span className="text-[9px] font-800 uppercase px-2 py-0.5 rounded-[6px] border bg-white/[0.04] text-text-3/70 border-white/[0.08]">
|
|
346
|
+
via {getPluginSourceLabel(p.catalogSource)}
|
|
347
|
+
</span>
|
|
348
|
+
)}
|
|
326
349
|
</div>
|
|
327
350
|
<p className="text-[13px] text-text-3/80 leading-relaxed mb-4 line-clamp-2">{p.description}</p>
|
|
328
351
|
|