tabby-ai-assistant 1.0.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.
Files changed (134) hide show
  1. package/README.md +232 -0
  2. package/dist/components/chat/chat-input.component.d.ts +65 -0
  3. package/dist/components/chat/chat-interface.component.d.ts +71 -0
  4. package/dist/components/chat/chat-message.component.d.ts +53 -0
  5. package/dist/components/chat/chat-settings.component.d.ts +62 -0
  6. package/dist/components/common/error-message.component.d.ts +11 -0
  7. package/dist/components/common/loading-spinner.component.d.ts +4 -0
  8. package/dist/components/security/consent-dialog.component.d.ts +11 -0
  9. package/dist/components/security/password-prompt.component.d.ts +10 -0
  10. package/dist/components/security/risk-confirm-dialog.component.d.ts +36 -0
  11. package/dist/components/settings/ai-settings-tab.component.d.ts +72 -0
  12. package/dist/components/settings/general-settings.component.d.ts +60 -0
  13. package/dist/components/settings/provider-config.component.d.ts +182 -0
  14. package/dist/components/settings/security-settings.component.d.ts +23 -0
  15. package/dist/components/terminal/ai-toolbar-button.component.d.ts +10 -0
  16. package/dist/components/terminal/command-preview.component.d.ts +15 -0
  17. package/dist/components/terminal/command-suggestion.component.d.ts +16 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.LICENSE.txt +18 -0
  21. package/dist/main.d.ts +8 -0
  22. package/dist/providers/tabby/ai-config.provider.d.ts +18 -0
  23. package/dist/providers/tabby/ai-hotkey.provider.d.ts +21 -0
  24. package/dist/providers/tabby/ai-settings-tab.provider.d.ts +11 -0
  25. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +17 -0
  26. package/dist/services/chat/chat-history.service.d.ts +67 -0
  27. package/dist/services/chat/chat-session.service.d.ts +58 -0
  28. package/dist/services/chat/command-generator.service.d.ts +49 -0
  29. package/dist/services/core/ai-assistant.service.d.ts +88 -0
  30. package/dist/services/core/ai-provider-manager.service.d.ts +119 -0
  31. package/dist/services/core/config-provider.service.d.ts +137 -0
  32. package/dist/services/core/logger.service.d.ts +21 -0
  33. package/dist/services/providers/anthropic-provider.service.d.ts +39 -0
  34. package/dist/services/providers/base-provider.service.d.ts +137 -0
  35. package/dist/services/providers/glm-provider.service.d.ts +91 -0
  36. package/dist/services/providers/minimax-provider.service.d.ts +93 -0
  37. package/dist/services/providers/openai-compatible.service.d.ts +39 -0
  38. package/dist/services/providers/openai-provider.service.d.ts +38 -0
  39. package/dist/services/security/consent-manager.service.d.ts +65 -0
  40. package/dist/services/security/password-manager.service.d.ts +67 -0
  41. package/dist/services/security/risk-assessment.service.d.ts +65 -0
  42. package/dist/services/security/security-validator.service.d.ts +36 -0
  43. package/dist/services/terminal/command-analyzer.service.d.ts +20 -0
  44. package/dist/services/terminal/context-menu.service.d.ts +24 -0
  45. package/dist/services/terminal/hotkey.service.d.ts +28 -0
  46. package/dist/services/terminal/terminal-context.service.d.ts +100 -0
  47. package/dist/types/ai.types.d.ts +107 -0
  48. package/dist/types/provider.types.d.ts +105 -0
  49. package/dist/types/security.types.d.ts +85 -0
  50. package/dist/types/terminal.types.d.ts +150 -0
  51. package/dist/utils/encryption.utils.d.ts +83 -0
  52. package/dist/utils/formatting.utils.d.ts +106 -0
  53. package/dist/utils/validation.utils.d.ts +83 -0
  54. package/integration-test-output.txt +50 -0
  55. package/integration-tests/api-integration.test.ts +183 -0
  56. package/jest.config.js +47 -0
  57. package/package.json +73 -0
  58. package/setup-jest.ts +37 -0
  59. package/src/components/chat/chat-input.component.html +61 -0
  60. package/src/components/chat/chat-input.component.scss +183 -0
  61. package/src/components/chat/chat-input.component.ts +149 -0
  62. package/src/components/chat/chat-interface.component.html +119 -0
  63. package/src/components/chat/chat-interface.component.scss +354 -0
  64. package/src/components/chat/chat-interface.component.ts +224 -0
  65. package/src/components/chat/chat-message.component.html +65 -0
  66. package/src/components/chat/chat-message.component.scss +178 -0
  67. package/src/components/chat/chat-message.component.ts +93 -0
  68. package/src/components/chat/chat-settings.component.html +132 -0
  69. package/src/components/chat/chat-settings.component.scss +172 -0
  70. package/src/components/chat/chat-settings.component.ts +168 -0
  71. package/src/components/common/error-message.component.ts +124 -0
  72. package/src/components/common/loading-spinner.component.ts +72 -0
  73. package/src/components/security/consent-dialog.component.ts +77 -0
  74. package/src/components/security/password-prompt.component.ts +79 -0
  75. package/src/components/security/risk-confirm-dialog.component.html +87 -0
  76. package/src/components/security/risk-confirm-dialog.component.scss +360 -0
  77. package/src/components/security/risk-confirm-dialog.component.ts +96 -0
  78. package/src/components/settings/ai-settings-tab.component.html +140 -0
  79. package/src/components/settings/ai-settings-tab.component.scss +371 -0
  80. package/src/components/settings/ai-settings-tab.component.ts +193 -0
  81. package/src/components/settings/general-settings.component.html +103 -0
  82. package/src/components/settings/general-settings.component.scss +285 -0
  83. package/src/components/settings/general-settings.component.ts +123 -0
  84. package/src/components/settings/provider-config.component.html +95 -0
  85. package/src/components/settings/provider-config.component.scss +60 -0
  86. package/src/components/settings/provider-config.component.ts +206 -0
  87. package/src/components/settings/security-settings.component.html +51 -0
  88. package/src/components/settings/security-settings.component.scss +66 -0
  89. package/src/components/settings/security-settings.component.ts +71 -0
  90. package/src/components/terminal/ai-toolbar-button.component.ts +49 -0
  91. package/src/components/terminal/command-preview.component.ts +185 -0
  92. package/src/components/terminal/command-suggestion.component.ts +128 -0
  93. package/src/index.ts +163 -0
  94. package/src/main.ts +16 -0
  95. package/src/providers/tabby/ai-config.provider.ts +70 -0
  96. package/src/providers/tabby/ai-hotkey.provider.ts +55 -0
  97. package/src/providers/tabby/ai-settings-tab.provider.ts +18 -0
  98. package/src/providers/tabby/ai-toolbar-button.provider.ts +49 -0
  99. package/src/services/chat/chat-history.service.ts +239 -0
  100. package/src/services/chat/chat-session.service.spec.ts +249 -0
  101. package/src/services/chat/chat-session.service.ts +180 -0
  102. package/src/services/chat/command-generator.service.ts +301 -0
  103. package/src/services/core/ai-assistant.service.ts +334 -0
  104. package/src/services/core/ai-provider-manager.service.ts +314 -0
  105. package/src/services/core/config-provider.service.ts +347 -0
  106. package/src/services/core/logger.service.ts +104 -0
  107. package/src/services/providers/anthropic-provider.service.ts +373 -0
  108. package/src/services/providers/base-provider.service.ts +369 -0
  109. package/src/services/providers/glm-provider.service.ts +467 -0
  110. package/src/services/providers/minimax-provider.service.ts +427 -0
  111. package/src/services/providers/openai-compatible.service.ts +394 -0
  112. package/src/services/providers/openai-provider.service.ts +376 -0
  113. package/src/services/security/consent-manager.service.ts +332 -0
  114. package/src/services/security/password-manager.service.ts +188 -0
  115. package/src/services/security/risk-assessment.service.ts +340 -0
  116. package/src/services/security/security-validator.service.ts +143 -0
  117. package/src/services/terminal/command-analyzer.service.ts +43 -0
  118. package/src/services/terminal/context-menu.service.ts +45 -0
  119. package/src/services/terminal/hotkey.service.ts +53 -0
  120. package/src/services/terminal/terminal-context.service.ts +317 -0
  121. package/src/styles/ai-assistant.scss +449 -0
  122. package/src/types/ai.types.ts +133 -0
  123. package/src/types/provider.types.ts +147 -0
  124. package/src/types/security.types.ts +103 -0
  125. package/src/types/terminal.types.ts +186 -0
  126. package/src/utils/encryption.utils.spec.ts +250 -0
  127. package/src/utils/encryption.utils.ts +271 -0
  128. package/src/utils/formatting.utils.ts +359 -0
  129. package/src/utils/validation.utils.spec.ts +225 -0
  130. package/src/utils/validation.utils.ts +314 -0
  131. package/tsconfig.json +45 -0
  132. package/webpack-docker.config.js +42 -0
  133. package/webpack.config.js +59 -0
  134. package/webpack.config.js.backup +57 -0
@@ -0,0 +1,427 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Anthropic } from '@anthropic-ai/sdk';
3
+ import { BaseAiProvider } from './base-provider.service';
4
+ import { ProviderCapability, HealthStatus, ValidationResult } from '../../types/provider.types';
5
+ import { ChatRequest, ChatResponse, CommandRequest, CommandResponse, ExplainRequest, ExplainResponse, AnalysisRequest, AnalysisResponse, MessageRole } from '../../types/ai.types';
6
+ import { LoggerService } from '../core/logger.service';
7
+
8
+ /**
9
+ * Minimax AI提供商
10
+ * 基于Anthropic Claude API,完全兼容Anthropic格式
11
+ */
12
+ @Injectable()
13
+ export class MinimaxProviderService extends BaseAiProvider {
14
+ readonly name = 'minimax';
15
+ readonly displayName = 'Minimax (MiniMax-M2)';
16
+ readonly capabilities = [
17
+ ProviderCapability.CHAT,
18
+ ProviderCapability.COMMAND_GENERATION,
19
+ ProviderCapability.COMMAND_EXPLANATION,
20
+ ProviderCapability.REASONING,
21
+ ProviderCapability.STREAMING
22
+ ];
23
+ readonly authConfig = {
24
+ type: 'bearer' as const,
25
+ credentials: {
26
+ apiKey: ''
27
+ }
28
+ };
29
+
30
+ private client: Anthropic | null = null;
31
+
32
+ constructor(logger: LoggerService) {
33
+ super(logger);
34
+ }
35
+
36
+ /**
37
+ * 配置提供商
38
+ */
39
+ configure(config: any): void {
40
+ super.configure(config);
41
+ this.authConfig.credentials.apiKey = config.apiKey || '';
42
+ this.initializeClient();
43
+ }
44
+
45
+ /**
46
+ * 初始化Anthropic客户端
47
+ */
48
+ private initializeClient(): void {
49
+ if (!this.config?.apiKey) {
50
+ this.logger.warn('Minimax API key not provided');
51
+ return;
52
+ }
53
+
54
+ try {
55
+ this.client = new Anthropic({
56
+ apiKey: this.config.apiKey,
57
+ baseURL: this.getBaseURL()
58
+ });
59
+
60
+ this.logger.info('Minimax client initialized', {
61
+ baseURL: this.getBaseURL(),
62
+ model: this.config.model || 'MiniMax-M2'
63
+ });
64
+ } catch (error) {
65
+ this.logger.error('Failed to initialize Minimax client', error);
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 聊天功能
72
+ */
73
+ async chat(request: ChatRequest): Promise<ChatResponse> {
74
+ if (!this.client) {
75
+ throw new Error('Minimax client not initialized');
76
+ }
77
+
78
+ this.logRequest(request);
79
+
80
+ try {
81
+ const response = await this.withRetry(async () => {
82
+ const result = await this.client!.messages.create({
83
+ model: this.config?.model || 'MiniMax-M2',
84
+ max_tokens: request.maxTokens || 1000,
85
+ system: request.systemPrompt || this.getDefaultSystemPrompt(),
86
+ messages: this.transformMessages(request.messages),
87
+ temperature: request.temperature || 1.0,
88
+ stream: request.stream || false
89
+ });
90
+
91
+ this.logResponse(result);
92
+ return result;
93
+ });
94
+
95
+ return this.transformChatResponse(response);
96
+
97
+ } catch (error) {
98
+ this.logError(error, { request });
99
+ throw new Error(`Minimax chat failed: ${error instanceof Error ? error.message : String(error)}`);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * 生成命令
105
+ */
106
+ async generateCommand(request: CommandRequest): Promise<CommandResponse> {
107
+ const prompt = this.buildCommandPrompt(request);
108
+
109
+ const chatRequest: ChatRequest = {
110
+ messages: [
111
+ {
112
+ id: this.generateId(),
113
+ role: MessageRole.USER,
114
+ content: prompt,
115
+ timestamp: new Date()
116
+ }
117
+ ],
118
+ maxTokens: 500,
119
+ temperature: 0.3
120
+ };
121
+
122
+ const response = await this.chat(chatRequest);
123
+ return this.parseCommandResponse(response.message.content);
124
+ }
125
+
126
+ /**
127
+ * 解释命令
128
+ */
129
+ async explainCommand(request: ExplainRequest): Promise<ExplainResponse> {
130
+ const prompt = this.buildExplainPrompt(request);
131
+
132
+ const chatRequest: ChatRequest = {
133
+ messages: [
134
+ {
135
+ id: this.generateId(),
136
+ role: MessageRole.USER,
137
+ content: prompt,
138
+ timestamp: new Date()
139
+ }
140
+ ],
141
+ maxTokens: 1000,
142
+ temperature: 0.5
143
+ };
144
+
145
+ const response = await this.chat(chatRequest);
146
+ return this.parseExplainResponse(response.message.content);
147
+ }
148
+
149
+ /**
150
+ * 分析结果
151
+ */
152
+ async analyzeResult(request: AnalysisRequest): Promise<AnalysisResponse> {
153
+ const prompt = this.buildAnalysisPrompt(request);
154
+
155
+ const chatRequest: ChatRequest = {
156
+ messages: [
157
+ {
158
+ id: this.generateId(),
159
+ role: MessageRole.USER,
160
+ content: prompt,
161
+ timestamp: new Date()
162
+ }
163
+ ],
164
+ maxTokens: 1000,
165
+ temperature: 0.7
166
+ };
167
+
168
+ const response = await this.chat(chatRequest);
169
+ return this.parseAnalysisResponse(response.message.content);
170
+ }
171
+
172
+ /**
173
+ * 健康检查
174
+ */
175
+ async healthCheck(): Promise<HealthStatus> {
176
+ try {
177
+ if (!this.client) {
178
+ return HealthStatus.UNHEALTHY;
179
+ }
180
+
181
+ // 简单的测试请求
182
+ const _response = await this.client.messages.create({
183
+ model: this.config?.model || 'MiniMax-M2',
184
+ max_tokens: 1,
185
+ messages: [
186
+ {
187
+ role: 'user',
188
+ content: 'Hi'
189
+ }
190
+ ]
191
+ });
192
+
193
+ this.lastHealthCheck = { status: HealthStatus.HEALTHY, timestamp: new Date() };
194
+ return HealthStatus.HEALTHY;
195
+
196
+ } catch (error) {
197
+ this.logger.error('Minimax health check failed', error);
198
+ this.lastHealthCheck = { status: HealthStatus.UNHEALTHY, timestamp: new Date() };
199
+ return HealthStatus.UNHEALTHY;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * 验证配置
205
+ */
206
+ validateConfig(): ValidationResult {
207
+ const result = super.validateConfig();
208
+
209
+ if (!this.config?.apiKey) {
210
+ return {
211
+ valid: false,
212
+ errors: [...(result.errors || []), 'Minimax API key is required']
213
+ };
214
+ }
215
+
216
+ // 验证API key格式(Minimax API key通常以sk-开头)
217
+ if (this.config.apiKey && !this.config.apiKey.startsWith('sk-')) {
218
+ result.warnings = [...(result.warnings || []), 'API key format might be invalid (should start with sk-)'];
219
+ }
220
+
221
+ return result;
222
+ }
223
+
224
+ /**
225
+ * 获取默认基础URL
226
+ */
227
+ protected getDefaultBaseURL(): string {
228
+ // 支持中国和国际端点
229
+ return 'https://api.minimaxi.com/anthropic';
230
+ }
231
+
232
+ /**
233
+ * 转换消息格式
234
+ */
235
+ protected transformMessages(messages: any[]): any[] {
236
+ return messages.map(msg => ({
237
+ role: msg.role,
238
+ content: [{ type: 'text', text: msg.content }]
239
+ }));
240
+ }
241
+
242
+ /**
243
+ * 转换聊天响应
244
+ */
245
+ private transformChatResponse(response: any): ChatResponse {
246
+ const content = response.content[0];
247
+ const text = content.type === 'text' ? content.text : '';
248
+
249
+ return {
250
+ message: {
251
+ id: this.generateId(),
252
+ role: MessageRole.ASSISTANT,
253
+ content: text,
254
+ timestamp: new Date()
255
+ },
256
+ usage: response.usage ? {
257
+ promptTokens: response.usage.input_tokens || 0,
258
+ completionTokens: response.usage.output_tokens || 0,
259
+ totalTokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0)
260
+ } : undefined
261
+ };
262
+ }
263
+
264
+ /**
265
+ * 构建命令生成提示
266
+ */
267
+ private buildCommandPrompt(request: CommandRequest): string {
268
+ let prompt = `请将以下自然语言描述转换为准确的终端命令:\n\n"${request.naturalLanguage}"\n\n`;
269
+
270
+ if (request.context) {
271
+ prompt += `当前环境:\n`;
272
+ if (request.context.currentDirectory) {
273
+ prompt += `- 当前目录:${request.context.currentDirectory}\n`;
274
+ }
275
+ if (request.context.operatingSystem) {
276
+ prompt += `- 操作系统:${request.context.operatingSystem}\n`;
277
+ }
278
+ if (request.context.shell) {
279
+ prompt += `- Shell:${request.context.shell}\n`;
280
+ }
281
+ }
282
+
283
+ prompt += `\n请直接返回JSON格式:\n`;
284
+ prompt += `{\n`;
285
+ prompt += ` "command": "具体命令",\n`;
286
+ prompt += ` "explanation": "命令解释",\n`;
287
+ prompt += ` "confidence": 0.95\n`;
288
+ prompt += `}\n`;
289
+
290
+ return prompt;
291
+ }
292
+
293
+ /**
294
+ * 构建命令解释提示
295
+ */
296
+ private buildExplainPrompt(request: ExplainRequest): string {
297
+ let prompt = `请详细解释以下终端命令:\n\n\`${request.command}\`\n\n`;
298
+
299
+ if (request.context?.currentDirectory) {
300
+ prompt += `当前目录:${request.context.currentDirectory}\n`;
301
+ }
302
+ if (request.context?.operatingSystem) {
303
+ prompt += `操作系统:${request.context.operatingSystem}\n`;
304
+ }
305
+
306
+ prompt += `\n请按以下JSON格式返回:\n`;
307
+ prompt += `{\n`;
308
+ prompt += ` "explanation": "整体解释",\n`;
309
+ prompt += ` "breakdown": [\n`;
310
+ prompt += ` {"part": "命令部分", "description": "说明"}\n`;
311
+ prompt += ` ],\n`;
312
+ prompt += ` "examples": ["使用示例"]\n`;
313
+ prompt += `}\n`;
314
+
315
+ return prompt;
316
+ }
317
+
318
+ /**
319
+ * 构建结果分析提示
320
+ */
321
+ private buildAnalysisPrompt(request: AnalysisRequest): string {
322
+ let prompt = `请分析以下命令执行结果:\n\n`;
323
+ prompt += `命令:${request.command}\n`;
324
+ prompt += `退出码:${request.exitCode}\n`;
325
+ prompt += `输出:\n${request.output}\n\n`;
326
+
327
+ if (request.context?.workingDirectory) {
328
+ prompt += `工作目录:${request.context.workingDirectory}\n`;
329
+ }
330
+
331
+ prompt += `\n请按以下JSON格式返回:\n`;
332
+ prompt += `{\n`;
333
+ prompt += ` "summary": "结果总结",\n`;
334
+ prompt += ` "insights": ["洞察1", "洞察2"],\n`;
335
+ prompt += ` "success": true/false,\n`;
336
+ prompt += ` "issues": [\n`;
337
+ prompt += ` {"severity": "warning|error|info", "message": "问题描述", "suggestion": "建议"}\n`;
338
+ prompt += ` ]\n`;
339
+ prompt += `}\n`;
340
+
341
+ return prompt;
342
+ }
343
+
344
+ /**
345
+ * 解析命令响应
346
+ */
347
+ private parseCommandResponse(content: string): CommandResponse {
348
+ try {
349
+ const match = content.match(/\{[\s\S]*\}/);
350
+ if (match) {
351
+ const parsed = JSON.parse(match[0]);
352
+ return {
353
+ command: parsed.command || '',
354
+ explanation: parsed.explanation || '',
355
+ confidence: parsed.confidence || 0.5
356
+ };
357
+ }
358
+ } catch (error) {
359
+ this.logger.warn('Failed to parse command response as JSON', error);
360
+ }
361
+
362
+ // 备用解析
363
+ const lines = content.split('\n').map(l => l.trim()).filter(l => l);
364
+ return {
365
+ command: lines[0] || '',
366
+ explanation: lines.slice(1).join(' ') || 'AI生成的命令',
367
+ confidence: 0.5
368
+ };
369
+ }
370
+
371
+ /**
372
+ * 解析解释响应
373
+ */
374
+ private parseExplainResponse(content: string): ExplainResponse {
375
+ try {
376
+ const match = content.match(/\{[\s\S]*\}/);
377
+ if (match) {
378
+ const parsed = JSON.parse(match[0]);
379
+ return {
380
+ explanation: parsed.explanation || '',
381
+ breakdown: parsed.breakdown || [],
382
+ examples: parsed.examples || []
383
+ };
384
+ }
385
+ } catch (error) {
386
+ this.logger.warn('Failed to parse explain response as JSON', error);
387
+ }
388
+
389
+ return {
390
+ explanation: content,
391
+ breakdown: []
392
+ };
393
+ }
394
+
395
+ /**
396
+ * 解析分析响应
397
+ */
398
+ private parseAnalysisResponse(content: string): AnalysisResponse {
399
+ try {
400
+ const match = content.match(/\{[\s\S]*\}/);
401
+ if (match) {
402
+ const parsed = JSON.parse(match[0]);
403
+ return {
404
+ summary: parsed.summary || '',
405
+ insights: parsed.insights || [],
406
+ success: parsed.success !== false,
407
+ issues: parsed.issues || []
408
+ };
409
+ }
410
+ } catch (error) {
411
+ this.logger.warn('Failed to parse analysis response as JSON', error);
412
+ }
413
+
414
+ return {
415
+ summary: content,
416
+ insights: [],
417
+ success: true
418
+ };
419
+ }
420
+
421
+ /**
422
+ * 获取默认系统提示
423
+ */
424
+ private getDefaultSystemPrompt(): string {
425
+ return `你是一个专业的终端命令助手,擅长将自然语言转换为准确的命令,并提供详细的解释。`;
426
+ }
427
+ }