mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-07-10 01:38:37 +00:00
resolve task outputs from user-scoped storage
This commit is contained in:
parent
d9fc758e0a
commit
1fa65f3cf3
4 changed files with 193 additions and 28 deletions
|
|
@ -633,6 +633,51 @@ export class FileReader {
|
|||
}
|
||||
}
|
||||
|
||||
private sanitizeIdentity(identity: string): string {
|
||||
return identity
|
||||
.split('@')[0]
|
||||
.replace(/[\\/*?:"<>|\s]/g, '_')
|
||||
.replace(/^\.+|\.+$/g, '');
|
||||
}
|
||||
|
||||
private getStorageIdentityCandidates(
|
||||
email: string,
|
||||
userId?: string | number | null
|
||||
): string[] {
|
||||
const candidates = [this.sanitizeIdentity(email)];
|
||||
if (userId !== undefined && userId !== null && userId !== '') {
|
||||
const rawUserId = String(userId);
|
||||
candidates.push(
|
||||
this.sanitizeIdentity(
|
||||
rawUserId.startsWith('user_') ? rawUserId : `user_${rawUserId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
return [...new Set(candidates.filter(Boolean))];
|
||||
}
|
||||
|
||||
private findProjectTaskPath(
|
||||
userHome: string,
|
||||
rootDir: 'eigent' | '.eigent',
|
||||
identities: string[],
|
||||
projectId: string,
|
||||
taskId: string
|
||||
): string | null {
|
||||
for (const identity of identities) {
|
||||
const taskPath = path.join(
|
||||
userHome,
|
||||
rootDir,
|
||||
identity,
|
||||
`project_${projectId}`,
|
||||
`task_${taskId}`
|
||||
);
|
||||
if (fs.existsSync(taskPath)) {
|
||||
return taskPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private findTaskInProjects(userDir: string, taskId: string): string | null {
|
||||
try {
|
||||
if (!fs.existsSync(userDir)) {
|
||||
|
|
@ -663,35 +708,50 @@ export class FileReader {
|
|||
private resolveTaskPaths(
|
||||
email: string,
|
||||
taskId: string,
|
||||
projectId?: string
|
||||
projectId?: string,
|
||||
userId?: string | number | null
|
||||
): {
|
||||
dirPath: string;
|
||||
logPath: string;
|
||||
} {
|
||||
const safeEmail = email
|
||||
.split('@')[0]
|
||||
.replace(/[\\/*?:"<>|\s]/g, '_')
|
||||
.replace(/^\.+|\.+$/g, '');
|
||||
const identities = this.getStorageIdentityCandidates(email, userId);
|
||||
const safeEmail = identities[0] || this.sanitizeIdentity(email);
|
||||
const userHome = app.getPath('home');
|
||||
|
||||
let dirPath: string;
|
||||
let logPath: string;
|
||||
|
||||
if (projectId) {
|
||||
dirPath = path.join(
|
||||
userHome,
|
||||
'eigent',
|
||||
safeEmail,
|
||||
`project_${projectId}`,
|
||||
`task_${taskId}`
|
||||
);
|
||||
logPath = path.join(
|
||||
userHome,
|
||||
'.eigent',
|
||||
safeEmail,
|
||||
`project_${projectId}`,
|
||||
`task_${taskId}`
|
||||
);
|
||||
dirPath =
|
||||
this.findProjectTaskPath(
|
||||
userHome,
|
||||
'eigent',
|
||||
identities,
|
||||
projectId,
|
||||
taskId
|
||||
) ||
|
||||
path.join(
|
||||
userHome,
|
||||
'eigent',
|
||||
safeEmail,
|
||||
`project_${projectId}`,
|
||||
`task_${taskId}`
|
||||
);
|
||||
logPath =
|
||||
this.findProjectTaskPath(
|
||||
userHome,
|
||||
'.eigent',
|
||||
identities,
|
||||
projectId,
|
||||
taskId
|
||||
) ||
|
||||
path.join(
|
||||
userHome,
|
||||
'.eigent',
|
||||
safeEmail,
|
||||
`project_${projectId}`,
|
||||
`task_${taskId}`
|
||||
);
|
||||
return { dirPath, logPath };
|
||||
}
|
||||
|
||||
|
|
@ -723,12 +783,14 @@ export class FileReader {
|
|||
public getFileList(
|
||||
email: string,
|
||||
taskId: string,
|
||||
projectId?: string
|
||||
projectId?: string,
|
||||
userId?: string | number | null
|
||||
): FileInfo[] {
|
||||
const { dirPath, logPath } = this.resolveTaskPaths(
|
||||
email,
|
||||
taskId,
|
||||
projectId
|
||||
projectId,
|
||||
userId
|
||||
);
|
||||
const camelLogPath = path.join(logPath, 'camel_logs');
|
||||
|
||||
|
|
|
|||
|
|
@ -1934,9 +1934,15 @@ function registerIpcHandlers() {
|
|||
|
||||
ipcMain.handle(
|
||||
'get-file-list',
|
||||
async (_, email: string, taskId: string, projectId?: string) => {
|
||||
async (
|
||||
_,
|
||||
email: string,
|
||||
taskId: string,
|
||||
projectId?: string,
|
||||
userId?: string | number | null
|
||||
) => {
|
||||
const manager = checkManagerInstance(fileReader, 'FileReader');
|
||||
return manager.getFileList(email, taskId, projectId);
|
||||
return manager.getFileList(email, taskId, projectId, userId);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ interface UploadOutcome {
|
|||
success: boolean;
|
||||
fileName: string;
|
||||
source: UploadFileSource;
|
||||
response?: unknown;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
|
|
@ -484,7 +485,8 @@ export function collectTaskUploadFiles(
|
|||
generatedFiles: GeneratedUploadFile[],
|
||||
messages: Message[],
|
||||
pendingAttaches: File[] = [],
|
||||
taskId = 'unknown_task'
|
||||
taskId = 'unknown_task',
|
||||
taskOutputFiles: FileInfo[] = []
|
||||
): UploadCandidate[] {
|
||||
const uploadCandidates: Array<
|
||||
Omit<UploadCandidate, 'uploadName'> & { relativePath?: string }
|
||||
|
|
@ -500,6 +502,28 @@ export function collectTaskUploadFiles(
|
|||
});
|
||||
}
|
||||
|
||||
for (const file of taskOutputFiles) {
|
||||
if (!file?.path || !file?.name || file.isFolder) continue;
|
||||
if (!isReadableLocalPath(file.path)) continue;
|
||||
uploadCandidates.push({
|
||||
path: file.path,
|
||||
name: file.name,
|
||||
relativePath: file.relativePath,
|
||||
source: 'project_output',
|
||||
});
|
||||
}
|
||||
|
||||
for (const file of messages.flatMap((message) => message.fileList || [])) {
|
||||
if (!file?.path || !file?.name || file.isFolder) continue;
|
||||
if (!isReadableLocalPath(file.path)) continue;
|
||||
uploadCandidates.push({
|
||||
path: file.path,
|
||||
name: file.name,
|
||||
relativePath: file.relativePath,
|
||||
source: 'project_output',
|
||||
});
|
||||
}
|
||||
|
||||
const attachmentFiles = [
|
||||
...messages.flatMap((message) => message.attaches || []),
|
||||
...pendingAttaches,
|
||||
|
|
@ -573,12 +597,21 @@ async function uploadTaskFiles(
|
|||
// TODO(file): rename endpoint to use project_id
|
||||
formData.append('task_id', uploadTargetId);
|
||||
|
||||
await uploadFile('/api/v1/chat/files/upload', formData);
|
||||
console.log('File uploaded successfully:', file.uploadName, file.source);
|
||||
const uploadResponse = await uploadFile(
|
||||
'/api/v1/chat/files/upload',
|
||||
formData
|
||||
);
|
||||
console.log('File uploaded successfully:', {
|
||||
fileName: file.uploadName,
|
||||
source: file.source,
|
||||
uploadTargetId,
|
||||
response: uploadResponse,
|
||||
});
|
||||
results.push({
|
||||
success: true,
|
||||
fileName: file.uploadName,
|
||||
source: file.source,
|
||||
response: uploadResponse,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('File upload failed:', file.uploadName, file.source, error);
|
||||
|
|
@ -1414,6 +1447,7 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
cloud_model_type,
|
||||
codex_model_type,
|
||||
email,
|
||||
user_id,
|
||||
} = getAuthStore();
|
||||
const workerList = getWorkerList();
|
||||
const { getLastUserMessage: _getLastUserMessage } = get();
|
||||
|
|
@ -3510,14 +3544,28 @@ const chatStore = (initial?: Partial<ChatStore>) =>
|
|||
'get-file-list',
|
||||
email,
|
||||
currentTaskId,
|
||||
uploadTargetId
|
||||
uploadTargetId,
|
||||
user_id
|
||||
)) as GeneratedUploadFile[]) || [];
|
||||
const taskOutputFiles = tasks[
|
||||
currentTaskId
|
||||
].taskAssigning.flatMap((agent) =>
|
||||
agent.tasks.flatMap((task) => task.fileList || [])
|
||||
);
|
||||
const filesToUpload = collectTaskUploadFiles(
|
||||
generatedFiles,
|
||||
tasks[currentTaskId].messages,
|
||||
tasks[currentTaskId].attaches,
|
||||
currentTaskId
|
||||
currentTaskId,
|
||||
taskOutputFiles
|
||||
);
|
||||
console.log('Task upload files collected:', {
|
||||
generatedFileCount: generatedFiles.length,
|
||||
taskOutputFileCount: taskOutputFiles.length,
|
||||
uploadCandidateCount: filesToUpload.length,
|
||||
uploadTargetId,
|
||||
taskId: currentTaskId,
|
||||
});
|
||||
|
||||
if (filesToUpload.length > 0) {
|
||||
const uploadResults = await uploadTaskFiles(
|
||||
|
|
|
|||
|
|
@ -332,6 +332,55 @@ describe('ChatStore - Core Functionality', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('collects generated files from task output file lists', () => {
|
||||
const uploadFiles = collectTaskUploadFiles([], [], [], 'task-789', [
|
||||
{
|
||||
path: '/Users/test/.eigent/user_1/space_x/index.html',
|
||||
name: 'index.html',
|
||||
type: 'html',
|
||||
},
|
||||
{
|
||||
path: 'https://example.com/files/remote.html',
|
||||
name: 'remote.html',
|
||||
type: 'html',
|
||||
},
|
||||
] as any);
|
||||
|
||||
expect(uploadFiles).toEqual([
|
||||
{
|
||||
path: '/Users/test/.eigent/user_1/space_x/index.html',
|
||||
name: 'index.html',
|
||||
uploadName: 'project_output/index.html',
|
||||
source: 'project_output',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps camel log upload names nested under camel_log', () => {
|
||||
const uploadFiles = collectTaskUploadFiles(
|
||||
[
|
||||
{
|
||||
path: '/Users/test/.eigent/user_1/project_p/task_t/camel_logs/agent/conv.json',
|
||||
name: 'conv.json',
|
||||
relativePath: 'agent',
|
||||
source: 'camel_log',
|
||||
},
|
||||
],
|
||||
[],
|
||||
[],
|
||||
'task-123'
|
||||
);
|
||||
|
||||
expect(uploadFiles).toEqual([
|
||||
{
|
||||
path: '/Users/test/.eigent/user_1/project_p/task_t/camel_logs/agent/conv.json',
|
||||
name: 'conv.json',
|
||||
uploadName: 'camel_log/agent/conv.json',
|
||||
source: 'camel_log',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cloud Model Platform Mapping', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue