mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 14:01:25 +00:00
chore: sync workspace changes
This commit is contained in:
parent
d24f983e2c
commit
bf58891edf
139 changed files with 5454 additions and 8986 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, it, expect, vi, type Mock } from "vitest";
|
||||
import { SandboxDaemonClient, SandboxDaemonError } from "../src/client.ts";
|
||||
import { SandboxAgent, SandboxAgentError } from "../src/client.ts";
|
||||
|
||||
function createMockFetch(
|
||||
response: unknown,
|
||||
|
|
@ -23,18 +23,18 @@ function createMockFetchError(status: number, problem: unknown): Mock<typeof fet
|
|||
);
|
||||
}
|
||||
|
||||
describe("SandboxDaemonClient", () => {
|
||||
describe("constructor", () => {
|
||||
it("creates client with baseUrl", () => {
|
||||
const client = new SandboxDaemonClient({
|
||||
describe("SandboxAgent", () => {
|
||||
describe("connect", () => {
|
||||
it("creates client with baseUrl", async () => {
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
});
|
||||
expect(client).toBeInstanceOf(SandboxDaemonClient);
|
||||
expect(client).toBeInstanceOf(SandboxAgent);
|
||||
});
|
||||
|
||||
it("strips trailing slash from baseUrl", async () => {
|
||||
const mockFetch = createMockFetch({ status: "ok" });
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080/",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -47,41 +47,33 @@ describe("SandboxDaemonClient", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("throws if fetch is not available", () => {
|
||||
it("throws if fetch is not available", async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
// @ts-expect-error - testing missing fetch
|
||||
globalThis.fetch = undefined;
|
||||
|
||||
expect(() => {
|
||||
new SandboxDaemonClient({
|
||||
await expect(
|
||||
SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
});
|
||||
}).toThrow("Fetch API is not available");
|
||||
})
|
||||
).rejects.toThrow("Fetch API is not available");
|
||||
|
||||
globalThis.fetch = originalFetch;
|
||||
});
|
||||
});
|
||||
|
||||
describe("connect", () => {
|
||||
it("creates client without spawn when baseUrl provided", async () => {
|
||||
const client = await SandboxDaemonClient.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
spawn: false,
|
||||
});
|
||||
expect(client).toBeInstanceOf(SandboxDaemonClient);
|
||||
});
|
||||
|
||||
it("throws when no baseUrl and spawn disabled", async () => {
|
||||
await expect(
|
||||
SandboxDaemonClient.connect({ spawn: false })
|
||||
).rejects.toThrow("baseUrl is required when autospawn is disabled");
|
||||
describe("start", () => {
|
||||
it("rejects when spawn disabled", async () => {
|
||||
await expect(SandboxAgent.start({ spawn: false })).rejects.toThrow(
|
||||
"SandboxAgent.start requires spawn to be enabled."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getHealth", () => {
|
||||
it("returns health response", async () => {
|
||||
const mockFetch = createMockFetch({ status: "ok" });
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -100,7 +92,7 @@ describe("SandboxDaemonClient", () => {
|
|||
it("returns agent list", async () => {
|
||||
const agents = { agents: [{ id: "claude", installed: true }] };
|
||||
const mockFetch = createMockFetch(agents);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -115,7 +107,7 @@ describe("SandboxDaemonClient", () => {
|
|||
it("creates session with agent", async () => {
|
||||
const response = { healthy: true, agentSessionId: "abc123" };
|
||||
const mockFetch = createMockFetch(response);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -136,7 +128,7 @@ describe("SandboxDaemonClient", () => {
|
|||
|
||||
it("encodes session ID in URL", async () => {
|
||||
const mockFetch = createMockFetch({ healthy: true });
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -155,7 +147,7 @@ describe("SandboxDaemonClient", () => {
|
|||
const mockFetch = vi.fn().mockResolvedValue(
|
||||
new Response(null, { status: 204 })
|
||||
);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -176,7 +168,7 @@ describe("SandboxDaemonClient", () => {
|
|||
it("returns events", async () => {
|
||||
const events = { events: [], hasMore: false };
|
||||
const mockFetch = createMockFetch(events);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -188,7 +180,7 @@ describe("SandboxDaemonClient", () => {
|
|||
|
||||
it("passes query parameters", async () => {
|
||||
const mockFetch = createMockFetch({ events: [], hasMore: false });
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -205,7 +197,7 @@ describe("SandboxDaemonClient", () => {
|
|||
describe("authentication", () => {
|
||||
it("includes authorization header when token provided", async () => {
|
||||
const mockFetch = createMockFetch({ status: "ok" });
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
token: "test-token",
|
||||
fetch: mockFetch,
|
||||
|
|
@ -227,7 +219,7 @@ describe("SandboxDaemonClient", () => {
|
|||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
it("throws SandboxDaemonError on non-ok response", async () => {
|
||||
it("throws SandboxAgentError on non-ok response", async () => {
|
||||
const problem = {
|
||||
type: "error",
|
||||
title: "Not Found",
|
||||
|
|
@ -235,20 +227,20 @@ describe("SandboxDaemonClient", () => {
|
|||
detail: "Session not found",
|
||||
};
|
||||
const mockFetch = createMockFetchError(404, problem);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
||||
await expect(client.getEvents("nonexistent")).rejects.toThrow(
|
||||
SandboxDaemonError
|
||||
SandboxAgentError
|
||||
);
|
||||
|
||||
try {
|
||||
await client.getEvents("nonexistent");
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(SandboxDaemonError);
|
||||
const error = e as SandboxDaemonError;
|
||||
expect(e).toBeInstanceOf(SandboxAgentError);
|
||||
const error = e as SandboxAgentError;
|
||||
expect(error.status).toBe(404);
|
||||
expect(error.problem?.title).toBe("Not Found");
|
||||
}
|
||||
|
|
@ -260,7 +252,7 @@ describe("SandboxDaemonClient", () => {
|
|||
const mockFetch = vi.fn().mockResolvedValue(
|
||||
new Response(null, { status: 204 })
|
||||
);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -284,7 +276,7 @@ describe("SandboxDaemonClient", () => {
|
|||
const mockFetch = vi.fn().mockResolvedValue(
|
||||
new Response(null, { status: 204 })
|
||||
);
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { existsSync } from "node:fs";
|
|||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { type ChildProcess } from "node:child_process";
|
||||
import { SandboxDaemonClient } from "../src/client.ts";
|
||||
import { spawnSandboxDaemon, isNodeRuntime } from "../src/spawn.ts";
|
||||
import { SandboxAgent } from "../src/client.ts";
|
||||
import { spawnSandboxAgent, isNodeRuntime } from "../src/spawn.ts";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -38,8 +38,8 @@ if (BINARY_PATH && !process.env.SANDBOX_AGENT_BIN) {
|
|||
}
|
||||
|
||||
describe.skipIf(SKIP_INTEGRATION)("Integration: spawn (local mode)", () => {
|
||||
it("spawns daemon and connects", async () => {
|
||||
const handle = await spawnSandboxDaemon({
|
||||
it("spawns server and connects", async () => {
|
||||
const handle = await spawnSandboxAgent({
|
||||
enabled: true,
|
||||
log: "silent",
|
||||
timeoutMs: 30000,
|
||||
|
|
@ -49,7 +49,7 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: spawn (local mode)", () => {
|
|||
expect(handle.baseUrl).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/);
|
||||
expect(handle.token).toBeTruthy();
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: handle.baseUrl,
|
||||
token: handle.token,
|
||||
});
|
||||
|
|
@ -61,8 +61,8 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: spawn (local mode)", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("SandboxDaemonClient.connect spawns automatically", async () => {
|
||||
const client = await SandboxDaemonClient.connect({
|
||||
it("SandboxAgent.start spawns automatically", async () => {
|
||||
const client = await SandboxAgent.start({
|
||||
spawn: { log: "silent", timeoutMs: 30000 },
|
||||
});
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: spawn (local mode)", () => {
|
|||
});
|
||||
|
||||
it("lists available agents", async () => {
|
||||
const client = await SandboxDaemonClient.connect({
|
||||
const client = await SandboxAgent.start({
|
||||
spawn: { log: "silent", timeoutMs: 30000 },
|
||||
});
|
||||
|
||||
|
|
@ -95,31 +95,31 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: spawn (local mode)", () => {
|
|||
});
|
||||
|
||||
describe.skipIf(SKIP_INTEGRATION)("Integration: connect (remote mode)", () => {
|
||||
let daemonProcess: ChildProcess;
|
||||
let serverProcess: ChildProcess;
|
||||
let baseUrl: string;
|
||||
let token: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Start daemon manually to simulate remote server
|
||||
const handle = await spawnSandboxDaemon({
|
||||
// Start server manually to simulate remote server
|
||||
const handle = await spawnSandboxAgent({
|
||||
enabled: true,
|
||||
log: "silent",
|
||||
timeoutMs: 30000,
|
||||
});
|
||||
daemonProcess = handle.child;
|
||||
serverProcess = handle.child;
|
||||
baseUrl = handle.baseUrl;
|
||||
token = handle.token;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (daemonProcess && daemonProcess.exitCode === null) {
|
||||
daemonProcess.kill("SIGTERM");
|
||||
if (serverProcess && serverProcess.exitCode === null) {
|
||||
serverProcess.kill("SIGTERM");
|
||||
await new Promise<void>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
daemonProcess.kill("SIGKILL");
|
||||
serverProcess.kill("SIGKILL");
|
||||
resolve();
|
||||
}, 5000);
|
||||
daemonProcess.once("exit", () => {
|
||||
serverProcess.once("exit", () => {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
});
|
||||
|
|
@ -128,26 +128,17 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: connect (remote mode)", () => {
|
|||
});
|
||||
|
||||
it("connects to remote server", async () => {
|
||||
const client = await SandboxDaemonClient.connect({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl,
|
||||
token,
|
||||
spawn: false,
|
||||
});
|
||||
|
||||
const health = await client.getHealth();
|
||||
expect(health.status).toBe("ok");
|
||||
});
|
||||
|
||||
it("creates client directly without spawn", () => {
|
||||
const client = new SandboxDaemonClient({
|
||||
baseUrl,
|
||||
token,
|
||||
});
|
||||
expect(client).toBeInstanceOf(SandboxDaemonClient);
|
||||
});
|
||||
|
||||
it("handles authentication", async () => {
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl,
|
||||
token,
|
||||
});
|
||||
|
|
@ -157,7 +148,7 @@ describe.skipIf(SKIP_INTEGRATION)("Integration: connect (remote mode)", () => {
|
|||
});
|
||||
|
||||
it("rejects invalid token on protected endpoints", async () => {
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl,
|
||||
token: "invalid-token",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, it, expect, vi, type Mock } from "vitest";
|
||||
import { SandboxDaemonClient } from "../src/client.ts";
|
||||
import { SandboxAgent } from "../src/client.ts";
|
||||
import type { UniversalEvent } from "../src/types.ts";
|
||||
|
||||
function createMockResponse(chunks: string[]): Response {
|
||||
|
|
@ -51,7 +51,7 @@ describe("SSE Parser", () => {
|
|||
const event = createEvent(1);
|
||||
const mockFetch = createMockFetch([`data: ${JSON.stringify(event)}\n\n`]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -73,7 +73,7 @@ describe("SSE Parser", () => {
|
|||
`data: ${JSON.stringify(event2)}\n\n`,
|
||||
]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -97,7 +97,7 @@ describe("SSE Parser", () => {
|
|||
fullMessage.slice(10),
|
||||
]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -118,7 +118,7 @@ describe("SSE Parser", () => {
|
|||
`data: ${JSON.stringify(event1)}\n\ndata: ${JSON.stringify(event2)}\n\n`,
|
||||
]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -139,7 +139,7 @@ describe("SSE Parser", () => {
|
|||
`data: ${JSON.stringify(event)}\n\n`,
|
||||
]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -158,7 +158,7 @@ describe("SSE Parser", () => {
|
|||
`data: ${JSON.stringify(event)}\r\n\r\n`,
|
||||
]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -174,7 +174,7 @@ describe("SSE Parser", () => {
|
|||
it("handles empty stream", async () => {
|
||||
const mockFetch = createMockFetch([]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
@ -190,7 +190,7 @@ describe("SSE Parser", () => {
|
|||
it("passes query parameters", async () => {
|
||||
const mockFetch = createMockFetch([]);
|
||||
|
||||
const client = new SandboxDaemonClient({
|
||||
const client = await SandboxAgent.connect({
|
||||
baseUrl: "http://localhost:8080",
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue