@lvce-editor/chat-view 6.9.0 → 6.10.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.
@@ -1269,15 +1269,9 @@ const sendMessagePortToChatNetworkWorker = async port => {
1269
1269
  const sendMessagePortToChatToolWorker = async port => {
1270
1270
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatToolWorker', port, 'HandleMessagePort.handleMessagePort');
1271
1271
  };
1272
- const writeFile = async (uri, text) => {
1273
- await invoke$1('FileSystem.writeFile', uri, text);
1274
- };
1275
1272
  const activateByEvent$1 = (event, assetDir, platform) => {
1276
1273
  return invoke$1('ExtensionHostManagement.activateByEvent', event, assetDir, platform);
1277
1274
  };
1278
- const getWorkspacePath = () => {
1279
- return invoke$1('Workspace.getPath');
1280
- };
1281
1275
  const getPreference = async key => {
1282
1276
  return await invoke$1('Preferences.get', key);
1283
1277
  };
@@ -3349,240 +3343,6 @@ const getTools = async () => {
3349
3343
  return invoke$3('ChatTool.getTools');
3350
3344
  };
3351
3345
 
3352
- const executeAskQuestionTool = args => {
3353
- const normalized = args && typeof args === 'object' ? args : {};
3354
- const question = Reflect.get(normalized, 'question');
3355
- const answers = Reflect.get(normalized, 'answers');
3356
- return JSON.stringify({
3357
- answers: Array.isArray(answers) ? answers.filter(answer => typeof answer === 'string') : [],
3358
- ok: true,
3359
- question: typeof question === 'string' ? question : ''
3360
- });
3361
- };
3362
-
3363
- const getToolErrorPayload = error => {
3364
- const rawStack = error && typeof error === 'object' ? Reflect.get(error, 'stack') : undefined;
3365
- return {
3366
- error: String(error),
3367
- ...(typeof rawStack === 'string' && rawStack.trim() ? {
3368
- errorStack: rawStack,
3369
- stack: rawStack
3370
- } : {})
3371
- };
3372
- };
3373
-
3374
- const executeGetWorkspaceUriTool = async (_args, _options) => {
3375
- try {
3376
- const workspaceUri = await getWorkspacePath();
3377
- return JSON.stringify({
3378
- workspaceUri
3379
- });
3380
- } catch (error) {
3381
- return JSON.stringify(getToolErrorPayload(error));
3382
- }
3383
- };
3384
-
3385
- const isAbsoluteUri$1 = value => {
3386
- return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value);
3387
- };
3388
- const executeListFilesTool = async (args, _options) => {
3389
- const uri = typeof args.uri === 'string' ? args.uri : '';
3390
- if (!uri || !isAbsoluteUri$1(uri)) {
3391
- return JSON.stringify({
3392
- error: 'Invalid argument: uri must be an absolute URI.'
3393
- });
3394
- }
3395
- try {
3396
- const entries = await invoke$1('FileSystem.readDirWithFileTypes', uri);
3397
- return JSON.stringify({
3398
- entries,
3399
- uri
3400
- });
3401
- } catch (error) {
3402
- return JSON.stringify({
3403
- ...getToolErrorPayload(error),
3404
- uri
3405
- });
3406
- }
3407
- };
3408
-
3409
- const isPathTraversalAttempt = path => {
3410
- if (!path) {
3411
- return false;
3412
- }
3413
- if (path.startsWith('/') || path.startsWith('\\')) {
3414
- return true;
3415
- }
3416
- if (path.startsWith('file://')) {
3417
- return true;
3418
- }
3419
- if (/^[a-zA-Z]:[\\/]/.test(path)) {
3420
- return true;
3421
- }
3422
- const segments = path.split(/[\\/]/);
3423
- return segments.includes('..');
3424
- };
3425
-
3426
- const normalizeRelativePath = path => {
3427
- const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
3428
- if (segments.length === 0) {
3429
- return '.';
3430
- }
3431
- return segments.join('/');
3432
- };
3433
-
3434
- const isAbsoluteUri = value => {
3435
- return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value);
3436
- };
3437
- const executeReadFileTool = async (args, _options) => {
3438
- const uri = typeof args.uri === 'string' ? args.uri : '';
3439
- if (uri) {
3440
- if (!isAbsoluteUri(uri)) {
3441
- return JSON.stringify({
3442
- error: 'Invalid argument: uri must be an absolute URI.'
3443
- });
3444
- }
3445
- try {
3446
- const content = await readFile(uri);
3447
- return JSON.stringify({
3448
- content,
3449
- uri
3450
- });
3451
- } catch (error) {
3452
- return JSON.stringify({
3453
- ...getToolErrorPayload(error),
3454
- uri
3455
- });
3456
- }
3457
- }
3458
- const filePath = typeof args.path === 'string' ? args.path : '';
3459
- if (!filePath || isPathTraversalAttempt(filePath)) {
3460
- return JSON.stringify({
3461
- error: 'Access denied: path must be relative and stay within the open workspace folder.'
3462
- });
3463
- }
3464
- const normalizedPath = normalizeRelativePath(filePath);
3465
- try {
3466
- const content = await readFile(normalizedPath);
3467
- return JSON.stringify({
3468
- content,
3469
- path: normalizedPath
3470
- });
3471
- } catch (error) {
3472
- return JSON.stringify({
3473
- ...getToolErrorPayload(error),
3474
- path: normalizedPath
3475
- });
3476
- }
3477
- };
3478
-
3479
- const maxPayloadLength = 40_000;
3480
- const executeRenderHtmlTool = async (args, _options) => {
3481
- const html = typeof args.html === 'string' ? args.html : '';
3482
- const css = typeof args.css === 'string' ? args.css : '';
3483
- const title = typeof args.title === 'string' ? args.title : '';
3484
- if (!html) {
3485
- return JSON.stringify({
3486
- error: 'Missing required argument: html'
3487
- });
3488
- }
3489
- if (html.length > maxPayloadLength || css.length > maxPayloadLength) {
3490
- return JSON.stringify({
3491
- error: 'Payload too large: keep html/css under 40,000 characters each.'
3492
- });
3493
- }
3494
- return JSON.stringify({
3495
- css,
3496
- html,
3497
- ok: true,
3498
- title
3499
- });
3500
- };
3501
-
3502
- const toLines = value => {
3503
- if (!value) {
3504
- return [];
3505
- }
3506
- const split = value.split('\n').map(line => line.endsWith('\r') ? line.slice(0, -1) : line);
3507
- if (split.length > 0 && split.at(-1) === '') {
3508
- split.pop();
3509
- }
3510
- return split;
3511
- };
3512
- const getLineCounts = (before, after) => {
3513
- const beforeLines = toLines(before);
3514
- const afterLines = toLines(after);
3515
- let start = 0;
3516
- while (start < beforeLines.length && start < afterLines.length && beforeLines[start] === afterLines[start]) {
3517
- start++;
3518
- }
3519
- let beforeEnd = beforeLines.length - 1;
3520
- let afterEnd = afterLines.length - 1;
3521
- while (beforeEnd >= start && afterEnd >= start && beforeLines[beforeEnd] === afterLines[afterEnd]) {
3522
- beforeEnd--;
3523
- afterEnd--;
3524
- }
3525
- return {
3526
- linesAdded: Math.max(0, afterEnd - start + 1),
3527
- linesDeleted: Math.max(0, beforeEnd - start + 1)
3528
- };
3529
- };
3530
- const isFileNotFoundError = error => {
3531
- const message = String(error).toLowerCase();
3532
- return message.includes('enoent') || message.includes('not found');
3533
- };
3534
- const executeWriteFileTool = async (args, _options) => {
3535
- const filePath = typeof args.path === 'string' ? args.path : '';
3536
- const content = typeof args.content === 'string' ? args.content : '';
3537
- if (!filePath || isPathTraversalAttempt(filePath)) {
3538
- return JSON.stringify({
3539
- error: 'Access denied: path must be relative and stay within the open workspace folder.'
3540
- });
3541
- }
3542
- const normalizedPath = normalizeRelativePath(filePath);
3543
- try {
3544
- let previousContent = '';
3545
- try {
3546
- previousContent = await readFile(normalizedPath);
3547
- } catch (error) {
3548
- if (!isFileNotFoundError(error)) {
3549
- throw error;
3550
- }
3551
- }
3552
- await writeFile(normalizedPath, content);
3553
- const {
3554
- linesAdded,
3555
- linesDeleted
3556
- } = getLineCounts(previousContent, content);
3557
- return JSON.stringify({
3558
- linesAdded,
3559
- linesDeleted,
3560
- ok: true,
3561
- path: normalizedPath
3562
- });
3563
- } catch (error) {
3564
- return JSON.stringify({
3565
- ...getToolErrorPayload(error),
3566
- path: normalizedPath
3567
- });
3568
- }
3569
- };
3570
-
3571
- const parseToolArguments = rawArguments => {
3572
- if (typeof rawArguments !== 'string') {
3573
- return {};
3574
- }
3575
- try {
3576
- const parsed = JSON.parse(rawArguments);
3577
- if (!parsed || typeof parsed !== 'object') {
3578
- return {};
3579
- }
3580
- return parsed;
3581
- } catch {
3582
- return {};
3583
- }
3584
- };
3585
-
3586
3346
  const stringifyToolOutput = output => {
3587
3347
  if (typeof output === 'string') {
3588
3348
  return output;
@@ -3590,35 +3350,14 @@ const stringifyToolOutput = output => {
3590
3350
  return JSON.stringify(output) ?? 'null';
3591
3351
  };
3592
3352
  const executeChatTool = async (name, rawArguments, options) => {
3593
- if (options.useChatToolWorker) {
3594
- const workerOutput = await execute(name, rawArguments, {
3595
- assetDir: options.assetDir,
3596
- platform: options.platform
3597
- });
3598
- return stringifyToolOutput(workerOutput);
3599
- }
3600
- const args = parseToolArguments(rawArguments);
3601
- if (name === 'read_file') {
3602
- return executeReadFileTool(args);
3603
- }
3604
- if (name === 'write_file') {
3605
- return executeWriteFileTool(args);
3606
- }
3607
- if (name === 'list_files') {
3608
- return executeListFilesTool(args);
3609
- }
3610
- if (name === 'getWorkspaceUri') {
3611
- return executeGetWorkspaceUriTool();
3353
+ if (!options.useChatToolWorker) {
3354
+ throw new Error('Chat tools must be executed in a web worker environment. Please set useChatToolWorker to true in the options.');
3612
3355
  }
3613
- if (name === 'render_html') {
3614
- return executeRenderHtmlTool(args);
3615
- }
3616
- if (name === 'ask_question') {
3617
- return executeAskQuestionTool(args);
3618
- }
3619
- return JSON.stringify({
3620
- error: `Unknown tool: ${name}`
3356
+ const workerOutput = await execute(name, rawArguments, {
3357
+ assetDir: options.assetDir,
3358
+ platform: options.platform
3621
3359
  });
3360
+ return stringifyToolOutput(workerOutput);
3622
3361
  };
3623
3362
 
3624
3363
  const getReadFileTool = () => {
@@ -7281,6 +7020,31 @@ Assistant: ${assistantText}`;
7281
7020
  return title && !isDefaultSessionTitle(title) ? title : '';
7282
7021
  };
7283
7022
 
7023
+ const isPathTraversalAttempt = path => {
7024
+ if (!path) {
7025
+ return false;
7026
+ }
7027
+ if (path.startsWith('/') || path.startsWith('\\')) {
7028
+ return true;
7029
+ }
7030
+ if (path.startsWith('file://')) {
7031
+ return true;
7032
+ }
7033
+ if (/^[a-zA-Z]:[\\/]/.test(path)) {
7034
+ return true;
7035
+ }
7036
+ const segments = path.split(/[\\/]/);
7037
+ return segments.includes('..');
7038
+ };
7039
+
7040
+ const normalizeRelativePath = path => {
7041
+ const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
7042
+ if (segments.length === 0) {
7043
+ return '.';
7044
+ }
7045
+ return segments.join('/');
7046
+ };
7047
+
7284
7048
  const mentionRegex = /(^|\s)@([^\s]+)/g;
7285
7049
  const maxMentionCount = 5;
7286
7050
  const parseMentionedPaths = value => {
@@ -10673,6 +10437,33 @@ const getToolCallWriteFileVirtualDom = toolCall => {
10673
10437
  type: Span
10674
10438
  }, text(` -${linesDeleted}`), ...(statusLabel ? [text(statusLabel)] : [])];
10675
10439
  };
10440
+ const getToolCallEditFileVirtualDom = toolCall => {
10441
+ const target = getReadFileTarget(toolCall.arguments);
10442
+ if (!target) {
10443
+ return [];
10444
+ }
10445
+ const fileName = getFileNameFromUri(target.title);
10446
+ const fileNameClickableProps = target.clickableUri ? {
10447
+ 'data-uri': target.clickableUri,
10448
+ onClick: HandleClickReadFile
10449
+ } : {};
10450
+ return [{
10451
+ childCount: 3,
10452
+ className: ChatOrderedListItem,
10453
+ title: target.title,
10454
+ type: Li
10455
+ }, {
10456
+ childCount: 0,
10457
+ className: FileIcon,
10458
+ type: Div
10459
+ }, text('edit_file '), {
10460
+ childCount: 1,
10461
+ className: ChatToolCallReadFileLink,
10462
+ title: target.clickableUri,
10463
+ ...fileNameClickableProps,
10464
+ type: Span
10465
+ }, text(fileName)];
10466
+ };
10676
10467
  const getToolCallDom = toolCall => {
10677
10468
  if (toolCall.name === 'getWorkspaceUri') {
10678
10469
  const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
@@ -10692,6 +10483,12 @@ const getToolCallDom = toolCall => {
10692
10483
  return virtualDom;
10693
10484
  }
10694
10485
  }
10486
+ if (toolCall.name === 'edit_file') {
10487
+ const virtualDom = getToolCallEditFileVirtualDom(toolCall);
10488
+ if (virtualDom.length > 0) {
10489
+ return virtualDom;
10490
+ }
10491
+ }
10695
10492
  if (toolCall.name === 'render_html') {
10696
10493
  const virtualDom = getToolCallRenderHtmlVirtualDom(toolCall);
10697
10494
  if (virtualDom.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "6.9.0",
3
+ "version": "6.10.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",