@swarmclawai/swarmclaw 1.2.4 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/bin/daemon-cmd.js +169 -0
- package/bin/server-cmd.js +3 -0
- package/bin/swarmclaw.js +11 -0
- package/package.json +17 -16
- package/src/app/api/agents/[id]/clone/route.ts +3 -32
- package/src/app/api/agents/[id]/route.ts +6 -158
- package/src/app/api/agents/[id]/status/route.ts +2 -3
- package/src/app/api/agents/[id]/thread/route.ts +4 -17
- package/src/app/api/agents/bulk/route.ts +5 -47
- package/src/app/api/agents/route.ts +5 -119
- package/src/app/api/agents/trash/route.ts +13 -24
- package/src/app/api/auth/route.ts +3 -9
- package/src/app/api/autonomy/estop/route.ts +5 -5
- package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
- package/src/app/api/chatrooms/[id]/route.ts +23 -2
- package/src/app/api/chatrooms/route.ts +13 -2
- package/src/app/api/chats/[id]/clear/route.ts +2 -13
- package/src/app/api/chats/[id]/deploy/route.ts +2 -3
- package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
- package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
- package/src/app/api/chats/[id]/queue/route.ts +17 -64
- package/src/app/api/chats/[id]/retry/route.ts +4 -22
- package/src/app/api/chats/[id]/route.ts +10 -138
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/migrate-messages/route.ts +7 -0
- package/src/app/api/chats/route.ts +13 -134
- package/src/app/api/connectors/[id]/access/route.ts +12 -229
- package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
- package/src/app/api/connectors/[id]/health/route.ts +12 -39
- package/src/app/api/connectors/[id]/route.ts +14 -122
- package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
- package/src/app/api/connectors/doctor/route.ts +1 -1
- package/src/app/api/connectors/route.ts +12 -70
- package/src/app/api/credentials/[id]/route.ts +2 -4
- package/src/app/api/credentials/route.ts +10 -19
- package/src/app/api/daemon/health-check/route.ts +3 -4
- package/src/app/api/daemon/route.ts +10 -8
- package/src/app/api/documents/route.ts +11 -10
- package/src/app/api/external-agents/route.ts +3 -3
- package/src/app/api/gateways/[id]/health/route.ts +2 -3
- package/src/app/api/gateways/[id]/route.ts +7 -122
- package/src/app/api/gateways/route.ts +3 -103
- package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
- package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
- package/src/app/api/openclaw/directory/route.ts +2 -2
- package/src/app/api/openclaw/history/route.ts +3 -5
- package/src/app/api/providers/[id]/route.test.ts +49 -0
- package/src/app/api/providers/ollama/route.ts +6 -5
- package/src/app/api/schedules/[id]/route.ts +14 -108
- package/src/app/api/schedules/[id]/run/route.ts +6 -67
- package/src/app/api/schedules/route.ts +9 -51
- package/src/app/api/settings/route.ts +4 -3
- package/src/app/api/setup/check-provider/route.ts +15 -1
- package/src/app/api/setup/openclaw-device/route.ts +2 -2
- package/src/app/api/system/status/route.ts +2 -2
- package/src/app/api/tasks/[id]/route.ts +16 -202
- package/src/app/api/tasks/bulk/route.ts +5 -86
- package/src/app/api/tasks/metrics/route.ts +2 -1
- package/src/app/api/tasks/route.ts +11 -171
- package/src/app/api/upload/route.ts +1 -1
- package/src/app/api/uploads/[filename]/route.ts +1 -1
- package/src/app/api/uploads/route.ts +1 -1
- package/src/app/api/webhooks/[id]/history/route.ts +2 -2
- package/src/app/layout.tsx +9 -6
- package/src/app/protocols/page.tsx +71 -89
- package/src/app/tasks/page.tsx +32 -32
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/auth/setup-wizard/index.tsx +4 -4
- package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
- package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
- package/src/components/auth/setup-wizard/utils.ts +1 -1
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
- package/src/components/connectors/connector-list.tsx +26 -40
- package/src/components/connectors/connector-sheet.tsx +95 -149
- package/src/components/gateways/gateway-sheet.tsx +61 -110
- package/src/components/layout/live-query-sync.tsx +121 -0
- package/src/components/protocols/structured-session-launcher.tsx +24 -45
- package/src/components/providers/app-query-provider.tsx +17 -0
- package/src/components/providers/provider-list.tsx +60 -61
- package/src/components/providers/provider-sheet.tsx +74 -56
- package/src/components/skills/skill-list.tsx +5 -18
- package/src/components/skills/skill-sheet.tsx +21 -20
- package/src/components/skills/skills-workspace.tsx +48 -87
- package/src/components/tasks/task-card.tsx +20 -13
- package/src/components/tasks/task-column.tsx +22 -7
- package/src/components/tasks/task-list.tsx +8 -11
- package/src/components/tasks/task-sheet.tsx +111 -103
- package/src/features/agents/queries.ts +20 -0
- package/src/features/chatrooms/queries.ts +20 -0
- package/src/features/chats/queries.ts +27 -0
- package/src/features/connectors/queries.ts +145 -0
- package/src/features/credentials/queries.ts +37 -0
- package/src/features/extensions/queries.ts +26 -0
- package/src/features/external-agents/queries.ts +36 -0
- package/src/features/gateways/queries.ts +274 -0
- package/src/features/missions/queries.ts +23 -0
- package/src/features/projects/queries.ts +20 -0
- package/src/features/protocols/queries.ts +149 -0
- package/src/features/providers/queries.ts +142 -0
- package/src/features/settings/queries.ts +20 -0
- package/src/features/skills/queries.ts +182 -0
- package/src/features/tasks/queries.ts +189 -0
- package/src/hooks/use-ws.ts +3 -2
- package/src/lib/app/api-client.ts +2 -2
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +1 -1
- package/src/lib/server/agents/agent-service.ts +429 -0
- package/src/lib/server/agents/agent-thread-session.ts +6 -5
- package/src/lib/server/agents/autonomy-contract.ts +1 -4
- package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
- package/src/lib/server/agents/delegation-advisory.ts +251 -0
- package/src/lib/server/agents/main-agent-loop.ts +98 -40
- package/src/lib/server/agents/subagent-runtime.ts +12 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
- package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
- package/src/lib/server/build-llm.ts +7 -15
- package/src/lib/server/capability-router.test.ts +70 -1
- package/src/lib/server/capability-router.ts +24 -99
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
- package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
- package/src/lib/server/chat-execution/message-classifier.ts +74 -32
- package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
- package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
- package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
- package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
- package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
- package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
- package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
- package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
- package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
- package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
- package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
- package/src/lib/server/chats/chat-session-service.ts +410 -0
- package/src/lib/server/connectors/access.ts +1 -1
- package/src/lib/server/connectors/commands.ts +7 -6
- package/src/lib/server/connectors/connector-inbound.ts +14 -7
- package/src/lib/server/connectors/connector-outbound.ts +16 -11
- package/src/lib/server/connectors/connector-service.ts +453 -0
- package/src/lib/server/connectors/delivery.ts +17 -12
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
- package/src/lib/server/connectors/media.ts +1 -1
- package/src/lib/server/connectors/response-media.ts +1 -1
- package/src/lib/server/connectors/session-consolidation.ts +11 -7
- package/src/lib/server/connectors/session.ts +9 -7
- package/src/lib/server/connectors/voice-note.ts +2 -1
- package/src/lib/server/context-manager.ts +20 -1
- package/src/lib/server/cost.ts +2 -3
- package/src/lib/server/credentials/credential-repository.ts +43 -4
- package/src/lib/server/credentials/credential-service.ts +112 -0
- package/src/lib/server/daemon/admin-metadata.ts +64 -0
- package/src/lib/server/daemon/controller.ts +577 -0
- package/src/lib/server/daemon/daemon-runtime.ts +352 -0
- package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
- package/src/lib/server/daemon/types.ts +101 -0
- package/src/lib/server/embeddings.ts +3 -9
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -2
- package/src/lib/server/execution-brief.test.ts +167 -0
- package/src/lib/server/execution-brief.ts +295 -0
- package/src/lib/server/execution-engine/chat-turn.ts +9 -0
- package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
- package/src/lib/server/execution-engine/index.ts +35 -0
- package/src/lib/server/execution-engine/task-attempt.ts +303 -0
- package/src/lib/server/execution-engine/types.ts +33 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
- package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
- package/src/lib/server/memory/session-archive-memory.ts +12 -10
- package/src/lib/server/messages/message-repository.ts +330 -0
- package/src/lib/server/missions/mission-service/core.ts +8 -6
- package/src/lib/server/openclaw/agent-resolver.ts +2 -3
- package/src/lib/server/openclaw/doctor.ts +1 -1
- package/src/lib/server/openclaw/gateway.test.ts +10 -1
- package/src/lib/server/openclaw/gateway.ts +5 -14
- package/src/lib/server/openclaw/health.ts +3 -11
- package/src/lib/server/openclaw/sync.ts +8 -6
- package/src/lib/server/persistence/storage-context.ts +3 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
- package/src/lib/server/protocols/protocol-normalization.ts +1 -1
- package/src/lib/server/protocols/protocol-queries.ts +13 -7
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
- package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
- package/src/lib/server/protocols/protocol-swarm.ts +8 -8
- package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
- package/src/lib/server/protocols/protocol-templates.ts +4 -2
- package/src/lib/server/protocols/protocol-types.ts +10 -7
- package/src/lib/server/provider-endpoint.ts +7 -12
- package/src/lib/server/provider-model-discovery.ts +2 -11
- package/src/lib/server/query-expansion.ts +5 -6
- package/src/lib/server/run-context.test.ts +365 -0
- package/src/lib/server/run-context.ts +367 -0
- package/src/lib/server/runtime/heartbeat-service.ts +7 -5
- package/src/lib/server/runtime/queue/core.ts +61 -190
- package/src/lib/server/runtime/run-ledger.ts +8 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
- package/src/lib/server/schedules/schedule-route-service.ts +230 -0
- package/src/lib/server/service-result.ts +16 -0
- package/src/lib/server/session-note.ts +2 -3
- package/src/lib/server/session-reset-policy.ts +4 -3
- package/src/lib/server/session-tools/connector.ts +9 -6
- package/src/lib/server/session-tools/context-mgmt.ts +58 -9
- package/src/lib/server/session-tools/crud.ts +162 -10
- package/src/lib/server/session-tools/delegate.ts +1 -1
- package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
- package/src/lib/server/session-tools/memory.ts +6 -4
- package/src/lib/server/session-tools/session-info.test.ts +56 -0
- package/src/lib/server/session-tools/session-info.ts +119 -12
- package/src/lib/server/session-tools/skill-runtime.ts +3 -1
- package/src/lib/server/session-tools/skills.ts +15 -15
- package/src/lib/server/session-tools/subagent.test.ts +115 -1
- package/src/lib/server/session-tools/subagent.ts +125 -7
- package/src/lib/server/session-tools/team-context.ts +4 -3
- package/src/lib/server/session-tools/wallet.ts +0 -58
- package/src/lib/server/sessions/session-lineage.ts +55 -0
- package/src/lib/server/sessions/session-repository.ts +2 -2
- package/src/lib/server/skills/learned-skills.ts +24 -23
- package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
- package/src/lib/server/skills/skill-repository.ts +136 -13
- package/src/lib/server/skills/skill-suggestions.ts +25 -28
- package/src/lib/server/storage-normalization.test.ts +44 -267
- package/src/lib/server/storage-normalization.ts +75 -0
- package/src/lib/server/storage.ts +19 -0
- package/src/lib/server/structured-extract.ts +3 -14
- package/src/lib/server/tasks/task-followups.ts +16 -11
- package/src/lib/server/tasks/task-result.test.ts +25 -29
- package/src/lib/server/tasks/task-result.ts +5 -9
- package/src/lib/server/tasks/task-route-service.ts +449 -0
- package/src/lib/server/text-normalization.ts +41 -0
- package/src/lib/server/tool-planning.ts +6 -42
- package/src/lib/server/upload-path.ts +5 -0
- package/src/lib/server/working-state/extraction.ts +614 -0
- package/src/lib/server/working-state/normalization.ts +866 -0
- package/src/lib/server/working-state/prompt.ts +60 -0
- package/src/lib/server/working-state/repository.ts +38 -0
- package/src/lib/server/working-state/service.test.ts +253 -0
- package/src/lib/server/working-state/service.ts +293 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/ws-client.ts +3 -3
- package/src/stores/slices/task-slice.ts +1 -4
- package/src/stores/use-chatroom-store.ts +2 -2
- package/src/types/index.ts +277 -12
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
|
+
import { logActivity } from '@/lib/server/activity/activity-log'
|
|
3
|
+
import {
|
|
4
|
+
deleteConnector,
|
|
5
|
+
loadConnector,
|
|
6
|
+
loadConnectorHealth,
|
|
7
|
+
loadConnectors,
|
|
8
|
+
upsertConnector,
|
|
9
|
+
} from '@/lib/server/connectors/connector-repository'
|
|
10
|
+
import {
|
|
11
|
+
buildConnectorAccessSnapshot,
|
|
12
|
+
resolveConnectorOwnerSenderId,
|
|
13
|
+
} from '@/lib/server/connectors/access'
|
|
14
|
+
import {
|
|
15
|
+
addAllowedSender,
|
|
16
|
+
approvePairingCode,
|
|
17
|
+
approvePendingSender,
|
|
18
|
+
clearSenderAddressingOverride,
|
|
19
|
+
normalizeSenderId,
|
|
20
|
+
parseAllowFromCsv,
|
|
21
|
+
parseDmAddressingMode,
|
|
22
|
+
parsePairingPolicy,
|
|
23
|
+
removeAllowedSender,
|
|
24
|
+
rejectPendingSender,
|
|
25
|
+
setSenderAddressingOverride,
|
|
26
|
+
senderMatchesAnyEntry,
|
|
27
|
+
} from '@/lib/server/connectors/pairing'
|
|
28
|
+
import {
|
|
29
|
+
ensureDaemonProcessRunning,
|
|
30
|
+
getDaemonConnectorRuntime,
|
|
31
|
+
listDaemonConnectorRuntime,
|
|
32
|
+
runDaemonConnectorAction,
|
|
33
|
+
} from '@/lib/server/daemon/controller'
|
|
34
|
+
import { log } from '@/lib/server/logger'
|
|
35
|
+
import { serviceFail, serviceOk } from '@/lib/server/service-result'
|
|
36
|
+
import { notify } from '@/lib/server/ws-hub'
|
|
37
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
38
|
+
import type {
|
|
39
|
+
Connector,
|
|
40
|
+
ConnectorAccessMutationAction,
|
|
41
|
+
ConnectorAccessMutationResponse,
|
|
42
|
+
ConnectorHealthEvent,
|
|
43
|
+
} from '@/types'
|
|
44
|
+
import type { ServiceResult } from '@/lib/server/service-result'
|
|
45
|
+
import type { DaemonConnectorRuntimeState } from '@/lib/server/daemon/types'
|
|
46
|
+
|
|
47
|
+
function cloneConnector<T extends Connector>(connector: T): T {
|
|
48
|
+
return {
|
|
49
|
+
...connector,
|
|
50
|
+
config: connector.config ? { ...connector.config } : {},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function persistConnector(connector: Connector): void {
|
|
55
|
+
connector.updatedAt = Date.now()
|
|
56
|
+
upsertConnector(connector.id, connector)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function applyRuntimeFields(connector: Connector, runtime: DaemonConnectorRuntimeState | null): Connector {
|
|
60
|
+
connector.status = runtime?.status
|
|
61
|
+
? runtime.status
|
|
62
|
+
: connector.lastError
|
|
63
|
+
? 'error'
|
|
64
|
+
: 'stopped'
|
|
65
|
+
|
|
66
|
+
if (connector.platform === 'whatsapp') {
|
|
67
|
+
connector.authenticated = runtime?.authenticated
|
|
68
|
+
connector.hasCredentials = runtime?.hasCredentials
|
|
69
|
+
if (runtime?.qrDataUrl) connector.qrDataUrl = runtime.qrDataUrl
|
|
70
|
+
else delete connector.qrDataUrl
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (runtime?.reconnectAttempts !== undefined) {
|
|
74
|
+
const ext = connector as unknown as Record<string, unknown>
|
|
75
|
+
ext.reconnectAttempts = runtime.reconnectAttempts
|
|
76
|
+
ext.nextRetryAt = runtime.nextRetryAt
|
|
77
|
+
ext.reconnectError = runtime.reconnectError
|
|
78
|
+
ext.reconnectExhausted = runtime.reconnectExhausted
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (runtime?.presence && connector.status === 'running') {
|
|
82
|
+
connector.presence = runtime.presence
|
|
83
|
+
} else {
|
|
84
|
+
delete connector.presence
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return connector
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setConnectorSenderList(connector: Connector, key: string, values: string[]): void {
|
|
91
|
+
if (!connector.config) connector.config = {}
|
|
92
|
+
if (values.length === 0) {
|
|
93
|
+
delete connector.config[key]
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
connector.config[key] = values.join(',')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function addConnectorSenderListEntry(connector: Connector, key: string, senderId: string): boolean {
|
|
100
|
+
const normalized = normalizeSenderId(senderId)
|
|
101
|
+
if (!normalized) return false
|
|
102
|
+
const current = parseAllowFromCsv(connector.config?.[key])
|
|
103
|
+
if (senderMatchesAnyEntry(normalized, current)) return false
|
|
104
|
+
setConnectorSenderList(connector, key, [...current, normalized])
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function removeConnectorSenderListEntry(connector: Connector, key: string, senderId: string): boolean {
|
|
109
|
+
const normalized = normalizeSenderId(senderId)
|
|
110
|
+
if (!normalized) return false
|
|
111
|
+
const current = parseAllowFromCsv(connector.config?.[key])
|
|
112
|
+
const next = current.filter((entry) => !senderMatchesAnyEntry(normalized, [entry]))
|
|
113
|
+
if (next.length === current.length) return false
|
|
114
|
+
setConnectorSenderList(connector, key, next)
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function requireSenderId(body: Record<string, unknown>): string {
|
|
119
|
+
const senderId = typeof body.senderId === 'string' ? body.senderId.trim() : ''
|
|
120
|
+
if (!senderId) throw new Error('senderId is required for this action')
|
|
121
|
+
return senderId
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function listConnectorsWithRuntime(): Promise<Record<string, Connector>> {
|
|
125
|
+
await ensureDaemonProcessRunning('api/connectors:get')
|
|
126
|
+
const connectors = Object.fromEntries(
|
|
127
|
+
Object.entries(loadConnectors()).map(([id, connector]) => [id, cloneConnector(connector)]),
|
|
128
|
+
) as Record<string, Connector>
|
|
129
|
+
const runtimeByConnector = await listDaemonConnectorRuntime()
|
|
130
|
+
for (const connector of Object.values(connectors)) {
|
|
131
|
+
applyRuntimeFields(connector, runtimeByConnector[connector.id] || null)
|
|
132
|
+
}
|
|
133
|
+
return connectors
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getConnectorWithRuntime(id: string): Promise<Connector | null> {
|
|
137
|
+
await ensureDaemonProcessRunning('api/connectors/[id]:get')
|
|
138
|
+
const connector = loadConnector(id)
|
|
139
|
+
if (!connector) return null
|
|
140
|
+
const current = cloneConnector(connector)
|
|
141
|
+
return applyRuntimeFields(current, await getDaemonConnectorRuntime(id))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function createConnector(body: Record<string, unknown>): Connector {
|
|
145
|
+
const id = genId()
|
|
146
|
+
const connector: Connector = {
|
|
147
|
+
id,
|
|
148
|
+
name: (body.name as string) || `${String(body.platform || '')} Connector`,
|
|
149
|
+
platform: body.platform as Connector['platform'],
|
|
150
|
+
agentId: (body.agentId as string | null | undefined) || null,
|
|
151
|
+
chatroomId: (body.chatroomId as string | null | undefined) || null,
|
|
152
|
+
credentialId: (body.credentialId as string | null | undefined) || null,
|
|
153
|
+
config: body.config && typeof body.config === 'object' && !Array.isArray(body.config)
|
|
154
|
+
? body.config as Record<string, string>
|
|
155
|
+
: {},
|
|
156
|
+
isEnabled: false,
|
|
157
|
+
status: 'stopped',
|
|
158
|
+
lastError: null,
|
|
159
|
+
createdAt: Date.now(),
|
|
160
|
+
updatedAt: Date.now(),
|
|
161
|
+
}
|
|
162
|
+
upsertConnector(id, connector)
|
|
163
|
+
notify('connectors')
|
|
164
|
+
return connector
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function autoStartConnectorIfNeeded(connector: Connector, body: Record<string, unknown>): Promise<void> {
|
|
168
|
+
const hasCredentials = connector.platform === 'whatsapp'
|
|
169
|
+
|| connector.platform === 'openclaw'
|
|
170
|
+
|| (connector.platform === 'bluebubbles' && (!!connector.credentialId || !!connector.config.password))
|
|
171
|
+
|| !!connector.credentialId
|
|
172
|
+
if (!hasCredentials || body.autoStart === false) return
|
|
173
|
+
try {
|
|
174
|
+
await runDaemonConnectorAction(connector.id, 'start', 'connectors:auto-start')
|
|
175
|
+
} catch (err: unknown) {
|
|
176
|
+
log.warn('connectors', `Auto-start failed for connector ${connector.id}`, errorMessage(err))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function updateConnectorFromRoute(id: string, body: Record<string, unknown>): Promise<ServiceResult<Connector>> {
|
|
181
|
+
await ensureDaemonProcessRunning('api/connectors/[id]:put')
|
|
182
|
+
const connector = loadConnector(id)
|
|
183
|
+
if (!connector) return serviceFail(404, 'Connector not found')
|
|
184
|
+
|
|
185
|
+
if (body.action === 'start' || body.action === 'stop' || body.action === 'repair') {
|
|
186
|
+
try {
|
|
187
|
+
if (body.action === 'start') {
|
|
188
|
+
await runDaemonConnectorAction(id, 'start', 'api/connectors/[id]:action:start')
|
|
189
|
+
logActivity({ entityType: 'connector', entityId: id, action: 'started', actor: 'user', summary: `Connector started: "${connector.name}"` })
|
|
190
|
+
} else if (body.action === 'stop') {
|
|
191
|
+
await runDaemonConnectorAction(id, 'stop', 'api/connectors/[id]:action:stop')
|
|
192
|
+
logActivity({ entityType: 'connector', entityId: id, action: 'stopped', actor: 'user', summary: `Connector stopped: "${connector.name}"` })
|
|
193
|
+
} else {
|
|
194
|
+
await runDaemonConnectorAction(id, 'repair', 'api/connectors/[id]:action:repair')
|
|
195
|
+
logActivity({ entityType: 'connector', entityId: id, action: 'started', actor: 'user', summary: `Connector repaired: "${connector.name}"` })
|
|
196
|
+
}
|
|
197
|
+
} catch (err: unknown) {
|
|
198
|
+
log.error('connectors', `Action failed for connector ${id}`, errorMessage(err))
|
|
199
|
+
return serviceFail(500, 'Connector action failed')
|
|
200
|
+
}
|
|
201
|
+
notify('connectors')
|
|
202
|
+
const updated = await getConnectorWithRuntime(id)
|
|
203
|
+
return serviceOk(updated || connector)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const next = cloneConnector(connector)
|
|
207
|
+
if (body.name !== undefined) next.name = typeof body.name === 'string' ? body.name : next.name
|
|
208
|
+
if (body.agentId !== undefined) next.agentId = typeof body.agentId === 'string' || body.agentId === null ? body.agentId : next.agentId
|
|
209
|
+
if (body.chatroomId !== undefined) next.chatroomId = typeof body.chatroomId === 'string' || body.chatroomId === null ? body.chatroomId : next.chatroomId
|
|
210
|
+
if (body.credentialId !== undefined) next.credentialId = typeof body.credentialId === 'string' || body.credentialId === null ? body.credentialId : next.credentialId
|
|
211
|
+
if (body.config !== undefined) next.config = body.config && typeof body.config === 'object' && !Array.isArray(body.config) ? body.config as Record<string, string> : next.config
|
|
212
|
+
if (body.isEnabled !== undefined) next.isEnabled = typeof body.isEnabled === 'boolean' ? body.isEnabled : next.isEnabled
|
|
213
|
+
persistConnector(next)
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const runtime = await getDaemonConnectorRuntime(id)
|
|
217
|
+
const wasRunning = runtime?.status === 'running'
|
|
218
|
+
const shouldStop = body.isEnabled === false
|
|
219
|
+
const shouldReload = wasRunning && (
|
|
220
|
+
body.name !== undefined
|
|
221
|
+
|| body.agentId !== undefined
|
|
222
|
+
|| body.chatroomId !== undefined
|
|
223
|
+
|| body.credentialId !== undefined
|
|
224
|
+
|| body.config !== undefined
|
|
225
|
+
|| body.isEnabled !== undefined
|
|
226
|
+
)
|
|
227
|
+
const shouldStart = body.isEnabled === true && !wasRunning
|
|
228
|
+
if (shouldStop) {
|
|
229
|
+
await runDaemonConnectorAction(id, 'stop', 'api/connectors/[id]:reload:stop')
|
|
230
|
+
} else if (shouldReload || shouldStart) {
|
|
231
|
+
await runDaemonConnectorAction(id, 'start', 'api/connectors/[id]:reload:start')
|
|
232
|
+
}
|
|
233
|
+
} catch (err: unknown) {
|
|
234
|
+
log.warn('connectors', `Failed to reload connector ${id} after update`, errorMessage(err))
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
notify('connectors')
|
|
238
|
+
return serviceOk(await getConnectorWithRuntime(id) || next)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export async function deleteConnectorFromRoute(id: string): Promise<ServiceResult<{ ok: true }>> {
|
|
242
|
+
const connector = loadConnector(id)
|
|
243
|
+
if (!connector) return serviceFail(404, 'Connector not found')
|
|
244
|
+
try {
|
|
245
|
+
await runDaemonConnectorAction(id, 'stop', 'api/connectors/[id]:delete')
|
|
246
|
+
} catch (err: unknown) {
|
|
247
|
+
log.warn('connectors', `Failed to stop connector ${id} during delete`, errorMessage(err))
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const { clearConnectorPairingState } = await import('@/lib/server/connectors/pairing')
|
|
251
|
+
clearConnectorPairingState(id)
|
|
252
|
+
} catch (err: unknown) {
|
|
253
|
+
log.warn('connectors', `Failed to clear pairing state for ${id}`, errorMessage(err))
|
|
254
|
+
}
|
|
255
|
+
deleteConnector(id)
|
|
256
|
+
notify('connectors')
|
|
257
|
+
return serviceOk({ ok: true as const })
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function getConnectorHealthForApi(id: string): { events: ConnectorHealthEvent[]; uptimePercent: number } | null {
|
|
261
|
+
const connector = loadConnector(id)
|
|
262
|
+
if (!connector) return null
|
|
263
|
+
const allHealth = loadConnectorHealth()
|
|
264
|
+
const events: ConnectorHealthEvent[] = []
|
|
265
|
+
for (const raw of Object.values(allHealth)) {
|
|
266
|
+
const entry = raw as ConnectorHealthEvent
|
|
267
|
+
if (entry.connectorId !== id) continue
|
|
268
|
+
events.push(entry)
|
|
269
|
+
}
|
|
270
|
+
events.sort((a, b) => a.timestamp.localeCompare(b.timestamp))
|
|
271
|
+
return { events, uptimePercent: computeUptime(events) }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function computeUptime(events: ConnectorHealthEvent[]): number {
|
|
275
|
+
if (events.length === 0) return 0
|
|
276
|
+
const firstTime = new Date(events[0].timestamp).getTime()
|
|
277
|
+
const now = Date.now()
|
|
278
|
+
const totalMs = now - firstTime
|
|
279
|
+
if (totalMs <= 0) return 100
|
|
280
|
+
let uptimeMs = 0
|
|
281
|
+
let lastUpAt: number | null = null
|
|
282
|
+
for (const event of events) {
|
|
283
|
+
const time = new Date(event.timestamp).getTime()
|
|
284
|
+
if (event.event === 'started' || event.event === 'reconnected') {
|
|
285
|
+
if (lastUpAt === null) lastUpAt = time
|
|
286
|
+
} else if (event.event === 'stopped' || event.event === 'error' || event.event === 'disconnected') {
|
|
287
|
+
if (lastUpAt !== null) {
|
|
288
|
+
uptimeMs += time - lastUpAt
|
|
289
|
+
lastUpAt = null
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (lastUpAt !== null) uptimeMs += now - lastUpAt
|
|
294
|
+
return Math.round((uptimeMs / totalMs) * 10000) / 100
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function updateConnectorAccess(
|
|
298
|
+
connectorId: string,
|
|
299
|
+
body: Record<string, unknown>,
|
|
300
|
+
): Promise<ServiceResult<ConnectorAccessMutationResponse>> {
|
|
301
|
+
await ensureDaemonProcessRunning('api/connectors/[id]/access:put')
|
|
302
|
+
const connector = loadConnector(connectorId)
|
|
303
|
+
if (!connector) return serviceFail(404, 'Connector not found')
|
|
304
|
+
const current = cloneConnector(connector)
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const action = typeof body.action === 'string' ? body.action.trim().toLowerCase() as ConnectorAccessMutationAction : null
|
|
308
|
+
if (!action) {
|
|
309
|
+
return serviceFail(400, 'Missing access action')
|
|
310
|
+
}
|
|
311
|
+
let connectorChanged = false
|
|
312
|
+
let responseSenderId = typeof body.senderId === 'string' ? body.senderId.trim() : ''
|
|
313
|
+
const responseSenderIdAlt = typeof body.senderIdAlt === 'string' ? body.senderIdAlt.trim() : ''
|
|
314
|
+
let summary = `Updated access controls for "${current.name}".`
|
|
315
|
+
|
|
316
|
+
switch (action) {
|
|
317
|
+
case 'set_policy': {
|
|
318
|
+
const rawPolicy = typeof body.dmPolicy === 'string' ? body.dmPolicy.trim() : ''
|
|
319
|
+
if (!rawPolicy) delete current.config.dmPolicy
|
|
320
|
+
else current.config.dmPolicy = parsePairingPolicy(rawPolicy, 'open')
|
|
321
|
+
connectorChanged = true
|
|
322
|
+
summary = `Updated DM policy for "${current.name}".`
|
|
323
|
+
break
|
|
324
|
+
}
|
|
325
|
+
case 'set_dm_addressing_mode': {
|
|
326
|
+
const rawMode = typeof body.dmAddressingMode === 'string' ? body.dmAddressingMode.trim() : ''
|
|
327
|
+
const nextMode = parseDmAddressingMode(rawMode || 'open', 'open')
|
|
328
|
+
if (nextMode === 'open') delete current.config.dmAddressingMode
|
|
329
|
+
else current.config.dmAddressingMode = nextMode
|
|
330
|
+
connectorChanged = true
|
|
331
|
+
summary = `Updated DM addressing mode for "${current.name}" to ${nextMode}.`
|
|
332
|
+
break
|
|
333
|
+
}
|
|
334
|
+
case 'allow_sender': {
|
|
335
|
+
const senderId = requireSenderId(body)
|
|
336
|
+
addAllowedSender(current.id, senderId)
|
|
337
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'denyFrom', senderId) || connectorChanged
|
|
338
|
+
summary = `Allowed sender ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
339
|
+
break
|
|
340
|
+
}
|
|
341
|
+
case 'remove_allowed_sender': {
|
|
342
|
+
const senderId = requireSenderId(body)
|
|
343
|
+
removeAllowedSender(current.id, senderId)
|
|
344
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'allowFrom', senderId) || connectorChanged
|
|
345
|
+
summary = `Removed connector-managed access for ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
346
|
+
break
|
|
347
|
+
}
|
|
348
|
+
case 'block_sender': {
|
|
349
|
+
const senderId = requireSenderId(body)
|
|
350
|
+
connectorChanged = addConnectorSenderListEntry(current, 'denyFrom', senderId) || connectorChanged
|
|
351
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'allowFrom', senderId) || connectorChanged
|
|
352
|
+
removeAllowedSender(current.id, senderId)
|
|
353
|
+
rejectPendingSender(current.id, senderId)
|
|
354
|
+
const ownerSenderId = resolveConnectorOwnerSenderId(current)
|
|
355
|
+
if (ownerSenderId && senderMatchesAnyEntry(senderId, [ownerSenderId])) {
|
|
356
|
+
delete current.config.ownerSenderId
|
|
357
|
+
connectorChanged = true
|
|
358
|
+
}
|
|
359
|
+
summary = `Blocked sender ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
360
|
+
break
|
|
361
|
+
}
|
|
362
|
+
case 'unblock_sender': {
|
|
363
|
+
const senderId = requireSenderId(body)
|
|
364
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'denyFrom', senderId) || connectorChanged
|
|
365
|
+
summary = `Removed sender ${normalizeSenderId(senderId)} from the deny list on "${current.name}".`
|
|
366
|
+
break
|
|
367
|
+
}
|
|
368
|
+
case 'approve_pairing': {
|
|
369
|
+
if (typeof body.code === 'string' && body.code.trim()) {
|
|
370
|
+
const approved = approvePairingCode(current.id, body.code)
|
|
371
|
+
if (!approved.ok) {
|
|
372
|
+
return serviceFail(400, approved.reason || 'Pairing approval failed.')
|
|
373
|
+
}
|
|
374
|
+
if (approved.senderId) {
|
|
375
|
+
responseSenderId = approved.senderId
|
|
376
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'denyFrom', approved.senderId) || connectorChanged
|
|
377
|
+
}
|
|
378
|
+
summary = `Approved pairing on "${current.name}".`
|
|
379
|
+
} else {
|
|
380
|
+
const senderId = requireSenderId(body)
|
|
381
|
+
const approved = approvePendingSender(current.id, senderId)
|
|
382
|
+
if (!approved.ok) {
|
|
383
|
+
return serviceFail(400, approved.reason || 'Pairing approval failed.')
|
|
384
|
+
}
|
|
385
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'denyFrom', senderId) || connectorChanged
|
|
386
|
+
summary = `Approved pairing for ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
387
|
+
}
|
|
388
|
+
break
|
|
389
|
+
}
|
|
390
|
+
case 'reject_pairing': {
|
|
391
|
+
const senderId = requireSenderId(body)
|
|
392
|
+
rejectPendingSender(current.id, senderId)
|
|
393
|
+
summary = `Rejected pairing for ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
394
|
+
break
|
|
395
|
+
}
|
|
396
|
+
case 'set_owner': {
|
|
397
|
+
const senderId = requireSenderId(body)
|
|
398
|
+
const normalized = normalizeSenderId(senderId)
|
|
399
|
+
if (!normalized) return serviceFail(400, 'Could not normalize owner sender ID')
|
|
400
|
+
current.config.ownerSenderId = normalized
|
|
401
|
+
connectorChanged = true
|
|
402
|
+
connectorChanged = removeConnectorSenderListEntry(current, 'denyFrom', normalized) || connectorChanged
|
|
403
|
+
summary = `Set connector owner for "${current.name}" to ${normalized}.`
|
|
404
|
+
break
|
|
405
|
+
}
|
|
406
|
+
case 'clear_owner': {
|
|
407
|
+
if (current.config?.ownerSenderId) {
|
|
408
|
+
delete current.config.ownerSenderId
|
|
409
|
+
connectorChanged = true
|
|
410
|
+
}
|
|
411
|
+
summary = `Cleared connector owner override for "${current.name}".`
|
|
412
|
+
break
|
|
413
|
+
}
|
|
414
|
+
case 'set_sender_dm_addressing': {
|
|
415
|
+
const senderId = requireSenderId(body)
|
|
416
|
+
const rawMode = typeof body.dmAddressingMode === 'string' ? body.dmAddressingMode.trim() : ''
|
|
417
|
+
const nextMode = parseDmAddressingMode(rawMode || 'open', 'open')
|
|
418
|
+
setSenderAddressingOverride(current.id, senderId, nextMode)
|
|
419
|
+
summary = `Updated DM addressing override for ${normalizeSenderId(senderId)} on "${current.name}" to ${nextMode}.`
|
|
420
|
+
break
|
|
421
|
+
}
|
|
422
|
+
case 'clear_sender_dm_addressing': {
|
|
423
|
+
const senderId = requireSenderId(body)
|
|
424
|
+
clearSenderAddressingOverride(current.id, senderId)
|
|
425
|
+
summary = `Cleared DM addressing override for ${normalizeSenderId(senderId)} on "${current.name}".`
|
|
426
|
+
break
|
|
427
|
+
}
|
|
428
|
+
default:
|
|
429
|
+
return serviceFail(400, `Unsupported access action: ${action}`)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (connectorChanged) persistConnector(current)
|
|
433
|
+
logActivity({
|
|
434
|
+
entityType: 'connector',
|
|
435
|
+
entityId: current.id,
|
|
436
|
+
action: 'access-updated',
|
|
437
|
+
actor: 'user',
|
|
438
|
+
summary,
|
|
439
|
+
detail: { action },
|
|
440
|
+
})
|
|
441
|
+
notify('connectors')
|
|
442
|
+
return serviceOk({
|
|
443
|
+
ok: true,
|
|
444
|
+
snapshot: buildConnectorAccessSnapshot({
|
|
445
|
+
connector: current,
|
|
446
|
+
senderId: responseSenderId || null,
|
|
447
|
+
senderIdAlt: responseSenderIdAlt || null,
|
|
448
|
+
}),
|
|
449
|
+
})
|
|
450
|
+
} catch (err: unknown) {
|
|
451
|
+
return serviceFail(400, errorMessage(err))
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Connector, MessageSource } from '@/types'
|
|
2
|
-
import { loadConnectors } from '
|
|
2
|
+
import { loadConnectors } from './connector-repository'
|
|
3
|
+
import { getMessages, replaceMessageAt } from '@/lib/server/messages/message-repository'
|
|
3
4
|
import { notify } from '../ws-hub'
|
|
4
5
|
import { resolveConnectorSessionPolicy, shouldReplyToInboundMessage } from './policy'
|
|
5
6
|
import { runningConnectors } from './runtime-state'
|
|
@@ -68,7 +69,7 @@ export async function recordConnectorOutboundDelivery(params: {
|
|
|
68
69
|
lastOutboundMessageId: params.messageId || session.connectorContext?.lastOutboundMessageId || null,
|
|
69
70
|
threadId: params.inbound.threadId || session.connectorContext?.threadId || null,
|
|
70
71
|
}
|
|
71
|
-
const history =
|
|
72
|
+
const history = getMessages(session.id)
|
|
72
73
|
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
73
74
|
const entry = history[i]
|
|
74
75
|
if (entry?.role !== 'assistant') continue
|
|
@@ -76,17 +77,21 @@ export async function recordConnectorOutboundDelivery(params: {
|
|
|
76
77
|
if (source.connectorId !== connector.id) continue
|
|
77
78
|
if (source.channelId !== params.inbound.channelId) continue
|
|
78
79
|
if (!source.messageId && params.messageId) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
const updatedEntry = {
|
|
81
|
+
...entry,
|
|
82
|
+
source: {
|
|
83
|
+
platform: source.platform || connector.platform,
|
|
84
|
+
connectorId: source.connectorId || connector.id,
|
|
85
|
+
connectorName: source.connectorName || connector.name,
|
|
86
|
+
channelId: source.channelId || params.inbound.channelId,
|
|
87
|
+
senderId: source.senderId,
|
|
88
|
+
senderName: source.senderName,
|
|
89
|
+
messageId: params.messageId,
|
|
90
|
+
replyToMessageId: source.replyToMessageId || params.inbound.messageId,
|
|
91
|
+
threadId: source.threadId || params.inbound.threadId,
|
|
92
|
+
},
|
|
89
93
|
}
|
|
94
|
+
replaceMessageAt(session.id, i, updatedEntry)
|
|
90
95
|
}
|
|
91
96
|
break
|
|
92
97
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { log } from '@/lib/server/logger'
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
-
import {
|
|
4
|
+
import { listCredentialIdsByProvider, resolveCredentialSecret } from '@/lib/server/credentials/credential-service'
|
|
5
|
+
import { loadSettings } from '../settings/settings-repository'
|
|
5
6
|
import { mimeFromPath } from './media'
|
|
6
7
|
import type { InboundMessage, InboundMedia } from './types'
|
|
7
8
|
import { errorMessage } from '@/lib/shared-utils'
|
|
@@ -90,25 +91,15 @@ function resolveOpenAiApiKey(preferredCredentialId?: string | null): string | nu
|
|
|
90
91
|
const envKey = String(process.env.SWARMCLAW_OPENAI_STT_API_KEY || process.env.OPENAI_API_KEY || '').trim()
|
|
91
92
|
if (envKey) return envKey
|
|
92
93
|
|
|
93
|
-
const creds = loadCredentials() as Record<string, { provider?: string; encryptedKey?: string }>
|
|
94
94
|
const candidates: string[] = []
|
|
95
95
|
if (preferredCredentialId) candidates.push(preferredCredentialId)
|
|
96
|
-
|
|
97
|
-
const provider = String(cred?.provider || '').trim().toLowerCase()
|
|
98
|
-
if (provider === 'openai') candidates.push(id)
|
|
99
|
-
}
|
|
96
|
+
candidates.push(...listCredentialIdsByProvider('openai'))
|
|
100
97
|
const seen = new Set<string>()
|
|
101
98
|
for (const id of candidates) {
|
|
102
99
|
if (!id || seen.has(id)) continue
|
|
103
100
|
seen.add(id)
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
if (provider !== 'openai') continue
|
|
107
|
-
if (!cred?.encryptedKey) continue
|
|
108
|
-
try {
|
|
109
|
-
const decrypted = decryptKey(cred.encryptedKey).trim()
|
|
110
|
-
if (decrypted) return decrypted
|
|
111
|
-
} catch { /* ignore invalid credential */ }
|
|
101
|
+
const decrypted = resolveCredentialSecret(id)?.trim()
|
|
102
|
+
if (decrypted) return decrypted
|
|
112
103
|
}
|
|
113
104
|
|
|
114
105
|
return null
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { genId } from '@/lib/id'
|
|
4
|
-
import { UPLOAD_DIR } from '../
|
|
4
|
+
import { UPLOAD_DIR } from '../upload-path'
|
|
5
5
|
import type { InboundMedia, InboundMediaType } from './types'
|
|
6
6
|
|
|
7
7
|
const MIME_EXT_MAP: Record<string, string> = {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Populates the field from existing senderId, senderIdAlt, channelId, channelIdAlt, peerKey.
|
|
4
4
|
*/
|
|
5
5
|
import { log } from '@/lib/server/logger'
|
|
6
|
-
import {
|
|
6
|
+
import { saveSession, loadSessions } from '@/lib/server/sessions/session-repository'
|
|
7
|
+
import { getMessages, getMessageCount, replaceAllMessages } from '@/lib/server/messages/message-repository'
|
|
8
|
+
import { loadSettings, saveSettings } from '../settings/settings-repository'
|
|
7
9
|
import type { Session } from '@/types'
|
|
8
10
|
import { isDirectConnectorSession } from './session-kind'
|
|
9
11
|
|
|
@@ -38,7 +40,7 @@ export function backfillAllKnownPeerIds(): { migrated: number; skipped: boolean
|
|
|
38
40
|
...ctx,
|
|
39
41
|
allKnownPeerIds: [...ids],
|
|
40
42
|
}
|
|
41
|
-
|
|
43
|
+
saveSession(session.id, session)
|
|
42
44
|
migrated++
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -63,19 +65,21 @@ export function pruneThreadConnectorMirrors(): { cleanedSessions: number; remove
|
|
|
63
65
|
|
|
64
66
|
for (const session of Object.values(sessions)) {
|
|
65
67
|
if (isDirectConnectorSession(session)) continue
|
|
66
|
-
|
|
68
|
+
const msgCount = getMessageCount(session.id)
|
|
69
|
+
if (msgCount === 0) continue
|
|
67
70
|
|
|
68
|
-
const
|
|
71
|
+
const allMessages = getMessages(session.id)
|
|
72
|
+
const filteredMessages = allMessages.filter((message) => !(
|
|
69
73
|
message?.historyExcluded === true
|
|
70
74
|
&& typeof message?.source?.connectorId === 'string'
|
|
71
75
|
&& message.source.connectorId.trim().length > 0
|
|
72
76
|
))
|
|
73
77
|
|
|
74
|
-
const removed =
|
|
78
|
+
const removed = allMessages.length - filteredMessages.length
|
|
75
79
|
if (removed <= 0) continue
|
|
76
80
|
|
|
77
|
-
session.
|
|
78
|
-
|
|
81
|
+
replaceAllMessages(session.id, filteredMessages)
|
|
82
|
+
saveSession(session.id, session)
|
|
79
83
|
cleanedSessions += 1
|
|
80
84
|
removedMessages += removed
|
|
81
85
|
}
|
|
@@ -5,6 +5,10 @@ import { WORKSPACE_DIR } from '../data-dir'
|
|
|
5
5
|
import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-session'
|
|
6
6
|
import { resolveEffectiveSessionMemoryScopeMode } from '@/lib/server/memory/session-memory-scope'
|
|
7
7
|
import { syncSessionArchiveMemory } from '@/lib/server/memory/session-archive-memory'
|
|
8
|
+
import {
|
|
9
|
+
appendMessage,
|
|
10
|
+
getLastMessage,
|
|
11
|
+
} from '@/lib/server/messages/message-repository'
|
|
8
12
|
import { loadAgents, loadSessions, loadStoredItem, upsertStoredItem } from '../storage'
|
|
9
13
|
import { notify } from '../ws-hub'
|
|
10
14
|
import {
|
|
@@ -416,7 +420,7 @@ function mirrorConnectorMessageToAgentThread(
|
|
|
416
420
|
: ensureAgentThreadSession(session.agentId)
|
|
417
421
|
if (!threadSession || threadSession.id === session.id) return
|
|
418
422
|
|
|
419
|
-
const last =
|
|
423
|
+
const last = getLastMessage(threadSession.id)
|
|
420
424
|
const source = message.source as MessageSource | undefined
|
|
421
425
|
const lastSource = (last?.source || null) as MessageSource | null
|
|
422
426
|
if (
|
|
@@ -431,17 +435,16 @@ function mirrorConnectorMessageToAgentThread(
|
|
|
431
435
|
return
|
|
432
436
|
}
|
|
433
437
|
|
|
434
|
-
|
|
435
|
-
threadSession.messages.push({
|
|
438
|
+
const mirrorMsg = {
|
|
436
439
|
...message,
|
|
437
440
|
time: typeof message.time === 'number' ? message.time : Date.now(),
|
|
438
441
|
historyExcluded: true,
|
|
439
|
-
} as Session['messages'][number]
|
|
442
|
+
} as Session['messages'][number]
|
|
443
|
+
appendMessage(threadSession.id, mirrorMsg)
|
|
440
444
|
threadSession.lastActiveAt = Date.now()
|
|
441
445
|
|
|
442
446
|
upsertStoredItem('sessions', threadSession.id, threadSession)
|
|
443
447
|
notify('sessions')
|
|
444
|
-
notify(`messages:${threadSession.id}`)
|
|
445
448
|
}
|
|
446
449
|
|
|
447
450
|
export function pushSessionMessage(
|
|
@@ -451,9 +454,8 @@ export function pushSessionMessage(
|
|
|
451
454
|
extra: Record<string, unknown> = {},
|
|
452
455
|
): void {
|
|
453
456
|
if (!text.trim()) return
|
|
454
|
-
if (!Array.isArray(session.messages)) session.messages = []
|
|
455
457
|
const message = { role, text: text.trim(), time: Date.now(), ...extra }
|
|
456
|
-
session.messages
|
|
458
|
+
appendMessage(session.id, message as Session['messages'][number])
|
|
457
459
|
session.lastActiveAt = Date.now()
|
|
458
460
|
mirrorConnectorMessageToAgentThread(session, message)
|
|
459
461
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { genId } from '@/lib/id'
|
|
4
|
-
import { loadAgent
|
|
4
|
+
import { loadAgent } from '@/lib/server/agents/agent-repository'
|
|
5
5
|
import { synthesizeElevenLabsMp3 } from '../elevenlabs'
|
|
6
|
+
import { UPLOAD_DIR } from '../upload-path'
|
|
6
7
|
import { isAudioMime, mimeFromPath } from './media'
|
|
7
8
|
|
|
8
9
|
export function resolveConnectorVoiceId(params: {
|