@yeaft/webchat-agent 1.0.647 → 1.0.649
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +217 -172
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/spreadsheet-worker.js +2 -0
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/workbench/file-ops.js +47 -15
- package/workbench/utils.js +8 -0
|
Binary file
|
package/package.json
CHANGED
package/workbench/file-ops.js
CHANGED
|
@@ -12,6 +12,26 @@ export const MAX_WORKBENCH_PREVIEW_BYTES = 20 * 1024 * 1024;
|
|
|
12
12
|
export const WORKBENCH_FILE_CHUNK_BYTES = 1024 * 1024;
|
|
13
13
|
export const WORKBENCH_VIDEO_CHUNK_BYTES = 1024 * 1024;
|
|
14
14
|
|
|
15
|
+
// Unknown extensions may be binary. Never open lossy UTF-8 or control bytes in
|
|
16
|
+
// an editable buffer: saving that buffer could irreversibly corrupt the file.
|
|
17
|
+
export function isBinaryFileContent(buffer) {
|
|
18
|
+
if (buffer.some(byte => byte === 0 || (byte < 32 && ![9, 10, 12, 13].includes(byte)))) return true;
|
|
19
|
+
try {
|
|
20
|
+
new TextDecoder('utf-8', { fatal: true }).decode(buffer);
|
|
21
|
+
return false;
|
|
22
|
+
} catch {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function assertPreviewSize(size) {
|
|
28
|
+
if (size <= MAX_WORKBENCH_PREVIEW_BYTES) return;
|
|
29
|
+
const error = new Error(`File is too large to transfer (${(size / 1024 / 1024).toFixed(1)} MB). The file limit is 20 MB.`);
|
|
30
|
+
error.code = 'FILE_PREVIEW_TOO_LARGE';
|
|
31
|
+
error.details = { sizeBytes: size, limitBytes: MAX_WORKBENCH_PREVIEW_BYTES };
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
async function canonicalWorkspaceReadPath(filePath, workDir, errorMessage = 'File is outside the active workspace.') {
|
|
16
36
|
const canonicalRoot = await realpath(resolve(workDir));
|
|
17
37
|
const canonicalFile = await realpath(resolveAndValidatePath(filePath, canonicalRoot));
|
|
@@ -199,20 +219,33 @@ export async function handleReadFile(msg) {
|
|
|
199
219
|
error.code = 'VIDEO_STREAM_REQUIRED';
|
|
200
220
|
throw error;
|
|
201
221
|
}
|
|
202
|
-
|
|
203
|
-
|
|
222
|
+
let mimeType = BINARY_EXTENSIONS[ext];
|
|
223
|
+
const fileStat = await stat(resolved);
|
|
224
|
+
if (!fileStat.isFile()) throw new Error('Path is not a regular file.');
|
|
225
|
+
if (mimeType) assertPreviewSize(fileStat.size);
|
|
226
|
+
// Keep the existing text-file size behavior. Unknown binary files are
|
|
227
|
+
// classified from a bounded prefix before allocating their full contents.
|
|
228
|
+
if (!mimeType) {
|
|
229
|
+
const handle = await open(resolved, 'r');
|
|
230
|
+
try {
|
|
231
|
+
const sample = Buffer.alloc(Math.min(fileStat.size, 8192));
|
|
232
|
+
const { bytesRead } = await handle.read(sample, 0, sample.length, 0);
|
|
233
|
+
// Do not mistake a UTF-8 character split at the sample boundary for
|
|
234
|
+
// binary. Streaming decoding still rejects invalid bytes within it.
|
|
235
|
+
const prefix = sample.subarray(0, bytesRead);
|
|
236
|
+
let invalidUtf8 = false;
|
|
237
|
+
try { new TextDecoder('utf-8', { fatal: true }).decode(prefix, { stream: bytesRead < fileStat.size }); }
|
|
238
|
+
catch { invalidUtf8 = true; }
|
|
239
|
+
if (invalidUtf8 || prefix.some(byte => byte === 0 || (byte < 32 && ![9, 10, 12, 13].includes(byte)))) {
|
|
240
|
+
mimeType = 'application/octet-stream';
|
|
241
|
+
assertPreviewSize(fileStat.size);
|
|
242
|
+
}
|
|
243
|
+
} finally { await handle.close(); }
|
|
244
|
+
}
|
|
245
|
+
const buffer = await readFile(resolved);
|
|
246
|
+
if (!mimeType && isBinaryFileContent(buffer)) mimeType = 'application/octet-stream';
|
|
204
247
|
if (mimeType) {
|
|
205
|
-
|
|
206
|
-
if (fileStat.size > MAX_WORKBENCH_PREVIEW_BYTES) {
|
|
207
|
-
const error = new Error(`File is too large to transfer (${(fileStat.size / 1024 / 1024).toFixed(1)} MB). The file limit is 20 MB.`);
|
|
208
|
-
error.code = 'FILE_PREVIEW_TOO_LARGE';
|
|
209
|
-
error.details = {
|
|
210
|
-
sizeBytes: fileStat.size,
|
|
211
|
-
limitBytes: MAX_WORKBENCH_PREVIEW_BYTES,
|
|
212
|
-
};
|
|
213
|
-
throw error;
|
|
214
|
-
}
|
|
215
|
-
const buffer = await readFile(resolved);
|
|
248
|
+
assertPreviewSize(buffer.length);
|
|
216
249
|
console.log('[Agent] Sending binary file_content:', { filePath: resolved, size: buffer.length, mimeType, conversationId });
|
|
217
250
|
await sendBinaryFile(msg, {
|
|
218
251
|
conversationId,
|
|
@@ -223,8 +256,7 @@ export async function handleReadFile(msg) {
|
|
|
223
256
|
requestedFilePath: filePath,
|
|
224
257
|
}, buffer, mimeType);
|
|
225
258
|
} else {
|
|
226
|
-
|
|
227
|
-
const content = await readFile(resolved, 'utf-8');
|
|
259
|
+
const content = buffer.toString('utf-8');
|
|
228
260
|
|
|
229
261
|
// 检测语言
|
|
230
262
|
const langMap = {
|
package/workbench/utils.js
CHANGED
|
@@ -30,6 +30,14 @@ export function validateGitPath(filePath) {
|
|
|
30
30
|
// Binary file extensions → MIME type mapping
|
|
31
31
|
export const BINARY_EXTENSIONS = {
|
|
32
32
|
'.pdf': 'application/pdf',
|
|
33
|
+
'.doc': 'application/msword',
|
|
34
|
+
'.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.m4a': 'audio/mp4',
|
|
35
|
+
'.flac': 'audio/flac', '.aac': 'audio/aac',
|
|
36
|
+
// Unsupported formats are downloads, never decoded into the text editor.
|
|
37
|
+
...Object.fromEntries(['.zip', '.gz', '.bz2', '.xz', '.7z', '.rar', '.tar',
|
|
38
|
+
'.exe', '.dll', '.so', '.dylib', '.wasm', '.bin', '.sqlite', '.sqlite3', '.db',
|
|
39
|
+
'.woff', '.woff2', '.ttf', '.otf', '.avif', '.heic', '.tif', '.tiff']
|
|
40
|
+
.map(ext => [ext, 'application/octet-stream'])),
|
|
33
41
|
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
34
42
|
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
35
43
|
'.xls': 'application/vnd.ms-excel',
|