@lvce-editor/chat-view 2.8.0 → 2.9.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.
- package/dist/chatViewWorkerMain.js +132 -113
- package/package.json +1 -1
|
@@ -2649,7 +2649,8 @@ const deleteSession = async (state, id) => {
|
|
|
2649
2649
|
};
|
|
2650
2650
|
|
|
2651
2651
|
const handleClickOpenApiApiKeySettings = async state => {
|
|
2652
|
-
|
|
2652
|
+
// Open the built-in settings editor so the user can inspect or edit their OpenAI API key.
|
|
2653
|
+
await invoke('Main.openUri', 'app://settings.json');
|
|
2653
2654
|
return state;
|
|
2654
2655
|
};
|
|
2655
2656
|
|
|
@@ -3053,8 +3054,6 @@ const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBa
|
|
|
3053
3054
|
}
|
|
3054
3055
|
};
|
|
3055
3056
|
|
|
3056
|
-
const OnFileSystem = 'onFileSystem';
|
|
3057
|
-
|
|
3058
3057
|
const isPathTraversalAttempt = path => {
|
|
3059
3058
|
if (!path) {
|
|
3060
3059
|
return false;
|
|
@@ -3071,6 +3070,7 @@ const isPathTraversalAttempt = path => {
|
|
|
3071
3070
|
const segments = path.split(/[\\/]/);
|
|
3072
3071
|
return segments.includes('..');
|
|
3073
3072
|
};
|
|
3073
|
+
|
|
3074
3074
|
const normalizeRelativePath = path => {
|
|
3075
3075
|
const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
|
|
3076
3076
|
if (segments.length === 0) {
|
|
@@ -3078,6 +3078,87 @@ const normalizeRelativePath = path => {
|
|
|
3078
3078
|
}
|
|
3079
3079
|
return segments.join('/');
|
|
3080
3080
|
};
|
|
3081
|
+
|
|
3082
|
+
const executeListFilesTool = async (args, _options) => {
|
|
3083
|
+
const folderPath = typeof args.path === 'string' && args.path ? args.path : '.';
|
|
3084
|
+
if (isPathTraversalAttempt(folderPath)) {
|
|
3085
|
+
return JSON.stringify({
|
|
3086
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
const normalizedPath = normalizeRelativePath(folderPath);
|
|
3090
|
+
try {
|
|
3091
|
+
const entries = await invoke('FileSystem.readDirWithFileTypes', normalizedPath);
|
|
3092
|
+
return JSON.stringify({
|
|
3093
|
+
entries,
|
|
3094
|
+
path: normalizedPath
|
|
3095
|
+
});
|
|
3096
|
+
} catch (error) {
|
|
3097
|
+
return JSON.stringify({
|
|
3098
|
+
error: String(error),
|
|
3099
|
+
path: normalizedPath
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
|
|
3104
|
+
const executeReadFileTool = async (args, _options) => {
|
|
3105
|
+
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3106
|
+
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3107
|
+
return JSON.stringify({
|
|
3108
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
const normalizedPath = normalizeRelativePath(filePath);
|
|
3112
|
+
try {
|
|
3113
|
+
const content = await readFile(normalizedPath);
|
|
3114
|
+
return JSON.stringify({
|
|
3115
|
+
content,
|
|
3116
|
+
path: normalizedPath
|
|
3117
|
+
});
|
|
3118
|
+
} catch (error) {
|
|
3119
|
+
return JSON.stringify({
|
|
3120
|
+
error: String(error),
|
|
3121
|
+
path: normalizedPath
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3126
|
+
const OnFileSystem = 'onFileSystem';
|
|
3127
|
+
|
|
3128
|
+
const executeFileSystemCommand = async (method, params, options) => {
|
|
3129
|
+
return executeProvider({
|
|
3130
|
+
assetDir: options.assetDir,
|
|
3131
|
+
event: OnFileSystem,
|
|
3132
|
+
method,
|
|
3133
|
+
noProviderFoundMessage: 'No file system provider found',
|
|
3134
|
+
params,
|
|
3135
|
+
platform: options.platform
|
|
3136
|
+
});
|
|
3137
|
+
};
|
|
3138
|
+
|
|
3139
|
+
const executeWriteFileTool = async (args, options) => {
|
|
3140
|
+
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3141
|
+
const content = typeof args.content === 'string' ? args.content : '';
|
|
3142
|
+
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3143
|
+
return JSON.stringify({
|
|
3144
|
+
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
const normalizedPath = normalizeRelativePath(filePath);
|
|
3148
|
+
try {
|
|
3149
|
+
await executeFileSystemCommand(FileSystemWriteFile, ['file', normalizedPath, content], options);
|
|
3150
|
+
return JSON.stringify({
|
|
3151
|
+
ok: true,
|
|
3152
|
+
path: normalizedPath
|
|
3153
|
+
});
|
|
3154
|
+
} catch (error) {
|
|
3155
|
+
return JSON.stringify({
|
|
3156
|
+
error: String(error),
|
|
3157
|
+
path: normalizedPath
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
};
|
|
3161
|
+
|
|
3081
3162
|
const parseToolArguments = rawArguments => {
|
|
3082
3163
|
if (typeof rawArguments !== 'string') {
|
|
3083
3164
|
return {};
|
|
@@ -3092,16 +3173,23 @@ const parseToolArguments = rawArguments => {
|
|
|
3092
3173
|
return {};
|
|
3093
3174
|
}
|
|
3094
3175
|
};
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3176
|
+
|
|
3177
|
+
const executeChatTool = async (name, rawArguments, options) => {
|
|
3178
|
+
const args = parseToolArguments(rawArguments);
|
|
3179
|
+
if (name === 'read_file') {
|
|
3180
|
+
return executeReadFileTool(args);
|
|
3181
|
+
}
|
|
3182
|
+
if (name === 'write_file') {
|
|
3183
|
+
return executeWriteFileTool(args, options);
|
|
3184
|
+
}
|
|
3185
|
+
if (name === 'list_files') {
|
|
3186
|
+
return executeListFilesTool(args);
|
|
3187
|
+
}
|
|
3188
|
+
return JSON.stringify({
|
|
3189
|
+
error: `Unknown tool: ${name}`
|
|
3103
3190
|
});
|
|
3104
3191
|
};
|
|
3192
|
+
|
|
3105
3193
|
const getBasicChatTools = () => {
|
|
3106
3194
|
return [{
|
|
3107
3195
|
function: {
|
|
@@ -3159,76 +3247,6 @@ const getBasicChatTools = () => {
|
|
|
3159
3247
|
type: 'function'
|
|
3160
3248
|
}];
|
|
3161
3249
|
};
|
|
3162
|
-
const executeChatTool = async (name, rawArguments, options) => {
|
|
3163
|
-
const args = parseToolArguments(rawArguments);
|
|
3164
|
-
if (name === 'read_file') {
|
|
3165
|
-
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3166
|
-
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3167
|
-
return JSON.stringify({
|
|
3168
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3169
|
-
});
|
|
3170
|
-
}
|
|
3171
|
-
const normalizedPath = normalizeRelativePath(filePath);
|
|
3172
|
-
try {
|
|
3173
|
-
const content = await readFile(normalizedPath);
|
|
3174
|
-
return JSON.stringify({
|
|
3175
|
-
content,
|
|
3176
|
-
path: normalizedPath
|
|
3177
|
-
});
|
|
3178
|
-
} catch (error) {
|
|
3179
|
-
return JSON.stringify({
|
|
3180
|
-
error: String(error),
|
|
3181
|
-
path: normalizedPath
|
|
3182
|
-
});
|
|
3183
|
-
}
|
|
3184
|
-
}
|
|
3185
|
-
if (name === 'write_file') {
|
|
3186
|
-
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
3187
|
-
const content = typeof args.content === 'string' ? args.content : '';
|
|
3188
|
-
if (!filePath || isPathTraversalAttempt(filePath)) {
|
|
3189
|
-
return JSON.stringify({
|
|
3190
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3191
|
-
});
|
|
3192
|
-
}
|
|
3193
|
-
const normalizedPath = normalizeRelativePath(filePath);
|
|
3194
|
-
try {
|
|
3195
|
-
await executeFileSystemCommand(FileSystemWriteFile, ['file', normalizedPath, content], options);
|
|
3196
|
-
return JSON.stringify({
|
|
3197
|
-
ok: true,
|
|
3198
|
-
path: normalizedPath
|
|
3199
|
-
});
|
|
3200
|
-
} catch (error) {
|
|
3201
|
-
return JSON.stringify({
|
|
3202
|
-
error: String(error),
|
|
3203
|
-
path: normalizedPath
|
|
3204
|
-
});
|
|
3205
|
-
}
|
|
3206
|
-
}
|
|
3207
|
-
if (name === 'list_files') {
|
|
3208
|
-
const folderPath = typeof args.path === 'string' && args.path ? args.path : '.';
|
|
3209
|
-
if (isPathTraversalAttempt(folderPath)) {
|
|
3210
|
-
return JSON.stringify({
|
|
3211
|
-
error: 'Access denied: path must be relative and stay within the open workspace folder.'
|
|
3212
|
-
});
|
|
3213
|
-
}
|
|
3214
|
-
const normalizedPath = normalizeRelativePath(folderPath);
|
|
3215
|
-
try {
|
|
3216
|
-
const entries = await invoke('FileSystem.readDirWithFileTypes', normalizedPath);
|
|
3217
|
-
return JSON.stringify({
|
|
3218
|
-
entries,
|
|
3219
|
-
path: normalizedPath
|
|
3220
|
-
});
|
|
3221
|
-
} catch (error) {
|
|
3222
|
-
return JSON.stringify({
|
|
3223
|
-
error: String(error),
|
|
3224
|
-
path: normalizedPath
|
|
3225
|
-
});
|
|
3226
|
-
}
|
|
3227
|
-
}
|
|
3228
|
-
return JSON.stringify({
|
|
3229
|
-
error: `Unknown tool: ${name}`
|
|
3230
|
-
});
|
|
3231
|
-
};
|
|
3232
3250
|
|
|
3233
3251
|
const getClientRequestIdHeader = () => {
|
|
3234
3252
|
return {
|
|
@@ -4049,6 +4067,12 @@ const getOpenApiErrorMessage = errorResult => {
|
|
|
4049
4067
|
const errorMessage = errorResult.errorMessage?.trim();
|
|
4050
4068
|
const hasErrorCode = typeof errorResult.errorCode === 'string' && errorResult.errorCode.length > 0;
|
|
4051
4069
|
const hasErrorType = typeof errorResult.errorType === 'string' && errorResult.errorType.length > 0;
|
|
4070
|
+
|
|
4071
|
+
// Provide a concise, user-friendly message when OpenAI reports an invalid API key.
|
|
4072
|
+
if (errorResult.errorCode === 'invalid_api_key') {
|
|
4073
|
+
const status = typeof errorResult.statusCode === 'number' ? errorResult.statusCode : 401;
|
|
4074
|
+
return `OpenAI request failed (status ${status}): Invalid API key. Please verify your OpenAI API key in Chat settings.`;
|
|
4075
|
+
}
|
|
4052
4076
|
if (errorResult.statusCode === 429) {
|
|
4053
4077
|
let prefix = 'OpenAI rate limit exceeded (429)';
|
|
4054
4078
|
if (hasErrorCode) {
|
|
@@ -5970,6 +5994,31 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
|
5970
5994
|
}, text(selectedSessionTitle), ...getChatHeaderActionsDom()];
|
|
5971
5995
|
};
|
|
5972
5996
|
|
|
5997
|
+
const getMessageNodeDom = node => {
|
|
5998
|
+
if (node.type === 'text') {
|
|
5999
|
+
return [{
|
|
6000
|
+
childCount: 1,
|
|
6001
|
+
className: Markdown,
|
|
6002
|
+
type: P
|
|
6003
|
+
}, text(node.text)];
|
|
6004
|
+
}
|
|
6005
|
+
return [{
|
|
6006
|
+
childCount: node.items.length,
|
|
6007
|
+
className: ChatOrderedList,
|
|
6008
|
+
type: Ol
|
|
6009
|
+
}, ...node.items.flatMap(item => {
|
|
6010
|
+
return [{
|
|
6011
|
+
childCount: 1,
|
|
6012
|
+
className: ChatOrderedListItem,
|
|
6013
|
+
type: Li
|
|
6014
|
+
}, text(item.text)];
|
|
6015
|
+
})];
|
|
6016
|
+
};
|
|
6017
|
+
|
|
6018
|
+
const getMessageContentDom = nodes => {
|
|
6019
|
+
return nodes.flatMap(getMessageNodeDom);
|
|
6020
|
+
};
|
|
6021
|
+
|
|
5973
6022
|
const getMissingApiKeyDom = ({
|
|
5974
6023
|
getApiKeyText,
|
|
5975
6024
|
inputName,
|
|
@@ -6272,28 +6321,6 @@ const parseMessageContent = rawMessage => {
|
|
|
6272
6321
|
type: 'text'
|
|
6273
6322
|
}] : nodes;
|
|
6274
6323
|
};
|
|
6275
|
-
const getMessageContentDom = nodes => {
|
|
6276
|
-
return nodes.flatMap(node => {
|
|
6277
|
-
if (node.type === 'text') {
|
|
6278
|
-
return [{
|
|
6279
|
-
childCount: 1,
|
|
6280
|
-
className: Markdown,
|
|
6281
|
-
type: P
|
|
6282
|
-
}, text(node.text)];
|
|
6283
|
-
}
|
|
6284
|
-
return [{
|
|
6285
|
-
childCount: node.items.length,
|
|
6286
|
-
className: ChatOrderedList,
|
|
6287
|
-
type: Ol
|
|
6288
|
-
}, ...node.items.flatMap(item => {
|
|
6289
|
-
return [{
|
|
6290
|
-
childCount: 1,
|
|
6291
|
-
className: ChatOrderedListItem,
|
|
6292
|
-
type: Li
|
|
6293
|
-
}, text(item.text)];
|
|
6294
|
-
})];
|
|
6295
|
-
});
|
|
6296
|
-
};
|
|
6297
6324
|
|
|
6298
6325
|
const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle') => {
|
|
6299
6326
|
const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
|
|
@@ -6630,30 +6657,22 @@ const saveState = state => {
|
|
|
6630
6657
|
const {
|
|
6631
6658
|
chatListScrollTop,
|
|
6632
6659
|
composerValue,
|
|
6633
|
-
height,
|
|
6634
6660
|
messagesScrollTop,
|
|
6635
6661
|
nextMessageId,
|
|
6636
6662
|
renamingSessionId,
|
|
6637
6663
|
selectedModelId,
|
|
6638
6664
|
selectedSessionId,
|
|
6639
|
-
viewMode
|
|
6640
|
-
width,
|
|
6641
|
-
x,
|
|
6642
|
-
y
|
|
6665
|
+
viewMode
|
|
6643
6666
|
} = state;
|
|
6644
6667
|
return {
|
|
6645
6668
|
chatListScrollTop,
|
|
6646
6669
|
composerValue,
|
|
6647
|
-
height,
|
|
6648
6670
|
messagesScrollTop,
|
|
6649
6671
|
nextMessageId,
|
|
6650
6672
|
renamingSessionId,
|
|
6651
6673
|
selectedModelId,
|
|
6652
6674
|
selectedSessionId,
|
|
6653
|
-
viewMode
|
|
6654
|
-
width,
|
|
6655
|
-
x,
|
|
6656
|
-
y
|
|
6675
|
+
viewMode
|
|
6657
6676
|
};
|
|
6658
6677
|
};
|
|
6659
6678
|
|