@lobehub/lobehub 2.0.0-next.73 → 2.0.0-next.75
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/.github/workflows/desktop-pr-build.yml +7 -3
- package/CHANGELOG.md +50 -0
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/controllers/LocalFileCtr.ts +55 -11
- package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +153 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +5 -0
- package/locales/ar/models.json +15 -0
- package/locales/ar/tool.json +12 -1
- package/locales/bg-BG/chat.json +5 -0
- package/locales/bg-BG/models.json +15 -0
- package/locales/bg-BG/tool.json +12 -1
- package/locales/de-DE/chat.json +5 -0
- package/locales/de-DE/models.json +15 -0
- package/locales/de-DE/tool.json +12 -1
- package/locales/en-US/models.json +15 -0
- package/locales/en-US/tool.json +12 -1
- package/locales/es-ES/chat.json +5 -0
- package/locales/es-ES/models.json +15 -0
- package/locales/es-ES/tool.json +12 -1
- package/locales/fa-IR/chat.json +5 -0
- package/locales/fa-IR/models.json +15 -0
- package/locales/fa-IR/tool.json +12 -1
- package/locales/fr-FR/chat.json +5 -0
- package/locales/fr-FR/models.json +15 -0
- package/locales/fr-FR/tool.json +12 -1
- package/locales/it-IT/chat.json +5 -0
- package/locales/it-IT/models.json +15 -0
- package/locales/it-IT/tool.json +12 -1
- package/locales/ja-JP/chat.json +5 -0
- package/locales/ja-JP/models.json +15 -0
- package/locales/ja-JP/tool.json +12 -1
- package/locales/ko-KR/chat.json +5 -0
- package/locales/ko-KR/models.json +15 -0
- package/locales/ko-KR/tool.json +12 -1
- package/locales/nl-NL/chat.json +5 -0
- package/locales/nl-NL/models.json +15 -0
- package/locales/nl-NL/tool.json +12 -1
- package/locales/pl-PL/chat.json +5 -0
- package/locales/pl-PL/models.json +15 -0
- package/locales/pl-PL/tool.json +12 -1
- package/locales/pt-BR/chat.json +5 -0
- package/locales/pt-BR/models.json +15 -0
- package/locales/pt-BR/tool.json +12 -1
- package/locales/ru-RU/chat.json +5 -0
- package/locales/ru-RU/models.json +15 -0
- package/locales/ru-RU/tool.json +12 -1
- package/locales/tr-TR/chat.json +5 -0
- package/locales/tr-TR/models.json +15 -0
- package/locales/tr-TR/tool.json +12 -1
- package/locales/vi-VN/chat.json +5 -0
- package/locales/vi-VN/models.json +15 -0
- package/locales/vi-VN/tool.json +12 -1
- package/locales/zh-CN/models.json +15 -0
- package/locales/zh-CN/tool.json +12 -1
- package/locales/zh-TW/chat.json +5 -0
- package/locales/zh-TW/models.json +15 -0
- package/locales/zh-TW/tool.json +12 -1
- package/package.json +2 -1
- package/packages/electron-client-ipc/src/types/localSystem.ts +4 -0
- package/scripts/prebuild.mts +15 -5
- package/src/app/[variants]/desktopRouter.config.tsx +0 -17
- package/src/app/[variants]/mobileRouter.config.tsx +0 -16
- package/src/app/[variants]/page.tsx +5 -4
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +23 -4
- package/src/locales/default/tool.ts +11 -0
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +5 -6
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +45 -182
- package/src/tools/executionRuntimes.ts +3 -0
- package/src/tools/local-system/ExecutionRuntime/index.ts +407 -0
- package/src/tools/local-system/Intervention/EditLocalFile/index.tsx +89 -0
- package/src/tools/local-system/Intervention/WriteFile/index.tsx +72 -0
- package/src/tools/local-system/Intervention/index.ts +4 -0
- package/src/tools/local-system/Render/EditLocalFile/index.tsx +67 -0
- package/src/tools/local-system/Render/ReadLocalFile/ReadFileView.tsx +53 -78
- package/src/tools/local-system/Render/index.ts +2 -0
- package/src/tools/local-system/index.ts +1 -0
- package/src/tools/local-system/type.ts +4 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
name: Desktop PR Build
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
4
|
+
pull_request:
|
|
5
5
|
types: [synchronize, labeled, unlabeled] # PR 更新或标签变化时触发
|
|
6
6
|
|
|
7
7
|
# 确保同一 PR 同一时间只运行一个相同的 workflow,取消正在进行的旧的运行
|
|
@@ -126,6 +126,7 @@ jobs:
|
|
|
126
126
|
run: npm run workflow:set-desktop-version ${{ needs.version.outputs.version }} nightly
|
|
127
127
|
|
|
128
128
|
# macOS 构建处理
|
|
129
|
+
# 注意:fork 的 PR 无法访问 secrets,会构建未签名版本
|
|
129
130
|
- name: Build artifact on macOS
|
|
130
131
|
if: runner.os == 'macOS'
|
|
131
132
|
run: npm run desktop:build
|
|
@@ -136,7 +137,7 @@ jobs:
|
|
|
136
137
|
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
|
|
137
138
|
# 默认添加一个加密 SECRET
|
|
138
139
|
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
|
|
139
|
-
# macOS
|
|
140
|
+
# macOS 签名和公证配置(fork 的 PR 访问不到 secrets,会跳过签名)
|
|
140
141
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
|
141
142
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
142
143
|
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }}
|
|
@@ -148,7 +149,8 @@ jobs:
|
|
|
148
149
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
|
149
150
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
150
151
|
|
|
151
|
-
#
|
|
152
|
+
# Windows 平台构建处理
|
|
153
|
+
# 注意:fork 的 PR 无法访问 secrets,会构建未签名版本
|
|
152
154
|
- name: Build artifact on Windows
|
|
153
155
|
if: runner.os == 'Windows'
|
|
154
156
|
run: npm run desktop:build
|
|
@@ -275,6 +277,8 @@ jobs:
|
|
|
275
277
|
publish-pr:
|
|
276
278
|
needs: [merge-mac-files, version]
|
|
277
279
|
name: Publish PR Build
|
|
280
|
+
# 只为非 fork 的 PR 发布(fork 的 PR 没有写权限)
|
|
281
|
+
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
278
282
|
runs-on: ubuntu-latest
|
|
279
283
|
# Grant write permissions for creating release and commenting on PR
|
|
280
284
|
permissions:
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.75](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.74...v2.0.0-next.75)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-18**</sup>
|
|
8
|
+
|
|
9
|
+
#### 💄 Styles
|
|
10
|
+
|
|
11
|
+
- **misc**: Update i18n.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Styles
|
|
19
|
+
|
|
20
|
+
- **misc**: Update i18n, closes [#10277](https://github.com/lobehub/lobe-chat/issues/10277) ([7563b62](https://github.com/lobehub/lobe-chat/commit/7563b62))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.74](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.73...v2.0.0-next.74)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-11-17**</sup>
|
|
33
|
+
|
|
34
|
+
#### ✨ Features
|
|
35
|
+
|
|
36
|
+
- **misc**: Edit local file render & intervention.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's improved
|
|
44
|
+
|
|
45
|
+
- **misc**: Edit local file render & intervention, closes [#10269](https://github.com/lobehub/lobe-chat/issues/10269) ([3785a71](https://github.com/lobehub/lobe-chat/commit/3785a71))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.73](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.72...v2.0.0-next.73)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2025-11-17**</sup>
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
WriteLocalFileParams,
|
|
19
19
|
} from '@lobechat/electron-client-ipc';
|
|
20
20
|
import { SYSTEM_FILES_TO_IGNORE, loadFile } from '@lobechat/file-loaders';
|
|
21
|
+
import { createPatch } from 'diff';
|
|
21
22
|
import { shell } from 'electron';
|
|
22
23
|
import fg from 'fast-glob';
|
|
23
24
|
import { Stats, constants } from 'node:fs';
|
|
@@ -94,26 +95,45 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
@ipcClientEvent('readLocalFile')
|
|
97
|
-
async readFile({
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
async readFile({
|
|
99
|
+
path: filePath,
|
|
100
|
+
loc,
|
|
101
|
+
fullContent,
|
|
102
|
+
}: LocalReadFileParams): Promise<LocalReadFileResult> {
|
|
103
|
+
const effectiveLoc = fullContent ? undefined : (loc ?? [0, 200]);
|
|
104
|
+
logger.debug('Starting to read file:', { filePath, fullContent, loc: effectiveLoc });
|
|
100
105
|
|
|
101
106
|
try {
|
|
102
107
|
const fileDocument = await loadFile(filePath);
|
|
103
108
|
|
|
104
|
-
const [startLine, endLine] = effectiveLoc;
|
|
105
109
|
const lines = fileDocument.content.split('\n');
|
|
106
110
|
const totalLineCount = lines.length;
|
|
107
111
|
const totalCharCount = fileDocument.content.length;
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
let content: string;
|
|
114
|
+
let charCount: number;
|
|
115
|
+
let lineCount: number;
|
|
116
|
+
let actualLoc: [number, number];
|
|
117
|
+
|
|
118
|
+
if (effectiveLoc === undefined) {
|
|
119
|
+
// Return full content
|
|
120
|
+
content = fileDocument.content;
|
|
121
|
+
charCount = totalCharCount;
|
|
122
|
+
lineCount = totalLineCount;
|
|
123
|
+
actualLoc = [0, totalLineCount];
|
|
124
|
+
} else {
|
|
125
|
+
// Return specified range
|
|
126
|
+
const [startLine, endLine] = effectiveLoc;
|
|
127
|
+
const selectedLines = lines.slice(startLine, endLine);
|
|
128
|
+
content = selectedLines.join('\n');
|
|
129
|
+
charCount = content.length;
|
|
130
|
+
lineCount = selectedLines.length;
|
|
131
|
+
actualLoc = effectiveLoc;
|
|
132
|
+
}
|
|
114
133
|
|
|
115
134
|
logger.debug('File read successfully:', {
|
|
116
135
|
filePath,
|
|
136
|
+
fullContent,
|
|
117
137
|
selectedLineCount: lineCount,
|
|
118
138
|
totalCharCount,
|
|
119
139
|
totalLineCount,
|
|
@@ -128,7 +148,7 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
128
148
|
fileType: fileDocument.fileType,
|
|
129
149
|
filename: fileDocument.filename,
|
|
130
150
|
lineCount,
|
|
131
|
-
loc:
|
|
151
|
+
loc: actualLoc,
|
|
132
152
|
// Line count for the selected range
|
|
133
153
|
modifiedTime: fileDocument.modifiedTime,
|
|
134
154
|
|
|
@@ -711,8 +731,32 @@ export default class LocalFileCtr extends ControllerModule {
|
|
|
711
731
|
// Write back to file
|
|
712
732
|
await writeFile(filePath, newContent, 'utf8');
|
|
713
733
|
|
|
714
|
-
|
|
734
|
+
// Generate diff for UI display
|
|
735
|
+
const patch = createPatch(filePath, content, newContent, '', '');
|
|
736
|
+
const diffText = `diff --git a${filePath} b${filePath}\n${patch}`;
|
|
737
|
+
|
|
738
|
+
// Calculate lines added and deleted from patch
|
|
739
|
+
const patchLines = patch.split('\n');
|
|
740
|
+
let linesAdded = 0;
|
|
741
|
+
let linesDeleted = 0;
|
|
742
|
+
|
|
743
|
+
for (const line of patchLines) {
|
|
744
|
+
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
745
|
+
linesAdded++;
|
|
746
|
+
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
747
|
+
linesDeleted++;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
logger.info(`${logPrefix} File edited successfully`, {
|
|
752
|
+
linesAdded,
|
|
753
|
+
linesDeleted,
|
|
754
|
+
replacements,
|
|
755
|
+
});
|
|
715
756
|
return {
|
|
757
|
+
diffText,
|
|
758
|
+
linesAdded,
|
|
759
|
+
linesDeleted,
|
|
716
760
|
replacements,
|
|
717
761
|
success: true,
|
|
718
762
|
};
|
|
@@ -183,6 +183,26 @@ describe('LocalFileCtr', () => {
|
|
|
183
183
|
expect(result.totalLineCount).toBe(5);
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
+
it('should read full file content when fullContent is true', async () => {
|
|
187
|
+
const mockFileContent = 'line1\nline2\nline3\nline4\nline5';
|
|
188
|
+
vi.mocked(mockLoadFile).mockResolvedValue({
|
|
189
|
+
content: mockFileContent,
|
|
190
|
+
filename: 'test.txt',
|
|
191
|
+
fileType: 'txt',
|
|
192
|
+
createdTime: new Date('2024-01-01'),
|
|
193
|
+
modifiedTime: new Date('2024-01-02'),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const result = await localFileCtr.readFile({ path: '/test/file.txt', fullContent: true });
|
|
197
|
+
|
|
198
|
+
expect(result.content).toBe(mockFileContent);
|
|
199
|
+
expect(result.lineCount).toBe(5);
|
|
200
|
+
expect(result.charCount).toBe(mockFileContent.length);
|
|
201
|
+
expect(result.totalLineCount).toBe(5);
|
|
202
|
+
expect(result.totalCharCount).toBe(mockFileContent.length);
|
|
203
|
+
expect(result.loc).toEqual([0, 5]);
|
|
204
|
+
});
|
|
205
|
+
|
|
186
206
|
it('should handle file read error', async () => {
|
|
187
207
|
vi.mocked(mockLoadFile).mockRejectedValue(new Error('File not found'));
|
|
188
208
|
|
|
@@ -392,4 +412,137 @@ describe('LocalFileCtr', () => {
|
|
|
392
412
|
});
|
|
393
413
|
});
|
|
394
414
|
});
|
|
415
|
+
|
|
416
|
+
describe('handleEditFile', () => {
|
|
417
|
+
it('should replace first occurrence successfully', async () => {
|
|
418
|
+
const originalContent = 'Hello world\nHello again\nGoodbye world';
|
|
419
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
420
|
+
vi.mocked(mockFsPromises.writeFile).mockResolvedValue(undefined);
|
|
421
|
+
|
|
422
|
+
const result = await localFileCtr.handleEditFile({
|
|
423
|
+
file_path: '/test/file.txt',
|
|
424
|
+
old_string: 'Hello',
|
|
425
|
+
new_string: 'Hi',
|
|
426
|
+
replace_all: false,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(result.success).toBe(true);
|
|
430
|
+
expect(result.replacements).toBe(1);
|
|
431
|
+
expect(result.linesAdded).toBe(1);
|
|
432
|
+
expect(result.linesDeleted).toBe(1);
|
|
433
|
+
expect(result.diffText).toContain('diff --git a/test/file.txt b/test/file.txt');
|
|
434
|
+
expect(mockFsPromises.writeFile).toHaveBeenCalledWith(
|
|
435
|
+
'/test/file.txt',
|
|
436
|
+
'Hi world\nHello again\nGoodbye world',
|
|
437
|
+
'utf8',
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('should replace all occurrences when replace_all is true', async () => {
|
|
442
|
+
const originalContent = 'Hello world\nHello again\nHello there';
|
|
443
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
444
|
+
vi.mocked(mockFsPromises.writeFile).mockResolvedValue(undefined);
|
|
445
|
+
|
|
446
|
+
const result = await localFileCtr.handleEditFile({
|
|
447
|
+
file_path: '/test/file.txt',
|
|
448
|
+
old_string: 'Hello',
|
|
449
|
+
new_string: 'Hi',
|
|
450
|
+
replace_all: true,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(result.success).toBe(true);
|
|
454
|
+
expect(result.replacements).toBe(3);
|
|
455
|
+
expect(result.linesAdded).toBe(3);
|
|
456
|
+
expect(result.linesDeleted).toBe(3);
|
|
457
|
+
expect(mockFsPromises.writeFile).toHaveBeenCalledWith(
|
|
458
|
+
'/test/file.txt',
|
|
459
|
+
'Hi world\nHi again\nHi there',
|
|
460
|
+
'utf8',
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('should handle multiline replacement correctly', async () => {
|
|
465
|
+
const originalContent = 'function test() {\n console.log("old");\n}';
|
|
466
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
467
|
+
vi.mocked(mockFsPromises.writeFile).mockResolvedValue(undefined);
|
|
468
|
+
|
|
469
|
+
const result = await localFileCtr.handleEditFile({
|
|
470
|
+
file_path: '/test/file.js',
|
|
471
|
+
old_string: 'console.log("old");',
|
|
472
|
+
new_string: 'console.log("new");\n console.log("added");',
|
|
473
|
+
replace_all: false,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
expect(result.success).toBe(true);
|
|
477
|
+
expect(result.replacements).toBe(1);
|
|
478
|
+
expect(result.linesAdded).toBe(2);
|
|
479
|
+
expect(result.linesDeleted).toBe(1);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should return error when old_string is not found', async () => {
|
|
483
|
+
const originalContent = 'Hello world';
|
|
484
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
485
|
+
|
|
486
|
+
const result = await localFileCtr.handleEditFile({
|
|
487
|
+
file_path: '/test/file.txt',
|
|
488
|
+
old_string: 'NonExistent',
|
|
489
|
+
new_string: 'New',
|
|
490
|
+
replace_all: false,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
expect(result.success).toBe(false);
|
|
494
|
+
expect(result.error).toBe('The specified old_string was not found in the file');
|
|
495
|
+
expect(result.replacements).toBe(0);
|
|
496
|
+
expect(mockFsPromises.writeFile).not.toHaveBeenCalled();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should handle file read error', async () => {
|
|
500
|
+
vi.mocked(mockFsPromises.readFile).mockRejectedValue(new Error('Permission denied'));
|
|
501
|
+
|
|
502
|
+
const result = await localFileCtr.handleEditFile({
|
|
503
|
+
file_path: '/test/file.txt',
|
|
504
|
+
old_string: 'Hello',
|
|
505
|
+
new_string: 'Hi',
|
|
506
|
+
replace_all: false,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
expect(result.success).toBe(false);
|
|
510
|
+
expect(result.error).toBe('Permission denied');
|
|
511
|
+
expect(result.replacements).toBe(0);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('should handle file write error', async () => {
|
|
515
|
+
const originalContent = 'Hello world';
|
|
516
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
517
|
+
vi.mocked(mockFsPromises.writeFile).mockRejectedValue(new Error('Disk full'));
|
|
518
|
+
|
|
519
|
+
const result = await localFileCtr.handleEditFile({
|
|
520
|
+
file_path: '/test/file.txt',
|
|
521
|
+
old_string: 'Hello',
|
|
522
|
+
new_string: 'Hi',
|
|
523
|
+
replace_all: false,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
expect(result.success).toBe(false);
|
|
527
|
+
expect(result.error).toBe('Disk full');
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('should generate correct diff format', async () => {
|
|
531
|
+
const originalContent = 'line 1\nline 2\nline 3';
|
|
532
|
+
vi.mocked(mockFsPromises.readFile).mockResolvedValue(originalContent);
|
|
533
|
+
vi.mocked(mockFsPromises.writeFile).mockResolvedValue(undefined);
|
|
534
|
+
|
|
535
|
+
const result = await localFileCtr.handleEditFile({
|
|
536
|
+
file_path: '/test/file.txt',
|
|
537
|
+
old_string: 'line 2',
|
|
538
|
+
new_string: 'modified line 2',
|
|
539
|
+
replace_all: false,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
expect(result.success).toBe(true);
|
|
543
|
+
expect(result.diffText).toContain('diff --git a/test/file.txt b/test/file.txt');
|
|
544
|
+
expect(result.diffText).toContain('-line 2');
|
|
545
|
+
expect(result.diffText).toContain('+modified line 2');
|
|
546
|
+
});
|
|
547
|
+
});
|
|
395
548
|
});
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"improvements": [
|
|
5
|
+
"Update i18n."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2025-11-18",
|
|
9
|
+
"version": "2.0.0-next.75"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"features": [
|
|
14
|
+
"Edit local file render & intervention."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2025-11-17",
|
|
18
|
+
"version": "2.0.0-next.74"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"features": [
|
package/locales/ar/chat.json
CHANGED
|
@@ -330,6 +330,11 @@
|
|
|
330
330
|
"screenshot": "لقطة شاشة",
|
|
331
331
|
"settings": "إعدادات التصدير",
|
|
332
332
|
"text": "نص",
|
|
333
|
+
"widthMode": {
|
|
334
|
+
"label": "وضع العرض",
|
|
335
|
+
"narrow": "وضع الشاشة الضيقة",
|
|
336
|
+
"wide": "وضع الشاشة الواسعة"
|
|
337
|
+
},
|
|
333
338
|
"withBackground": "تضمين صورة الخلفية",
|
|
334
339
|
"withFooter": "تضمين تذييل",
|
|
335
340
|
"withPluginInfo": "تضمين معلومات البرنامج المساعد",
|
package/locales/ar/models.json
CHANGED
|
@@ -2225,6 +2225,9 @@
|
|
|
2225
2225
|
"megrez-3b-instruct": {
|
|
2226
2226
|
"description": "Megrez 3B Instruct هو نموذج صغير الحجم وعالي الكفاءة أطلقته شركة Wuwen Xinqiong."
|
|
2227
2227
|
},
|
|
2228
|
+
"meituan/longcat-flash-chat": {
|
|
2229
|
+
"description": "نموذج أساسي غير تأملي مفتوح المصدر من Meituan، مُحسَّن للتفاعل الحواري ومهام الوكلاء الذكيين، ويتميز في استدعاء الأدوات وسيناريوهات التفاعل المعقدة متعددة الجولات."
|
|
2230
|
+
},
|
|
2228
2231
|
"meta-llama-3-70b-instruct": {
|
|
2229
2232
|
"description": "نموذج قوي بحجم 70 مليار معلمة يتفوق في التفكير، والترميز، وتطبيقات اللغة الواسعة."
|
|
2230
2233
|
},
|
|
@@ -2456,6 +2459,9 @@
|
|
|
2456
2459
|
"minimax-m2": {
|
|
2457
2460
|
"description": "MiniMax M2 هو نموذج لغوي كبير وفعّال، تم تطويره خصيصًا لتلبية احتياجات الترميز وتدفقات عمل الوكلاء."
|
|
2458
2461
|
},
|
|
2462
|
+
"minimax/minimax-m2": {
|
|
2463
|
+
"description": "مصمم خصيصًا للترميز الفعّال وتدفقات عمل الوكلاء."
|
|
2464
|
+
},
|
|
2459
2465
|
"ministral-3b-latest": {
|
|
2460
2466
|
"description": "Ministral 3B هو نموذج حافة عالمي المستوى من Mistral."
|
|
2461
2467
|
},
|
|
@@ -3371,6 +3377,12 @@
|
|
|
3371
3377
|
"wizardlm2:8x22b": {
|
|
3372
3378
|
"description": "WizardLM 2 هو نموذج لغوي تقدمه Microsoft AI، يتميز بأداء ممتاز في الحوار المعقد، واللغات المتعددة، والاستدلال، والمساعدين الذكيين."
|
|
3373
3379
|
},
|
|
3380
|
+
"x-ai/grok-4-fast": {
|
|
3381
|
+
"description": "يسعدنا أن نعلن عن إصدار Grok 4 Fast، وهو أحدث تقدم لنا في نماذج الاستدلال الفعّالة من حيث التكلفة."
|
|
3382
|
+
},
|
|
3383
|
+
"x-ai/grok-code-fast-1": {
|
|
3384
|
+
"description": "يسعدنا إطلاق grok-code-fast-1، وهو نموذج استدلال سريع وفعّال من حيث التكلفة يتميز في ترميز الوكلاء."
|
|
3385
|
+
},
|
|
3374
3386
|
"x1": {
|
|
3375
3387
|
"description": "سيتم ترقية نموذج Spark X1 بشكل أكبر، حيث ستحقق المهام العامة مثل الاستدلال، وتوليد النصوص، وفهم اللغة نتائج تتماشى مع OpenAI o1 و DeepSeek R1."
|
|
3376
3388
|
},
|
|
@@ -3431,6 +3443,9 @@
|
|
|
3431
3443
|
"yi-vision-v2": {
|
|
3432
3444
|
"description": "نموذج مهام بصرية معقدة، يوفر فهمًا عالي الأداء وقدرات تحليلية بناءً على صور متعددة."
|
|
3433
3445
|
},
|
|
3446
|
+
"z-ai/glm-4.6": {
|
|
3447
|
+
"description": "GLM-4.6 هو النموذج الرائد الأحدث من Zhipu، ويتفوق على الجيل السابق في الترميز المتقدم، ومعالجة النصوص الطويلة، والاستدلال، وقدرات الوكلاء الذكيين."
|
|
3448
|
+
},
|
|
3434
3449
|
"zai-org/GLM-4.5": {
|
|
3435
3450
|
"description": "GLM-4.5 هو نموذج أساسي مصمم لتطبيقات الوكلاء الذكية، يستخدم بنية Mixture-of-Experts (MoE). تم تحسينه بعمق في مجالات استدعاء الأدوات، تصفح الويب، هندسة البرمجيات، وبرمجة الواجهة الأمامية، ويدعم التكامل السلس مع وكلاء الكود مثل Claude Code وRoo Code. يستخدم وضع استدلال مختلط ليتكيف مع سيناريوهات الاستدلال المعقدة والاستخدام اليومي."
|
|
3436
3451
|
},
|
package/locales/ar/tool.json
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"prompt": "كلمة تلميح"
|
|
16
16
|
},
|
|
17
17
|
"localFiles": {
|
|
18
|
+
"editFile": {
|
|
19
|
+
"newString": "استبدال بـ",
|
|
20
|
+
"oldString": "البحث عن",
|
|
21
|
+
"replaceAll": "استبدال جميع المطابقات",
|
|
22
|
+
"replaceFirst": "استبدال أول مطابقة فقط"
|
|
23
|
+
},
|
|
18
24
|
"file": "ملف",
|
|
19
25
|
"folder": "مجلد",
|
|
20
26
|
"moveFiles": {
|
|
@@ -34,7 +40,12 @@
|
|
|
34
40
|
"readFile": "قراءة الملف",
|
|
35
41
|
"readFileError": "فشل في قراءة الملف، يرجى التحقق من صحة مسار الملف",
|
|
36
42
|
"readFiles": "قراءة الملفات",
|
|
37
|
-
"readFilesError": "فشل في قراءة الملفات، يرجى التحقق من صحة مسار الملف"
|
|
43
|
+
"readFilesError": "فشل في قراءة الملفات، يرجى التحقق من صحة مسار الملف",
|
|
44
|
+
"writeFile": {
|
|
45
|
+
"characters": "أحرف",
|
|
46
|
+
"preview": "معاينة المحتوى",
|
|
47
|
+
"truncated": "تم الاقتطاع"
|
|
48
|
+
}
|
|
38
49
|
},
|
|
39
50
|
"search": {
|
|
40
51
|
"createNewSearch": "إنشاء سجل بحث جديد",
|
package/locales/bg-BG/chat.json
CHANGED
|
@@ -330,6 +330,11 @@
|
|
|
330
330
|
"screenshot": "Екранна снимка",
|
|
331
331
|
"settings": "Настройки за експортиране",
|
|
332
332
|
"text": "Текст",
|
|
333
|
+
"widthMode": {
|
|
334
|
+
"label": "Режим на ширина",
|
|
335
|
+
"narrow": "Режим за тесен екран",
|
|
336
|
+
"wide": "Режим за широк екран"
|
|
337
|
+
},
|
|
333
338
|
"withBackground": "Включи фоново изображение",
|
|
334
339
|
"withFooter": "Включи долен колонтитул",
|
|
335
340
|
"withPluginInfo": "Включи информация за плъгина",
|
|
@@ -2225,6 +2225,9 @@
|
|
|
2225
2225
|
"megrez-3b-instruct": {
|
|
2226
2226
|
"description": "Megrez 3B Instruct е ефективен модел с малък брой параметри, разработен от Wuwen Xinqiong."
|
|
2227
2227
|
},
|
|
2228
|
+
"meituan/longcat-flash-chat": {
|
|
2229
|
+
"description": "Longcat Flash Chat е с отворен код от Meituan и представлява базов модел без мисловни процеси, оптимизиран за диалогови взаимодействия и задачи на интелигентни агенти, с изключителна ефективност при използване на инструменти и в сложни многократни взаимодействия."
|
|
2230
|
+
},
|
|
2228
2231
|
"meta-llama-3-70b-instruct": {
|
|
2229
2232
|
"description": "Мощен модел с 70 милиарда параметри, отличаващ се в разсъждения, кодиране и широки езикови приложения."
|
|
2230
2233
|
},
|
|
@@ -2456,6 +2459,9 @@
|
|
|
2456
2459
|
"minimax-m2": {
|
|
2457
2460
|
"description": "MiniMax M2 е ефективен голям езиков модел, създаден специално за кодиране и работни процеси с агенти."
|
|
2458
2461
|
},
|
|
2462
|
+
"minimax/minimax-m2": {
|
|
2463
|
+
"description": "Създаден специално за ефективно кодиране и работни потоци с агенти."
|
|
2464
|
+
},
|
|
2459
2465
|
"ministral-3b-latest": {
|
|
2460
2466
|
"description": "Ministral 3B е световен лидер сред моделите на Mistral."
|
|
2461
2467
|
},
|
|
@@ -3371,6 +3377,12 @@
|
|
|
3371
3377
|
"wizardlm2:8x22b": {
|
|
3372
3378
|
"description": "WizardLM 2 е езиков модел, предоставен от Microsoft AI, който се отличава в сложни диалози, многоезичност, разсъждение и интелигентни асистенти."
|
|
3373
3379
|
},
|
|
3380
|
+
"x-ai/grok-4-fast": {
|
|
3381
|
+
"description": "С радост представяме Grok 4 Fast — нашият най-нов напредък в модели за ефективно и икономично извеждане."
|
|
3382
|
+
},
|
|
3383
|
+
"x-ai/grok-code-fast-1": {
|
|
3384
|
+
"description": "С гордост представяме grok-code-fast-1 — бърз и икономичен модел за извеждане, който се отличава в агентно кодиране."
|
|
3385
|
+
},
|
|
3374
3386
|
"x1": {
|
|
3375
3387
|
"description": "Моделът Spark X1 ще бъде допълнително обновен, като на базата на водещите в страната резултати в математически задачи, ще постигне ефекти в общи задачи като разсъждение, генериране на текст и разбиране на език, сравними с OpenAI o1 и DeepSeek R1."
|
|
3376
3388
|
},
|
|
@@ -3431,6 +3443,9 @@
|
|
|
3431
3443
|
"yi-vision-v2": {
|
|
3432
3444
|
"description": "Модел за сложни визуални задачи, предлагащ висока производителност в разбирането и анализа на базата на множество изображения."
|
|
3433
3445
|
},
|
|
3446
|
+
"z-ai/glm-4.6": {
|
|
3447
|
+
"description": "GLM-4.6 е най-новият флагмански модел на Zhipu, който значително надминава предшествениците си в напреднало кодиране, обработка на дълги текстове, извеждане и способности на интелигентни агенти."
|
|
3448
|
+
},
|
|
3434
3449
|
"zai-org/GLM-4.5": {
|
|
3435
3450
|
"description": "GLM-4.5 е базов модел, специално създаден за интелигентни агенти, използващ архитектура с микс от експерти (Mixture-of-Experts). Той е дълбоко оптимизиран за използване на инструменти, уеб браузване, софтуерно инженерство и фронтенд програмиране, и поддържа безпроблемна интеграция с кодови агенти като Claude Code и Roo Code. GLM-4.5 използва смесен режим на разсъждение, подходящ за сложни и ежедневни приложения."
|
|
3436
3451
|
},
|
package/locales/bg-BG/tool.json
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"prompt": "подсказка"
|
|
16
16
|
},
|
|
17
17
|
"localFiles": {
|
|
18
|
+
"editFile": {
|
|
19
|
+
"newString": "Замени с",
|
|
20
|
+
"oldString": "Търсене на съдържание",
|
|
21
|
+
"replaceAll": "Замени всички съвпадения",
|
|
22
|
+
"replaceFirst": "Замени само първото съвпадение"
|
|
23
|
+
},
|
|
18
24
|
"file": "Файл",
|
|
19
25
|
"folder": "Папка",
|
|
20
26
|
"moveFiles": {
|
|
@@ -34,7 +40,12 @@
|
|
|
34
40
|
"readFile": "Прочети файл",
|
|
35
41
|
"readFileError": "Неуспешно четене на файла, моля, проверете дали пътят към файла е правилен",
|
|
36
42
|
"readFiles": "Прочети файлове",
|
|
37
|
-
"readFilesError": "Неуспешно четене на файловете, моля, проверете дали пътят към файловете е правилен"
|
|
43
|
+
"readFilesError": "Неуспешно четене на файловете, моля, проверете дали пътят към файловете е правилен",
|
|
44
|
+
"writeFile": {
|
|
45
|
+
"characters": "Знаци",
|
|
46
|
+
"preview": "Преглед на съдържанието",
|
|
47
|
+
"truncated": "Съкратено"
|
|
48
|
+
}
|
|
38
49
|
},
|
|
39
50
|
"search": {
|
|
40
51
|
"createNewSearch": "Създаване на нова търсене",
|
package/locales/de-DE/chat.json
CHANGED
|
@@ -330,6 +330,11 @@
|
|
|
330
330
|
"screenshot": "Screenshot",
|
|
331
331
|
"settings": "Exporteinstellungen",
|
|
332
332
|
"text": "Text",
|
|
333
|
+
"widthMode": {
|
|
334
|
+
"label": "Breitenmodus",
|
|
335
|
+
"narrow": "Schmalbildmodus",
|
|
336
|
+
"wide": "Breitbildmodus"
|
|
337
|
+
},
|
|
333
338
|
"withBackground": "Mit Hintergrundbild",
|
|
334
339
|
"withFooter": "Mit Fußzeile",
|
|
335
340
|
"withPluginInfo": "Mit Plugin-Informationen",
|
|
@@ -2225,6 +2225,9 @@
|
|
|
2225
2225
|
"megrez-3b-instruct": {
|
|
2226
2226
|
"description": "Megrez 3B Instruct ist ein effizientes Modell mit geringer Parameteranzahl, entwickelt von Wuwen Xinqiong."
|
|
2227
2227
|
},
|
|
2228
|
+
"meituan/longcat-flash-chat": {
|
|
2229
|
+
"description": "Ein von Meituan entwickeltes Open-Source-Basismodell, das speziell für dialogorientierte Interaktionen und agentenbasierte Aufgaben optimiert wurde und sich besonders bei Werkzeugaufrufen und komplexen mehrstufigen Dialogszenarien auszeichnet."
|
|
2230
|
+
},
|
|
2228
2231
|
"meta-llama-3-70b-instruct": {
|
|
2229
2232
|
"description": "Ein leistungsstarkes Modell mit 70 Milliarden Parametern, das in den Bereichen Schlussfolgerungen, Programmierung und breiten Sprachanwendungen herausragt."
|
|
2230
2233
|
},
|
|
@@ -2456,6 +2459,9 @@
|
|
|
2456
2459
|
"minimax-m2": {
|
|
2457
2460
|
"description": "MiniMax M2 ist ein leistungsstarkes, effizientes Sprachmodell, das speziell für Programmier- und Agenten-Workflows entwickelt wurde."
|
|
2458
2461
|
},
|
|
2462
|
+
"minimax/minimax-m2": {
|
|
2463
|
+
"description": "Speziell entwickelt für effizientes Codieren und Agenten-Workflows."
|
|
2464
|
+
},
|
|
2459
2465
|
"ministral-3b-latest": {
|
|
2460
2466
|
"description": "Ministral 3B ist das weltbeste Edge-Modell von Mistral."
|
|
2461
2467
|
},
|
|
@@ -3371,6 +3377,12 @@
|
|
|
3371
3377
|
"wizardlm2:8x22b": {
|
|
3372
3378
|
"description": "WizardLM 2 ist ein Sprachmodell von Microsoft AI, das in komplexen Dialogen, mehrsprachigen Anwendungen, Schlussfolgerungen und intelligenten Assistenten besonders gut abschneidet."
|
|
3373
3379
|
},
|
|
3380
|
+
"x-ai/grok-4-fast": {
|
|
3381
|
+
"description": "Wir freuen uns, Grok 4 Fast vorzustellen – unseren neuesten Fortschritt im Bereich kosteneffizienter Inferenzmodelle."
|
|
3382
|
+
},
|
|
3383
|
+
"x-ai/grok-code-fast-1": {
|
|
3384
|
+
"description": "Wir freuen uns, grok-code-fast-1 zu präsentieren – ein schnelles und kosteneffizientes Inferenzmodell mit hervorragender Leistung im Bereich Agenten-Codierung."
|
|
3385
|
+
},
|
|
3374
3386
|
"x1": {
|
|
3375
3387
|
"description": "Das Spark X1 Modell wird weiter verbessert und erreicht in allgemeinen Aufgaben wie Schlussfolgerungen, Textgenerierung und Sprachverständnis Ergebnisse, die mit OpenAI o1 und DeepSeek R1 vergleichbar sind, basierend auf der bereits führenden Leistung in mathematischen Aufgaben."
|
|
3376
3388
|
},
|
|
@@ -3431,6 +3443,9 @@
|
|
|
3431
3443
|
"yi-vision-v2": {
|
|
3432
3444
|
"description": "Ein Modell für komplexe visuelle Aufgaben, das leistungsstarke Verständnis- und Analysefähigkeiten auf der Grundlage mehrerer Bilder bietet."
|
|
3433
3445
|
},
|
|
3446
|
+
"z-ai/glm-4.6": {
|
|
3447
|
+
"description": "Das neueste Flaggschiffmodell von Zhipu, GLM-4.6, übertrifft seine Vorgänger deutlich in den Bereichen fortgeschrittenes Codieren, Verarbeitung langer Texte, logisches Schließen und agentenbasierte Fähigkeiten."
|
|
3448
|
+
},
|
|
3434
3449
|
"zai-org/GLM-4.5": {
|
|
3435
3450
|
"description": "GLM-4.5 ist ein speziell für Agentenanwendungen entwickeltes Basismodell mit Mixture-of-Experts-Architektur. Es ist tief optimiert für Werkzeugaufrufe, Web-Browsing, Softwareentwicklung und Frontend-Programmierung und unterstützt nahtlos die Integration in Code-Agenten wie Claude Code und Roo Code. GLM-4.5 verwendet einen hybriden Inferenzmodus und ist für komplexe Schlussfolgerungen sowie den Alltagsgebrauch geeignet."
|
|
3436
3451
|
},
|
package/locales/de-DE/tool.json
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"prompt": "Hinweiswort"
|
|
16
16
|
},
|
|
17
17
|
"localFiles": {
|
|
18
|
+
"editFile": {
|
|
19
|
+
"newString": "Ersetzen durch",
|
|
20
|
+
"oldString": "Suchbegriff",
|
|
21
|
+
"replaceAll": "Alle Vorkommen ersetzen",
|
|
22
|
+
"replaceFirst": "Nur erstes Vorkommen ersetzen"
|
|
23
|
+
},
|
|
18
24
|
"file": "Datei",
|
|
19
25
|
"folder": "Ordner",
|
|
20
26
|
"moveFiles": {
|
|
@@ -34,7 +40,12 @@
|
|
|
34
40
|
"readFile": "Datei lesen",
|
|
35
41
|
"readFileError": "Fehler beim Lesen der Datei, bitte überprüfen Sie den Dateipfad",
|
|
36
42
|
"readFiles": "Dateien lesen",
|
|
37
|
-
"readFilesError": "Fehler beim Lesen der Dateien, bitte überprüfen Sie den Dateipfad"
|
|
43
|
+
"readFilesError": "Fehler beim Lesen der Dateien, bitte überprüfen Sie den Dateipfad",
|
|
44
|
+
"writeFile": {
|
|
45
|
+
"characters": "Zeichen",
|
|
46
|
+
"preview": "Vorschau des Inhalts",
|
|
47
|
+
"truncated": "Abgeschnitten"
|
|
48
|
+
}
|
|
38
49
|
},
|
|
39
50
|
"search": {
|
|
40
51
|
"createNewSearch": "Neue Suchanfrage erstellen",
|