@bytexbyte/nxtlinq-ai-agent-core-development 0.2.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/dist/agent/ChatOrchestrator.d.ts +48 -0
- package/dist/agent/ChatOrchestrator.d.ts.map +1 -0
- package/dist/agent/ChatOrchestrator.js +311 -0
- package/dist/agent/NxtlinqAgent.d.ts +65 -0
- package/dist/agent/NxtlinqAgent.d.ts.map +1 -0
- package/dist/agent/NxtlinqAgent.js +256 -0
- package/dist/agent/errors.d.ts +4 -0
- package/dist/agent/errors.d.ts.map +1 -0
- package/dist/agent/errors.js +6 -0
- package/dist/agent/extractReplyText.d.ts +3 -0
- package/dist/agent/extractReplyText.d.ts.map +1 -0
- package/dist/agent/extractReplyText.js +16 -0
- package/dist/api/hosts.d.ts +4 -0
- package/dist/api/hosts.d.ts.map +1 -0
- package/dist/api/hosts.js +18 -0
- package/dist/api/nxtlinq-api.d.ts +9 -0
- package/dist/api/nxtlinq-api.d.ts.map +1 -0
- package/dist/api/nxtlinq-api.js +499 -0
- package/dist/api/parse-sse.d.ts +9 -0
- package/dist/api/parse-sse.d.ts.map +1 -0
- package/dist/api/parse-sse.js +97 -0
- package/dist/api/tts.d.ts +19 -0
- package/dist/api/tts.d.ts.map +1 -0
- package/dist/api/tts.js +46 -0
- package/dist/constants/storageKeys.d.ts +6 -0
- package/dist/constants/storageKeys.d.ts.map +1 -0
- package/dist/constants/storageKeys.js +5 -0
- package/dist/history/messageHistory.d.ts +18 -0
- package/dist/history/messageHistory.d.ts.map +1 -0
- package/dist/history/messageHistory.js +48 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/ports/HttpPort.d.ts +6 -0
- package/dist/ports/HttpPort.d.ts.map +1 -0
- package/dist/ports/HttpPort.js +3 -0
- package/dist/ports/PlatformPorts.d.ts +12 -0
- package/dist/ports/PlatformPorts.d.ts.map +1 -0
- package/dist/ports/PlatformPorts.js +1 -0
- package/dist/ports/StoragePort.d.ts +10 -0
- package/dist/ports/StoragePort.d.ts.map +1 -0
- package/dist/ports/StoragePort.js +33 -0
- package/dist/ports/WebRTCPort.d.ts +68 -0
- package/dist/ports/WebRTCPort.d.ts.map +1 -0
- package/dist/ports/WebRTCPort.js +10 -0
- package/dist/ports/createBrowserWebRTCPort.d.ts +7 -0
- package/dist/ports/createBrowserWebRTCPort.d.ts.map +1 -0
- package/dist/ports/createBrowserWebRTCPort.js +140 -0
- package/dist/ports/index.d.ts +5 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +4 -0
- package/dist/types/agent-config.d.ts +40 -0
- package/dist/types/agent-config.d.ts.map +1 -0
- package/dist/types/agent-config.js +1 -0
- package/dist/types/ait-api.d.ts +393 -0
- package/dist/types/ait-api.d.ts.map +1 -0
- package/dist/types/ait-api.js +1 -0
- package/dist/voice/app-channel-dispatcher.d.ts +14 -0
- package/dist/voice/app-channel-dispatcher.d.ts.map +1 -0
- package/dist/voice/app-channel-dispatcher.js +171 -0
- package/dist/voice/create-voice-session.d.ts +8 -0
- package/dist/voice/create-voice-session.d.ts.map +1 -0
- package/dist/voice/create-voice-session.js +37 -0
- package/dist/voice/output-audio-level.d.ts +26 -0
- package/dist/voice/output-audio-level.d.ts.map +1 -0
- package/dist/voice/output-audio-level.js +132 -0
- package/dist/voice/remote-audio-gain.d.ts +10 -0
- package/dist/voice/remote-audio-gain.d.ts.map +1 -0
- package/dist/voice/remote-audio-gain.js +19 -0
- package/dist/voice/start-voice-session.d.ts +13 -0
- package/dist/voice/start-voice-session.d.ts.map +1 -0
- package/dist/voice/start-voice-session.js +303 -0
- package/dist/voice/transcript.d.ts +10 -0
- package/dist/voice/transcript.d.ts.map +1 -0
- package/dist/voice/transcript.js +50 -0
- package/dist/voice/trigger-voice-greeting.d.ts +14 -0
- package/dist/voice/trigger-voice-greeting.d.ts.map +1 -0
- package/dist/voice/trigger-voice-greeting.js +28 -0
- package/dist/voice/types.d.ts +138 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +1 -0
- package/dist/voice/voice-user-input.d.ts +19 -0
- package/dist/voice/voice-user-input.d.ts.map +1 -0
- package/dist/voice/voice-user-input.js +10 -0
- package/package.json +41 -0
- package/src/agent/ChatOrchestrator.ts +380 -0
- package/src/agent/NxtlinqAgent.ts +325 -0
- package/src/agent/errors.ts +6 -0
- package/src/agent/extractReplyText.ts +22 -0
- package/src/api/hosts.ts +20 -0
- package/src/api/nxtlinq-api.ts +656 -0
- package/src/api/parse-sse.ts +104 -0
- package/src/api/tts.ts +69 -0
- package/src/constants/storageKeys.ts +5 -0
- package/src/history/messageHistory.ts +65 -0
- package/src/index.ts +70 -0
- package/src/ports/HttpPort.ts +12 -0
- package/src/ports/PlatformPorts.ts +12 -0
- package/src/ports/StoragePort.ts +37 -0
- package/src/ports/WebRTCPort.ts +54 -0
- package/src/ports/createBrowserWebRTCPort.ts +163 -0
- package/src/ports/index.ts +4 -0
- package/src/types/agent-config.ts +51 -0
- package/src/types/ait-api.ts +303 -0
- package/src/voice/app-channel-dispatcher.ts +201 -0
- package/src/voice/create-voice-session.ts +53 -0
- package/src/voice/output-audio-level.ts +153 -0
- package/src/voice/remote-audio-gain.ts +31 -0
- package/src/voice/start-voice-session.ts +369 -0
- package/src/voice/transcript.ts +44 -0
- package/src/voice/trigger-voice-greeting.ts +47 -0
- package/src/voice/types.ts +154 -0
- package/src/voice/voice-user-input.ts +32 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
import type { AITApi } from '../types/ait-api';
|
|
2
|
+
import { STORAGE_KEYS } from '../constants/storageKeys';
|
|
3
|
+
import type { PlatformPorts } from '../ports/PlatformPorts';
|
|
4
|
+
import { createDefaultHttpPort } from '../ports/HttpPort';
|
|
5
|
+
import { getAiAgentApiHost, getAitServiceApiHost } from './hosts';
|
|
6
|
+
import { parseSSEResponse, parseSSEText } from './parse-sse';
|
|
7
|
+
import { postTextTts, type PostTextTtsResult } from './tts';
|
|
8
|
+
|
|
9
|
+
export type { PostTextTtsResult };
|
|
10
|
+
export { postTextTts, buildTextTtsDataUri } from './tts';
|
|
11
|
+
|
|
12
|
+
export type CoreAITApi = Omit<AITApi, 'voice'>;
|
|
13
|
+
|
|
14
|
+
export type ApiClientDeps = Pick<PlatformPorts, 'storage' | 'http' | 'getTimezone'>;
|
|
15
|
+
|
|
16
|
+
/** RN/Hermes 的 URLSearchParams 常缺少 set/append,改用手動組 query。 */
|
|
17
|
+
function withQuery(
|
|
18
|
+
baseUrl: string,
|
|
19
|
+
query: Record<string, string | number | undefined | null>,
|
|
20
|
+
): string {
|
|
21
|
+
const pairs: string[] = [];
|
|
22
|
+
for (const [key, value] of Object.entries(query)) {
|
|
23
|
+
if (value == null || value === '') continue;
|
|
24
|
+
pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
25
|
+
}
|
|
26
|
+
if (pairs.length === 0) return baseUrl;
|
|
27
|
+
const sep = baseUrl.includes('?') ? '&' : '?';
|
|
28
|
+
return `${baseUrl}${sep}${pairs.join('&')}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createApiHelpers(deps: ApiClientDeps) {
|
|
32
|
+
const fetchFn = deps.http?.fetch ?? createDefaultHttpPort().fetch;
|
|
33
|
+
const getTimezone = deps.getTimezone ?? (() => {
|
|
34
|
+
try {
|
|
35
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
36
|
+
} catch {
|
|
37
|
+
return 'UTC';
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const getAuthHeader = async (): Promise<Record<string, string>> => {
|
|
42
|
+
const token = await deps.storage.getItem(STORAGE_KEYS.AIT_SERVICE_ACCESS_TOKEN);
|
|
43
|
+
return token ? { Authorization: `Bearer ${JSON.parse(token)}` } : {};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const makeRequest = async (
|
|
47
|
+
url: string,
|
|
48
|
+
options: RequestInit,
|
|
49
|
+
apiKey: string,
|
|
50
|
+
apiSecret: string,
|
|
51
|
+
): Promise<any> => {
|
|
52
|
+
const auth = await getAuthHeader();
|
|
53
|
+
const response = await fetchFn(url, {
|
|
54
|
+
...options,
|
|
55
|
+
headers: {
|
|
56
|
+
'X-API-Key': apiKey,
|
|
57
|
+
'X-API-Secret': apiSecret,
|
|
58
|
+
...auth,
|
|
59
|
+
...options.headers,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
return response.json();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const getWalletContext = async () => {
|
|
66
|
+
const walletAddress = await deps.storage.getItem(STORAGE_KEYS.WALLET_ADDRESS);
|
|
67
|
+
const aitTokenRaw = await deps.storage.getItem(STORAGE_KEYS.AIT_SERVICE_ACCESS_TOKEN);
|
|
68
|
+
const aitToken = aitTokenRaw ? JSON.parse(aitTokenRaw) : null;
|
|
69
|
+
return { walletAddress, aitToken };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return { fetchFn, getTimezone, getAuthHeader, makeRequest, getWalletContext };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// AIT API module
|
|
76
|
+
const createAITApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
77
|
+
getAITByServiceIdAndController: async (params: { serviceId: string; controller: string; customUsername?: string }, token: string) => {
|
|
78
|
+
const url = withQuery(
|
|
79
|
+
`${getAitServiceApiHost()}/api/ait/service/${params.serviceId}/controller/${params.controller}`,
|
|
80
|
+
{ customUsername: params.customUsername },
|
|
81
|
+
);
|
|
82
|
+
return helpers.makeRequest(
|
|
83
|
+
url,
|
|
84
|
+
{ method: 'GET' },
|
|
85
|
+
apiKey,
|
|
86
|
+
apiSecret
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
createAIT: async (params: any, token: string) => {
|
|
91
|
+
return helpers.makeRequest(
|
|
92
|
+
`${getAitServiceApiHost()}/api/ait`,
|
|
93
|
+
{
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: { 'Content-Type': 'application/json' },
|
|
96
|
+
body: JSON.stringify(params)
|
|
97
|
+
},
|
|
98
|
+
apiKey,
|
|
99
|
+
apiSecret
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Wallet API module
|
|
105
|
+
const createWalletApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
106
|
+
verifyWallet: async (params: any, token: string) => {
|
|
107
|
+
return helpers.makeRequest(
|
|
108
|
+
`${getAitServiceApiHost()}/api/wallet`,
|
|
109
|
+
{
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify(params)
|
|
113
|
+
},
|
|
114
|
+
apiKey,
|
|
115
|
+
apiSecret
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
getWallet: async (params: { address: string }, token: string) => {
|
|
120
|
+
return helpers.makeRequest(
|
|
121
|
+
`${getAitServiceApiHost()}/api/wallet/address/${params.address}`,
|
|
122
|
+
{ method: 'GET' },
|
|
123
|
+
apiKey,
|
|
124
|
+
apiSecret
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Metadata API module
|
|
130
|
+
const createMetadataApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
131
|
+
createMetadata: async (metadata: any, token: string) => {
|
|
132
|
+
return helpers.makeRequest(
|
|
133
|
+
`${getAitServiceApiHost()}/api/metadata`,
|
|
134
|
+
{
|
|
135
|
+
method: 'POST',
|
|
136
|
+
headers: { 'Content-Type': 'application/json' },
|
|
137
|
+
body: JSON.stringify(metadata)
|
|
138
|
+
},
|
|
139
|
+
apiKey,
|
|
140
|
+
apiSecret
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Auth API module
|
|
146
|
+
const createAuthApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
147
|
+
getNonce: async (params: { address: string }) => {
|
|
148
|
+
return helpers.makeRequest(
|
|
149
|
+
`${getAitServiceApiHost()}/api/auth/address/${params.address}/nonce`,
|
|
150
|
+
{ method: 'GET' },
|
|
151
|
+
apiKey,
|
|
152
|
+
apiSecret
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
signIn: async (params: any) => {
|
|
157
|
+
return helpers.makeRequest(
|
|
158
|
+
`${getAitServiceApiHost()}/api/auth`,
|
|
159
|
+
{
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: { 'Content-Type': 'application/json' },
|
|
162
|
+
body: JSON.stringify(params)
|
|
163
|
+
},
|
|
164
|
+
apiKey,
|
|
165
|
+
apiSecret
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Agent API module
|
|
171
|
+
const createAgentApi = (helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
172
|
+
sendMessage: async (params: {
|
|
173
|
+
model?: string;
|
|
174
|
+
apiKey: string;
|
|
175
|
+
apiSecret: string;
|
|
176
|
+
pseudoId: string;
|
|
177
|
+
externalId?: string;
|
|
178
|
+
customUserInfo?: Record<string, any>;
|
|
179
|
+
customUsername?: string;
|
|
180
|
+
message: string;
|
|
181
|
+
attachments?: Array<{
|
|
182
|
+
type: 'image' | 'file';
|
|
183
|
+
url: string;
|
|
184
|
+
name: string;
|
|
185
|
+
mimeType: string;
|
|
186
|
+
}>;
|
|
187
|
+
context?: Array<{ role: string; text: string }>;
|
|
188
|
+
isRetry?: boolean;
|
|
189
|
+
/** 客戶端步驟耗時(如 Azure Speech STT),會寫入後端 ai_service_call_log */
|
|
190
|
+
clientPipelineTimings?: Array<{ name: string; durationMs: number }>;
|
|
191
|
+
onPiiProgress?: (step: 'scan_start' | 'scan_complete' | 'send_start' | 'done', data?: any) => void;
|
|
192
|
+
/** Token chunks from `*-stream` routes (`event: text_delta`). */
|
|
193
|
+
onStreamDelta?: (text: string) => void;
|
|
194
|
+
}) => {
|
|
195
|
+
try {
|
|
196
|
+
const model = params.model || 'open-ai';
|
|
197
|
+
const { walletAddress, aitToken } = await helpers.getWalletContext();
|
|
198
|
+
const auth = await helpers.getAuthHeader();
|
|
199
|
+
|
|
200
|
+
const requestBody = {
|
|
201
|
+
apiKey: params.apiKey,
|
|
202
|
+
apiSecret: params.apiSecret,
|
|
203
|
+
pseudoId: params.pseudoId,
|
|
204
|
+
externalId: params.externalId,
|
|
205
|
+
customUserInfo: params.customUserInfo,
|
|
206
|
+
customUsername: params.customUsername,
|
|
207
|
+
message: params.message,
|
|
208
|
+
attachments: params.attachments,
|
|
209
|
+
walletAddress: walletAddress || undefined,
|
|
210
|
+
aitToken: aitToken || undefined,
|
|
211
|
+
timezone: helpers.getTimezone(),
|
|
212
|
+
...(params.isRetry ? { isRetry: true } : {}),
|
|
213
|
+
...(params.clientPipelineTimings &&
|
|
214
|
+
params.clientPipelineTimings.length > 0
|
|
215
|
+
? { clientPipelineTimings: params.clientPipelineTimings }
|
|
216
|
+
: {}),
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const useSSE = Boolean(
|
|
220
|
+
params.onPiiProgress || params.onStreamDelta || model.endsWith('-stream')
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/${model}`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: {
|
|
226
|
+
'Content-Type': 'application/json',
|
|
227
|
+
'X-Wallet-Address': walletAddress || '',
|
|
228
|
+
'X-AIT-Token': aitToken || '',
|
|
229
|
+
...auth,
|
|
230
|
+
...(params.onPiiProgress ? { 'X-Stream-PII': '1' } : {}),
|
|
231
|
+
},
|
|
232
|
+
body: JSON.stringify(requestBody),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
throw new Error('Failed to send message');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const contentType = response.headers.get('content-type') || '';
|
|
240
|
+
|
|
241
|
+
if (useSSE && contentType.includes('text/event-stream')) {
|
|
242
|
+
const sseHandlers = {
|
|
243
|
+
onPiiProgress: params.onPiiProgress,
|
|
244
|
+
onStreamDelta: params.onStreamDelta,
|
|
245
|
+
};
|
|
246
|
+
// Always buffer first: RN often exposes a `body` stream that closes
|
|
247
|
+
// immediately without data, which breaks incremental parsing.
|
|
248
|
+
const rawText = await response.text();
|
|
249
|
+
if (rawText.trimStart().startsWith('event:') || rawText.includes('\nevent:')) {
|
|
250
|
+
return parseSSEText(rawText, sseHandlers);
|
|
251
|
+
}
|
|
252
|
+
// Gateway returned JSON despite SSE content-type.
|
|
253
|
+
return rawText ? JSON.parse(rawText) : {};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return await response.json();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error('Failed to send message:', error);
|
|
259
|
+
return { error: error instanceof Error ? error.message : 'Failed to send message' };
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
generateSuggestions: async (params: {
|
|
263
|
+
apiKey: string;
|
|
264
|
+
apiSecret: string;
|
|
265
|
+
pseudoId: string;
|
|
266
|
+
externalId?: string;
|
|
267
|
+
}) => {
|
|
268
|
+
try {
|
|
269
|
+
const auth = await helpers.getAuthHeader();
|
|
270
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/suggestions`, {
|
|
271
|
+
method: 'POST',
|
|
272
|
+
headers: {
|
|
273
|
+
'Content-Type': 'application/json',
|
|
274
|
+
'X-API-Key': params.apiKey,
|
|
275
|
+
'X-API-Secret': params.apiSecret,
|
|
276
|
+
...auth,
|
|
277
|
+
},
|
|
278
|
+
body: JSON.stringify({
|
|
279
|
+
apiKey: params.apiKey,
|
|
280
|
+
apiSecret: params.apiSecret,
|
|
281
|
+
pseudoId: params.pseudoId,
|
|
282
|
+
externalId: params.externalId || undefined,
|
|
283
|
+
}),
|
|
284
|
+
});
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
throw new Error('Failed to generate suggestions');
|
|
287
|
+
}
|
|
288
|
+
return await response.json();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.error('Failed to generate suggestions:', error);
|
|
291
|
+
return { error: error instanceof Error ? error.message : 'Failed to generate suggestions' };
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
getMessageHistory: async (params: {
|
|
295
|
+
apiKey: string;
|
|
296
|
+
apiSecret: string;
|
|
297
|
+
pseudoId: string;
|
|
298
|
+
externalId?: string;
|
|
299
|
+
last?: number;
|
|
300
|
+
}) => {
|
|
301
|
+
try {
|
|
302
|
+
const url = withQuery(
|
|
303
|
+
`${getAiAgentApiHost()}/api/message/pseudoId/${encodeURIComponent(params.pseudoId)}`,
|
|
304
|
+
{
|
|
305
|
+
externalId: params.externalId,
|
|
306
|
+
last: params.last != null ? params.last : undefined,
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
const auth = await helpers.getAuthHeader();
|
|
310
|
+
const response = await helpers.fetchFn(url, {
|
|
311
|
+
method: 'GET',
|
|
312
|
+
headers: {
|
|
313
|
+
'X-API-Key': params.apiKey,
|
|
314
|
+
'X-API-Secret': params.apiSecret,
|
|
315
|
+
...auth,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
throw new Error('Failed to fetch message history');
|
|
320
|
+
}
|
|
321
|
+
return await response.json() as {
|
|
322
|
+
messages: Array<{
|
|
323
|
+
id: string;
|
|
324
|
+
role: 'user' | 'assistant';
|
|
325
|
+
content: string;
|
|
326
|
+
createdAt?: string;
|
|
327
|
+
}>;
|
|
328
|
+
};
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error('Failed to fetch message history:', error);
|
|
331
|
+
return { error: error instanceof Error ? error.message : 'Failed to fetch message history' };
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
updateMessagesExternalIdByPseudoId: async (params: {
|
|
335
|
+
apiKey: string;
|
|
336
|
+
apiSecret: string;
|
|
337
|
+
pseudoId: string;
|
|
338
|
+
externalId: string;
|
|
339
|
+
}) => {
|
|
340
|
+
try {
|
|
341
|
+
const auth = await helpers.getAuthHeader();
|
|
342
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/message/pseudoId/${params.pseudoId}`, {
|
|
343
|
+
method: 'PUT',
|
|
344
|
+
headers: {
|
|
345
|
+
'Content-Type': 'application/json',
|
|
346
|
+
'X-API-Key': params.apiKey,
|
|
347
|
+
'X-API-Secret': params.apiSecret,
|
|
348
|
+
...auth,
|
|
349
|
+
},
|
|
350
|
+
body: JSON.stringify({
|
|
351
|
+
apiKey: params.apiKey,
|
|
352
|
+
apiSecret: params.apiSecret,
|
|
353
|
+
externalId: params.externalId,
|
|
354
|
+
}),
|
|
355
|
+
});
|
|
356
|
+
if (!response.ok) {
|
|
357
|
+
throw new Error('Failed to update message history');
|
|
358
|
+
}
|
|
359
|
+
return await response.json();
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('Failed to update message history:', error);
|
|
362
|
+
return { error: error instanceof Error ? error.message : 'Failed to update message history' };
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
cloneUserProfileByPseudoId: async (params: {
|
|
366
|
+
apiKey: string;
|
|
367
|
+
apiSecret: string;
|
|
368
|
+
pseudoId: string;
|
|
369
|
+
externalId: string;
|
|
370
|
+
}) => {
|
|
371
|
+
try {
|
|
372
|
+
const auth = await helpers.getAuthHeader();
|
|
373
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/userProfile/pseudoId/${params.pseudoId}`, {
|
|
374
|
+
method: 'PUT',
|
|
375
|
+
headers: {
|
|
376
|
+
'Content-Type': 'application/json',
|
|
377
|
+
'X-API-Key': params.apiKey,
|
|
378
|
+
'X-API-Secret': params.apiSecret,
|
|
379
|
+
...auth,
|
|
380
|
+
},
|
|
381
|
+
body: JSON.stringify({
|
|
382
|
+
apiKey: params.apiKey,
|
|
383
|
+
apiSecret: params.apiSecret,
|
|
384
|
+
externalId: params.externalId,
|
|
385
|
+
}),
|
|
386
|
+
});
|
|
387
|
+
if (!response.ok) {
|
|
388
|
+
throw new Error('Failed to clone user profile');
|
|
389
|
+
}
|
|
390
|
+
return await response.json();
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error('Failed to clone user profile:', error);
|
|
393
|
+
return { error: error instanceof Error ? error.message : 'Failed to clone user profile' };
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
cloneUserTopicByPseudoId: async (params: {
|
|
397
|
+
apiKey: string;
|
|
398
|
+
apiSecret: string;
|
|
399
|
+
pseudoId: string;
|
|
400
|
+
externalId: string;
|
|
401
|
+
}) => {
|
|
402
|
+
try {
|
|
403
|
+
const auth = await helpers.getAuthHeader();
|
|
404
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/userTopic/pseudoId/${params.pseudoId}`, {
|
|
405
|
+
method: 'PUT',
|
|
406
|
+
headers: {
|
|
407
|
+
'Content-Type': 'application/json',
|
|
408
|
+
'X-API-Key': params.apiKey,
|
|
409
|
+
'X-API-Secret': params.apiSecret,
|
|
410
|
+
...auth,
|
|
411
|
+
},
|
|
412
|
+
body: JSON.stringify({
|
|
413
|
+
apiKey: params.apiKey,
|
|
414
|
+
apiSecret: params.apiSecret,
|
|
415
|
+
externalId: params.externalId,
|
|
416
|
+
}),
|
|
417
|
+
});
|
|
418
|
+
if (!response.ok) {
|
|
419
|
+
throw new Error('Failed to clone user topic');
|
|
420
|
+
}
|
|
421
|
+
return await response.json();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error('Failed to clone user topic:', error);
|
|
424
|
+
return { error: error instanceof Error ? error.message : 'Failed to clone user topic' };
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
checkToolPermission: async (params: { apiKey: string, apiSecret: string, aitToken: string, controller: string, toolName: string; }) => {
|
|
428
|
+
try {
|
|
429
|
+
const auth = await helpers.getAuthHeader();
|
|
430
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/utils/checkToolPermission`, {
|
|
431
|
+
method: 'POST',
|
|
432
|
+
headers: {
|
|
433
|
+
'Content-Type': 'application/json',
|
|
434
|
+
'X-API-Key': params.apiKey,
|
|
435
|
+
'X-API-Secret': params.apiSecret,
|
|
436
|
+
...auth,
|
|
437
|
+
},
|
|
438
|
+
body: JSON.stringify({
|
|
439
|
+
apiKey: params.apiKey,
|
|
440
|
+
apiSecret: params.apiSecret,
|
|
441
|
+
aitToken: params.aitToken,
|
|
442
|
+
controller: params.controller,
|
|
443
|
+
toolName: params.toolName,
|
|
444
|
+
}),
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
return await response.json();
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error('Failed to clone user topic:', error);
|
|
450
|
+
return { error: error instanceof Error ? error.message : 'Failed to clone user topic' };
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
getServiceModels: async (params: { apiKey: string; apiSecret: string; }) => {
|
|
454
|
+
try {
|
|
455
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/service-models`, {
|
|
456
|
+
method: 'GET',
|
|
457
|
+
headers: {
|
|
458
|
+
'x-api-key': params.apiKey,
|
|
459
|
+
'x-api-secret': params.apiSecret,
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
if (!response.ok) {
|
|
464
|
+
throw new Error('Failed to fetch service models');
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return await response.json();
|
|
468
|
+
} catch (error) {
|
|
469
|
+
console.error('Failed to fetch service models:', error);
|
|
470
|
+
return { error: error instanceof Error ? error.message : 'Failed to fetch service models' };
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
updateMessageContent: async (params: {
|
|
474
|
+
apiKey: string;
|
|
475
|
+
apiSecret: string;
|
|
476
|
+
messageId: string;
|
|
477
|
+
content: string;
|
|
478
|
+
}) => {
|
|
479
|
+
try {
|
|
480
|
+
const auth = await helpers.getAuthHeader();
|
|
481
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/message/${params.messageId}`, {
|
|
482
|
+
method: 'PUT',
|
|
483
|
+
headers: {
|
|
484
|
+
'Content-Type': 'application/json',
|
|
485
|
+
'X-API-Key': params.apiKey,
|
|
486
|
+
'X-API-Secret': params.apiSecret,
|
|
487
|
+
...auth,
|
|
488
|
+
},
|
|
489
|
+
body: JSON.stringify({
|
|
490
|
+
apiKey: params.apiKey,
|
|
491
|
+
apiSecret: params.apiSecret,
|
|
492
|
+
content: params.content,
|
|
493
|
+
}),
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
if (!response.ok) {
|
|
497
|
+
throw new Error('Failed to update message content');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return await response.json();
|
|
501
|
+
} catch (error) {
|
|
502
|
+
console.error('Failed to update message content:', error);
|
|
503
|
+
return { error: error instanceof Error ? error.message : 'Failed to update message content' };
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
uploadAttachment: async (params: {
|
|
507
|
+
apiKey: string;
|
|
508
|
+
apiSecret: string;
|
|
509
|
+
pseudoId: string;
|
|
510
|
+
file: File;
|
|
511
|
+
}): Promise<{ url: string; key?: string; name?: string; mimeType?: string } | { error: string }> => {
|
|
512
|
+
try {
|
|
513
|
+
const originalName = params.file.name;
|
|
514
|
+
const safeName = originalName.replace(/[^\x00-\x7F.a-zA-Z0-9_-]/g, '_').replace(/^\.+/, '') || 'file';
|
|
515
|
+
const fileForUpload = safeName !== originalName
|
|
516
|
+
? new File([params.file], safeName, { type: params.file.type, lastModified: params.file.lastModified })
|
|
517
|
+
: params.file;
|
|
518
|
+
|
|
519
|
+
const form = new FormData();
|
|
520
|
+
form.append('file', fileForUpload);
|
|
521
|
+
form.append('pseudoId', params.pseudoId);
|
|
522
|
+
if (safeName !== originalName) {
|
|
523
|
+
form.append('originalName', originalName);
|
|
524
|
+
}
|
|
525
|
+
const { walletAddress, aitToken } = await helpers.getWalletContext();
|
|
526
|
+
const headers: Record<string, string> = {
|
|
527
|
+
'X-API-Key': params.apiKey,
|
|
528
|
+
'X-API-Secret': params.apiSecret,
|
|
529
|
+
'X-Wallet-Address': walletAddress || '',
|
|
530
|
+
'X-AIT-Token': aitToken || '',
|
|
531
|
+
};
|
|
532
|
+
const response = await helpers.fetchFn(`${getAiAgentApiHost()}/api/upload`, {
|
|
533
|
+
method: 'POST',
|
|
534
|
+
headers,
|
|
535
|
+
body: form,
|
|
536
|
+
});
|
|
537
|
+
const text = await response.text();
|
|
538
|
+
let data: { url?: string; key?: string; name?: string; mimeType?: string; error?: string };
|
|
539
|
+
try {
|
|
540
|
+
data = text ? JSON.parse(text) : {};
|
|
541
|
+
} catch {
|
|
542
|
+
data = {};
|
|
543
|
+
}
|
|
544
|
+
if (!response.ok) {
|
|
545
|
+
return { error: data.error || response.statusText || 'Upload failed' };
|
|
546
|
+
}
|
|
547
|
+
if (!data.url) {
|
|
548
|
+
return { error: data.error || 'Upload failed' };
|
|
549
|
+
}
|
|
550
|
+
return data as { url: string; key?: string; name?: string; mimeType?: string };
|
|
551
|
+
} catch (e) {
|
|
552
|
+
console.error('Upload attachment failed:', e);
|
|
553
|
+
return { error: e instanceof Error ? e.message : 'Upload failed' };
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// Permissions API module
|
|
559
|
+
const createPermissionsApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
560
|
+
getServicePermissions: async (params: { serviceId: string; groupName?: string }) => {
|
|
561
|
+
try {
|
|
562
|
+
const url = withQuery(
|
|
563
|
+
`${getAiAgentApiHost()}/api/permissions/service/${params.serviceId}`,
|
|
564
|
+
{
|
|
565
|
+
groupName: params.groupName,
|
|
566
|
+
_t: Date.now(),
|
|
567
|
+
},
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const auth = await helpers.getAuthHeader();
|
|
571
|
+
const response = await helpers.fetchFn(url, {
|
|
572
|
+
method: 'GET',
|
|
573
|
+
cache: 'no-store',
|
|
574
|
+
headers: {
|
|
575
|
+
'X-API-Key': apiKey,
|
|
576
|
+
'X-API-Secret': apiSecret,
|
|
577
|
+
'Content-Type': 'application/json',
|
|
578
|
+
...auth,
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
if (!response.ok) {
|
|
583
|
+
throw new Error('Failed to fetch permissions');
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return await response.json();
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.error('Failed to fetch permissions:', error);
|
|
589
|
+
return { error: error instanceof Error ? error.message : 'Failed to fetch permissions' };
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Cognitive API Module
|
|
595
|
+
const createCognitiveApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
596
|
+
getCognitiveToken: async () => {
|
|
597
|
+
try {
|
|
598
|
+
const url = `${getAiAgentApiHost()}/api/cognitive/token`;
|
|
599
|
+
const auth = await helpers.getAuthHeader();
|
|
600
|
+
|
|
601
|
+
const response = await helpers.fetchFn(url, {
|
|
602
|
+
method: 'GET',
|
|
603
|
+
headers: {
|
|
604
|
+
'X-API-Key': apiKey,
|
|
605
|
+
'X-API-Secret': apiSecret,
|
|
606
|
+
'Content-Type': 'application/json',
|
|
607
|
+
...auth,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
if (!response.ok) {
|
|
612
|
+
throw new Error('Failed to fetch permissions');
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return await response.json();
|
|
616
|
+
} catch (error) {
|
|
617
|
+
console.error('Failed to fetch cognitive token:', error);
|
|
618
|
+
return { error: error instanceof Error ? error.message : 'Failed to fetch cognitive token' };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
function resolveApiDeps(deps: ApiClientDeps): ReturnType<typeof createApiHelpers> {
|
|
624
|
+
return createApiHelpers({
|
|
625
|
+
storage: deps.storage,
|
|
626
|
+
http: deps.http ?? createDefaultHttpPort(),
|
|
627
|
+
getTimezone: deps.getTimezone,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Platform-agnostic API client (no `voice` — attach via Web/RN layer). */
|
|
632
|
+
const createTtsApi = (apiKey: string, apiSecret: string, helpers: ReturnType<typeof createApiHelpers>) => ({
|
|
633
|
+
postTextTts: (text: string) =>
|
|
634
|
+
postTextTts(
|
|
635
|
+
{ apiKey, apiSecret, text },
|
|
636
|
+
helpers.fetchFn,
|
|
637
|
+
),
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
export function createNxtlinqApiWithDeps(
|
|
641
|
+
apiKey: string,
|
|
642
|
+
apiSecret: string,
|
|
643
|
+
deps: ApiClientDeps,
|
|
644
|
+
): CoreAITApi {
|
|
645
|
+
const helpers = resolveApiDeps(deps);
|
|
646
|
+
return {
|
|
647
|
+
ait: createAITApi(apiKey, apiSecret, helpers),
|
|
648
|
+
wallet: createWalletApi(apiKey, apiSecret, helpers),
|
|
649
|
+
metadata: createMetadataApi(apiKey, apiSecret, helpers),
|
|
650
|
+
auth: createAuthApi(apiKey, apiSecret, helpers),
|
|
651
|
+
agent: createAgentApi(helpers),
|
|
652
|
+
permissions: createPermissionsApi(apiKey, apiSecret, helpers),
|
|
653
|
+
cognitive: createCognitiveApi(apiKey, apiSecret, helpers),
|
|
654
|
+
tts: createTtsApi(apiKey, apiSecret, helpers),
|
|
655
|
+
};
|
|
656
|
+
}
|