feat(adiuvAI): drizzle-executor read action returns kind+totalSize, supports offset/length
This commit is contained in:
@@ -493,21 +493,21 @@ export class DrizzleExecutor {
|
||||
}
|
||||
|
||||
private async handleReadProjectFolderFile(payload: WsToolCall): Promise<Record<string, unknown>> {
|
||||
const { projectId, relativePath } = (payload.data ?? {}) as {
|
||||
const { projectId, relativePath, offset, length } = (payload.data ?? {}) as {
|
||||
projectId: string;
|
||||
relativePath: string;
|
||||
offset?: number;
|
||||
length?: number;
|
||||
};
|
||||
|
||||
// Re-check guards even though backend tool also guards
|
||||
if (!relativePath || relativePath.includes('..') || path.isAbsolute(relativePath)) {
|
||||
throw new ExecutorError('Access denied');
|
||||
}
|
||||
|
||||
const proj = getDb().select().from(projects).where(eq(projects.id, projectId)).get();
|
||||
if (!proj?.folderPath) return { content: '' };
|
||||
if (!proj?.folderPath) return { content: '', kind: 'missing', totalSize: 0 };
|
||||
|
||||
const abs = path.join(proj.folderPath, relativePath);
|
||||
// Confine to folderPath
|
||||
if (!path.resolve(abs).startsWith(path.resolve(proj.folderPath))) {
|
||||
throw new ExecutorError('Access denied');
|
||||
}
|
||||
@@ -518,23 +518,37 @@ export class DrizzleExecutor {
|
||||
|
||||
if (['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
|
||||
const buf = await fs.promises.readFile(abs);
|
||||
return { content: buf.toString('base64') };
|
||||
return { content: buf.toString('base64'), kind: 'image', totalSize: stat.size };
|
||||
}
|
||||
|
||||
if (stat.size > MAX_READ_SIZE_BYTES) {
|
||||
const buf = Buffer.alloc(MAX_READ_SIZE_BYTES);
|
||||
const fd = await fs.promises.open(abs, 'r');
|
||||
try {
|
||||
await fd.read(buf, 0, MAX_READ_SIZE_BYTES, 0);
|
||||
} finally {
|
||||
await fd.close();
|
||||
}
|
||||
return { content: buf.toString('utf8') + '\n[…truncated]' };
|
||||
// PDF + DOCX: return full base64; backend extracts text + slices.
|
||||
if (ext === '.pdf' || ext === '.docx') {
|
||||
const buf = await fs.promises.readFile(abs);
|
||||
return {
|
||||
content: buf.toString('base64'),
|
||||
kind: ext === '.pdf' ? 'pdf' : 'docx',
|
||||
totalSize: stat.size,
|
||||
};
|
||||
}
|
||||
|
||||
return { content: await fs.promises.readFile(abs, 'utf-8') };
|
||||
// Text: slice at offset/length on Electron side to keep WS payload small.
|
||||
const start = Math.max(0, offset ?? 0);
|
||||
const want = Math.max(1, Math.min(length ?? MAX_READ_SIZE_BYTES, MAX_READ_SIZE_BYTES));
|
||||
const end = Math.min(start + want, stat.size);
|
||||
const len = Math.max(0, end - start);
|
||||
if (len === 0) {
|
||||
return { content: '', kind: 'text', totalSize: stat.size };
|
||||
}
|
||||
const buf = Buffer.alloc(len);
|
||||
const fd = await fs.promises.open(abs, 'r');
|
||||
try {
|
||||
await fd.read(buf, 0, len, start);
|
||||
} finally {
|
||||
await fd.close();
|
||||
}
|
||||
return { content: buf.toString('utf8'), kind: 'text', totalSize: stat.size };
|
||||
} catch {
|
||||
return { content: '' };
|
||||
return { content: '', kind: 'error', totalSize: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user