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,43 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Observable, of } from 'rxjs';
3
+
4
+ /**
5
+ * 命令分析服务
6
+ * 分析终端命令并提供智能建议
7
+ */
8
+ @Injectable({
9
+ providedIn: 'root'
10
+ })
11
+ export class CommandAnalyzerService {
12
+ constructor() {}
13
+
14
+ /**
15
+ * 分析命令
16
+ */
17
+ analyzeCommand(command: string): Observable<any> {
18
+ // TODO: 实现命令分析逻辑
19
+ return of({
20
+ type: 'unknown',
21
+ suggestions: [],
22
+ explanation: ''
23
+ });
24
+ }
25
+
26
+ /**
27
+ * 获取命令历史
28
+ */
29
+ getCommandHistory(): string[] {
30
+ return [];
31
+ }
32
+
33
+ /**
34
+ * 解析命令参数
35
+ */
36
+ parseCommand(command: string): any {
37
+ const parts = command.trim().split(/\s+/);
38
+ return {
39
+ command: parts[0],
40
+ args: parts.slice(1)
41
+ };
42
+ }
43
+ }
@@ -0,0 +1,45 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+
4
+ /**
5
+ * 上下文菜单服务
6
+ * 管理终端上下文菜单
7
+ */
8
+ @Injectable({
9
+ providedIn: 'root'
10
+ })
11
+ export class ContextMenuService {
12
+ private menuItemSelected$ = new Subject<any>();
13
+
14
+ constructor() {}
15
+
16
+ /**
17
+ * 显示上下文菜单
18
+ */
19
+ showMenu(x: number, y: number, items: any[]): void {
20
+ // TODO: 实现上下文菜单显示
21
+ console.log('Show context menu at', x, y, items);
22
+ }
23
+
24
+ /**
25
+ * 隐藏上下文菜单
26
+ */
27
+ hideMenu(): void {
28
+ // TODO: 实现上下文菜单隐藏
29
+ console.log('Hide context menu');
30
+ }
31
+
32
+ /**
33
+ * 订阅菜单项选择事件
34
+ */
35
+ onMenuItemSelected(): any {
36
+ return this.menuItemSelected$.asObservable();
37
+ }
38
+
39
+ /**
40
+ * 触发菜单项选择
41
+ */
42
+ selectMenuItem(item: any): void {
43
+ this.menuItemSelected$.next(item);
44
+ }
45
+ }
@@ -0,0 +1,53 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+
4
+ /**
5
+ * 热键服务
6
+ * 管理全局热键注册和处理
7
+ */
8
+ @Injectable({
9
+ providedIn: 'root'
10
+ })
11
+ export class HotkeyService {
12
+ private hotkeyPressed$ = new Subject<string>();
13
+
14
+ constructor() {}
15
+
16
+ /**
17
+ * 注册热键
18
+ */
19
+ registerHotkey(key: string, callback: () => void): void {
20
+ // TODO: 实现热键注册
21
+ console.log('Register hotkey', key);
22
+ }
23
+
24
+ /**
25
+ * 取消注册热键
26
+ */
27
+ unregisterHotkey(key: string): void {
28
+ // TODO: 实现热键取消注册
29
+ console.log('Unregister hotkey', key);
30
+ }
31
+
32
+ /**
33
+ * 订阅热键按下事件
34
+ */
35
+ onHotkeyPressed(): any {
36
+ return this.hotkeyPressed$.asObservable();
37
+ }
38
+
39
+ /**
40
+ * 触发热键事件
41
+ */
42
+ triggerHotkey(key: string): void {
43
+ this.hotkeyPressed$.next(key);
44
+ }
45
+
46
+ /**
47
+ * 检查热键是否已注册
48
+ */
49
+ isHotkeyRegistered(key: string): boolean {
50
+ // TODO: 实现热键检查
51
+ return false;
52
+ }
53
+ }
@@ -0,0 +1,317 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Subject, Observable } from 'rxjs';
3
+ import { TerminalContext, TerminalSession, TerminalError, CommandResult, SystemInfo, ProcessInfo } from '../../types/terminal.types';
4
+ import { LoggerService } from '../core/logger.service';
5
+
6
+ @Injectable({ providedIn: 'root' })
7
+ export class TerminalContextService {
8
+ private currentContext: TerminalContext | null = null;
9
+ private contextChange$ = new Subject<TerminalContext>();
10
+ private errorDetected$ = new Subject<TerminalError>();
11
+ private commandExecuted$ = new Subject<CommandResult>();
12
+
13
+ constructor(private logger: LoggerService) {
14
+ this.initializeContext();
15
+ }
16
+
17
+ private async initializeContext(): Promise<void> {
18
+ try {
19
+ const session = await this.getCurrentSession();
20
+ const systemInfo = await this.getSystemInfo();
21
+ const projectInfo = await this.detectProjectInfo();
22
+
23
+ this.currentContext = {
24
+ session,
25
+ isRunning: false,
26
+ recentCommands: [],
27
+ systemInfo,
28
+ projectInfo
29
+ };
30
+
31
+ this.logger.info('Terminal context initialized', { context: this.currentContext });
32
+ } catch (error) {
33
+ this.logger.error('Failed to initialize terminal context', error);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * 获取当前终端上下文
39
+ */
40
+ getCurrentContext(): TerminalContext | null {
41
+ return this.currentContext;
42
+ }
43
+
44
+ /**
45
+ * 订阅上下文变化
46
+ */
47
+ onContextChange(): Observable<TerminalContext> {
48
+ return this.contextChange$.asObservable();
49
+ }
50
+
51
+ /**
52
+ * 订阅错误检测
53
+ */
54
+ onError(): Observable<TerminalError> {
55
+ return this.errorDetected$.asObservable();
56
+ }
57
+
58
+ /**
59
+ * 订阅命令执行
60
+ */
61
+ onCommandExecuted(): Observable<CommandResult> {
62
+ return this.commandExecuted$.asObservable();
63
+ }
64
+
65
+ /**
66
+ * 更新当前命令
67
+ */
68
+ updateCurrentCommand(command: string): void {
69
+ if (!this.currentContext) return;
70
+
71
+ this.currentContext.currentCommand = command;
72
+ this.contextChange$.next(this.currentContext);
73
+ this.logger.debug('Current command updated', { command });
74
+ }
75
+
76
+ /**
77
+ * 设置命令执行结果
78
+ */
79
+ setCommandResult(result: CommandResult): void {
80
+ if (!this.currentContext) return;
81
+
82
+ this.currentContext.lastCommand = result.command;
83
+ this.currentContext.lastOutput = result.stdout;
84
+ this.currentContext.lastError = result.stderr;
85
+ this.currentContext.exitCode = result.exitCode;
86
+ this.currentContext.isRunning = false;
87
+
88
+ // 添加到历史
89
+ this.currentContext.recentCommands.unshift(result.command);
90
+ if (this.currentContext.recentCommands.length > 50) {
91
+ this.currentContext.recentCommands = this.currentContext.recentCommands.slice(0, 50);
92
+ }
93
+
94
+ // 检查是否出错
95
+ if (!result.success || result.exitCode !== 0) {
96
+ const error = this.analyzeError(result);
97
+ this.errorDetected$.next(error);
98
+ }
99
+
100
+ this.commandExecuted$.next(result);
101
+ this.contextChange$.next(this.currentContext);
102
+
103
+ this.logger.debug('Command result set', { result });
104
+ }
105
+
106
+ /**
107
+ * 设置运行状态
108
+ */
109
+ setRunningStatus(isRunning: boolean, process?: ProcessInfo): void {
110
+ if (!this.currentContext) return;
111
+
112
+ this.currentContext.isRunning = isRunning;
113
+ this.currentContext.runningProcess = process || undefined;
114
+ this.contextChange$.next(this.currentContext);
115
+ }
116
+
117
+ /**
118
+ * 更改当前工作目录
119
+ */
120
+ changeDirectory(path: string): void {
121
+ if (!this.currentContext) return;
122
+
123
+ this.currentContext.session.cwd = path;
124
+ this.currentContext.session.lastActivity = new Date();
125
+ this.contextChange$.next(this.currentContext);
126
+
127
+ this.logger.debug('Directory changed', { path });
128
+ }
129
+
130
+ /**
131
+ * 检测错误并生成分析
132
+ */
133
+ private analyzeError(result: CommandResult): TerminalError {
134
+ let type: TerminalError['type'] = 'unknown';
135
+ let message = result.stderr || 'Unknown error';
136
+
137
+ // 基于错误消息和退出码确定错误类型
138
+ if (result.stderr.includes('command not found')) {
139
+ type = 'command_not_found';
140
+ } else if (result.stderr.includes('Permission denied')) {
141
+ type = 'permission_denied';
142
+ } else if (result.stderr.includes('No such file or directory')) {
143
+ type = 'file_not_found';
144
+ } else if (result.stderr.includes('Syntax error')) {
145
+ type = 'syntax_error';
146
+ }
147
+
148
+ // 生成修复建议
149
+ const suggestions = this.generateSuggestions(type, result.command, result.stderr);
150
+
151
+ return {
152
+ type,
153
+ message,
154
+ command: result.command,
155
+ exitCode: result.exitCode,
156
+ suggestions,
157
+ timestamp: new Date()
158
+ };
159
+ }
160
+
161
+ /**
162
+ * 生成错误修复建议
163
+ */
164
+ private generateSuggestions(type: TerminalError['type'], _command?: string, _errorMsg?: string): string[] {
165
+ const suggestions: string[] = [];
166
+
167
+ switch (type) {
168
+ case 'command_not_found':
169
+ suggestions.push('检查命令拼写是否正确');
170
+ suggestions.push('使用 `which <command>` 或 `command -v <command>` 确认命令是否存在');
171
+ suggestions.push('使用 `apt install <package>` (Ubuntu/Debian) 或 `brew install <package>` (macOS) 安装缺失命令');
172
+ break;
173
+
174
+ case 'permission_denied':
175
+ suggestions.push('使用 `sudo` 提升权限');
176
+ suggestions.push('检查文件权限:`ls -l <file>`');
177
+ suggestions.push('使用 `chmod` 修改文件权限');
178
+ break;
179
+
180
+ case 'file_not_found':
181
+ suggestions.push('检查文件路径是否正确');
182
+ suggestions.push('使用 `pwd` 确认当前目录');
183
+ suggestions.push('使用 `ls` 查看当前目录内容');
184
+ break;
185
+
186
+ case 'syntax_error':
187
+ suggestions.push('检查命令语法');
188
+ suggestions.push('使用 `man <command>` 查看命令手册');
189
+ suggestions.push('使用 `--help` 或 `-h` 查看帮助信息');
190
+ break;
191
+ }
192
+
193
+ return suggestions;
194
+ }
195
+
196
+ /**
197
+ * 获取当前会话信息
198
+ */
199
+ private async getCurrentSession(): Promise<TerminalSession> {
200
+ // 这里应该从Tabby API获取真实的会话信息
201
+ const env: Record<string, string> = {};
202
+ Object.keys(process.env).forEach(key => {
203
+ const value = process.env[key];
204
+ if (value !== undefined) {
205
+ env[key] = value;
206
+ }
207
+ });
208
+
209
+ return {
210
+ sessionId: this.generateSessionId(),
211
+ cwd: process.cwd(),
212
+ shell: this.detectShell(),
213
+ user: env.USER || env.USERNAME,
214
+ hostname: env.HOSTNAME || 'localhost',
215
+ environment: env,
216
+ startTime: new Date(),
217
+ lastActivity: new Date()
218
+ };
219
+ }
220
+
221
+ /**
222
+ * 获取系统信息
223
+ */
224
+ private async getSystemInfo(): Promise<SystemInfo> {
225
+ // 使用静态信息代替动态检测(避免Node.js模块依赖)
226
+ return {
227
+ platform: 'browser',
228
+ arch: 'unknown',
229
+ type: 'Browser',
230
+ release: 'N/A',
231
+ cpus: 0,
232
+ totalMemory: 0,
233
+ availableMemory: 0,
234
+ nodeVersion: 'N/A'
235
+ };
236
+ }
237
+
238
+ /**
239
+ * 获取系统信息 - 公共接口
240
+ */
241
+ getSystemInfoPublic(): SystemInfo | null {
242
+ return this.currentContext?.systemInfo || null;
243
+ }
244
+
245
+ /**
246
+ * 检测项目信息
247
+ */
248
+ private async detectProjectInfo(): Promise<any> {
249
+ // TODO: 实现项目检测逻辑
250
+ // 检测 .git, package.json, pom.xml, build.gradle 等
251
+ return null;
252
+ }
253
+
254
+ /**
255
+ * 检测当前Shell
256
+ */
257
+ private detectShell(): string {
258
+ return process.env.SHELL || process.env.COMSPEC || 'unknown';
259
+ }
260
+
261
+ /**
262
+ * 生成会话ID
263
+ */
264
+ private generateSessionId(): string {
265
+ return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
266
+ }
267
+
268
+ /**
269
+ * 获取环境变量
270
+ */
271
+ getEnvironmentVariables(): Record<string, string> {
272
+ return this.currentContext?.session.environment || {};
273
+ }
274
+
275
+ /**
276
+ * 获取当前工作目录
277
+ */
278
+ getCurrentDirectory(): string {
279
+ return this.currentContext?.session.cwd || '';
280
+ }
281
+
282
+ /**
283
+ * 获取最近命令历史
284
+ */
285
+ getRecentCommands(): string[] {
286
+ return this.currentContext?.recentCommands || [];
287
+ }
288
+
289
+ /**
290
+ * 获取项目信息
291
+ */
292
+ getProjectInfo(): any {
293
+ return this.currentContext?.projectInfo || null;
294
+ }
295
+
296
+ /**
297
+ * 检查是否有错误
298
+ */
299
+ hasError(): boolean {
300
+ return !!(this.currentContext?.lastError && this.currentContext.exitCode !== 0);
301
+ }
302
+
303
+ /**
304
+ * 获取最后一条错误
305
+ */
306
+ getLastError(): TerminalError | null {
307
+ if (!this.hasError()) return null;
308
+
309
+ return {
310
+ type: 'unknown',
311
+ message: this.currentContext?.lastError || '',
312
+ command: this.currentContext?.lastCommand,
313
+ exitCode: this.currentContext?.exitCode,
314
+ timestamp: new Date()
315
+ };
316
+ }
317
+ }