@opensumi/ide-ai-native 3.8.2-next-1741418699.0 → 3.8.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/lib/browser/ai-core.contribution.d.ts.map +1 -1
- package/lib/browser/ai-core.contribution.js +20 -0
- package/lib/browser/ai-core.contribution.js.map +1 -1
- package/lib/browser/chat/apply.service.js +1 -1
- package/lib/browser/chat/apply.service.js.map +1 -1
- package/lib/browser/chat/chat-manager.service.d.ts +2 -1
- package/lib/browser/chat/chat-manager.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-manager.service.js +26 -7
- package/lib/browser/chat/chat-manager.service.js.map +1 -1
- package/lib/browser/chat/chat-model.d.ts +3 -3
- package/lib/browser/chat/chat-model.d.ts.map +1 -1
- package/lib/browser/chat/chat-model.js +22 -9
- package/lib/browser/chat/chat-model.js.map +1 -1
- package/lib/browser/chat/chat-proxy.service.d.ts +1 -0
- package/lib/browser/chat/chat-proxy.service.d.ts.map +1 -1
- package/lib/browser/chat/chat-proxy.service.js +2 -0
- package/lib/browser/chat/chat-proxy.service.js.map +1 -1
- package/lib/browser/chat/chat.view.d.ts.map +1 -1
- package/lib/browser/chat/chat.view.js +59 -2
- package/lib/browser/chat/chat.view.js.map +1 -1
- package/lib/browser/components/ApplyStatus.d.ts +7 -0
- package/lib/browser/components/ApplyStatus.d.ts.map +1 -0
- package/lib/browser/components/ApplyStatus.js +32 -0
- package/lib/browser/components/ApplyStatus.js.map +1 -0
- package/lib/browser/components/ChangeList.d.ts +17 -0
- package/lib/browser/components/ChangeList.d.ts.map +1 -0
- package/lib/browser/components/ChangeList.js +72 -0
- package/lib/browser/components/ChangeList.js.map +1 -0
- package/lib/browser/components/change-list.module.less +126 -0
- package/lib/browser/components/chat-history.module.less +1 -1
- package/lib/browser/components/components.module.less +14 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +4 -4
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/mcp/base-apply.service.d.ts +15 -7
- package/lib/browser/mcp/base-apply.service.d.ts.map +1 -1
- package/lib/browser/mcp/base-apply.service.js +84 -54
- package/lib/browser/mcp/base-apply.service.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-config.view.js +18 -28
- package/lib/browser/mcp/config/components/mcp-config.view.js.map +1 -1
- package/lib/browser/mcp/config/components/mcp-server-form.d.ts.map +1 -1
- package/lib/browser/mcp/config/components/mcp-server-form.js +25 -33
- package/lib/browser/mcp/config/components/mcp-server-form.js.map +1 -1
- package/lib/browser/mcp/tools/components/EditFile.js +3 -24
- package/lib/browser/mcp/tools/components/EditFile.js.map +1 -1
- package/lib/browser/mcp/tools/components/ExpandableFileList.js +3 -2
- package/lib/browser/mcp/tools/components/ExpandableFileList.js.map +1 -1
- package/lib/browser/mcp/tools/handlers/EditFile.js +2 -2
- package/lib/browser/mcp/tools/handlers/EditFile.js.map +1 -1
- package/lib/browser/model/msg-history-manager.d.ts +0 -2
- package/lib/browser/model/msg-history-manager.d.ts.map +1 -1
- package/lib/browser/model/msg-history-manager.js +1 -6
- package/lib/browser/model/msg-history-manager.js.map +1 -1
- package/lib/browser/preferences/schema.d.ts.map +1 -1
- package/lib/browser/preferences/schema.js +8 -0
- package/lib/browser/preferences/schema.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff-manager.js +2 -2
- package/lib/browser/widget/inline-diff/inline-diff-manager.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.controller.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.service.d.ts +3 -2
- package/lib/browser/widget/inline-diff/inline-diff.service.d.ts.map +1 -1
- package/lib/browser/widget/inline-diff/inline-diff.service.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts +0 -33
- package/lib/browser/widget/inline-stream-diff/live-preview.component.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.component.js +1 -6
- package/lib/browser/widget/inline-stream-diff/live-preview.component.js.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.d.ts.map +1 -1
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js +15 -14
- package/lib/browser/widget/inline-stream-diff/live-preview.decoration.js.map +1 -1
- package/lib/common/index.d.ts +7 -2
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +3 -2
- package/lib/common/index.js.map +1 -1
- package/lib/common/types.d.ts +33 -0
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js +6 -1
- package/lib/common/types.js.map +1 -1
- package/package.json +23 -23
- package/src/browser/ai-core.contribution.ts +28 -0
- package/src/browser/chat/apply.service.ts +1 -1
- package/src/browser/chat/chat-manager.service.ts +54 -33
- package/src/browser/chat/chat-model.ts +22 -8
- package/src/browser/chat/chat-proxy.service.ts +2 -0
- package/src/browser/chat/chat.view.tsx +78 -4
- package/src/browser/components/ApplyStatus.tsx +44 -0
- package/src/browser/components/ChangeList.tsx +131 -0
- package/src/browser/components/change-list.module.less +126 -0
- package/src/browser/components/chat-history.module.less +1 -1
- package/src/browser/components/components.module.less +14 -0
- package/src/browser/index.ts +5 -4
- package/src/browser/mcp/base-apply.service.ts +93 -64
- package/src/browser/mcp/config/components/mcp-config.view.tsx +12 -23
- package/src/browser/mcp/config/components/mcp-server-form.tsx +54 -68
- package/src/browser/mcp/tools/components/EditFile.tsx +3 -37
- package/src/browser/mcp/tools/components/ExpandableFileList.tsx +3 -1
- package/src/browser/mcp/tools/handlers/EditFile.ts +2 -2
- package/src/browser/model/msg-history-manager.ts +2 -11
- package/src/browser/preferences/schema.ts +8 -0
- package/src/browser/widget/inline-diff/inline-diff-manager.tsx +2 -2
- package/src/browser/widget/inline-diff/inline-diff.controller.ts +2 -1
- package/src/browser/widget/inline-diff/inline-diff.service.ts +3 -2
- package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +4 -4
- package/src/browser/widget/inline-stream-diff/live-preview.component.tsx +0 -34
- package/src/browser/widget/inline-stream-diff/live-preview.decoration.tsx +8 -9
- package/src/common/index.ts +9 -2
- package/src/common/types.ts +35 -0
|
@@ -527,3 +527,17 @@
|
|
|
527
527
|
align-items: center;
|
|
528
528
|
}
|
|
529
529
|
}
|
|
530
|
+
|
|
531
|
+
.status {
|
|
532
|
+
:global {
|
|
533
|
+
.codicon-pass {
|
|
534
|
+
color: #52c41a;
|
|
535
|
+
}
|
|
536
|
+
.codicon-error {
|
|
537
|
+
color: var(--debugConsole-errorForeground);
|
|
538
|
+
}
|
|
539
|
+
.codicon-circle-slash {
|
|
540
|
+
color: var(--input-placeholderForeground);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
package/src/browser/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
IChatAgentService,
|
|
26
26
|
IChatInternalService,
|
|
27
27
|
IChatManagerService,
|
|
28
|
+
InlineDiffServiceToken,
|
|
28
29
|
SumiMCPServerProxyServicePath,
|
|
29
30
|
TokenMCPServerProxyService,
|
|
30
31
|
} from '../common';
|
|
@@ -193,14 +194,14 @@ export class AINativeModule extends BrowserModule {
|
|
|
193
194
|
token: IAIInlineCompletionsProvider,
|
|
194
195
|
useClass: AIInlineCompletionsProvider,
|
|
195
196
|
},
|
|
196
|
-
{
|
|
197
|
-
token: InlineDiffService,
|
|
198
|
-
useClass: InlineDiffService,
|
|
199
|
-
},
|
|
200
197
|
{
|
|
201
198
|
token: ChatAgentPromptProvider,
|
|
202
199
|
useClass: DefaultChatAgentPromptProvider,
|
|
203
200
|
},
|
|
201
|
+
{
|
|
202
|
+
token: InlineDiffServiceToken,
|
|
203
|
+
useClass: InlineDiffService,
|
|
204
|
+
},
|
|
204
205
|
{
|
|
205
206
|
token: BaseApplyService,
|
|
206
207
|
useClass: ApplyService,
|
|
@@ -26,17 +26,13 @@ import { Deferred, DisposableMap, Emitter, IDisposable, URI, path } from '@opens
|
|
|
26
26
|
import { SumiReadableStream } from '@opensumi/ide-utils/lib/stream';
|
|
27
27
|
import { EditOperation } from '@opensumi/monaco-editor-core/esm/vs/editor/common/core/editOperation';
|
|
28
28
|
|
|
29
|
-
import { IChatInternalService } from '../../common';
|
|
29
|
+
import { IChatInternalService, IInlineDiffService, InlineDiffServiceToken } from '../../common';
|
|
30
30
|
import { CodeBlockData, CodeBlockStatus } from '../../common/types';
|
|
31
31
|
import { ChatInternalService } from '../chat/chat.internal.service';
|
|
32
32
|
import { InlineChatController } from '../widget/inline-chat/inline-chat-controller';
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
InlineDiffService,
|
|
37
|
-
LiveInlineDiffPreviewer,
|
|
38
|
-
} from '../widget/inline-diff';
|
|
39
|
-
import { BaseInlineStreamDiffHandler } from '../widget/inline-stream-diff/inline-stream-diff.handler';
|
|
33
|
+
import { BaseInlineDiffPreviewer, InlineDiffController, LiveInlineDiffPreviewer } from '../widget/inline-diff';
|
|
34
|
+
|
|
35
|
+
import type { BaseInlineStreamDiffHandler } from '../widget/inline-stream-diff/inline-stream-diff.handler';
|
|
40
36
|
|
|
41
37
|
export abstract class BaseApplyService extends WithEventBus {
|
|
42
38
|
@Autowired(IChatInternalService)
|
|
@@ -48,8 +44,8 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
48
44
|
@Autowired(WorkbenchEditorService)
|
|
49
45
|
protected readonly editorService: WorkbenchEditorService;
|
|
50
46
|
|
|
51
|
-
@Autowired(
|
|
52
|
-
private readonly inlineDiffService:
|
|
47
|
+
@Autowired(InlineDiffServiceToken)
|
|
48
|
+
private readonly inlineDiffService: IInlineDiffService;
|
|
53
49
|
|
|
54
50
|
@Autowired(IMarkerService)
|
|
55
51
|
private readonly markerService: IMarkerService;
|
|
@@ -155,7 +151,7 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
155
151
|
// 使用最后一个版本内容渲染 apply 内容
|
|
156
152
|
if (filePendingApplies.length > 0 && filePendingApplies[0].updatedCode) {
|
|
157
153
|
const editor = event.payload.group.codeEditor.monacoEditor;
|
|
158
|
-
this.renderApplyResult(editor, filePendingApplies[0], filePendingApplies[0].updatedCode);
|
|
154
|
+
await this.renderApplyResult(editor, filePendingApplies[0], filePendingApplies[0].updatedCode);
|
|
159
155
|
}
|
|
160
156
|
}
|
|
161
157
|
|
|
@@ -183,9 +179,10 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
183
179
|
return sessionCodeBlocks.filter((block) => block.status === 'pending').map((block) => block.relativePath);
|
|
184
180
|
}
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
getSessionCodeBlocks(sessionId?: string) {
|
|
183
|
+
const sessionModel = sessionId
|
|
184
|
+
? this.chatInternalService.getSession(sessionId)
|
|
185
|
+
: this.chatInternalService.sessionModel;
|
|
189
186
|
if (!sessionModel) {
|
|
190
187
|
throw new Error(`Session ${sessionId} not found`);
|
|
191
188
|
}
|
|
@@ -225,7 +222,12 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
225
222
|
this.onCodeBlockUpdateEmitter.fire(codeBlock);
|
|
226
223
|
}
|
|
227
224
|
|
|
228
|
-
async registerCodeBlock(
|
|
225
|
+
async registerCodeBlock(
|
|
226
|
+
relativePath: string,
|
|
227
|
+
content: string,
|
|
228
|
+
toolCallId: string,
|
|
229
|
+
instructions?: string,
|
|
230
|
+
): Promise<CodeBlockData> {
|
|
229
231
|
const lastMessageId = this.chatInternalService.sessionModel.history.lastMessageId!;
|
|
230
232
|
const uriCodeBlocks = this.getUriCodeBlocks(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
|
|
231
233
|
const originalModelRef = await this.editorDocumentModelService.createModelReference(
|
|
@@ -240,6 +242,7 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
240
242
|
createdAt: Date.now(),
|
|
241
243
|
toolCallId,
|
|
242
244
|
messageId: lastMessageId,
|
|
245
|
+
instructions,
|
|
243
246
|
// TODO: 支持range
|
|
244
247
|
originalCode: originalModelRef.instance.getText(),
|
|
245
248
|
};
|
|
@@ -295,22 +298,17 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
295
298
|
if (!result) {
|
|
296
299
|
throw new Error('Failed to open file');
|
|
297
300
|
}
|
|
298
|
-
|
|
299
|
-
codeBlock.updatedCode = fastApplyFileResult.result;
|
|
300
|
-
codeBlock.status = 'pending';
|
|
301
|
-
this.updateCodeBlock(codeBlock);
|
|
302
|
-
}
|
|
303
|
-
const applyResult = await this.renderApplyResult(
|
|
301
|
+
const res = await this.renderApplyResult(
|
|
304
302
|
result.group.codeEditor.monacoEditor,
|
|
305
303
|
codeBlock,
|
|
306
304
|
(fastApplyFileResult.result || fastApplyFileResult.stream)!,
|
|
307
305
|
fastApplyFileResult.range,
|
|
308
306
|
);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
307
|
+
codeBlock.updatedCode = res.updatedCode;
|
|
308
|
+
codeBlock.status = 'pending';
|
|
309
|
+
// 用户实际接受的 apply 结果
|
|
310
|
+
codeBlock.applyResult = res.result;
|
|
311
|
+
this.updateCodeBlock(codeBlock);
|
|
314
312
|
|
|
315
313
|
return codeBlock;
|
|
316
314
|
} catch (err) {
|
|
@@ -322,31 +320,33 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
322
320
|
}
|
|
323
321
|
}
|
|
324
322
|
|
|
323
|
+
/**
|
|
324
|
+
* 渲染apply结果(支持流式和直接输出结果)
|
|
325
|
+
* 副作用:渲染时会添加accept、reject操作监听器,监听到结果时会自动更新codeBlock的result
|
|
326
|
+
*/
|
|
325
327
|
async renderApplyResult(
|
|
326
328
|
editor: ICodeEditor,
|
|
327
329
|
codeBlock: CodeBlockData,
|
|
328
330
|
updatedContentOrStream: string | SumiReadableStream<IChatProgress>,
|
|
329
331
|
range?: Range,
|
|
330
|
-
): Promise<{ diff: string; diagnosticInfos: IMarker[] }
|
|
331
|
-
const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
|
|
332
|
+
): Promise<{ result?: { diff: string; diagnosticInfos: IMarker[] }; updatedCode: string }> {
|
|
333
|
+
const deferred = new Deferred<{ result?: { diff: string; diagnosticInfos: IMarker[] }; updatedCode: string }>();
|
|
332
334
|
const inlineDiffController = InlineDiffController.get(editor)!;
|
|
333
335
|
range = range || editor.getModel()!.getFullModelRange();
|
|
334
336
|
|
|
335
337
|
if (typeof updatedContentOrStream === 'string') {
|
|
338
|
+
const updatedContent = updatedContentOrStream;
|
|
336
339
|
const editorCurrentContent = editor.getModel()!.getValue();
|
|
337
340
|
const uri = URI.file(path.join(this.appConfig.workspaceDir, codeBlock.relativePath));
|
|
338
341
|
const document = this.editorDocumentModelService.getModelReference(uri);
|
|
339
|
-
if (editorCurrentContent !==
|
|
340
|
-
editor.getModel()?.pushEditOperations([], [EditOperation.replace(range,
|
|
342
|
+
if (editorCurrentContent !== updatedContent || document?.instance.dirty) {
|
|
343
|
+
editor.getModel()?.pushEditOperations([], [EditOperation.replace(range, updatedContent)], () => null);
|
|
341
344
|
await this.editorService.save(uri);
|
|
342
345
|
}
|
|
343
346
|
const uriPendingCodeBlocks = this.getUriCodeBlocks(uri)?.filter((block) => block.status === 'pending');
|
|
344
347
|
const earlistPendingCodeBlock = uriPendingCodeBlocks?.[uriPendingCodeBlocks.length - 1];
|
|
345
|
-
if ((earlistPendingCodeBlock
|
|
346
|
-
|
|
347
|
-
this.updateCodeBlock(codeBlock);
|
|
348
|
-
deferred.resolve();
|
|
349
|
-
return;
|
|
348
|
+
if ((earlistPendingCodeBlock || codeBlock)?.originalCode === updatedContent) {
|
|
349
|
+
throw new Error('No changes applied');
|
|
350
350
|
}
|
|
351
351
|
// Create diff previewer
|
|
352
352
|
const previewer = inlineDiffController.createDiffPreviewer(
|
|
@@ -374,13 +374,16 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
374
374
|
|
|
375
375
|
const { diff, rangesFromDiffHunk } = this.getDiffResult(
|
|
376
376
|
codeBlock.originalCode,
|
|
377
|
-
|
|
377
|
+
updatedContent,
|
|
378
378
|
codeBlock.relativePath,
|
|
379
379
|
);
|
|
380
380
|
const diagnosticInfos = this.getDiagnosticInfos(editor.getModel()!.uri.toString(), rangesFromDiffHunk);
|
|
381
381
|
deferred.resolve({
|
|
382
|
-
|
|
383
|
-
|
|
382
|
+
result: {
|
|
383
|
+
diff,
|
|
384
|
+
diagnosticInfos,
|
|
385
|
+
},
|
|
386
|
+
updatedCode: updatedContent,
|
|
384
387
|
});
|
|
385
388
|
} else {
|
|
386
389
|
const controller = new InlineChatController();
|
|
@@ -408,21 +411,21 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
408
411
|
this.addDispose(
|
|
409
412
|
// 流式输出结束后,转为直接输出逻辑
|
|
410
413
|
previewer.getNode()!.onDiffFinished(async (diffModel) => {
|
|
411
|
-
codeBlock.updatedCode = diffModel.newFullRangeTextLines.join('\n');
|
|
412
414
|
// TODO: 添加 reapply
|
|
415
|
+
const updatedCode = diffModel.newFullRangeTextLines.join('\n');
|
|
413
416
|
// 实际应用结果为空,则取消
|
|
414
|
-
if (codeBlock.
|
|
415
|
-
codeBlock.status = 'failed';
|
|
416
|
-
this.updateCodeBlock(codeBlock);
|
|
417
|
+
if (codeBlock.originalCode === updatedCode) {
|
|
417
418
|
previewer.dispose();
|
|
418
419
|
deferred.reject(new Error('no changes applied'));
|
|
419
420
|
return;
|
|
420
421
|
}
|
|
421
|
-
codeBlock.status = 'pending';
|
|
422
|
-
this.updateCodeBlock(codeBlock);
|
|
423
422
|
previewer.dispose();
|
|
424
|
-
|
|
425
|
-
|
|
423
|
+
try {
|
|
424
|
+
const res = await this.renderApplyResult(editor, codeBlock, updatedCode);
|
|
425
|
+
deferred.resolve(res);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
deferred.reject(err);
|
|
428
|
+
}
|
|
426
429
|
}),
|
|
427
430
|
);
|
|
428
431
|
this.activePreviewerMap.set(codeBlock.relativePath, previewer);
|
|
@@ -477,14 +480,36 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
477
480
|
}
|
|
478
481
|
}
|
|
479
482
|
|
|
480
|
-
processAll(
|
|
481
|
-
const codeBlocks =
|
|
483
|
+
processAll(type: 'accept' | 'reject', uri?: URI): void {
|
|
484
|
+
const codeBlocks = uri
|
|
485
|
+
? this.getUriCodeBlocks(uri)?.filter((block) => block.status === 'pending')
|
|
486
|
+
: this.getSessionCodeBlocks().filter((block) => block.status === 'pending');
|
|
482
487
|
if (!codeBlocks?.length) {
|
|
483
488
|
throw new Error('No pending code block found');
|
|
484
489
|
}
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
490
|
+
const relativePaths = uri
|
|
491
|
+
? [codeBlocks[0].relativePath]
|
|
492
|
+
: codeBlocks
|
|
493
|
+
.map((block) => block.relativePath)
|
|
494
|
+
.reduce((acc, cur) => {
|
|
495
|
+
if (acc.includes(cur)) {
|
|
496
|
+
return acc;
|
|
497
|
+
}
|
|
498
|
+
acc.push(cur);
|
|
499
|
+
return acc;
|
|
500
|
+
}, [] as string[]);
|
|
501
|
+
relativePaths.forEach((relativePath) => {
|
|
502
|
+
this.doProcess(type, relativePath);
|
|
503
|
+
});
|
|
504
|
+
codeBlocks.forEach((codeBlock) => {
|
|
505
|
+
codeBlock.status = type === 'accept' ? 'success' : 'cancelled';
|
|
506
|
+
// TODO: 批量更新
|
|
507
|
+
this.updateCodeBlock(codeBlock);
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
protected doProcess(type: 'accept' | 'reject', relativePath: string) {
|
|
512
|
+
const decorationModel = this.activePreviewerMap.get(relativePath)?.getNode()?.livePreviewDiffDecorationModel;
|
|
488
513
|
if (!decorationModel) {
|
|
489
514
|
throw new Error('No active previewer found');
|
|
490
515
|
}
|
|
@@ -493,19 +518,13 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
493
518
|
} else {
|
|
494
519
|
decorationModel.discardUnProcessed();
|
|
495
520
|
}
|
|
496
|
-
this.editorService.save(
|
|
497
|
-
codeBlocks.forEach((codeBlock) => {
|
|
498
|
-
codeBlock.status = type === 'accept' ? 'success' : 'cancelled';
|
|
499
|
-
// TODO: 批量更新
|
|
500
|
-
this.updateCodeBlock(codeBlock);
|
|
501
|
-
});
|
|
521
|
+
this.editorService.save(URI.file(path.join(this.appConfig.workspaceDir, relativePath)));
|
|
502
522
|
}
|
|
503
523
|
|
|
504
524
|
protected listenPartialEdit(model: ITextModel, codeBlock: CodeBlockData) {
|
|
505
525
|
const deferred = new Deferred<{ diff: string; diagnosticInfos: IMarker[] }>();
|
|
506
526
|
const uriString = model.uri.toString();
|
|
507
527
|
const toDispose = this.inlineDiffService.onPartialEdit((event) => {
|
|
508
|
-
// TODO 支持自动保存
|
|
509
528
|
if (
|
|
510
529
|
event.totalPartialEditCount === event.resolvedPartialEditCount &&
|
|
511
530
|
event.uri.path === model.uri.path.toString()
|
|
@@ -526,11 +545,19 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
526
545
|
actionSource: ActionSourceEnum.Chat,
|
|
527
546
|
sessionId: this.chatInternalService.sessionModel.sessionId,
|
|
528
547
|
isReceive: true,
|
|
529
|
-
|
|
530
|
-
|
|
548
|
+
// 是否有丢弃部分代码
|
|
549
|
+
isDrop: event.acceptPartialEditCount !== event.totalPartialEditCount,
|
|
550
|
+
code: appliedResult,
|
|
551
|
+
originCode: codeBlock.originalCode,
|
|
531
552
|
message: JSON.stringify({
|
|
532
553
|
diff,
|
|
533
554
|
diagnosticInfos,
|
|
555
|
+
instructions: codeBlock.instructions,
|
|
556
|
+
codeEdit: codeBlock.codeEdit,
|
|
557
|
+
partialEditCount: event.totalPartialEditCount,
|
|
558
|
+
acceptPartialEditCount: event.acceptPartialEditCount,
|
|
559
|
+
addedLinesCount: event.totalAddedLinesCount,
|
|
560
|
+
deletedLinesCount: event.totalDeletedLinesCount,
|
|
534
561
|
}),
|
|
535
562
|
});
|
|
536
563
|
deferred.resolve({
|
|
@@ -548,8 +575,11 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
548
575
|
sessionId: this.chatInternalService.sessionModel.sessionId,
|
|
549
576
|
isReceive: false,
|
|
550
577
|
isDrop: true,
|
|
551
|
-
code: codeBlock.codeEdit,
|
|
552
578
|
originCode: codeBlock.originalCode,
|
|
579
|
+
message: JSON.stringify({
|
|
580
|
+
instructions: codeBlock.instructions,
|
|
581
|
+
codeEdit: codeBlock.codeEdit,
|
|
582
|
+
}),
|
|
553
583
|
});
|
|
554
584
|
}
|
|
555
585
|
this.editorListenerMap.disposeKey(uriString);
|
|
@@ -565,8 +595,8 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
565
595
|
.split('\n')
|
|
566
596
|
.map((line) => {
|
|
567
597
|
if (line.startsWith('@@')) {
|
|
568
|
-
const [, , , start,
|
|
569
|
-
return new Range(parseInt(start, 10), 0, parseInt(
|
|
598
|
+
const [, , , start, lineCount] = line.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)!;
|
|
599
|
+
return new Range(parseInt(start, 10), 0, parseInt(start, 10) + parseInt(lineCount, 10) - 1, 0);
|
|
570
600
|
}
|
|
571
601
|
return null;
|
|
572
602
|
})
|
|
@@ -587,7 +617,6 @@ export abstract class BaseApplyService extends WithEventBus {
|
|
|
587
617
|
result?: string;
|
|
588
618
|
}>;
|
|
589
619
|
|
|
590
|
-
// TODO: 支持使用内存中的document获取诊断信息,实现并行apply accept
|
|
591
620
|
protected getDiagnosticInfos(uri: string, ranges: Range[]) {
|
|
592
621
|
const markers = this.markerService.getManager().getMarkers({ resource: uri });
|
|
593
622
|
return markers.filter(
|
|
@@ -4,8 +4,8 @@ import React, { useCallback } from 'react';
|
|
|
4
4
|
import { Badge } from '@opensumi/ide-components';
|
|
5
5
|
import { AINativeSettingSectionsId, ILogger, useInjectable } from '@opensumi/ide-core-browser';
|
|
6
6
|
import { PreferenceService } from '@opensumi/ide-core-browser/lib/preferences';
|
|
7
|
-
import {
|
|
8
|
-
import { IMessageService } from '@opensumi/ide-overlay';
|
|
7
|
+
import { localize } from '@opensumi/ide-core-common';
|
|
8
|
+
import { IMessageService } from '@opensumi/ide-overlay/lib/common';
|
|
9
9
|
|
|
10
10
|
import { BUILTIN_MCP_SERVER_NAME, ISumiMCPServerBackend, SumiMCPServerProxyServicePath } from '../../../../common';
|
|
11
11
|
import { MCPServerDescription } from '../../../../common/mcp-server-manager';
|
|
@@ -18,13 +18,13 @@ import { MCPServerForm, MCPServerFormData } from './mcp-server-form';
|
|
|
18
18
|
export const MCPConfigView: React.FC = () => {
|
|
19
19
|
const mcpServerProxyService = useInjectable<MCPServerProxyService>(MCPServerProxyService);
|
|
20
20
|
const preferenceService = useInjectable<PreferenceService>(PreferenceService);
|
|
21
|
-
const messageService = useInjectable<IMessageService>(IMessageService);
|
|
22
21
|
const sumiMCPServerBackendProxy = useInjectable<ISumiMCPServerBackend>(SumiMCPServerProxyServicePath);
|
|
23
22
|
const logger = useInjectable<ILogger>(ILogger);
|
|
23
|
+
const messageService = useInjectable<IMessageService>(IMessageService);
|
|
24
24
|
const [servers, setServers] = React.useState<MCPServer[]>([]);
|
|
25
25
|
const [formVisible, setFormVisible] = React.useState(false);
|
|
26
26
|
const [editingServer, setEditingServer] = React.useState<MCPServerFormData | undefined>();
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
const loadServers = useCallback(async () => {
|
|
29
29
|
const allServers = await mcpServerProxyService.$getServers();
|
|
30
30
|
setServers(allServers);
|
|
@@ -44,7 +44,6 @@ export const MCPConfigView: React.FC = () => {
|
|
|
44
44
|
const handleServerControl = useCallback(
|
|
45
45
|
async (serverName: string, start: boolean) => {
|
|
46
46
|
try {
|
|
47
|
-
setLoadingServer(serverName);
|
|
48
47
|
if (start) {
|
|
49
48
|
await mcpServerProxyService.$startServer(serverName);
|
|
50
49
|
} else {
|
|
@@ -88,14 +87,12 @@ export const MCPConfigView: React.FC = () => {
|
|
|
88
87
|
});
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers
|
|
90
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
|
|
92
91
|
await loadServers();
|
|
93
|
-
setLoadingServer(undefined);
|
|
94
92
|
} catch (error) {
|
|
95
93
|
const msg = error.message || error;
|
|
96
94
|
logger.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:`, error);
|
|
97
|
-
messageService.error(
|
|
98
|
-
setLoadingServer(undefined);
|
|
95
|
+
messageService.error(`Failed to ${start ? 'start' : 'stop'} server ${serverName}:` + msg);
|
|
99
96
|
}
|
|
100
97
|
},
|
|
101
98
|
[mcpServerProxyService, preferenceService, sumiMCPServerBackendProxy, loadServers],
|
|
@@ -124,7 +121,7 @@ export const MCPConfigView: React.FC = () => {
|
|
|
124
121
|
const servers = preferenceService.get<MCPServerFormData[]>(AINativeSettingSectionsId.MCPServers, []);
|
|
125
122
|
const updatedServers = servers.filter((s) => s.name !== serverName);
|
|
126
123
|
sumiMCPServerBackendProxy.removeServer(serverName);
|
|
127
|
-
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers
|
|
124
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, updatedServers);
|
|
128
125
|
await loadServers();
|
|
129
126
|
},
|
|
130
127
|
[editingServer, formVisible],
|
|
@@ -143,7 +140,7 @@ export const MCPConfigView: React.FC = () => {
|
|
|
143
140
|
setServers(servers as MCPServer[]);
|
|
144
141
|
setFormVisible(false);
|
|
145
142
|
await sumiMCPServerBackendProxy.addOrUpdateServer(data as MCPServerDescription);
|
|
146
|
-
await preferenceService.set(AINativeSettingSectionsId.MCPServers, servers
|
|
143
|
+
await preferenceService.set(AINativeSettingSectionsId.MCPServers, servers);
|
|
147
144
|
await loadServers();
|
|
148
145
|
},
|
|
149
146
|
[servers, formVisible, loadServers],
|
|
@@ -167,10 +164,10 @@ export const MCPConfigView: React.FC = () => {
|
|
|
167
164
|
<div className={styles.header}>
|
|
168
165
|
<div>
|
|
169
166
|
<h2 className={styles.title}>MCP Servers</h2>
|
|
170
|
-
<p className={styles.description}>
|
|
167
|
+
<p className={styles.description}>Manage your MCP server connections.</p>
|
|
171
168
|
</div>
|
|
172
169
|
<button className={styles.addButton} onClick={handleAddServer}>
|
|
173
|
-
+
|
|
170
|
+
+ Add new MCP server
|
|
174
171
|
</button>
|
|
175
172
|
</div>
|
|
176
173
|
<div className={styles.serversList}>
|
|
@@ -191,15 +188,7 @@ export const MCPConfigView: React.FC = () => {
|
|
|
191
188
|
title={server.isStarted ? 'Stop' : 'Start'}
|
|
192
189
|
onClick={() => handleServerControl(server.name, !server.isStarted)}
|
|
193
190
|
>
|
|
194
|
-
<i
|
|
195
|
-
className={`codicon ${
|
|
196
|
-
loadingServer === server.name
|
|
197
|
-
? 'codicon-loading kt-icon-loading'
|
|
198
|
-
: server.isStarted
|
|
199
|
-
? 'codicon-debug-stop'
|
|
200
|
-
: 'codicon-debug-start'
|
|
201
|
-
}`}
|
|
202
|
-
/>
|
|
191
|
+
<i className={`codicon ${server.isStarted ? 'codicon-debug-stop' : 'codicon-debug-start'}`} />
|
|
203
192
|
</button>
|
|
204
193
|
{server.name !== BUILTIN_MCP_SERVER_NAME && (
|
|
205
194
|
<button className={styles.iconButton} title='Delete' onClick={() => handleDeleteServer(server.name)}>
|
|
@@ -212,7 +201,7 @@ export const MCPConfigView: React.FC = () => {
|
|
|
212
201
|
<div className={styles.detailRow}>
|
|
213
202
|
<span className={styles.detailLabel}>Status:</span>
|
|
214
203
|
<span className={`${styles.serverStatus} ${server.isStarted ? styles.running : styles.stopped}`}>
|
|
215
|
-
{server.isStarted ?
|
|
204
|
+
{server.isStarted ? 'Running' : 'Stopped'}
|
|
216
205
|
</span>
|
|
217
206
|
</div>
|
|
218
207
|
{server.type && (
|
|
@@ -67,75 +67,35 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
|
|
|
67
67
|
);
|
|
68
68
|
}, [initialData]);
|
|
69
69
|
|
|
70
|
-
const
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
[existingServers, initialData],
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const handleSubmit = useCallback(
|
|
100
|
-
(e: FormEvent) => {
|
|
101
|
-
e.preventDefault();
|
|
102
|
-
const isValid = validateForm(formData);
|
|
103
|
-
if (!isValid) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const form = {
|
|
107
|
-
...formData,
|
|
108
|
-
};
|
|
109
|
-
if (formData.type === MCP_SERVER_TYPE.SSE) {
|
|
110
|
-
form.serverHost = form.serverHost?.trim();
|
|
111
|
-
} else {
|
|
112
|
-
const args = argsText.split(' ').filter(Boolean);
|
|
113
|
-
const env = envText
|
|
114
|
-
.split('\n')
|
|
115
|
-
.filter(Boolean)
|
|
116
|
-
.reduce((acc, line) => {
|
|
117
|
-
const [key, value] = line.split('=');
|
|
118
|
-
if (key && value) {
|
|
119
|
-
acc[key.trim()] = value.trim();
|
|
120
|
-
}
|
|
121
|
-
return acc;
|
|
122
|
-
}, {} as Record<string, string>);
|
|
123
|
-
form.args = args;
|
|
124
|
-
form.env = env;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
setFormData({
|
|
128
|
-
...formData,
|
|
129
|
-
command: '',
|
|
130
|
-
serverHost: '',
|
|
131
|
-
args: [],
|
|
132
|
-
env: {},
|
|
133
|
-
});
|
|
70
|
+
const handleSubmit = (e: FormEvent) => {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
const isValid = validateForm(formData);
|
|
73
|
+
if (!isValid) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const form = {
|
|
77
|
+
...formData,
|
|
78
|
+
};
|
|
79
|
+
if (formData.type === MCP_SERVER_TYPE.SSE) {
|
|
80
|
+
form.serverHost = form.serverHost?.trim();
|
|
81
|
+
} else {
|
|
82
|
+
const args = argsText.split(' ').filter(Boolean);
|
|
83
|
+
const env = envText
|
|
84
|
+
.split('\n')
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.reduce((acc, line) => {
|
|
87
|
+
const [key, value] = line.split('=');
|
|
88
|
+
if (key && value) {
|
|
89
|
+
acc[key.trim()] = value.trim();
|
|
90
|
+
}
|
|
91
|
+
return acc;
|
|
92
|
+
}, {} as Record<string, string>);
|
|
93
|
+
form.args = args;
|
|
94
|
+
form.env = env;
|
|
95
|
+
}
|
|
134
96
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
[formData, argsText, envText, onSave, validateForm],
|
|
138
|
-
);
|
|
97
|
+
onSave(form);
|
|
98
|
+
};
|
|
139
99
|
|
|
140
100
|
const handleCommandChange = useCallback(
|
|
141
101
|
(e: ChangeEvent<HTMLInputElement>) => {
|
|
@@ -223,6 +183,32 @@ export const MCPServerForm: FC<Props> = ({ visible, initialData, onSave, onCance
|
|
|
223
183
|
}
|
|
224
184
|
}, [formData, argsText, envText]);
|
|
225
185
|
|
|
186
|
+
const validateForm = useCallback(
|
|
187
|
+
(formData: MCPServerFormData) => {
|
|
188
|
+
if (formData.name.trim() === '') {
|
|
189
|
+
messageService.error(localize('ai.native.mcp.name.isRequired'));
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (existingServers.some((server) => server.name.toLocaleLowerCase() === formData.name.toLocaleLowerCase())) {
|
|
193
|
+
messageService.error(formatLocalize('ai.native.mcp.serverNameExists', formData.name));
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (formData.type === MCP_SERVER_TYPE.SSE) {
|
|
197
|
+
const isServerHostValid = formData.serverHost?.trim() !== '';
|
|
198
|
+
if (!isServerHostValid) {
|
|
199
|
+
messageService.error(localize('ai.native.mcp.serverHost.isRequired'));
|
|
200
|
+
}
|
|
201
|
+
return isServerHostValid;
|
|
202
|
+
}
|
|
203
|
+
const isCommandValid = formData.command?.trim() !== '';
|
|
204
|
+
if (!isCommandValid) {
|
|
205
|
+
messageService.error(localize('ai.native.mcp.command.isRequired'));
|
|
206
|
+
}
|
|
207
|
+
return isCommandValid;
|
|
208
|
+
},
|
|
209
|
+
[existingServers],
|
|
210
|
+
);
|
|
211
|
+
|
|
226
212
|
return (
|
|
227
213
|
<Modal
|
|
228
214
|
title={initialData ? localize('ai.native.mcp.editMCPServer.title') : localize('ai.native.mcp.addMCPServer.title')}
|