diff --git a/src/lib/apis/terminal/index.ts b/src/lib/apis/terminal/index.ts index a8b0ff19ad..9a95b2f915 100644 --- a/src/lib/apis/terminal/index.ts +++ b/src/lib/apis/terminal/index.ts @@ -428,6 +428,42 @@ export const uploadToTerminal = async ( return res; }; +// ponytail: serialize new uploads in this page; cross-client races need server-side protection. +let newFileUploadQueue: Promise = Promise.resolve(); + +export const uploadNewFileToTerminal = async ( + baseUrl: string, + apiKey: string, + directory: string, + file: File, + sessionId?: string +): Promise<{ path: string; size: number } | null> => { + const upload = newFileUploadQueue.then(async () => { + const listing = await listFiles(baseUrl, apiKey, directory, sessionId); + if (!listing) return null; + + const names = new Set(listing.entries.map((entry) => entry.name)); + const extensionIndex = file.name.startsWith('.') ? -1 : file.name.lastIndexOf('.'); + const stem = extensionIndex > 0 ? file.name.slice(0, extensionIndex) : file.name; + const extension = extensionIndex > 0 ? file.name.slice(extensionIndex) : ''; + let name = file.name; + for (let suffix = 1; names.has(name); suffix++) { + name = `${stem} (${suffix})${extension}`; + } + + const uploadFile = + name === file.name + ? file + : new File([file], name, { type: file.type, lastModified: file.lastModified }); + return uploadToTerminal(baseUrl, apiKey, directory, uploadFile, sessionId); + }); + newFileUploadQueue = upload.then( + () => undefined, + () => undefined + ); + return upload; +}; + export const createDirectory = async ( baseUrl: string, apiKey: string, diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index d45a9477f7..4c72d45ef7 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -60,7 +60,7 @@ getWeekday } from '$lib/utils'; import { uploadFile } from '$lib/apis/files'; - import { getCwd, uploadToTerminal } from '$lib/apis/terminal'; + import { getCwd, uploadNewFileToTerminal } from '$lib/apis/terminal'; import { generateAutoCompletion } from '$lib/apis'; import { deleteFileById } from '$lib/apis/files'; import { getChatById } from '$lib/apis/chats'; @@ -947,7 +947,7 @@ chatId || undefined ) )?.cwd || '/'; - const uploadedFile = await uploadToTerminal( + const uploadedFile = await uploadNewFileToTerminal( filesystemUploadTerminal.url, filesystemUploadTerminal.key, cwd, @@ -958,6 +958,7 @@ if (uploadedFile) { fileItem.type = 'filesystem'; fileItem.status = 'uploaded'; + fileItem.name = uploadedFile.path.split('/').pop() || file.name; fileItem.id = uploadedFile.path; fileItem.path = uploadedFile.path; fileItem.url = uploadedFile.path;