@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
|
@@ -13,6 +13,7 @@ import { ChatroomPickerList } from '@/components/shared/chatroom-picker-list'
|
|
|
13
13
|
import { SheetFooter } from '@/components/shared/sheet-footer'
|
|
14
14
|
import { SectionLabel } from '@/components/shared/section-label'
|
|
15
15
|
import { HintTip } from '@/components/shared/hint-tip'
|
|
16
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
16
17
|
import { useChatroomStore } from '@/stores/use-chatroom-store'
|
|
17
18
|
import { ConnectorHealth } from '@/components/connectors/connector-health'
|
|
18
19
|
|
|
@@ -53,6 +54,82 @@ interface ConnectorDoctorResponse {
|
|
|
53
54
|
policy?: ConnectorDoctorPolicyPreview | null
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
interface ConnectorConfigOption {
|
|
58
|
+
value: string
|
|
59
|
+
label: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface ConnectorConfigField {
|
|
63
|
+
key: string
|
|
64
|
+
label: string
|
|
65
|
+
placeholder: string
|
|
66
|
+
help?: string
|
|
67
|
+
type?: 'text' | 'select' | 'tags'
|
|
68
|
+
options?: ConnectorConfigOption[]
|
|
69
|
+
emptyLabel?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const FIELD_HINTS: Record<string, string> = {
|
|
73
|
+
channelIds: "Find these in your platform's developer settings. Leave empty to allow all channels",
|
|
74
|
+
chatIds: "Find these in your platform's developer settings. Leave empty to allow all chats",
|
|
75
|
+
roomIds: 'Leave empty to allow all rooms visible to the bot',
|
|
76
|
+
spaceIds: 'Leave empty to allow all configured spaces',
|
|
77
|
+
allowedJids: 'Phone numbers in international format, or WhatsApp JIDs. Leave empty to allow all',
|
|
78
|
+
allowFrom: 'Only needed for allowlist or pairing DM policy modes',
|
|
79
|
+
scopes: 'Press Enter after each scope to add it',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const BOOLEAN_SELECT_OPTIONS: ConnectorConfigOption[] = [
|
|
83
|
+
{ value: 'true', label: 'Enabled' },
|
|
84
|
+
{ value: 'false', label: 'Disabled' },
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
const THINKING_LEVEL_OPTIONS: ConnectorConfigOption[] = [
|
|
88
|
+
{ value: 'minimal', label: 'Minimal' },
|
|
89
|
+
{ value: 'low', label: 'Low' },
|
|
90
|
+
{ value: 'medium', label: 'Medium' },
|
|
91
|
+
{ value: 'high', label: 'High' },
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
const DM_POLICY_OPTIONS: ConnectorConfigOption[] = [
|
|
95
|
+
{ value: 'open', label: 'Open' },
|
|
96
|
+
{ value: 'allowlist', label: 'Allowlist' },
|
|
97
|
+
{ value: 'pairing', label: 'Pairing approval' },
|
|
98
|
+
{ value: 'disabled', label: 'Disabled' },
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const SESSION_SCOPE_OPTIONS: ConnectorConfigOption[] = [
|
|
102
|
+
{ value: 'main', label: 'Main thread' },
|
|
103
|
+
{ value: 'channel', label: 'Per channel' },
|
|
104
|
+
{ value: 'peer', label: 'Per sender' },
|
|
105
|
+
{ value: 'channel-peer', label: 'Per channel + sender' },
|
|
106
|
+
{ value: 'thread', label: 'Per thread/topic' },
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
const REPLY_MODE_OPTIONS: ConnectorConfigOption[] = [
|
|
110
|
+
{ value: 'off', label: 'Off' },
|
|
111
|
+
{ value: 'first', label: 'Reply to first inbound' },
|
|
112
|
+
{ value: 'all', label: 'Reply to every inbound' },
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
const THREAD_BINDING_OPTIONS: ConnectorConfigOption[] = [
|
|
116
|
+
{ value: 'off', label: 'Off' },
|
|
117
|
+
{ value: 'prefer', label: 'Prefer thread context' },
|
|
118
|
+
{ value: 'strict', label: 'Require thread context' },
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
const GROUP_POLICY_OPTIONS: ConnectorConfigOption[] = [
|
|
122
|
+
{ value: 'open', label: 'Open' },
|
|
123
|
+
{ value: 'mention', label: 'Mention only' },
|
|
124
|
+
{ value: 'reply-or-mention', label: 'Reply or mention' },
|
|
125
|
+
{ value: 'disabled', label: 'Disabled' },
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
const RESET_MODE_OPTIONS: ConnectorConfigOption[] = [
|
|
129
|
+
{ value: 'idle', label: 'Idle reset' },
|
|
130
|
+
{ value: 'daily', label: 'Daily reset' },
|
|
131
|
+
]
|
|
132
|
+
|
|
56
133
|
const PLATFORMS: {
|
|
57
134
|
id: ConnectorPlatform
|
|
58
135
|
label: string
|
|
@@ -60,7 +137,7 @@ const PLATFORMS: {
|
|
|
60
137
|
setupSteps: string[]
|
|
61
138
|
tokenLabel: string
|
|
62
139
|
tokenHelp: string
|
|
63
|
-
configFields:
|
|
140
|
+
configFields: ConnectorConfigField[]
|
|
64
141
|
}[] = [
|
|
65
142
|
{
|
|
66
143
|
id: 'discord',
|
|
@@ -77,7 +154,7 @@ const PLATFORMS: {
|
|
|
77
154
|
tokenLabel: 'Bot Token',
|
|
78
155
|
tokenHelp: 'From Discord Developer Portal > Your App > Bot > Token',
|
|
79
156
|
configFields: [
|
|
80
|
-
{ key: 'channelIds', label: 'Channel IDs', placeholder: '123456789,987654321', help: 'Leave empty to listen in all channels the bot can see' },
|
|
157
|
+
{ key: 'channelIds', label: 'Channel IDs', placeholder: '123456789,987654321', help: 'Leave empty to listen in all channels the bot can see', type: 'tags' },
|
|
81
158
|
],
|
|
82
159
|
},
|
|
83
160
|
{
|
|
@@ -92,7 +169,7 @@ const PLATFORMS: {
|
|
|
92
169
|
tokenLabel: 'Bot Token',
|
|
93
170
|
tokenHelp: 'From @BotFather after creating your bot',
|
|
94
171
|
configFields: [
|
|
95
|
-
{ key: 'chatIds', label: 'Chat IDs', placeholder: '-100123456789', help: 'Leave empty to respond in all chats. Use negative IDs for groups.' },
|
|
172
|
+
{ key: 'chatIds', label: 'Chat IDs', placeholder: '-100123456789', help: 'Leave empty to respond in all chats. Use negative IDs for groups.', type: 'tags' },
|
|
96
173
|
],
|
|
97
174
|
},
|
|
98
175
|
{
|
|
@@ -111,7 +188,7 @@ const PLATFORMS: {
|
|
|
111
188
|
tokenHelp: 'From Slack App > OAuth & Permissions > Bot User OAuth Token',
|
|
112
189
|
configFields: [
|
|
113
190
|
{ key: 'appToken', label: 'App-Level Token (xapp-...)', placeholder: 'xapp-1-...', help: 'Required for Socket Mode. From Slack App > Basic Information > App-Level Tokens' },
|
|
114
|
-
{ key: 'channelIds', label: 'Channel IDs', placeholder: 'C0123456789', help: 'Leave empty to listen in all channels the bot is in' },
|
|
191
|
+
{ key: 'channelIds', label: 'Channel IDs', placeholder: 'C0123456789', help: 'Leave empty to listen in all channels the bot is in', type: 'tags' },
|
|
115
192
|
],
|
|
116
193
|
},
|
|
117
194
|
{
|
|
@@ -127,7 +204,9 @@ const PLATFORMS: {
|
|
|
127
204
|
tokenLabel: '',
|
|
128
205
|
tokenHelp: '',
|
|
129
206
|
configFields: [
|
|
130
|
-
{ key: '
|
|
207
|
+
{ key: 'dmPolicy', label: 'DM Policy', placeholder: 'open', help: 'How new direct-message senders are handled. Leave unset to use the platform default.', type: 'select', options: DM_POLICY_OPTIONS, emptyLabel: 'Not set (default: open)' },
|
|
208
|
+
{ key: 'allowFrom', label: 'Approved DM Senders', placeholder: '15551234567,447700900123', help: 'Optional allowlist used by allowlist/pairing mode.', type: 'tags' },
|
|
209
|
+
{ key: 'allowedJids', label: 'Allowed Numbers/Groups', placeholder: '1234567890,MyGroup', help: 'Leave empty to respond to all messages', type: 'tags' },
|
|
131
210
|
{ key: 'outboundJid', label: 'Default Outbound Recipient', placeholder: '15551234567 or 15551234567@s.whatsapp.net', help: 'Used by connector_message_tool when the agent sends proactive WhatsApp updates without an explicit "to" value' },
|
|
132
211
|
],
|
|
133
212
|
},
|
|
@@ -147,8 +226,8 @@ const PLATFORMS: {
|
|
|
147
226
|
{ key: 'sessionKey', label: 'Chat Key Filter', placeholder: 'main', help: 'Optional. If set, only inbound events for this OpenClaw chat are processed.' },
|
|
148
227
|
{ key: 'nodeId', label: 'Client Label', placeholder: 'swarmclaw', help: 'Optional display label shown in OpenClaw presence metadata.' },
|
|
149
228
|
{ key: 'role', label: 'Gateway Role', placeholder: 'operator', help: 'Optional role claim for connect handshake. Default is operator.' },
|
|
150
|
-
{ key: 'scopes', label: 'Scopes (CSV)', placeholder: 'operator.read,operator.write', help: 'Optional comma-separated scopes for OpenClaw connect.' },
|
|
151
|
-
{ key: 'tickWatchdog', label: 'Tick Watchdog', placeholder: 'true', help: '
|
|
229
|
+
{ key: 'scopes', label: 'Scopes (CSV)', placeholder: 'operator.read,operator.write', help: 'Optional comma-separated scopes for OpenClaw connect.', type: 'tags' },
|
|
230
|
+
{ key: 'tickWatchdog', label: 'Tick Watchdog', placeholder: 'true', help: 'Enable or disable stale-tick reconnect watchdog.', type: 'select', options: BOOLEAN_SELECT_OPTIONS, emptyLabel: 'Not set (default: enabled)' },
|
|
152
231
|
{ key: 'tickIntervalMs', label: 'Tick Interval Override (ms)', placeholder: '30000', help: 'Optional watchdog interval override when policy tick is unavailable.' },
|
|
153
232
|
],
|
|
154
233
|
},
|
|
@@ -166,9 +245,9 @@ const PLATFORMS: {
|
|
|
166
245
|
tokenHelp: 'Server password used for /api/v1/ping and /api/v1/message/text',
|
|
167
246
|
configFields: [
|
|
168
247
|
{ key: 'serverUrl', label: 'Server URL', placeholder: 'http://127.0.0.1:1234', help: 'BlueBubbles server URL (no trailing /api path needed)' },
|
|
169
|
-
{ key: 'chatIds', label: 'Allowed Chat IDs', placeholder: 'iMessage;-;+15551234567', help: 'Optional comma-separated chat IDs/guid fragments. Leave empty for all chats.' },
|
|
170
|
-
{ key: 'dmPolicy', label: 'DM Policy', placeholder: 'open
|
|
171
|
-
{ key: 'allowFrom', label: 'Allowed Sender IDs', placeholder: '+15551234567,test@example.com', help: 'Optional
|
|
248
|
+
{ key: 'chatIds', label: 'Allowed Chat IDs', placeholder: 'iMessage;-;+15551234567', help: 'Optional comma-separated chat IDs/guid fragments. Leave empty for all chats.', type: 'tags' },
|
|
249
|
+
{ key: 'dmPolicy', label: 'DM Policy', placeholder: 'open', help: 'Access policy for direct-message senders. Leave unset to use the platform default.', type: 'select', options: DM_POLICY_OPTIONS, emptyLabel: 'Not set (default: open)' },
|
|
250
|
+
{ key: 'allowFrom', label: 'Allowed Sender IDs', placeholder: '+15551234567,test@example.com', help: 'Optional sender allowlist used by allowlist/pairing mode.', type: 'tags' },
|
|
172
251
|
{ key: 'outboundTarget', label: 'Default Outbound Target', placeholder: 'iMessage;-;+15551234567', help: 'Used when proactive sends omit "to".' },
|
|
173
252
|
{ key: 'webhookSecret', label: 'Webhook Secret', placeholder: 'optional-shared-secret', help: 'Optional secret required by /api/connectors/{id}/webhook (header: x-connector-secret or ?secret=...)' },
|
|
174
253
|
{ key: 'timeoutMs', label: 'Request Timeout (ms)', placeholder: '10000', help: 'Optional BlueBubbles API timeout in milliseconds.' },
|
|
@@ -188,7 +267,7 @@ const PLATFORMS: {
|
|
|
188
267
|
tokenHelp: 'Matrix access token for the bot user',
|
|
189
268
|
configFields: [
|
|
190
269
|
{ key: 'homeserverUrl', label: 'Homeserver URL', placeholder: 'https://matrix.org', help: 'The Matrix homeserver URL' },
|
|
191
|
-
{ key: 'roomIds', label: 'Room IDs', placeholder: '!abc123:matrix.org', help: 'Comma-separated room IDs. Leave empty for all rooms.' },
|
|
270
|
+
{ key: 'roomIds', label: 'Room IDs', placeholder: '!abc123:matrix.org', help: 'Comma-separated room IDs. Leave empty for all rooms.', type: 'tags' },
|
|
192
271
|
],
|
|
193
272
|
},
|
|
194
273
|
{
|
|
@@ -204,7 +283,7 @@ const PLATFORMS: {
|
|
|
204
283
|
tokenLabel: 'Service Account JSON',
|
|
205
284
|
tokenHelp: 'Paste the full service account JSON key file contents',
|
|
206
285
|
configFields: [
|
|
207
|
-
{ key: 'spaceIds', label: 'Space IDs', placeholder: 'spaces/AAAA123', help: 'Comma-separated Google Chat space IDs' },
|
|
286
|
+
{ key: 'spaceIds', label: 'Space IDs', placeholder: 'spaces/AAAA123', help: 'Comma-separated Google Chat space IDs', type: 'tags' },
|
|
208
287
|
{ key: 'webhookSecret', label: 'Webhook Secret', placeholder: 'optional-shared-secret', help: 'Optional secret required by /api/connectors/{id}/webhook (header: x-connector-secret or ?secret=...)' },
|
|
209
288
|
],
|
|
210
289
|
},
|
|
@@ -241,18 +320,21 @@ const PLATFORMS: {
|
|
|
241
320
|
configFields: [
|
|
242
321
|
{ key: 'phoneNumber', label: 'Phone Number', placeholder: '+1234567890', help: 'Pre-registered Signal phone number' },
|
|
243
322
|
{ key: 'signalCliPath', label: 'signal-cli Path', placeholder: 'signal-cli', help: 'Path to signal-cli binary (defaults to signal-cli)' },
|
|
244
|
-
{ key: 'signalCliMode', label: 'Mode', placeholder: 'stdio', help: 'stdio
|
|
323
|
+
{ key: 'signalCliMode', label: 'Mode', placeholder: 'stdio', help: 'How SwarmClaw talks to signal-cli.', type: 'select', options: [{ value: 'stdio', label: 'stdio' }, { value: 'http', label: 'HTTP API' }], emptyLabel: 'Not set (default: stdio)' },
|
|
245
324
|
{ key: 'signalCliHttpUrl', label: 'HTTP API URL', placeholder: 'http://localhost:8080', help: 'Only needed for http mode' },
|
|
246
325
|
],
|
|
247
326
|
},
|
|
248
327
|
]
|
|
249
328
|
|
|
250
|
-
const COMMON_CONFIG_FIELDS:
|
|
329
|
+
const COMMON_CONFIG_FIELDS: ConnectorConfigField[] = [
|
|
251
330
|
{
|
|
252
331
|
key: 'thinkingLevel',
|
|
253
332
|
label: 'Thinking Level',
|
|
254
333
|
placeholder: 'minimal | low | medium | high',
|
|
255
334
|
help: 'Default reasoning depth for new/reset direct connector sessions.',
|
|
335
|
+
type: 'select',
|
|
336
|
+
options: THINKING_LEVEL_OPTIONS,
|
|
337
|
+
emptyLabel: 'Not set (agent default)',
|
|
256
338
|
},
|
|
257
339
|
{
|
|
258
340
|
key: 'providerOverride',
|
|
@@ -271,24 +353,36 @@ const COMMON_CONFIG_FIELDS: { key: string; label: string; placeholder: string; h
|
|
|
271
353
|
label: 'Session Scope',
|
|
272
354
|
placeholder: 'main | channel | peer | channel-peer | thread',
|
|
273
355
|
help: 'Conversation identity policy. Defaults to channel-peer for DMs and channel for groups.',
|
|
356
|
+
type: 'select',
|
|
357
|
+
options: SESSION_SCOPE_OPTIONS,
|
|
358
|
+
emptyLabel: 'Not set (platform default)',
|
|
274
359
|
},
|
|
275
360
|
{
|
|
276
361
|
key: 'replyMode',
|
|
277
362
|
label: 'Reply Mode',
|
|
278
363
|
placeholder: 'off | first | all',
|
|
279
364
|
help: 'Whether outbound replies should attach to the triggering inbound message.',
|
|
365
|
+
type: 'select',
|
|
366
|
+
options: REPLY_MODE_OPTIONS,
|
|
367
|
+
emptyLabel: 'Not set (platform default)',
|
|
280
368
|
},
|
|
281
369
|
{
|
|
282
370
|
key: 'threadBinding',
|
|
283
371
|
label: 'Thread Binding',
|
|
284
372
|
placeholder: 'off | prefer | strict',
|
|
285
373
|
help: 'Prefer or require thread/topic-specific sessions when the platform exposes thread IDs.',
|
|
374
|
+
type: 'select',
|
|
375
|
+
options: THREAD_BINDING_OPTIONS,
|
|
376
|
+
emptyLabel: 'Not set (platform default)',
|
|
286
377
|
},
|
|
287
378
|
{
|
|
288
379
|
key: 'groupPolicy',
|
|
289
380
|
label: 'Group Policy',
|
|
290
381
|
placeholder: 'open | mention | reply-or-mention | disabled',
|
|
291
382
|
help: 'Controls whether the agent speaks in group chats without being mentioned or replied to.',
|
|
383
|
+
type: 'select',
|
|
384
|
+
options: GROUP_POLICY_OPTIONS,
|
|
385
|
+
emptyLabel: 'Not set (platform default)',
|
|
292
386
|
},
|
|
293
387
|
{
|
|
294
388
|
key: 'idleTimeoutSec',
|
|
@@ -307,6 +401,9 @@ const COMMON_CONFIG_FIELDS: { key: string; label: string; placeholder: string; h
|
|
|
307
401
|
label: 'Reset Mode',
|
|
308
402
|
placeholder: 'idle | daily',
|
|
309
403
|
help: 'Freshness policy for connector sessions. Daily resets use the fields below.',
|
|
404
|
+
type: 'select',
|
|
405
|
+
options: RESET_MODE_OPTIONS,
|
|
406
|
+
emptyLabel: 'Not set (default: idle)',
|
|
310
407
|
},
|
|
311
408
|
{
|
|
312
409
|
key: 'sessionDailyResetAt',
|
|
@@ -331,18 +428,27 @@ const COMMON_CONFIG_FIELDS: { key: string; label: string; placeholder: string; h
|
|
|
331
428
|
label: 'Status Reactions',
|
|
332
429
|
placeholder: 'true | false',
|
|
333
430
|
help: 'When supported, add lightweight platform-native reactions for processing/sent/silent states.',
|
|
431
|
+
type: 'select',
|
|
432
|
+
options: BOOLEAN_SELECT_OPTIONS,
|
|
433
|
+
emptyLabel: 'Not set (default: enabled)',
|
|
334
434
|
},
|
|
335
435
|
{
|
|
336
436
|
key: 'typingIndicators',
|
|
337
437
|
label: 'Typing Indicators',
|
|
338
438
|
placeholder: 'true | false',
|
|
339
439
|
help: 'When supported, keep a native typing/working indicator alive while the agent is running.',
|
|
440
|
+
type: 'select',
|
|
441
|
+
options: BOOLEAN_SELECT_OPTIONS,
|
|
442
|
+
emptyLabel: 'Not set (default: enabled)',
|
|
340
443
|
},
|
|
341
444
|
{
|
|
342
445
|
key: 'taskFollowups',
|
|
343
446
|
label: 'Task Follow-ups',
|
|
344
447
|
placeholder: 'true | false',
|
|
345
448
|
help: 'Enable automatic connector follow-up messages when this agent completes or fails a task.',
|
|
449
|
+
type: 'select',
|
|
450
|
+
options: BOOLEAN_SELECT_OPTIONS,
|
|
451
|
+
emptyLabel: 'Not set (default: enabled)',
|
|
346
452
|
},
|
|
347
453
|
{
|
|
348
454
|
key: 'taskFollowupTemplate',
|
|
@@ -411,6 +517,9 @@ export function ConnectorSheet() {
|
|
|
411
517
|
const [doctorWarnings, setDoctorWarnings] = useState<string[]>([])
|
|
412
518
|
const [doctorPolicy, setDoctorPolicy] = useState<ConnectorDoctorPolicyPreview | null>(null)
|
|
413
519
|
const [doctorLoading, setDoctorLoading] = useState(false)
|
|
520
|
+
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
521
|
+
const [confirmWhatsAppAction, setConfirmWhatsAppAction] = useState<'unlink' | 'repair' | null>(null)
|
|
522
|
+
const [deleting, setDeleting] = useState(false)
|
|
414
523
|
|
|
415
524
|
const editing = editingId ? connectors[editingId] as Connector | undefined : null
|
|
416
525
|
|
|
@@ -503,6 +612,9 @@ export function ConnectorSheet() {
|
|
|
503
612
|
setDoctorWarnings([])
|
|
504
613
|
setDoctorPolicy(null)
|
|
505
614
|
setDoctorLoading(false)
|
|
615
|
+
setConfirmDelete(false)
|
|
616
|
+
setConfirmWhatsAppAction(null)
|
|
617
|
+
setDeleting(false)
|
|
506
618
|
return
|
|
507
619
|
}
|
|
508
620
|
const timer = window.setTimeout(() => {
|
|
@@ -560,11 +672,37 @@ export function ConnectorSheet() {
|
|
|
560
672
|
}
|
|
561
673
|
|
|
562
674
|
const handleDelete = async () => {
|
|
563
|
-
if (!editing
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
675
|
+
if (!editing) return
|
|
676
|
+
setDeleting(true)
|
|
677
|
+
try {
|
|
678
|
+
await api('DELETE', `/connectors/${editing.id}`)
|
|
679
|
+
await loadConnectors()
|
|
680
|
+
setConfirmDelete(false)
|
|
681
|
+
setOpen(false)
|
|
682
|
+
setEditingId(null)
|
|
683
|
+
} catch (err: unknown) {
|
|
684
|
+
toast.error(`Failed to delete connector: ${err instanceof Error ? err.message : String(err)}`)
|
|
685
|
+
} finally {
|
|
686
|
+
setDeleting(false)
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const handleWhatsAppRepair = async (mode: 'unlink' | 'repair') => {
|
|
691
|
+
if (!editing) return
|
|
692
|
+
setActionLoading(true)
|
|
693
|
+
try {
|
|
694
|
+
await api('PUT', `/connectors/${editing.id}`, { action: 'repair' })
|
|
695
|
+
setWaAuthenticated(false)
|
|
696
|
+
setWaHasCreds(false)
|
|
697
|
+
setQrDataUrl(null)
|
|
698
|
+
setWaConnecting(true)
|
|
699
|
+
setConfirmWhatsAppAction(null)
|
|
700
|
+
await loadConnectors()
|
|
701
|
+
} catch (err: unknown) {
|
|
702
|
+
toast.error(`Failed to ${mode === 'unlink' ? 'unlink' : 're-pair'}: ${err instanceof Error ? err.message : String(err)}`)
|
|
703
|
+
} finally {
|
|
704
|
+
setActionLoading(false)
|
|
705
|
+
}
|
|
568
706
|
}
|
|
569
707
|
|
|
570
708
|
const platformConfig = ALL_PLATFORMS.find((p) => p.id === platform) || ALL_PLATFORMS[0]
|
|
@@ -573,6 +711,120 @@ export function ConnectorSheet() {
|
|
|
573
711
|
|
|
574
712
|
const inputClass = "w-full px-4 py-3 rounded-[12px] border border-white/[0.08] bg-surface text-text text-[14px] outline-none transition-all placeholder:text-text-3/50 focus:border-white/[0.15]"
|
|
575
713
|
|
|
714
|
+
const updateConfigValue = useCallback((key: string, value: string) => {
|
|
715
|
+
setConfig((prev) => {
|
|
716
|
+
const next = { ...prev }
|
|
717
|
+
if (value.trim() === '') delete next[key]
|
|
718
|
+
else next[key] = value
|
|
719
|
+
return next
|
|
720
|
+
})
|
|
721
|
+
}, [])
|
|
722
|
+
|
|
723
|
+
const renderConfigField = useCallback((field: ConnectorConfigField) => {
|
|
724
|
+
const isTagField = field.type === 'tags'
|
|
725
|
+
if (isTagField) {
|
|
726
|
+
const tags = (config[field.key] || '').split(',').map((s) => s.trim()).filter(Boolean)
|
|
727
|
+
return (
|
|
728
|
+
<div key={field.key} className="mb-6">
|
|
729
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
730
|
+
{field.label} <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
731
|
+
{FIELD_HINTS[field.key] && <HintTip text={FIELD_HINTS[field.key]} />}
|
|
732
|
+
</label>
|
|
733
|
+
{field.help && <p className="text-[12px] text-text-3/60 mb-2">{field.help}</p>}
|
|
734
|
+
<div className="flex flex-wrap gap-2 mb-2">
|
|
735
|
+
{tags.map((tag, i) => (
|
|
736
|
+
<span key={i} className="flex items-center gap-1.5 px-3 py-1.5 rounded-[8px] bg-accent-soft/50 border border-accent-bright/20 text-[12px] font-mono text-accent-bright">
|
|
737
|
+
{tag}
|
|
738
|
+
<button
|
|
739
|
+
aria-label={`Remove ${tag}`}
|
|
740
|
+
onClick={() => updateConfigValue(field.key, tags.filter((_, j) => j !== i).join(','))}
|
|
741
|
+
className="ml-0.5 w-4 h-4 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors cursor-pointer text-accent-bright/50 hover:text-accent-bright"
|
|
742
|
+
>
|
|
743
|
+
<svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round">
|
|
744
|
+
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
|
745
|
+
</svg>
|
|
746
|
+
</button>
|
|
747
|
+
</span>
|
|
748
|
+
))}
|
|
749
|
+
</div>
|
|
750
|
+
<div className="flex gap-2">
|
|
751
|
+
<input
|
|
752
|
+
id={`tag-input-${field.key}`}
|
|
753
|
+
placeholder={field.placeholder}
|
|
754
|
+
className={`${inputClass} font-mono text-[13px] flex-1`}
|
|
755
|
+
style={{ fontFamily: undefined }}
|
|
756
|
+
onKeyDown={(e) => {
|
|
757
|
+
if (e.key === 'Enter' || e.key === ',') {
|
|
758
|
+
e.preventDefault()
|
|
759
|
+
const input = e.currentTarget
|
|
760
|
+
const val = input.value.trim().replace(/,/g, '')
|
|
761
|
+
if (val) {
|
|
762
|
+
const next = tags.length > 0 ? `${tags.join(',')},${val}` : val
|
|
763
|
+
updateConfigValue(field.key, next)
|
|
764
|
+
input.value = ''
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}}
|
|
768
|
+
/>
|
|
769
|
+
<button
|
|
770
|
+
type="button"
|
|
771
|
+
onClick={() => {
|
|
772
|
+
const input = document.getElementById(`tag-input-${field.key}`) as HTMLInputElement | null
|
|
773
|
+
const val = input?.value.trim().replace(/,/g, '')
|
|
774
|
+
if (val) {
|
|
775
|
+
const next = tags.length > 0 ? `${tags.join(',')},${val}` : val
|
|
776
|
+
updateConfigValue(field.key, next)
|
|
777
|
+
if (input) input.value = ''
|
|
778
|
+
}
|
|
779
|
+
}}
|
|
780
|
+
className="px-4 py-2.5 rounded-[10px] bg-accent-soft/50 text-accent-bright text-[12px] font-600 hover:bg-accent-soft transition-colors cursor-pointer border border-accent-bright/20"
|
|
781
|
+
>
|
|
782
|
+
Add
|
|
783
|
+
</button>
|
|
784
|
+
</div>
|
|
785
|
+
</div>
|
|
786
|
+
)
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (field.type === 'select' && field.options?.length) {
|
|
790
|
+
return (
|
|
791
|
+
<div key={field.key} className="mb-6">
|
|
792
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
793
|
+
{field.label} <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
794
|
+
</label>
|
|
795
|
+
{field.help && <p className="text-[12px] text-text-3/60 mb-2">{field.help}</p>}
|
|
796
|
+
<select
|
|
797
|
+
value={config[field.key] || ''}
|
|
798
|
+
onChange={(e) => updateConfigValue(field.key, e.target.value)}
|
|
799
|
+
className={`${inputClass} appearance-none cursor-pointer`}
|
|
800
|
+
style={{ fontFamily: 'inherit' }}
|
|
801
|
+
>
|
|
802
|
+
<option value="">{field.emptyLabel || 'Not set'}</option>
|
|
803
|
+
{field.options.map((option) => (
|
|
804
|
+
<option key={option.value} value={option.value}>{option.label}</option>
|
|
805
|
+
))}
|
|
806
|
+
</select>
|
|
807
|
+
</div>
|
|
808
|
+
)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return (
|
|
812
|
+
<div key={field.key} className="mb-6">
|
|
813
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
814
|
+
{field.label} <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
815
|
+
</label>
|
|
816
|
+
{field.help && <p className="text-[12px] text-text-3/60 mb-2">{field.help}</p>}
|
|
817
|
+
<input
|
|
818
|
+
value={config[field.key] || ''}
|
|
819
|
+
onChange={(e) => updateConfigValue(field.key, e.target.value)}
|
|
820
|
+
placeholder={field.placeholder}
|
|
821
|
+
className={`${inputClass} ${field.key.toLowerCase().includes('token') || field.key.toLowerCase().includes('secret') ? 'font-mono text-[13px]' : ''}`}
|
|
822
|
+
style={{ fontFamily: field.key.toLowerCase().includes('token') || field.key.toLowerCase().includes('secret') ? undefined : 'inherit' }}
|
|
823
|
+
/>
|
|
824
|
+
</div>
|
|
825
|
+
)
|
|
826
|
+
}, [config, inputClass, updateConfigValue])
|
|
827
|
+
|
|
576
828
|
return (
|
|
577
829
|
<BottomSheet open={open} onClose={() => { setOpen(false); setEditingId(null) }} wide>
|
|
578
830
|
<div className="mb-8">
|
|
@@ -816,95 +1068,23 @@ export function ConnectorSheet() {
|
|
|
816
1068
|
)}
|
|
817
1069
|
|
|
818
1070
|
{/* Platform-specific config */}
|
|
819
|
-
{
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
{tags.map((tag, i) => (
|
|
837
|
-
<span key={i} className="flex items-center gap-1.5 px-3 py-1.5 rounded-[8px] bg-accent-soft/50 border border-accent-bright/20 text-[12px] font-mono text-accent-bright">
|
|
838
|
-
{tag}
|
|
839
|
-
<button
|
|
840
|
-
aria-label={`Remove ${tag}`}
|
|
841
|
-
onClick={() => {
|
|
842
|
-
const next = tags.filter((_, j) => j !== i).join(',')
|
|
843
|
-
setConfig({ ...config, [field.key]: next })
|
|
844
|
-
}}
|
|
845
|
-
className="ml-0.5 w-4 h-4 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors cursor-pointer text-accent-bright/50 hover:text-accent-bright"
|
|
846
|
-
>
|
|
847
|
-
<svg width="8" height="8" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round">
|
|
848
|
-
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
|
849
|
-
</svg>
|
|
850
|
-
</button>
|
|
851
|
-
</span>
|
|
852
|
-
))}
|
|
853
|
-
</div>
|
|
854
|
-
<div className="flex gap-2">
|
|
855
|
-
<input
|
|
856
|
-
id={`tag-input-${field.key}`}
|
|
857
|
-
placeholder={field.placeholder}
|
|
858
|
-
className={`${inputClass} font-mono text-[13px] flex-1`}
|
|
859
|
-
style={{ fontFamily: undefined }}
|
|
860
|
-
onKeyDown={(e) => {
|
|
861
|
-
if (e.key === 'Enter' || e.key === ',') {
|
|
862
|
-
e.preventDefault()
|
|
863
|
-
const input = e.currentTarget
|
|
864
|
-
const val = input.value.trim().replace(/,/g, '')
|
|
865
|
-
if (val) {
|
|
866
|
-
const next = tags.length > 0 ? `${tags.join(',')},${val}` : val
|
|
867
|
-
setConfig({ ...config, [field.key]: next })
|
|
868
|
-
input.value = ''
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}}
|
|
872
|
-
/>
|
|
873
|
-
<button
|
|
874
|
-
type="button"
|
|
875
|
-
onClick={() => {
|
|
876
|
-
const input = document.getElementById(`tag-input-${field.key}`) as HTMLInputElement
|
|
877
|
-
const val = input?.value.trim().replace(/,/g, '')
|
|
878
|
-
if (val) {
|
|
879
|
-
const next = tags.length > 0 ? `${tags.join(',')},${val}` : val
|
|
880
|
-
setConfig({ ...config, [field.key]: next })
|
|
881
|
-
input.value = ''
|
|
882
|
-
}
|
|
883
|
-
}}
|
|
884
|
-
className="px-4 py-2.5 rounded-[10px] bg-accent-soft/50 text-accent-bright text-[12px] font-600 hover:bg-accent-soft transition-colors cursor-pointer border border-accent-bright/20"
|
|
885
|
-
>
|
|
886
|
-
Add
|
|
887
|
-
</button>
|
|
888
|
-
</div>
|
|
889
|
-
</div>
|
|
890
|
-
)
|
|
891
|
-
}
|
|
892
|
-
return (
|
|
893
|
-
<div key={field.key} className="mb-6">
|
|
894
|
-
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
895
|
-
{field.label} <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
896
|
-
</label>
|
|
897
|
-
{field.help && <p className="text-[12px] text-text-3/60 mb-2">{field.help}</p>}
|
|
898
|
-
<input
|
|
899
|
-
value={config[field.key] || ''}
|
|
900
|
-
onChange={(e) => setConfig({ ...config, [field.key]: e.target.value })}
|
|
901
|
-
placeholder={field.placeholder}
|
|
902
|
-
className={`${inputClass} font-mono text-[13px]`}
|
|
903
|
-
style={{ fontFamily: undefined }}
|
|
904
|
-
/>
|
|
905
|
-
</div>
|
|
906
|
-
)
|
|
907
|
-
})}
|
|
1071
|
+
{platformConfig.configFields.length > 0 && (
|
|
1072
|
+
<div className="mb-2">
|
|
1073
|
+
<SectionLabel>Platform Settings</SectionLabel>
|
|
1074
|
+
<p className="text-[12px] text-text-3/60 mb-4">
|
|
1075
|
+
Settings specific to {platformConfig.label}. Leave optional values unset unless you need to override the defaults.
|
|
1076
|
+
</p>
|
|
1077
|
+
{platformConfig.configFields.map((field) => renderConfigField(field))}
|
|
1078
|
+
</div>
|
|
1079
|
+
)}
|
|
1080
|
+
|
|
1081
|
+
<div className="mb-2">
|
|
1082
|
+
<SectionLabel>Routing & Autonomy</SectionLabel>
|
|
1083
|
+
<p className="text-[12px] text-text-3/60 mb-4">
|
|
1084
|
+
Conversation identity, reply behavior, reset policy, and other connector runtime overrides.
|
|
1085
|
+
</p>
|
|
1086
|
+
{COMMON_CONFIG_FIELDS.map((field) => renderConfigField(field))}
|
|
1087
|
+
</div>
|
|
908
1088
|
|
|
909
1089
|
{/* Start/Stop controls for editing */}
|
|
910
1090
|
{editing && (() => {
|
|
@@ -973,9 +1153,15 @@ export function ConnectorSheet() {
|
|
|
973
1153
|
· Thread: <span className="text-text-2">{doctorPolicy.threadBinding || 'prefer'}</span>
|
|
974
1154
|
</div>
|
|
975
1155
|
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
976
|
-
|
|
1156
|
+
DMs: <span className="text-text-2">{config.dmPolicy || 'open'}</span>{' '}
|
|
1157
|
+
· Group: <span className="text-text-2">{doctorPolicy.groupPolicy || 'reply-or-mention'}</span>{' '}
|
|
977
1158
|
· Debounce: <span className="text-text-2">{doctorPolicy.inboundDebounceMs ?? 700}ms</span>
|
|
978
1159
|
</div>
|
|
1160
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
1161
|
+
Allowlist: <span className="text-text-2">{config.allowFrom ? config.allowFrom.split(',').map((entry) => entry.trim()).filter(Boolean).length : 0}</span>{' '}
|
|
1162
|
+
· Reactions: <span className="text-text-2">{doctorPolicy.statusReactions === false ? 'off' : 'on'}</span>{' '}
|
|
1163
|
+
· Typing: <span className="text-text-2">{doctorPolicy.typingIndicators === false ? 'off' : 'on'}</span>
|
|
1164
|
+
</div>
|
|
979
1165
|
<div className="rounded-[10px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[12px] text-text-3/80">
|
|
980
1166
|
Reset: <span className="text-text-2">{doctorPolicy.resetMode || 'idle'}</span>{' '}
|
|
981
1167
|
{doctorPolicy.resetMode === 'daily'
|
|
@@ -1007,12 +1193,6 @@ export function ConnectorSheet() {
|
|
|
1007
1193
|
</p>
|
|
1008
1194
|
</div>
|
|
1009
1195
|
|
|
1010
|
-
{editing && (
|
|
1011
|
-
<div className="mb-6 p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01]">
|
|
1012
|
-
<ConnectorHealth connectorId={editing.id} />
|
|
1013
|
-
</div>
|
|
1014
|
-
)}
|
|
1015
|
-
|
|
1016
1196
|
{/* WhatsApp QR code */}
|
|
1017
1197
|
{editing && editing.platform === 'whatsapp' && (editing.status === 'running' || waConnecting) && qrDataUrl && (
|
|
1018
1198
|
<div className="mb-6 p-5 rounded-[14px] border border-white/[0.06] bg-white/[0.01] text-center"
|
|
@@ -1034,22 +1214,7 @@ export function ConnectorSheet() {
|
|
|
1034
1214
|
<div className="text-[13px] font-600 text-green-400 mb-1">Connected</div>
|
|
1035
1215
|
<p className="text-[11px] text-text-3 mb-3">WhatsApp is paired and listening for messages</p>
|
|
1036
1216
|
<button
|
|
1037
|
-
onClick={
|
|
1038
|
-
if (!confirm('Unlink this device? You will need to scan a new QR code.')) return
|
|
1039
|
-
setActionLoading(true)
|
|
1040
|
-
try {
|
|
1041
|
-
await api('PUT', `/connectors/${editing.id}`, { action: 'repair' })
|
|
1042
|
-
setWaAuthenticated(false)
|
|
1043
|
-
setWaHasCreds(false)
|
|
1044
|
-
setQrDataUrl(null)
|
|
1045
|
-
setWaConnecting(true)
|
|
1046
|
-
await loadConnectors()
|
|
1047
|
-
} catch (err: unknown) {
|
|
1048
|
-
toast.error(`Failed to unlink: ${err instanceof Error ? err.message : String(err)}`)
|
|
1049
|
-
} finally {
|
|
1050
|
-
setActionLoading(false)
|
|
1051
|
-
}
|
|
1052
|
-
}}
|
|
1217
|
+
onClick={() => setConfirmWhatsAppAction('unlink')}
|
|
1053
1218
|
disabled={actionLoading}
|
|
1054
1219
|
className="text-[12px] text-text-3 hover:text-red-400 transition-colors cursor-pointer bg-transparent border-none underline underline-offset-2"
|
|
1055
1220
|
style={{ fontFamily: 'inherit' }}
|
|
@@ -1075,22 +1240,7 @@ export function ConnectorSheet() {
|
|
|
1075
1240
|
</p>
|
|
1076
1241
|
{waHasCreds && (
|
|
1077
1242
|
<button
|
|
1078
|
-
onClick={
|
|
1079
|
-
if (!confirm('Force re-pair? This will clear saved credentials and show a new QR code.')) return
|
|
1080
|
-
setActionLoading(true)
|
|
1081
|
-
try {
|
|
1082
|
-
await api('PUT', `/connectors/${editing.id}`, { action: 'repair' })
|
|
1083
|
-
setWaAuthenticated(false)
|
|
1084
|
-
setWaHasCreds(false)
|
|
1085
|
-
setQrDataUrl(null)
|
|
1086
|
-
setWaConnecting(true)
|
|
1087
|
-
await loadConnectors()
|
|
1088
|
-
} catch (err: unknown) {
|
|
1089
|
-
toast.error(`Failed to re-pair: ${err instanceof Error ? err.message : String(err)}`)
|
|
1090
|
-
} finally {
|
|
1091
|
-
setActionLoading(false)
|
|
1092
|
-
}
|
|
1093
|
-
}}
|
|
1243
|
+
onClick={() => setConfirmWhatsAppAction('repair')}
|
|
1094
1244
|
disabled={actionLoading}
|
|
1095
1245
|
className="mt-3 text-[12px] text-text-3 hover:text-amber-400 transition-colors cursor-pointer bg-transparent border-none underline underline-offset-2"
|
|
1096
1246
|
style={{ fontFamily: 'inherit' }}
|
|
@@ -1123,11 +1273,50 @@ export function ConnectorSheet() {
|
|
|
1123
1273
|
saveLabel={saving ? 'Saving...' : editing ? 'Save' : 'Create Connector'}
|
|
1124
1274
|
saveDisabled={saving || (routeMode === 'agent' ? !agentId : !chatroomId)}
|
|
1125
1275
|
left={editing && (
|
|
1126
|
-
<button
|
|
1276
|
+
<button
|
|
1277
|
+
onClick={() => setConfirmDelete(true)}
|
|
1278
|
+
disabled={deleting}
|
|
1279
|
+
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 disabled:cursor-not-allowed disabled:opacity-60"
|
|
1280
|
+
style={{ fontFamily: 'inherit' }}
|
|
1281
|
+
>
|
|
1127
1282
|
Delete
|
|
1128
1283
|
</button>
|
|
1129
1284
|
)}
|
|
1130
1285
|
/>
|
|
1286
|
+
<ConfirmDialog
|
|
1287
|
+
open={confirmDelete}
|
|
1288
|
+
title="Delete Connector?"
|
|
1289
|
+
message={editing ? `Delete "${editing.name}"? This will stop the connector and remove its configuration from the app.` : 'Delete this connector?'}
|
|
1290
|
+
confirmLabel={deleting ? 'Deleting...' : 'Delete'}
|
|
1291
|
+
confirmDisabled={deleting}
|
|
1292
|
+
cancelDisabled={deleting}
|
|
1293
|
+
danger
|
|
1294
|
+
onConfirm={() => { void handleDelete() }}
|
|
1295
|
+
onCancel={() => { if (!deleting) setConfirmDelete(false) }}
|
|
1296
|
+
/>
|
|
1297
|
+
<ConfirmDialog
|
|
1298
|
+
open={!!confirmWhatsAppAction}
|
|
1299
|
+
title={confirmWhatsAppAction === 'unlink' ? 'Unlink Device?' : 'Force Re-pair?'}
|
|
1300
|
+
message={
|
|
1301
|
+
confirmWhatsAppAction === 'unlink'
|
|
1302
|
+
? 'Unlink this device? You will need to scan a new QR code.'
|
|
1303
|
+
: 'Force re-pair? This will clear saved credentials and show a new QR code.'
|
|
1304
|
+
}
|
|
1305
|
+
confirmLabel={
|
|
1306
|
+
actionLoading
|
|
1307
|
+
? confirmWhatsAppAction === 'unlink'
|
|
1308
|
+
? 'Unlinking...'
|
|
1309
|
+
: 'Re-pairing...'
|
|
1310
|
+
: confirmWhatsAppAction === 'unlink'
|
|
1311
|
+
? 'Unlink'
|
|
1312
|
+
: 'Re-pair'
|
|
1313
|
+
}
|
|
1314
|
+
confirmDisabled={actionLoading}
|
|
1315
|
+
cancelDisabled={actionLoading}
|
|
1316
|
+
danger
|
|
1317
|
+
onConfirm={() => { if (confirmWhatsAppAction) void handleWhatsAppRepair(confirmWhatsAppAction) }}
|
|
1318
|
+
onCancel={() => { if (!actionLoading) setConfirmWhatsAppAction(null) }}
|
|
1319
|
+
/>
|
|
1131
1320
|
</BottomSheet>
|
|
1132
1321
|
)
|
|
1133
1322
|
}
|