|
1 | 1 | import type { |
2 | 2 | ChannelAuthMode, |
3 | | - ChannelConnection, |
4 | | - ChannelConnectionsByMode, |
| 3 | + ChannelConnectionResult, |
| 4 | + ChannelDefinition, |
| 5 | + ChannelStatusEntry, |
5 | 6 | ChannelType, |
6 | 7 | } from '../../types/channels'; |
7 | 8 | import { callCoreRpc } from '../coreRpcClient'; |
8 | 9 |
|
9 | 10 | interface ConnectChannelPayload { |
10 | 11 | authMode: ChannelAuthMode; |
11 | | - credentials?: { botToken?: string; apiKey?: string }; |
12 | | -} |
13 | | - |
14 | | -interface ConnectChannelResponse { |
15 | | - connection?: ChannelConnection; |
16 | | - oauthUrl?: string; |
17 | | -} |
18 | | - |
19 | | -interface ChannelConnectionsResponse { |
20 | | - defaultMessagingChannel: ChannelType; |
21 | | - connections: Record<ChannelType, ChannelConnectionsByMode>; |
22 | | -} |
23 | | - |
24 | | -interface AuthProfileSummary { |
25 | | - provider: string; |
26 | | - profileName: string; |
27 | | -} |
28 | | - |
29 | | -interface OAuthIntegrationSummary { |
30 | | - id: string; |
31 | | - provider: string; |
32 | | -} |
33 | | - |
34 | | -const SUPPORTED_CHANNELS: ChannelType[] = ['telegram', 'discord']; |
35 | | -const SUPPORTED_AUTH_MODES: ChannelAuthMode[] = ['managed_dm', 'oauth', 'bot_token', 'api_key']; |
36 | | - |
37 | | -function emptyChannelConnectionsResponse(): ChannelConnectionsResponse { |
38 | | - return { defaultMessagingChannel: 'telegram', connections: { telegram: {}, discord: {} } }; |
39 | | -} |
40 | | - |
41 | | -function isSupportedChannel(value: string): value is ChannelType { |
42 | | - return SUPPORTED_CHANNELS.includes(value as ChannelType); |
43 | | -} |
44 | | - |
45 | | -function makeConnectedChannelConnection( |
46 | | - channel: ChannelType, |
47 | | - authMode: ChannelAuthMode |
48 | | -): ChannelConnection { |
49 | | - return { |
50 | | - channel, |
51 | | - authMode, |
52 | | - status: 'connected', |
53 | | - selectedDefault: false, |
54 | | - lastError: undefined, |
55 | | - capabilities: authMode === 'managed_dm' ? ['dm'] : ['read', 'write'], |
56 | | - updatedAt: new Date().toISOString(), |
57 | | - }; |
| 12 | + credentials?: Record<string, string>; |
58 | 13 | } |
59 | 14 |
|
60 | 15 | export const channelConnectionsApi = { |
61 | | - listConnections: async (): Promise<ChannelConnectionsResponse> => { |
62 | | - const [profilesResponse, integrationsResponse] = await Promise.all([ |
63 | | - callCoreRpc<{ result: AuthProfileSummary[] }>({ |
64 | | - method: 'openhuman.auth.list_provider_credentials', |
65 | | - params: {}, |
66 | | - }), |
67 | | - callCoreRpc<{ result: OAuthIntegrationSummary[] }>({ |
68 | | - method: 'openhuman.auth.oauth_list_integrations', |
69 | | - params: {}, |
70 | | - }), |
71 | | - ]); |
72 | | - |
73 | | - const output = emptyChannelConnectionsResponse(); |
74 | | - const profiles = profilesResponse.result ?? []; |
75 | | - const integrations = integrationsResponse.result ?? []; |
76 | | - |
77 | | - for (const profile of profiles) { |
78 | | - if (!isSupportedChannel(profile.provider)) continue; |
79 | | - const authMode = profile.profileName as ChannelAuthMode; |
80 | | - if (!SUPPORTED_AUTH_MODES.includes(authMode) || authMode === 'oauth') continue; |
81 | | - output.connections[profile.provider][authMode] = makeConnectedChannelConnection( |
82 | | - profile.provider, |
83 | | - authMode |
84 | | - ); |
85 | | - } |
86 | | - |
87 | | - for (const integration of integrations) { |
88 | | - if (!isSupportedChannel(integration.provider)) continue; |
89 | | - output.connections[integration.provider].oauth = makeConnectedChannelConnection( |
90 | | - integration.provider, |
91 | | - 'oauth' |
92 | | - ); |
93 | | - } |
| 16 | + /** Fetch all available channel definitions from the backend. */ |
| 17 | + listDefinitions: async (): Promise<ChannelDefinition[]> => { |
| 18 | + const result = await callCoreRpc<ChannelDefinition[]>({ |
| 19 | + method: 'openhuman.channels_list', |
| 20 | + params: {}, |
| 21 | + }); |
| 22 | + return result; |
| 23 | + }, |
94 | 24 |
|
95 | | - return output; |
| 25 | + /** Get connection status for one or all channels. */ |
| 26 | + listStatus: async (channel?: ChannelType): Promise<ChannelStatusEntry[]> => { |
| 27 | + const params: Record<string, string> = {}; |
| 28 | + if (channel) params.channel = channel; |
| 29 | + const result = await callCoreRpc<ChannelStatusEntry[]>({ |
| 30 | + method: 'openhuman.channels_status', |
| 31 | + params, |
| 32 | + }); |
| 33 | + return result; |
96 | 34 | }, |
97 | 35 |
|
| 36 | + /** Connect a channel with the given auth mode and credentials. */ |
98 | 37 | connectChannel: async ( |
99 | 38 | channel: ChannelType, |
100 | 39 | payload: ConnectChannelPayload |
101 | | - ): Promise<ConnectChannelResponse> => { |
102 | | - if (payload.authMode === 'oauth') { |
103 | | - const response = await callCoreRpc<{ result: { oauthUrl: string } }>({ |
104 | | - method: 'openhuman.auth.oauth_connect', |
105 | | - params: { provider: channel, skillId: channel }, |
106 | | - }); |
107 | | - return { |
108 | | - oauthUrl: response.result.oauthUrl, |
109 | | - connection: makeConnectedChannelConnection(channel, payload.authMode), |
110 | | - }; |
111 | | - } |
112 | | - |
113 | | - const token = |
114 | | - payload.authMode === 'bot_token' |
115 | | - ? payload.credentials?.botToken?.trim() |
116 | | - : payload.authMode === 'api_key' |
117 | | - ? payload.credentials?.apiKey?.trim() |
118 | | - : undefined; |
119 | | - |
120 | | - await callCoreRpc({ |
121 | | - method: 'openhuman.auth.store_provider_credentials', |
122 | | - params: { |
123 | | - provider: channel, |
124 | | - profile: payload.authMode, |
125 | | - token, |
126 | | - fields: { authMode: payload.authMode }, |
127 | | - setActive: true, |
128 | | - }, |
| 40 | + ): Promise<ChannelConnectionResult> => { |
| 41 | + const result = await callCoreRpc<ChannelConnectionResult>({ |
| 42 | + method: 'openhuman.channels_connect', |
| 43 | + params: { channel, authMode: payload.authMode, credentials: payload.credentials ?? {} }, |
129 | 44 | }); |
130 | | - |
131 | | - return { connection: makeConnectedChannelConnection(channel, payload.authMode) }; |
| 45 | + return result; |
132 | 46 | }, |
133 | 47 |
|
| 48 | + /** Disconnect a channel for a given auth mode. */ |
134 | 49 | disconnectChannel: async (channel: ChannelType, authMode: ChannelAuthMode): Promise<void> => { |
135 | | - if (authMode === 'oauth') { |
136 | | - const listResponse = await callCoreRpc<{ result: OAuthIntegrationSummary[] }>({ |
137 | | - method: 'openhuman.auth.oauth_list_integrations', |
138 | | - params: {}, |
139 | | - }); |
140 | | - const integrationIds = (listResponse.result ?? []) |
141 | | - .filter(item => item.provider === channel) |
142 | | - .map(item => item.id); |
143 | | - |
144 | | - await Promise.all( |
145 | | - integrationIds.map(integrationId => |
146 | | - callCoreRpc({ |
147 | | - method: 'openhuman.auth.oauth_revoke_integration', |
148 | | - params: { integrationId }, |
149 | | - }) |
150 | | - ) |
151 | | - ); |
152 | | - return; |
153 | | - } |
| 50 | + await callCoreRpc({ method: 'openhuman.channels_disconnect', params: { channel, authMode } }); |
| 51 | + }, |
154 | 52 |
|
155 | | - await callCoreRpc({ |
156 | | - method: 'openhuman.auth.remove_provider_credentials', |
157 | | - params: { provider: channel, profile: authMode }, |
| 53 | + /** Test channel credentials without persisting. */ |
| 54 | + testChannel: async ( |
| 55 | + channel: ChannelType, |
| 56 | + authMode: ChannelAuthMode, |
| 57 | + credentials: Record<string, string> |
| 58 | + ): Promise<{ success: boolean; message: string }> => { |
| 59 | + const result = await callCoreRpc<{ success: boolean; message: string }>({ |
| 60 | + method: 'openhuman.channels_test', |
| 61 | + params: { channel, authMode, credentials }, |
158 | 62 | }); |
| 63 | + return result; |
159 | 64 | }, |
160 | 65 |
|
| 66 | + /** Placeholder for default channel preference sync. */ |
161 | 67 | updatePreferences: async (defaultMessagingChannel: ChannelType): Promise<void> => { |
162 | 68 | void defaultMessagingChannel; |
163 | 69 | }, |
|
0 commit comments