@xagent-ai/cli 1.2.0 → 1.2.2
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 +1 -1
- package/README_CN.md +1 -1
- package/dist/agents.js +164 -164
- package/dist/agents.js.map +1 -1
- package/dist/ai-client.d.ts +4 -6
- package/dist/ai-client.d.ts.map +1 -1
- package/dist/ai-client.js +137 -115
- package/dist/ai-client.js.map +1 -1
- package/dist/auth.js +4 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +184 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +65 -81
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +5 -31
- package/dist/conversation.js.map +1 -1
- package/dist/memory.d.ts +5 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +77 -37
- package/dist/memory.js.map +1 -1
- package/dist/remote-ai-client.d.ts +1 -8
- package/dist/remote-ai-client.d.ts.map +1 -1
- package/dist/remote-ai-client.js +55 -65
- package/dist/remote-ai-client.js.map +1 -1
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +166 -0
- package/dist/retry.js.map +1 -0
- package/dist/session.d.ts +0 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +243 -312
- package/dist/session.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +91 -9
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +18 -17
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +149 -139
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts +48 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +254 -0
- package/dist/theme.js.map +1 -1
- package/dist/tools/edit-diff.d.ts +32 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +185 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +129 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools.d.ts +19 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +979 -631
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +6 -31
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agents.ts +504 -504
- package/src/ai-client.ts +1559 -1458
- package/src/auth.ts +4 -4
- package/src/cli.ts +195 -1
- package/src/config.ts +3 -3
- package/src/memory.ts +55 -14
- package/src/remote-ai-client.ts +663 -683
- package/src/retry.ts +217 -0
- package/src/session.ts +1736 -1840
- package/src/slash-commands.ts +98 -9
- package/src/smart-approval.ts +626 -625
- package/src/system-prompt-generator.ts +853 -843
- package/src/theme.ts +284 -0
- package/src/tools.ts +390 -70
package/src/remote-ai-client.ts
CHANGED
|
@@ -1,684 +1,664 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
import https from 'https';
|
|
3
|
-
import axios from 'axios';
|
|
4
|
-
import { ChatMessage, SessionOutput, ToolCall } from './types.js';
|
|
5
|
-
import { ChatCompletionResponse, ChatCompletionOptions, Message } from './ai-client.js';
|
|
6
|
-
import { getLogger } from './logger.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
private
|
|
67
|
-
private
|
|
68
|
-
private
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
logger.debug(
|
|
83
|
-
logger.debug(`[RemoteAIClient]
|
|
84
|
-
logger.debug(`[RemoteAIClient]
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
logger.debug(`[RemoteAIClient]
|
|
113
|
-
logger.debug(`[RemoteAIClient]
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
'
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.log('[RemoteAIClient]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
let
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
console.error(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (!
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
console.log('
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
console.log(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
console.log('
|
|
407
|
-
console.log('
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
*/
|
|
665
|
-
async deleteConversation(conversationId: string): Promise<void> {
|
|
666
|
-
const url = `${this.agentApi}/conversations/${conversationId}`;
|
|
667
|
-
if (this.showAIDebugInfo) {
|
|
668
|
-
console.log('[RemoteAIClient] Deleting conversation:', url);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
672
|
-
|
|
673
|
-
const response = await axios.delete(url, {
|
|
674
|
-
headers: {
|
|
675
|
-
'Authorization': `Bearer ${this.authToken}`
|
|
676
|
-
},
|
|
677
|
-
httpsAgent
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
if (!response.status.toString().startsWith('2')) {
|
|
681
|
-
throw new Error('Failed to delete conversation');
|
|
682
|
-
}
|
|
683
|
-
}
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import https from 'https';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { ChatMessage, SessionOutput, ToolCall } from './types.js';
|
|
5
|
+
import { ChatCompletionResponse, ChatCompletionOptions, Message, renderMarkdown, displayMessages } from './ai-client.js';
|
|
6
|
+
import { getLogger } from './logger.js';
|
|
7
|
+
import { withRetry, RetryConfig } from './retry.js';
|
|
8
|
+
|
|
9
|
+
const logger = getLogger();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Token invalid error - thrown when the authentication token is no longer valid
|
|
13
|
+
*/
|
|
14
|
+
export class TokenInvalidError extends Error {
|
|
15
|
+
constructor(message: string = 'Authentication token is invalid or expired') {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'TokenInvalidError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RemoteChatOptions {
|
|
22
|
+
model?: string;
|
|
23
|
+
taskId?: string;
|
|
24
|
+
status?: 'begin' | 'continue' | 'end' | 'cancel';
|
|
25
|
+
conversationId?: string;
|
|
26
|
+
context?: {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
workspace?: string;
|
|
29
|
+
recentFiles?: string[];
|
|
30
|
+
};
|
|
31
|
+
toolResults?: Array<{
|
|
32
|
+
toolCallId: string;
|
|
33
|
+
toolName: string;
|
|
34
|
+
result: any;
|
|
35
|
+
}>;
|
|
36
|
+
tools?: Array<{
|
|
37
|
+
type: 'function';
|
|
38
|
+
function: {
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
parameters: {
|
|
42
|
+
type: 'object';
|
|
43
|
+
properties: Record<string, any>;
|
|
44
|
+
required?: string[];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface RemoteChatResponse {
|
|
52
|
+
content: string;
|
|
53
|
+
reasoningContent?: string;
|
|
54
|
+
toolCalls?: ToolCall[];
|
|
55
|
+
conversationId: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface RemoteVLMResponse {
|
|
59
|
+
content: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Remote AI Client - communicates with xagent-web service
|
|
64
|
+
*/
|
|
65
|
+
export class RemoteAIClient extends EventEmitter {
|
|
66
|
+
private authToken: string;
|
|
67
|
+
private webBaseUrl: string;
|
|
68
|
+
private agentApi: string;
|
|
69
|
+
private vlmApi: string;
|
|
70
|
+
private showAIDebugInfo: boolean;
|
|
71
|
+
|
|
72
|
+
constructor(authToken: string, webBaseUrl: string, showAIDebugInfo: boolean = false) {
|
|
73
|
+
super();
|
|
74
|
+
logger.debug(`[RemoteAIClient] Constructor called, authToken: ${authToken ? authToken.substring(0, 30) + '...' : 'empty'}`);
|
|
75
|
+
this.authToken = authToken;
|
|
76
|
+
this.webBaseUrl = webBaseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
77
|
+
this.agentApi = `${this.webBaseUrl}/api/agent`;
|
|
78
|
+
this.vlmApi = `${this.webBaseUrl}/api/agent/vlm`;
|
|
79
|
+
this.showAIDebugInfo = showAIDebugInfo;
|
|
80
|
+
|
|
81
|
+
if (this.showAIDebugInfo) {
|
|
82
|
+
logger.debug('[RemoteAIClient] Initialization complete');
|
|
83
|
+
logger.debug(`[RemoteAIClient] Web Base URL: ${this.webBaseUrl}`);
|
|
84
|
+
logger.debug(`[RemoteAIClient] Agent API: ${this.agentApi}`);
|
|
85
|
+
logger.debug(`[RemoteAIClient] VLM API: ${this.vlmApi}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Non-streaming chat - send messages and receive full response
|
|
91
|
+
*/
|
|
92
|
+
async chat(
|
|
93
|
+
messages: ChatMessage[],
|
|
94
|
+
remoteChatOptions: RemoteChatOptions = {}
|
|
95
|
+
): Promise<SessionOutput> {
|
|
96
|
+
// Pass complete messages array to backend, backend forwards directly to LLM
|
|
97
|
+
const requestBody = {
|
|
98
|
+
messages: messages, // Pass complete message history
|
|
99
|
+
taskId: remoteChatOptions.taskId,
|
|
100
|
+
status: remoteChatOptions.status || 'begin',
|
|
101
|
+
conversationId: remoteChatOptions.conversationId,
|
|
102
|
+
context: remoteChatOptions.context,
|
|
103
|
+
options: {
|
|
104
|
+
model: remoteChatOptions.model
|
|
105
|
+
},
|
|
106
|
+
toolResults: remoteChatOptions.toolResults,
|
|
107
|
+
tools: remoteChatOptions.tools
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const url = `${this.agentApi}/chat`;
|
|
111
|
+
if (this.showAIDebugInfo) {
|
|
112
|
+
logger.debug(`[RemoteAIClient] Sending request to: ${url}`);
|
|
113
|
+
logger.debug(`[RemoteAIClient] Token prefix: ${this.authToken.substring(0, 20)}...`);
|
|
114
|
+
logger.debug(`[RemoteAIClient] Message count: ${messages.length}`);
|
|
115
|
+
if (remoteChatOptions.tools) {
|
|
116
|
+
logger.debug(`[RemoteAIClient] Tool count: ${remoteChatOptions.tools.length}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await axios.post(url, requestBody, {
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
127
|
+
},
|
|
128
|
+
httpsAgent,
|
|
129
|
+
timeout: 300000
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Check for 401 and throw TokenInvalidError
|
|
133
|
+
if (response.status === 401) {
|
|
134
|
+
throw new TokenInvalidError('Authentication token is invalid or expired. Please log in again.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const data = response.data;
|
|
138
|
+
logger.debug('[RemoteAIClient] response received, status:', String(response.status));
|
|
139
|
+
if (this.showAIDebugInfo) {
|
|
140
|
+
console.log('[RemoteAIClient] Received response, content length:', data.content?.length || 0);
|
|
141
|
+
console.log('[RemoteAIClient] toolCalls count:', data.toolCalls?.length || 0);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
role: 'assistant',
|
|
146
|
+
content: data.content || '',
|
|
147
|
+
reasoningContent: data.reasoningContent || '',
|
|
148
|
+
toolCalls: data.toolCalls,
|
|
149
|
+
timestamp: Date.now()
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
} catch (error: any) {
|
|
153
|
+
if (this.showAIDebugInfo) {
|
|
154
|
+
console.log('[RemoteAIClient] Request exception:', error.message);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Provide user-friendly error messages based on status code
|
|
158
|
+
if (error.response) {
|
|
159
|
+
const status = error.response.status;
|
|
160
|
+
let errorMessage: string;
|
|
161
|
+
let userFriendlyMessage: string;
|
|
162
|
+
|
|
163
|
+
switch (status) {
|
|
164
|
+
case 400:
|
|
165
|
+
errorMessage = 'Bad Request';
|
|
166
|
+
userFriendlyMessage = 'Invalid request parameters. Please check your input and try again.';
|
|
167
|
+
break;
|
|
168
|
+
case 401:
|
|
169
|
+
throw new TokenInvalidError('Authentication token is invalid or expired. Please log in again.');
|
|
170
|
+
case 413:
|
|
171
|
+
errorMessage = 'Payload Too Large';
|
|
172
|
+
userFriendlyMessage = 'Request data is too large. Please reduce input content or screenshot size and try again.';
|
|
173
|
+
break;
|
|
174
|
+
case 429:
|
|
175
|
+
errorMessage = 'Too Many Requests';
|
|
176
|
+
userFriendlyMessage = 'XAgent service rate limit exceeded. Please wait a moment and try again.';
|
|
177
|
+
break;
|
|
178
|
+
case 500:
|
|
179
|
+
// Try to parse server's detailed error message
|
|
180
|
+
try {
|
|
181
|
+
const errorData = error.response.data || null;
|
|
182
|
+
errorMessage = errorData?.error || 'Internal Server Error';
|
|
183
|
+
if (errorData?.error && errorData?.errorType === 'AI_SERVICE_ERROR') {
|
|
184
|
+
userFriendlyMessage = `${errorData.error}\n\nSuggestion: ${errorData.suggestion}`;
|
|
185
|
+
} else {
|
|
186
|
+
userFriendlyMessage = errorData?.error || 'Server error. Please try again later. If the problem persists, contact the administrator.';
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
errorMessage = 'Internal Server Error';
|
|
190
|
+
userFriendlyMessage = 'Server error. Please try again later. If the problem persists, contact the administrator.';
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case 502:
|
|
194
|
+
errorMessage = 'Bad Gateway';
|
|
195
|
+
userFriendlyMessage = 'Gateway error. Service temporarily unavailable. Please try again later.';
|
|
196
|
+
break;
|
|
197
|
+
case 503:
|
|
198
|
+
errorMessage = 'Service Unavailable';
|
|
199
|
+
userFriendlyMessage = 'AI service request timed out. Please try again.';
|
|
200
|
+
break;
|
|
201
|
+
case 504:
|
|
202
|
+
errorMessage = 'Gateway Timeout';
|
|
203
|
+
userFriendlyMessage = 'Gateway timeout. Please try again later.';
|
|
204
|
+
break;
|
|
205
|
+
default:
|
|
206
|
+
try {
|
|
207
|
+
errorMessage = error.response.data?.error || `HTTP ${status}`;
|
|
208
|
+
} catch {
|
|
209
|
+
errorMessage = `HTTP ${status}`;
|
|
210
|
+
}
|
|
211
|
+
userFriendlyMessage = `Request failed with status code: ${status}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Print user-friendly error message
|
|
215
|
+
console.error(`\n❌ Request failed (${status})`);
|
|
216
|
+
console.error(` ${userFriendlyMessage}`);
|
|
217
|
+
if (this.showAIDebugInfo) {
|
|
218
|
+
console.error(` Original error: ${errorMessage}`);
|
|
219
|
+
}
|
|
220
|
+
throw new Error(userFriendlyMessage);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Network error or other error
|
|
224
|
+
// Check if error is retryable
|
|
225
|
+
const isRetryable = this.isRetryableError(error);
|
|
226
|
+
if (!isRetryable) {
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Retry with exponential backoff
|
|
231
|
+
const retryResult = await withRetry(async () => {
|
|
232
|
+
const response = await axios.post(url, requestBody, {
|
|
233
|
+
headers: {
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
236
|
+
},
|
|
237
|
+
httpsAgent,
|
|
238
|
+
timeout: 300000
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (response.status === 401) {
|
|
242
|
+
throw new TokenInvalidError('Authentication token is invalid or expired. Please log in again.');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
role: 'assistant' as const,
|
|
247
|
+
content: response.data.content || '',
|
|
248
|
+
reasoningContent: response.data.reasoningContent || '',
|
|
249
|
+
toolCalls: response.data.toolCalls,
|
|
250
|
+
timestamp: Date.now()
|
|
251
|
+
};
|
|
252
|
+
}, { maxRetries: 3, baseDelay: 1000, maxDelay: 10000, jitter: true });
|
|
253
|
+
|
|
254
|
+
if (!retryResult.success) {
|
|
255
|
+
throw retryResult.error || new Error('Retry failed');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!retryResult.data) {
|
|
259
|
+
throw new Error('Retry returned empty response');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return retryResult.data;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private isRetryableError(error: any): boolean {
|
|
267
|
+
// Timeout or network error (no response received)
|
|
268
|
+
if (error.code === 'ECONNABORTED' || !error.response) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
// 5xx server errors
|
|
272
|
+
if (error.response?.status && error.response.status >= 500) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
// 429 rate limit
|
|
276
|
+
if (error.response?.status === 429) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Mark task as completed
|
|
284
|
+
* Call backend to update task status to 'end'
|
|
285
|
+
*/
|
|
286
|
+
async completeTask(taskId: string): Promise<void> {
|
|
287
|
+
if (!taskId) {
|
|
288
|
+
logger.debug('[RemoteAIClient] completeTask called with empty taskId, skipping');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
logger.debug(`[RemoteAIClient] completeTask called: taskId=${taskId}`);
|
|
293
|
+
|
|
294
|
+
const url = `${this.agentApi}/chat`;
|
|
295
|
+
const requestBody = {
|
|
296
|
+
taskId,
|
|
297
|
+
status: 'end',
|
|
298
|
+
messages: [],
|
|
299
|
+
options: {}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const response = await axios.post(url, requestBody, {
|
|
306
|
+
headers: {
|
|
307
|
+
'Content-Type': 'application/json',
|
|
308
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
309
|
+
},
|
|
310
|
+
httpsAgent
|
|
311
|
+
});
|
|
312
|
+
logger.debug(`[RemoteAIClient] completeTask response status: ${response.status}`);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('[RemoteAIClient] Failed to mark task as completed:', error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Mark task as cancelled
|
|
320
|
+
* Call backend to update task status to 'cancel'
|
|
321
|
+
*/
|
|
322
|
+
async cancelTask(taskId: string): Promise<void> {
|
|
323
|
+
if (!taskId) return;
|
|
324
|
+
|
|
325
|
+
const url = `${this.agentApi}/chat`;
|
|
326
|
+
const requestBody = {
|
|
327
|
+
taskId,
|
|
328
|
+
status: 'cancel',
|
|
329
|
+
messages: [],
|
|
330
|
+
options: {}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await axios.post(url, requestBody, {
|
|
337
|
+
headers: {
|
|
338
|
+
'Content-Type': 'application/json',
|
|
339
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
340
|
+
},
|
|
341
|
+
httpsAgent
|
|
342
|
+
});
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error('[RemoteAIClient] Failed to mark task as cancelled:', error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Unified LLM call interface - same return type as aiClient.chatCompletion
|
|
350
|
+
* Implements transparency: caller doesn't need to know remote vs local mode
|
|
351
|
+
*/
|
|
352
|
+
async chatCompletion(
|
|
353
|
+
messages: ChatMessage[],
|
|
354
|
+
options: ChatCompletionOptions = {}
|
|
355
|
+
): Promise<ChatCompletionResponse> {
|
|
356
|
+
const model = options.model || 'remote-llm';
|
|
357
|
+
|
|
358
|
+
// Debug output for request
|
|
359
|
+
if (this.showAIDebugInfo) {
|
|
360
|
+
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
361
|
+
console.log('║ AI REQUEST DEBUG (REMOTE) ║');
|
|
362
|
+
console.log('╚══════════════════════════════════════════════════════════╝');
|
|
363
|
+
console.log(`📦 Model: ${model}`);
|
|
364
|
+
console.log(`🌐 Base URL: ${this.webBaseUrl}`);
|
|
365
|
+
console.log(`💬 Total Messages: ${messages.length} items`);
|
|
366
|
+
if (options.temperature !== undefined) console.log(`🌡️ Temperature: ${options.temperature}`);
|
|
367
|
+
if (options.maxTokens) console.log(`📏 Max Tokens: ${options.maxTokens}`);
|
|
368
|
+
if (options.tools?.length) console.log(`🔧 Tools: ${options.tools.length} items`);
|
|
369
|
+
if (options.thinkingTokens) console.log(`🧠 Thinking Tokens: ${options.thinkingTokens}`);
|
|
370
|
+
console.log('─'.repeat(60));
|
|
371
|
+
|
|
372
|
+
// Display system messages separately
|
|
373
|
+
const systemMsgs = messages.filter(m => m.role === 'system');
|
|
374
|
+
const otherMsgs = messages.filter(m => m.role !== 'system');
|
|
375
|
+
|
|
376
|
+
if (systemMsgs.length > 0) {
|
|
377
|
+
const systemContent = typeof systemMsgs[0].content === 'string'
|
|
378
|
+
? systemMsgs[0].content
|
|
379
|
+
: JSON.stringify(systemMsgs[0].content);
|
|
380
|
+
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
381
|
+
console.log('│ 🟫 SYSTEM │');
|
|
382
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
383
|
+
console.log(renderMarkdown(systemContent).split('\n').map(l => '│ ' + l).join('\n'));
|
|
384
|
+
console.log('└─────────────────────────────────────────────────────────────┘');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Display other messages
|
|
388
|
+
displayMessages(otherMsgs);
|
|
389
|
+
|
|
390
|
+
console.log('\n📤 Sending request to Remote API...\n');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Call existing chat method
|
|
394
|
+
const response = await this.chat(messages, {
|
|
395
|
+
conversationId: undefined,
|
|
396
|
+
tools: options.tools as any,
|
|
397
|
+
toolResults: undefined,
|
|
398
|
+
context: undefined,
|
|
399
|
+
model: options.model,
|
|
400
|
+
taskId: (options as any).taskId,
|
|
401
|
+
status: 'begin' // Mark as beginning of task
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Debug output for response
|
|
405
|
+
if (this.showAIDebugInfo) {
|
|
406
|
+
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
407
|
+
console.log('║ AI RESPONSE DEBUG (REMOTE) ║');
|
|
408
|
+
console.log('╚══════════════════════════════════════════════════════════╝');
|
|
409
|
+
console.log(`🆔 ID: remote-${Date.now()}`);
|
|
410
|
+
console.log(`🤖 Model: ${model}`);
|
|
411
|
+
console.log(`🏁 Finish Reason: stop`);
|
|
412
|
+
|
|
413
|
+
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
414
|
+
console.log('│ 🤖 ASSISTANT │');
|
|
415
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
416
|
+
|
|
417
|
+
// Display reasoning_content (if present)
|
|
418
|
+
if (response.reasoningContent) {
|
|
419
|
+
console.log('│ 🧠 REASONING:');
|
|
420
|
+
console.log('│ ───────────────────────────────────────────────────────────');
|
|
421
|
+
const reasoningLines = renderMarkdown(response.reasoningContent).split('\n');
|
|
422
|
+
for (const line of reasoningLines.slice(0, 15)) {
|
|
423
|
+
console.log('│ ' + line.slice(0, 62));
|
|
424
|
+
}
|
|
425
|
+
if (response.reasoningContent.length > 800) console.log('│ ... (truncated)');
|
|
426
|
+
console.log('│ ───────────────────────────────────────────────────────────');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Display content
|
|
430
|
+
console.log('│ 💬 CONTENT:');
|
|
431
|
+
console.log('│ ───────────────────────────────────────────────────────────');
|
|
432
|
+
const lines = renderMarkdown(response.content).split('\n');
|
|
433
|
+
for (const line of lines.slice(0, 40)) {
|
|
434
|
+
console.log('│ ' + line.slice(0, 62));
|
|
435
|
+
}
|
|
436
|
+
if (lines.length > 40) {
|
|
437
|
+
console.log(`│ ... (${lines.length - 40} more lines)`);
|
|
438
|
+
}
|
|
439
|
+
console.log('└─────────────────────────────────────────────────────────────┘');
|
|
440
|
+
|
|
441
|
+
// Display tool calls if present
|
|
442
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
443
|
+
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
444
|
+
console.log('│ 🔧 TOOL CALLS │');
|
|
445
|
+
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
446
|
+
for (let i = 0; i < response.toolCalls.length; i++) {
|
|
447
|
+
const tc = response.toolCalls[i];
|
|
448
|
+
console.log(`│ ${i + 1}. ${tc.function?.name || 'unknown'}`);
|
|
449
|
+
if (tc.function?.arguments) {
|
|
450
|
+
const args = typeof tc.function.arguments === 'string'
|
|
451
|
+
? JSON.parse(tc.function.arguments)
|
|
452
|
+
: tc.function.arguments;
|
|
453
|
+
const argsStr = JSON.stringify(args, null, 2).split('\n').slice(0, 5).join('\n');
|
|
454
|
+
console.log('│ Args:', argsStr.slice(0, 50) + (argsStr.length > 50 ? '...' : ''));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
console.log('└─────────────────────────────────────────────────────────────┘');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
console.log('\n╔══════════════════════════════════════════════════════════╗');
|
|
461
|
+
console.log('║ RESPONSE ENDED ║');
|
|
462
|
+
console.log('╚══════════════════════════════════════════════════════════╝\n');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Convert to ChatCompletionResponse format (consistent with local mode)
|
|
466
|
+
return {
|
|
467
|
+
id: `remote-${Date.now()}`,
|
|
468
|
+
object: 'chat.completion',
|
|
469
|
+
created: Date.now(),
|
|
470
|
+
model: options.model || 'remote-llm',
|
|
471
|
+
choices: [
|
|
472
|
+
{
|
|
473
|
+
index: 0,
|
|
474
|
+
message: {
|
|
475
|
+
role: 'assistant',
|
|
476
|
+
content: response.content,
|
|
477
|
+
reasoning_content: response.reasoningContent || '',
|
|
478
|
+
tool_calls: response.toolCalls
|
|
479
|
+
},
|
|
480
|
+
finish_reason: 'stop'
|
|
481
|
+
}
|
|
482
|
+
],
|
|
483
|
+
usage: undefined
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Invoke VLM for image understanding
|
|
489
|
+
* @param messages - full messages array (consistent with local mode)
|
|
490
|
+
* @param systemPrompt - system prompt (optional, for reference)
|
|
491
|
+
* @param remoteChatOptions - other options including AbortSignal, taskId
|
|
492
|
+
*/
|
|
493
|
+
async invokeVLM(
|
|
494
|
+
messages: any[],
|
|
495
|
+
_systemPrompt?: string,
|
|
496
|
+
remoteChatOptions: RemoteChatOptions = {}
|
|
497
|
+
): Promise<string> {
|
|
498
|
+
// Forward complete messages to backend (same format as local mode)
|
|
499
|
+
const requestBody = {
|
|
500
|
+
messages, // Pass complete messages array
|
|
501
|
+
taskId: remoteChatOptions.taskId,
|
|
502
|
+
status: remoteChatOptions.status || 'begin',
|
|
503
|
+
context: remoteChatOptions.context,
|
|
504
|
+
options: {
|
|
505
|
+
model: remoteChatOptions.model
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
if (this.showAIDebugInfo) {
|
|
510
|
+
console.log('[RemoteAIClient] VLM sending request to:', this.vlmApi);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Handle abort signal
|
|
514
|
+
const controller = remoteChatOptions.signal ? new AbortController() : undefined;
|
|
515
|
+
const abortSignal = remoteChatOptions.signal || controller?.signal;
|
|
516
|
+
|
|
517
|
+
// If external signal is provided, listen to it
|
|
518
|
+
if (remoteChatOptions.signal) {
|
|
519
|
+
remoteChatOptions.signal.addEventListener?.('abort', () => controller?.abort());
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
const response = await axios.post(this.vlmApi, requestBody, {
|
|
524
|
+
headers: {
|
|
525
|
+
'Content-Type': 'application/json',
|
|
526
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
527
|
+
},
|
|
528
|
+
signal: abortSignal,
|
|
529
|
+
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
|
|
530
|
+
timeout: 120000
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
if (this.showAIDebugInfo) {
|
|
534
|
+
console.log('[RemoteAIClient] VLM response status:', response.status);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const data = response.data as RemoteVLMResponse;
|
|
538
|
+
return data.content || '';
|
|
539
|
+
|
|
540
|
+
} catch (error: any) {
|
|
541
|
+
if (this.showAIDebugInfo) {
|
|
542
|
+
console.log('[RemoteAIClient] VLM request exception:', error.message);
|
|
543
|
+
}
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Validate if the current token is still valid
|
|
550
|
+
* Returns true if valid, false otherwise
|
|
551
|
+
*/
|
|
552
|
+
async validateToken(): Promise<boolean> {
|
|
553
|
+
try {
|
|
554
|
+
const url = `${this.webBaseUrl}/api/auth/me`;
|
|
555
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
556
|
+
const response = await axios.get(url, {
|
|
557
|
+
httpsAgent,
|
|
558
|
+
timeout: 10000
|
|
559
|
+
});
|
|
560
|
+
return response.status === 200;
|
|
561
|
+
} catch {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async getConversations(): Promise<any[]> {
|
|
567
|
+
const url = `${this.agentApi}/conversations`;
|
|
568
|
+
if (this.showAIDebugInfo) {
|
|
569
|
+
console.log('[RemoteAIClient] Getting conversation list:', url);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
573
|
+
|
|
574
|
+
const response = await axios.get(url, {
|
|
575
|
+
headers: {
|
|
576
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
577
|
+
},
|
|
578
|
+
httpsAgent
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
if (response.status !== 200) {
|
|
582
|
+
throw new Error('Failed to get conversation list');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const data = response.data as { conversations?: any[] };
|
|
586
|
+
return data.conversations || [];
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Get conversation details
|
|
591
|
+
*/
|
|
592
|
+
async getConversation(conversationId: string): Promise<any> {
|
|
593
|
+
const url = `${this.agentApi}/conversations/${conversationId}`;
|
|
594
|
+
if (this.showAIDebugInfo) {
|
|
595
|
+
console.log('[RemoteAIClient] Getting conversation details:', url);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
599
|
+
|
|
600
|
+
const response = await axios.get(url, {
|
|
601
|
+
headers: {
|
|
602
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
603
|
+
},
|
|
604
|
+
httpsAgent
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
if (response.status !== 200) {
|
|
608
|
+
throw new Error('Failed to get conversation details');
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const data = response.data as { conversation?: any };
|
|
612
|
+
return data.conversation;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Create new conversation
|
|
617
|
+
*/
|
|
618
|
+
async createConversation(title?: string): Promise<any> {
|
|
619
|
+
const url = `${this.agentApi}/conversations`;
|
|
620
|
+
if (this.showAIDebugInfo) {
|
|
621
|
+
console.log('[RemoteAIClient] Creating conversation:', url);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
625
|
+
|
|
626
|
+
const response = await axios.post(url, { title }, {
|
|
627
|
+
headers: {
|
|
628
|
+
'Content-Type': 'application/json',
|
|
629
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
630
|
+
},
|
|
631
|
+
httpsAgent
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
if (response.status !== 200) {
|
|
635
|
+
throw new Error('Failed to create conversation');
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const data = response.data as { conversation?: any };
|
|
639
|
+
return data.conversation;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Delete conversation
|
|
644
|
+
*/
|
|
645
|
+
async deleteConversation(conversationId: string): Promise<void> {
|
|
646
|
+
const url = `${this.agentApi}/conversations/${conversationId}`;
|
|
647
|
+
if (this.showAIDebugInfo) {
|
|
648
|
+
console.log('[RemoteAIClient] Deleting conversation:', url);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
652
|
+
|
|
653
|
+
const response = await axios.delete(url, {
|
|
654
|
+
headers: {
|
|
655
|
+
'Authorization': `Bearer ${this.authToken}`
|
|
656
|
+
},
|
|
657
|
+
httpsAgent
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
if (!response.status.toString().startsWith('2')) {
|
|
661
|
+
throw new Error('Failed to delete conversation');
|
|
662
|
+
}
|
|
663
|
+
}
|
|
684
664
|
}
|