mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 01:04:42 +00:00
Stabilize SDK mode integration test
This commit is contained in:
parent
24e99ac5e7
commit
ec8b6afea9
274 changed files with 5412 additions and 7893 deletions
|
|
@ -5,9 +5,9 @@ const target = resolve(process.cwd(), "src/generated/openapi.ts");
|
|||
let source = readFileSync(target, "utf8");
|
||||
|
||||
const replacements = [
|
||||
["components[\"schemas\"][\"McpCommand\"]", "string"],
|
||||
["components[\"schemas\"][\"McpOAuthConfigOrDisabled\"]", "Record<string, unknown> | null"],
|
||||
["components[\"schemas\"][\"McpRemoteTransport\"]", "string"],
|
||||
['components["schemas"]["McpCommand"]', "string"],
|
||||
['components["schemas"]["McpOAuthConfigOrDisabled"]', "Record<string, unknown> | null"],
|
||||
['components["schemas"]["McpRemoteTransport"]', "string"],
|
||||
];
|
||||
|
||||
for (const [from, to] of replacements) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
* Do not make direct changes to the file.
|
||||
*/
|
||||
|
||||
|
||||
export interface paths {
|
||||
"/v1/acp": {
|
||||
get: operations["get_v1_acp_servers"];
|
||||
|
|
@ -235,7 +234,23 @@ export interface components {
|
|||
agents: components["schemas"]["AgentInfo"][];
|
||||
};
|
||||
/** @enum {string} */
|
||||
ErrorType: "invalid_request" | "conflict" | "unsupported_agent" | "agent_not_installed" | "install_failed" | "agent_process_exited" | "token_invalid" | "permission_denied" | "not_acceptable" | "unsupported_media_type" | "not_found" | "session_not_found" | "session_already_exists" | "mode_not_supported" | "stream_error" | "timeout";
|
||||
ErrorType:
|
||||
| "invalid_request"
|
||||
| "conflict"
|
||||
| "unsupported_agent"
|
||||
| "agent_not_installed"
|
||||
| "install_failed"
|
||||
| "agent_process_exited"
|
||||
| "token_invalid"
|
||||
| "permission_denied"
|
||||
| "not_acceptable"
|
||||
| "unsupported_media_type"
|
||||
| "not_found"
|
||||
| "session_not_found"
|
||||
| "session_already_exists"
|
||||
| "mode_not_supported"
|
||||
| "stream_error"
|
||||
| "timeout";
|
||||
FsActionResponse: {
|
||||
path: string;
|
||||
};
|
||||
|
|
@ -294,35 +309,37 @@ export interface components {
|
|||
directory: string;
|
||||
mcpName: string;
|
||||
};
|
||||
McpServerConfig: ({
|
||||
args?: string[];
|
||||
command: string;
|
||||
cwd?: string | null;
|
||||
enabled?: boolean | null;
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
/** Format: int64 */
|
||||
timeoutMs?: number | null;
|
||||
/** @enum {string} */
|
||||
type: "local";
|
||||
}) | ({
|
||||
bearerTokenEnvVar?: string | null;
|
||||
enabled?: boolean | null;
|
||||
envHeaders?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
oauth?: Record<string, unknown> | null | null;
|
||||
/** Format: int64 */
|
||||
timeoutMs?: number | null;
|
||||
transport?: string | null;
|
||||
/** @enum {string} */
|
||||
type: "remote";
|
||||
url: string;
|
||||
});
|
||||
McpServerConfig:
|
||||
| {
|
||||
args?: string[];
|
||||
command: string;
|
||||
cwd?: string | null;
|
||||
enabled?: boolean | null;
|
||||
env?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
/** Format: int64 */
|
||||
timeoutMs?: number | null;
|
||||
/** @enum {string} */
|
||||
type: "local";
|
||||
}
|
||||
| {
|
||||
bearerTokenEnvVar?: string | null;
|
||||
enabled?: boolean | null;
|
||||
envHeaders?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
headers?: {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
oauth?: Record<string, unknown> | null | null;
|
||||
/** Format: int64 */
|
||||
timeoutMs?: number | null;
|
||||
transport?: string | null;
|
||||
/** @enum {string} */
|
||||
type: "remote";
|
||||
url: string;
|
||||
};
|
||||
ProblemDetails: {
|
||||
detail?: string | null;
|
||||
instance?: string | null;
|
||||
|
|
@ -476,7 +493,6 @@ export type $defs = Record<string, never>;
|
|||
export type external = Record<string, never>;
|
||||
|
||||
export interface operations {
|
||||
|
||||
get_v1_acp_servers: {
|
||||
responses: {
|
||||
/** @description Active ACP server instances */
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ export type {
|
|||
|
||||
export type { InspectorUrlOptions } from "./inspector.ts";
|
||||
|
||||
export {
|
||||
InMemorySessionPersistDriver,
|
||||
} from "./types.ts";
|
||||
export { InMemorySessionPersistDriver } from "./types.ts";
|
||||
|
||||
export type {
|
||||
AcpEnvelope,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import type { ChildProcess } from "node:child_process";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import {
|
||||
assertExecutable,
|
||||
formatNonExecutableBinaryMessage,
|
||||
} from "@sandbox-agent/cli-shared";
|
||||
import { assertExecutable, formatNonExecutableBinaryMessage } from "@sandbox-agent/cli-shared";
|
||||
|
||||
export type SandboxAgentSpawnLogMode = "inherit" | "pipe" | "silent";
|
||||
|
||||
|
|
@ -40,17 +37,12 @@ export function isNodeRuntime(): boolean {
|
|||
return typeof process !== "undefined" && !!process.versions?.node;
|
||||
}
|
||||
|
||||
export async function spawnSandboxAgent(
|
||||
options: SandboxAgentSpawnOptions,
|
||||
fetcher?: typeof fetch,
|
||||
): Promise<SandboxAgentSpawnHandle> {
|
||||
export async function spawnSandboxAgent(options: SandboxAgentSpawnOptions, fetcher?: typeof fetch): Promise<SandboxAgentSpawnHandle> {
|
||||
if (!isNodeRuntime()) {
|
||||
throw new Error("Autospawn requires a Node.js runtime.");
|
||||
}
|
||||
|
||||
const {
|
||||
spawn,
|
||||
} = await import("node:child_process");
|
||||
const { spawn } = await import("node:child_process");
|
||||
const crypto = await import("node:crypto");
|
||||
const fs = await import("node:fs");
|
||||
const path = await import("node:path");
|
||||
|
|
@ -82,17 +74,11 @@ export async function spawnSandboxAgent(
|
|||
bunInstallBlocks: [
|
||||
{
|
||||
label: "Project install",
|
||||
commands: [
|
||||
`bun pm trust ${TRUST_PACKAGES}`,
|
||||
"bun add sandbox-agent",
|
||||
],
|
||||
commands: [`bun pm trust ${TRUST_PACKAGES}`, "bun add sandbox-agent"],
|
||||
},
|
||||
{
|
||||
label: "Global install",
|
||||
commands: [
|
||||
`bun pm -g trust ${TRUST_PACKAGES}`,
|
||||
"bun add -g sandbox-agent",
|
||||
],
|
||||
commands: [`bun pm -g trust ${TRUST_PACKAGES}`, "bun add -g sandbox-agent"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
|
@ -189,13 +175,7 @@ async function getFreePort(net: typeof import("node:net"), host: string): Promis
|
|||
});
|
||||
}
|
||||
|
||||
async function waitForHealth(
|
||||
baseUrl: string,
|
||||
fetcher: typeof fetch | undefined,
|
||||
timeoutMs: number,
|
||||
child: ChildProcess,
|
||||
token: string,
|
||||
): Promise<void> {
|
||||
async function waitForHealth(baseUrl: string, fetcher: typeof fetch | undefined, timeoutMs: number, child: ChildProcess, token: string): Promise<void> {
|
||||
if (!fetcher) {
|
||||
throw new Error("Fetch API is not available; provide a fetch implementation.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
import type {
|
||||
AnyMessage,
|
||||
NewSessionRequest,
|
||||
SessionConfigOption,
|
||||
SessionModeState,
|
||||
} from "acp-http-client";
|
||||
import type { AnyMessage, NewSessionRequest, SessionConfigOption, SessionModeState } from "acp-http-client";
|
||||
import type { components, operations } from "./generated/openapi.ts";
|
||||
|
||||
export type ProblemDetails = components["schemas"]["ProblemDetails"];
|
||||
|
|
@ -84,10 +79,7 @@ export interface ProcessTerminalErrorFrame {
|
|||
message: string;
|
||||
}
|
||||
|
||||
export type ProcessTerminalServerFrame =
|
||||
| ProcessTerminalReadyFrame
|
||||
| ProcessTerminalExitFrame
|
||||
| ProcessTerminalErrorFrame;
|
||||
export type ProcessTerminalServerFrame = ProcessTerminalReadyFrame | ProcessTerminalExitFrame | ProcessTerminalErrorFrame;
|
||||
|
||||
export type TerminalReadyStatus = ProcessTerminalReadyFrame;
|
||||
export type TerminalExitStatus = ProcessTerminalExitFrame;
|
||||
|
|
@ -163,10 +155,7 @@ export class InMemorySessionPersistDriver implements SessionPersistDriver {
|
|||
|
||||
constructor(options: InMemorySessionPersistDriverOptions = {}) {
|
||||
this.maxSessions = normalizeCap(options.maxSessions, DEFAULT_MAX_SESSIONS);
|
||||
this.maxEventsPerSession = normalizeCap(
|
||||
options.maxEventsPerSession,
|
||||
DEFAULT_MAX_EVENTS_PER_SESSION,
|
||||
);
|
||||
this.maxEventsPerSession = normalizeCap(options.maxEventsPerSession, DEFAULT_MAX_EVENTS_PER_SESSION);
|
||||
}
|
||||
|
||||
async getSession(id: string): Promise<SessionRecord | null> {
|
||||
|
|
@ -245,15 +234,9 @@ export class InMemorySessionPersistDriver implements SessionPersistDriver {
|
|||
function cloneSessionRecord(session: SessionRecord): SessionRecord {
|
||||
return {
|
||||
...session,
|
||||
sessionInit: session.sessionInit
|
||||
? (JSON.parse(JSON.stringify(session.sessionInit)) as SessionRecord["sessionInit"])
|
||||
: undefined,
|
||||
configOptions: session.configOptions
|
||||
? (JSON.parse(JSON.stringify(session.configOptions)) as SessionRecord["configOptions"])
|
||||
: undefined,
|
||||
modes: session.modes
|
||||
? (JSON.parse(JSON.stringify(session.modes)) as SessionRecord["modes"])
|
||||
: session.modes,
|
||||
sessionInit: session.sessionInit ? (JSON.parse(JSON.stringify(session.sessionInit)) as SessionRecord["sessionInit"]) : undefined,
|
||||
configOptions: session.configOptions ? (JSON.parse(JSON.stringify(session.configOptions)) as SessionRecord["configOptions"]) : undefined,
|
||||
modes: session.modes ? (JSON.parse(JSON.stringify(session.modes)) as SessionRecord["modes"]) : session.modes,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -277,11 +260,7 @@ type JsonRequestBody<T> = T extends {
|
|||
? B
|
||||
: never;
|
||||
|
||||
type QueryParams<T> = T extends { parameters: { query: infer Q } }
|
||||
? Q
|
||||
: T extends { parameters: { query?: infer Q } }
|
||||
? Q
|
||||
: never;
|
||||
type QueryParams<T> = T extends { parameters: { query: infer Q } } ? Q : T extends { parameters: { query?: infer Q } } ? Q : never;
|
||||
|
||||
function normalizeCap(value: number | undefined, fallback: number): number {
|
||||
if (!Number.isFinite(value) || (value ?? 0) < 1) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function prepareMockAgentDataHome(dataHome: string): Record<string, strin
|
|||
runtimeEnv.XDG_DATA_HOME = dataHome;
|
||||
}
|
||||
|
||||
const nodeScript = String.raw`#!/usr/bin/env node
|
||||
const nodeScript = String.raw`#!/usr/bin/env node
|
||||
const { createInterface } = require("node:readline");
|
||||
|
||||
let nextSession = 0;
|
||||
|
|
@ -225,13 +225,9 @@ rl.on("line", (line) => {
|
|||
const processDir = join(installDir, "agent_processes");
|
||||
mkdirSync(processDir, { recursive: true });
|
||||
|
||||
const runner = process.platform === "win32"
|
||||
? join(processDir, "mock-acp.cmd")
|
||||
: join(processDir, "mock-acp");
|
||||
const runner = process.platform === "win32" ? join(processDir, "mock-acp.cmd") : join(processDir, "mock-acp");
|
||||
|
||||
const scriptFile = process.platform === "win32"
|
||||
? join(processDir, "mock-acp.js")
|
||||
: runner;
|
||||
const scriptFile = process.platform === "win32" ? join(processDir, "mock-acp.js") : runner;
|
||||
|
||||
writeFileSync(scriptFile, nodeScript);
|
||||
|
||||
|
|
|
|||
|
|
@ -538,9 +538,17 @@ describe("Integration: TypeScript SDK flat session API", () => {
|
|||
const session = await sdk.createSession({ agent: "mock" });
|
||||
await session.setMode("plan");
|
||||
|
||||
const modes = await session.getModes();
|
||||
expect(modes?.currentModeId).toBe("plan");
|
||||
expect((await session.getConfigOptions()).find((o) => o.category === "mode")?.currentValue).toBe("plan");
|
||||
const modes = await waitForAsync(async () => {
|
||||
const current = await session.getModes();
|
||||
return current?.currentModeId === "plan" ? current : null;
|
||||
});
|
||||
expect(modes.currentModeId).toBe("plan");
|
||||
|
||||
const modeOption = await waitForAsync(async () => {
|
||||
const option = (await session.getConfigOptions()).find((o) => o.category === "mode");
|
||||
return option?.currentValue === "plan" ? option : null;
|
||||
});
|
||||
expect(modeOption.currentValue).toBe("plan");
|
||||
|
||||
await sdk.dispose();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue