feat(adiuvAI): drizzle-executor read action returns kind+totalSize, supports offset/length

This commit is contained in:
Roberto
2026-05-12 17:34:19 +02:00
parent 659607a1e9
commit b3d85b93f1

View File

@@ -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 };
}
}