mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Add exploreFileSystem() to DaemonClient + E2E tests
- Add exploreFileSystem(agentId, path, mode) method to DaemonClient
- Sends file_explorer_request, waits for file_explorer_response
- Returns {path, mode, directory, file, error} payload
- Add 3 E2E tests:
- "lists directory contents" - verifies entries with name, kind, size
- "reads file contents" - verifies file object with path, kind, content
- "returns error for non-existent path" - verifies error handling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from "vitest";
|
||||
import { mkdtempSync, writeFileSync, existsSync, rmSync } from "fs";
|
||||
import { mkdtempSync, writeFileSync, existsSync, rmSync, mkdirSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import path from "path";
|
||||
import {
|
||||
@@ -1334,4 +1334,124 @@ describe("daemon E2E", () => {
|
||||
60000 // 1 minute timeout
|
||||
);
|
||||
});
|
||||
|
||||
describe("exploreFileSystem", () => {
|
||||
test(
|
||||
"lists directory contents",
|
||||
async () => {
|
||||
const cwd = tmpCwd();
|
||||
|
||||
// Create test files and directories
|
||||
writeFileSync(path.join(cwd, "test.txt"), "hello world\n");
|
||||
writeFileSync(path.join(cwd, "data.json"), '{"key": "value"}\n');
|
||||
mkdirSync(path.join(cwd, "subdir"));
|
||||
writeFileSync(path.join(cwd, "subdir", "nested.txt"), "nested content\n");
|
||||
|
||||
// Create agent in the directory
|
||||
const agent = await ctx.client.createAgent({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
title: "File Explorer Test",
|
||||
});
|
||||
|
||||
expect(agent.id).toBeTruthy();
|
||||
expect(agent.status).toBe("idle");
|
||||
|
||||
// List directory contents
|
||||
const result = await ctx.client.exploreFileSystem(agent.id, cwd, "list");
|
||||
|
||||
// Verify listing returned without error
|
||||
expect(result.error).toBeNull();
|
||||
expect(result.mode).toBe("list");
|
||||
expect(result.directory).toBeTruthy();
|
||||
expect(result.directory!.entries).toBeTruthy();
|
||||
|
||||
// Find expected entries
|
||||
const entries = result.directory!.entries;
|
||||
const testTxt = entries.find((e) => e.name === "test.txt");
|
||||
const dataJson = entries.find((e) => e.name === "data.json");
|
||||
const subdir = entries.find((e) => e.name === "subdir");
|
||||
|
||||
expect(testTxt).toBeTruthy();
|
||||
expect(testTxt!.kind).toBe("file");
|
||||
expect(testTxt!.size).toBeGreaterThan(0);
|
||||
|
||||
expect(dataJson).toBeTruthy();
|
||||
expect(dataJson!.kind).toBe("file");
|
||||
|
||||
expect(subdir).toBeTruthy();
|
||||
expect(subdir!.kind).toBe("directory");
|
||||
|
||||
// Cleanup
|
||||
await ctx.client.deleteAgent(agent.id);
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
},
|
||||
60000 // 1 minute timeout
|
||||
);
|
||||
|
||||
test(
|
||||
"reads file contents",
|
||||
async () => {
|
||||
const cwd = tmpCwd();
|
||||
const testContent = "This is test file content.\nLine 2.";
|
||||
const testFile = path.join(cwd, "readme.txt");
|
||||
writeFileSync(testFile, testContent);
|
||||
|
||||
// Create agent in the directory
|
||||
const agent = await ctx.client.createAgent({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
title: "File Read Test",
|
||||
});
|
||||
|
||||
expect(agent.id).toBeTruthy();
|
||||
|
||||
// Read file contents
|
||||
const result = await ctx.client.exploreFileSystem(agent.id, testFile, "file");
|
||||
|
||||
// Verify file read
|
||||
expect(result.error).toBeNull();
|
||||
expect(result.mode).toBe("file");
|
||||
expect(result.file).toBeTruthy();
|
||||
// Server may return basename or full path
|
||||
expect(result.file!.path).toContain("readme.txt");
|
||||
expect(result.file!.kind).toBe("text");
|
||||
expect(result.file!.content).toBe(testContent);
|
||||
expect(result.file!.size).toBe(testContent.length);
|
||||
|
||||
// Cleanup
|
||||
await ctx.client.deleteAgent(agent.id);
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
},
|
||||
60000 // 1 minute timeout
|
||||
);
|
||||
|
||||
test(
|
||||
"returns error for non-existent path",
|
||||
async () => {
|
||||
const cwd = tmpCwd();
|
||||
|
||||
// Create agent
|
||||
const agent = await ctx.client.createAgent({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
title: "File Explorer Error Test",
|
||||
});
|
||||
|
||||
expect(agent.id).toBeTruthy();
|
||||
|
||||
// Try to list non-existent path
|
||||
const nonExistent = path.join(cwd, "does-not-exist");
|
||||
const result = await ctx.client.exploreFileSystem(agent.id, nonExistent, "list");
|
||||
|
||||
// Should return error
|
||||
expect(result.error).toBeTruthy();
|
||||
|
||||
// Cleanup
|
||||
await ctx.client.deleteAgent(agent.id);
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
},
|
||||
60000 // 1 minute timeout
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -432,6 +432,63 @@ export class DaemonClient {
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// File Explorer
|
||||
// ============================================================================
|
||||
|
||||
async exploreFileSystem(
|
||||
agentId: string,
|
||||
path: string,
|
||||
mode: "list" | "file" = "list"
|
||||
): Promise<{
|
||||
path: string;
|
||||
mode: "list" | "file";
|
||||
directory: {
|
||||
path: string;
|
||||
entries: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
kind: "file" | "directory";
|
||||
size: number;
|
||||
modifiedAt: string;
|
||||
}>;
|
||||
} | null;
|
||||
file: {
|
||||
path: string;
|
||||
kind: "text" | "image" | "binary";
|
||||
encoding: "utf-8" | "base64" | "none";
|
||||
content?: string;
|
||||
mimeType?: string;
|
||||
size: number;
|
||||
modifiedAt: string;
|
||||
} | null;
|
||||
error: string | null;
|
||||
}> {
|
||||
const startPosition = this.messageQueue.length;
|
||||
|
||||
this.send({ type: "file_explorer_request", agentId, path, mode });
|
||||
|
||||
return this.waitFor(
|
||||
(msg) => {
|
||||
if (
|
||||
msg.type === "file_explorer_response" &&
|
||||
msg.payload.agentId === agentId
|
||||
) {
|
||||
return {
|
||||
path: msg.payload.path,
|
||||
mode: msg.payload.mode,
|
||||
directory: msg.payload.directory,
|
||||
file: msg.payload.file,
|
||||
error: msg.payload.error,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
10000,
|
||||
{ skipQueueBefore: startPosition }
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Permissions
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user