feat: daytona sandboxes (#59)

* feat: daytona sandboxes

Signed-off-by: Ivan Dagelic <dagelic.ivan@gmail.com>

* fix: change daytona snapshot namespace

Signed-off-by: Ivan Dagelic <dagelic.ivan@gmail.com>

* update yarn.lock and run formatter

---------

Signed-off-by: Ivan Dagelic <dagelic.ivan@gmail.com>
Co-authored-by: Brace Sproul <braceasproul@gmail.com>
This commit is contained in:
Ivan Dagelic 2025-05-31 00:59:14 +02:00 • committed by GitHub
parent c0d312601f
commit 65b68a1a42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 323 additions and 323 deletions

View file

@ -43,8 +43,8 @@ ANTHROPIC_API_KEY=""
OPENAI_API_KEY=""
GOOGLE_API_KEY=""
# E2B API key for accessing and modifying the code in the cloud sandbox.
E2B_API_KEY=""
# Daytona API key for accessing and modifying the code in the cloud sandbox.
DAYTONA_API_KEY=""
# Your GitHub PAT with access to the repositories you want to modify.
GITHUB_PAT=""

View file

@ -9,8 +9,8 @@ ANTHROPIC_API_KEY=""
OPENAI_API_KEY=""
GOOGLE_API_KEY=""
# E2B API key for accessing and modifying the code in the cloud sandbox.
E2B_API_KEY=""
# Daytona API key for accessing and modifying the code in the cloud sandbox.
DAYTONA_API_KEY=""
# Your GitHub PAT with access to the repositories you want to modify.
GITHUB_PAT=""

View file

@ -1,29 +0,0 @@
# Always start from the base E2B image
FROM e2bdev/code-interpreter:latest
# Install dependencies in a single layer
RUN apt-get update && apt-get install -y \
git \
curl \
ripgrep \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js (LTS version) via NodeSource
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
# Update corepack to latest version (to avoid outdated signature issues)
RUN npm install -g corepack@latest
# Enable corepack for yarn and pnpm (creates shims)
RUN corepack enable yarn pnpm
# Pre-download specific versions to avoid download on first use
RUN corepack prepare yarn@stable --activate \
&& corepack prepare pnpm@latest --activate
# Set the working directory
WORKDIR /app

View file

@ -1,18 +0,0 @@
# This is a config for E2B sandbox template.
# You can use template ID (eh0860emqx28qyxmbctu) to create a sandbox:
# Python SDK
# from e2b import Sandbox, AsyncSandbox
# sandbox = Sandbox("eh0860emqx28qyxmbctu") # Sync sandbox
# sandbox = await AsyncSandbox.create("eh0860emqx28qyxmbctu") # Async sandbox
# JS SDK
# import { Sandbox } from 'e2b'
# const sandbox = await Sandbox.create('eh0860emqx28qyxmbctu')
team_id = "af4eaf9c-3d40-415c-bb98-4831c47745cf"
memory_mb = 2_048
cpu_count = 4
start_cmd = "/root/.jupyter/start-up.sh"
dockerfile = "e2b.Dockerfile"
template_id = "eh0860emqx28qyxmbctu"

View file

@ -22,7 +22,7 @@
"run:from-plan": "npx tsx scripts/run-from-plan.ts"
},
"dependencies": {
"@e2b/code-interpreter": "1.2.0-beta.4",
"@daytonaio/sdk": "^0.18.1",
"@langchain/anthropic": "^0.3.20",
"@langchain/core": "^0.3.56",
"@langchain/google-genai": "^0.2.9",

View file

@ -3,6 +3,7 @@ import { Client } from "@langchain/langgraph-sdk";
import { v4 as uuidv4 } from "uuid";
import { graph } from "../src/index.js";
import { createLogger, LogLevel } from "../src/utils/logger.js";
import { SANDBOX_ROOT_DIR } from "../src/constants.js";
const logger = createLogger(LogLevel.INFO, "From Plan Script");
@ -88,11 +89,11 @@ The user wants to implement a GitHub OAuth authentication server in the \`/apps/
3. Making pull requests and pushing changes
## Codebase Files and Descriptions
- **Project root**: \`/home/user/open-swe/\` - Main monorepo directory
- **Apps directory**: \`/home/user/open-swe/apps/\` - Contains multiple apps including auth, docs, and open-swe
- **Auth app directory**: \`/home/user/open-swe/apps/auth/\` - Target directory for implementation, currently contains only scaffolding
- **Auth package.json**: \`/home/user/open-swe/apps/auth/package.json\` - Contains basic TypeScript/Node.js setup with name "@open-swe/auth", includes dev dependencies for TypeScript, Jest, ESLint, Prettier
- **Auth src directory**: \`/home/user/open-swe/apps/auth/src/\` - Contains only an empty \`index.ts\` file
- **Project root**: \`${SANDBOX_ROOT_DIR}/open-swe/\` - Main monorepo directory
- **Apps directory**: \`${SANDBOX_ROOT_DIR}/open-swe/apps/\` - Contains multiple apps including auth, docs, and open-swe
- **Auth app directory**: \`${SANDBOX_ROOT_DIR}/open-swe/apps/auth/\` - Target directory for implementation, currently contains only scaffolding
- **Auth package.json**: \`${SANDBOX_ROOT_DIR}/open-swe/apps/auth/package.json\` - Contains basic TypeScript/Node.js setup with name "@open-swe/auth", includes dev dependencies for TypeScript, Jest, ESLint, Prettier
- **Auth src directory**: \`${SANDBOX_ROOT_DIR}/open-swe/apps/auth/src/\` - Contains only an empty \`index.ts\` file
- **Auth config files**: Directory includes standard config files (.gitignore, .dockerignore, .prettierrc, eslint.config.js, jest.config.js, tsconfig.json, turbo.json)
## Key Repository Insights and Learnings

View file

@ -3,3 +3,5 @@ export const TIMEOUT_MS = 300_000; // 5 minutes
export const TIMEOUT_EXTENSION_OPT = {
timeoutMs: TIMEOUT_MS,
};
export const SANDBOX_ROOT_DIR = "/home/daytona";
export const SNAPSHOT_NAME = "daytonaio/langchain-open-swe:0.1.0";

View file

@ -7,7 +7,7 @@ import {
} from "../tools/index.js";
import { getRepoAbsolutePath } from "../utils/git/index.js";
import { formatPlanPrompt } from "../utils/plan-prompt.js";
import { pauseSandbox } from "../utils/sandbox.js";
import { stopSandbox } from "../utils/sandbox.js";
import { createLogger, LogLevel } from "../utils/logger.js";
import { getCurrentTask } from "../utils/current-task.js";
import { getMessageContentString } from "../utils/message/content.js";
@ -134,11 +134,11 @@ export async function generateAction(
]);
const hasToolCalls = !!response.tool_calls?.length;
// No tool calls means the graph is going to end. Pause the sandbox.
// No tool calls means the graph is going to end. Stop the sandbox.
let newSandboxSessionId: string | undefined;
if (!hasToolCalls && state.sandboxSessionId) {
logger.info("No tool calls found. Pausing sandbox...");
newSandboxSessionId = await pauseSandbox(state.sandboxSessionId);
logger.info("No tool calls found. Stopping sandbox...");
newSandboxSessionId = await stopSandbox(state.sandboxSessionId);
}
logger.info("Generated action", {

View file

@ -1,4 +1,4 @@
import { Sandbox } from "@e2b/code-interpreter";
import { Sandbox } from "@daytonaio/sdk";
import { createLogger, LogLevel } from "../utils/logger.js";
import {
GraphState,
@ -6,7 +6,6 @@ import {
GraphUpdate,
TargetRepository,
} from "../types.js";
import { TIMEOUT_EXTENSION_OPT } from "../constants.js";
import {
checkoutBranch,
configureGitUserInRepo,
@ -15,11 +14,11 @@ import {
pullLatestChanges,
} from "../utils/git/index.js";
import { getSandboxErrorFields } from "../utils/sandbox-error-fields.js";
import { daytonaClient } from "../utils/sandbox.js";
import { SNAPSHOT_NAME } from "../constants.js";
const logger = createLogger(LogLevel.INFO, "Initialize");
const SANDBOX_TEMPLATE_ID = "eh0860emqx28qyxmbctu";
async function cloneRepo(sandbox: Sandbox, targetRepository: TargetRepository) {
if (!process.env.GITHUB_PAT) {
throw new Error("GITHUB_PAT environment variable not set.");
@ -39,10 +38,7 @@ async function cloneRepo(sandbox: Sandbox, targetRepository: TargetRepository) {
logger.info("Cloning repository", {
command: gitCloneCommand.join(" "),
});
return await sandbox.commands.run(
gitCloneCommand.join(" "),
TIMEOUT_EXTENSION_OPT,
);
return await sandbox.process.executeCommand(gitCloneCommand.join(" "));
} catch (e) {
const errorFields = getSandboxErrorFields(e);
logger.error("Failed to clone repository", errorFields ?? e);
@ -78,13 +74,10 @@ export async function initialize(
sandboxSessionId,
});
// Resume the sandbox if the session ID is in the config.
const newSandbox = await Sandbox.resume(
sandboxSessionId,
TIMEOUT_EXTENSION_OPT,
);
await pullLatestChanges(absoluteRepoDir, newSandbox);
const existingSandbox = await daytonaClient().get(sandboxSessionId);
await pullLatestChanges(absoluteRepoDir, existingSandbox);
return {
sandboxSessionId: newSandbox.sandboxId,
sandboxSessionId: existingSandbox.id,
};
} catch (e) {
// Error thrown, log it and continue. Will create a new sandbox session since the resumption failed.
@ -93,16 +86,15 @@ export async function initialize(
}
logger.info("Creating sandbox...");
const sandbox = await Sandbox.create(
SANDBOX_TEMPLATE_ID,
TIMEOUT_EXTENSION_OPT,
);
const sandbox = await daytonaClient().create({
image: SNAPSHOT_NAME,
});
const res = await cloneRepo(sandbox, targetRepository);
if (res.error) {
if (res.exitCode !== 0) {
// TODO: This should probably be an interrupt.
logger.error("Failed to clone repository", res.error);
throw new Error(`Failed to clone repository.\n${res.error}`);
logger.error("Failed to clone repository", res.result);
throw new Error(`Failed to clone repository.\n${res.result}`);
}
logger.info("Repository cloned successfully.");
@ -123,7 +115,7 @@ export async function initialize(
}
return {
sandboxSessionId: sandbox.sandboxId,
sandboxSessionId: sandbox.id,
targetRepository,
};
}

View file

@ -5,7 +5,7 @@ import {
HumanInterrupt,
HumanResponse,
} from "@langchain/langgraph/prebuilt";
import { resumeSandbox } from "../utils/sandbox.js";
import { startSandbox } from "../utils/sandbox.js";
export async function interruptPlan(state: GraphState): Promise<Command> {
const { proposedPlan } = state;
@ -36,8 +36,7 @@ export async function interruptPlan(state: GraphState): Promise<Command> {
}
if (interruptRes.type === "accept") {
const newSandboxSessionId = (await resumeSandbox(state.sandboxSessionId))
.sandboxId;
const newSandboxSessionId = (await startSandbox(state.sandboxSessionId)).id;
// Plan was accepted, route to the generate-action node to start taking actions.
return new Command({
@ -55,8 +54,7 @@ export async function interruptPlan(state: GraphState): Promise<Command> {
}
if (interruptRes.type === "edit") {
const newSandboxSessionId = (await resumeSandbox(state.sandboxSessionId))
.sandboxId;
const newSandboxSessionId = (await startSandbox(state.sandboxSessionId)).id;
// Plan was edited, route to the generate-action node to start taking actions.
const editedPlan = (interruptRes.args as ActionRequest).args.plan

View file

@ -1,5 +1,4 @@
import { GraphConfig, GraphState, GraphUpdate, PlanItem } from "../types.js";
import { Sandbox } from "@e2b/code-interpreter";
import {
checkoutBranchAndCommit,
createPullRequest,
@ -13,6 +12,7 @@ import { loadModel, Task } from "../utils/load-model.js";
import { formatPlanPromptWithSummaries } from "../utils/plan-prompt.js";
import { isHumanMessage, ToolMessage } from "@langchain/core/messages";
import { getMessageContentString } from "../utils/message/content.js";
import { daytonaClient } from "../utils/sandbox.js";
const logger = createLogger(LogLevel.INFO, "Open PR");
@ -66,7 +66,7 @@ export async function openPullRequest(
);
}
const sandbox = await Sandbox.connect(sandboxSessionId);
const sandbox = await daytonaClient().get(sandboxSessionId);
const { owner, repo } = state.targetRepository;

View file

@ -2,7 +2,7 @@ import { isAIMessage } from "@langchain/core/messages";
import { GraphState } from "../types.js";
import { HumanInterrupt, HumanResponse } from "@langchain/langgraph/prebuilt";
import { END, interrupt, Command } from "@langchain/langgraph";
import { pauseSandbox, resumeSandbox } from "../utils/sandbox.js";
import { stopSandbox, startSandbox } from "../utils/sandbox.js";
const constructDescription = (helpRequest: string): string => {
return `The agent has requested help. Here is the help request:
@ -21,7 +21,7 @@ export async function requestHelp(state: GraphState): Promise<Command> {
if (!sandboxSessionId) {
throw new Error("Sandbox session ID not found.");
}
await pauseSandbox(sandboxSessionId);
await stopSandbox(sandboxSessionId);
const toolCall = lastMessage.tool_calls[0];
@ -52,7 +52,7 @@ export async function requestHelp(state: GraphState): Promise<Command> {
if (typeof interruptRes.args !== "string") {
throw new Error("Interrupt response expected to be a string.");
}
await resumeSandbox(sandboxSessionId);
await startSandbox(sandboxSessionId);
return new Command({
goto: "generate-action",
update: {

View file

@ -11,13 +11,13 @@ import {
getChangedFilesStatus,
getRepoAbsolutePath,
} from "../utils/git/index.js";
import { Sandbox } from "@e2b/code-interpreter";
import {
formatBadArgsError,
zodSchemaToString,
} from "../utils/zod-to-string.js";
import { Command } from "@langchain/langgraph";
import { truncateOutput } from "../utils/truncate-outputs.js";
import { daytonaClient } from "../utils/sandbox.js";
const logger = createLogger(LogLevel.INFO, "TakeAction");
@ -127,7 +127,7 @@ export async function takeAction(
// Always check if there are changed files after running a tool.
// If there are, commit them.
const sandbox = await Sandbox.connect(state.sandboxSessionId);
const sandbox = await daytonaClient().get(state.sandboxSessionId);
const changedFiles = await getChangedFilesStatus(
getRepoAbsolutePath(state.targetRepository),
sandbox,

View file

@ -7,7 +7,7 @@ import { sessionPlanTool } from "../../../tools/index.js";
import { GraphConfig } from "../../../types.js";
import { loadModel, Task } from "../../../utils/load-model.js";
import { PlannerGraphState, PlannerGraphUpdate } from "../types.js";
import { pauseSandbox } from "../../../utils/sandbox.js";
import { stopSandbox } from "../../../utils/sandbox.js";
const systemPrompt = `You are operating as a terminal-based agentic coding assistant built by LangChain. It wraps LLM models to enable natural language interaction with a local codebase. You are expected to be precise, safe, and helpful.
@ -68,8 +68,8 @@ export async function generatePlan(
let newSessionId: string | undefined;
if (state.sandboxSessionId) {
// Pause before returning, as the next step will be to interrupt the graph.
newSessionId = await pauseSandbox(state.sandboxSessionId);
// Stop before returning, as the next step will be to interrupt the graph.
newSessionId = await stopSandbox(state.sandboxSessionId);
}
return {

View file

@ -2,11 +2,12 @@ import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { applyPatch } from "diff";
import { GraphState } from "../types.js";
import { Sandbox } from "@e2b/code-interpreter";
import { readFile, writeFile } from "../utils/read-write.js";
import { getCurrentTaskInput } from "@langchain/langgraph";
import { fixGitPatch } from "../utils/diff.js";
import { createLogger, LogLevel } from "../utils/logger.js";
import { daytonaClient } from "../utils/sandbox.js";
import { SANDBOX_ROOT_DIR } from "../constants.js";
const logger = createLogger(LogLevel.INFO, "ApplyPatchTool");
@ -19,9 +20,9 @@ const applyPatchToolSchema = z.object({
file_path: z.string().describe("The file path to apply the diff to."),
workdir: z
.string()
.default("/home/user")
.default(SANDBOX_ROOT_DIR)
.describe(
"The working directory for the command. Ensure this path is NOT included in any command arguments, as it will be added automatically. Defaults to '/home/user' as this is the root directory of the sandbox.",
`The working directory for the command. Ensure this path is NOT included in any command arguments, as it will be added automatically. Defaults to '${SANDBOX_ROOT_DIR}' as this is the root directory of the sandbox.`,
),
});
@ -38,7 +39,7 @@ export const applyPatchTool = tool(
const { diff, file_path, workdir } = input;
const sandbox = await Sandbox.connect(sandboxSessionId);
const sandbox = await daytonaClient().get(sandboxSessionId);
const { success: readFileSuccess, output: readFileOutput } = await readFile(
sandbox,

View file

@ -1,11 +1,12 @@
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { Sandbox } from "@e2b/code-interpreter";
import { Sandbox } from "@daytonaio/sdk";
import { GraphState } from "../types.js";
import { getCurrentTaskInput } from "@langchain/langgraph";
import { TIMEOUT_MS } from "../constants.js";
import { getSandboxErrorFields } from "../utils/sandbox-error-fields.js";
import { createLogger, LogLevel } from "../utils/logger.js";
import { daytonaClient } from "../utils/sandbox.js";
import { SANDBOX_ROOT_DIR } from "../constants.js";
const logger = createLogger(LogLevel.INFO, "ShellTool");
@ -20,9 +21,9 @@ const shellToolSchema = z.object({
command: z.array(z.string()).describe("The command to run"),
workdir: z
.string()
.default("/home/user")
.default(SANDBOX_ROOT_DIR)
.describe(
"The working directory for the command. Ensure this path is NOT included in any command arguments, as it will be added automatically. Defaults to '/home/user' as this is the root directory of the sandbox.",
`The working directory for the command. Ensure this path is NOT included in any command arguments, as it will be added automatically. Defaults to '${SANDBOX_ROOT_DIR}' as this is the root directory of the sandbox.`,
),
timeout: z
.number()
@ -48,28 +49,29 @@ export const shellTool = tool(
);
}
sandbox = await Sandbox.connect(sandboxSessionId);
sandbox = await daytonaClient().get(sandboxSessionId);
const { command, workdir, timeout } = input;
const result = await sandbox.commands.run(command.join(" "), {
timeoutMs: timeout ?? DEFAULT_COMMAND_TIMEOUT,
cwd: workdir,
envs: DEFAULT_ENV,
});
const response = await sandbox.process.executeCommand(
command.join(" "),
workdir,
DEFAULT_ENV,
timeout ?? DEFAULT_COMMAND_TIMEOUT,
);
if (result.error) {
if (response.exitCode !== 0) {
logger.error("Failed to run command", {
error: result.error,
error_result: result,
error: response.result,
error_result: response,
input,
});
return {
result: `Command failed. Exit code: ${result.exitCode}\nError: ${result.error}\nStderr:\n${result.stderr}`,
result: `Command failed. Exit code: ${response.exitCode}\nResult: ${response.result}\nStdout:\n${response.artifacts?.stdout}`,
status: "error",
};
}
return {
result: result.stdout,
result: response.result,
status: "success",
};
} catch (e) {
@ -80,7 +82,7 @@ export const shellTool = tool(
error: errorFields,
});
return {
result: `Command failed. Exit code: ${errorFields.exitCode}\nError: ${errorFields.error}\nStderr:\n${errorFields.stderr}\nStdout:\n${errorFields.stdout}`,
result: `Command failed. Exit code: ${errorFields.exitCode}\nError: ${errorFields.result}\nStdout:\n${errorFields.artifacts?.stdout}`,
status: "error",
};
}
@ -97,17 +99,6 @@ export const shellTool = tool(
"FAILED TO RUN COMMAND: " +
(e instanceof Error ? e.message : "Unknown error"),
);
} finally {
try {
if (sandbox) {
// Add an extra 5 min timeout to the sandbox.
await sandbox.setTimeout(TIMEOUT_MS);
}
} catch (_) {
logger.warn(
"Failed to set timeout for sandbox inside 'finally' block for shell tool.",
);
}
}
},
{

View file

@ -1,9 +1,10 @@
import { Octokit } from "@octokit/rest";
import { CommandResult, Sandbox } from "@e2b/code-interpreter";
import { Sandbox } from "@daytonaio/sdk";
import { createLogger, LogLevel } from "../logger.js";
import { GraphConfig, TargetRepository } from "../../types.js";
import { TIMEOUT_MS } from "../../constants.js";
import { TIMEOUT_MS, SANDBOX_ROOT_DIR } from "../../constants.js";
import { getSandboxErrorFields } from "../sandbox-error-fields.js";
import { ExecuteResponse } from "@daytonaio/sdk/dist/types/ExecuteResponse.js";
const logger = createLogger(LogLevel.INFO, "GitUtil");
@ -15,7 +16,7 @@ export function getRepoAbsolutePath(
throw new Error("No repository name provided");
}
return `/home/user/${repoName}`;
return `${SANDBOX_ROOT_DIR}/${repoName}`;
}
export function getBranchName(config: GraphConfig): string {
@ -31,27 +32,27 @@ export async function checkoutBranch(
absoluteRepoDir: string,
branchName: string,
sandbox: Sandbox,
): Promise<CommandResult | false> {
): Promise<ExecuteResponse | false> {
logger.info(`Checking out branch '${branchName}'...`);
try {
const getCurrentBranchOutput = await sandbox.commands.run(
const getCurrentBranchOutput = await sandbox.process.executeCommand(
"git branch --show-current",
{ cwd: absoluteRepoDir },
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
await sandbox.setTimeout(TIMEOUT_MS);
if (getCurrentBranchOutput.exitCode !== 0) {
logger.error(`Failed to get current branch`, {
getCurrentBranchOutput,
});
} else {
const currentBranch = getCurrentBranchOutput.stdout.trim();
const currentBranch = getCurrentBranchOutput.result.trim();
if (currentBranch === branchName) {
logger.info(`Already on branch '${branchName}'. No checkout needed.`);
return {
stdout: `Already on branch ${branchName}`,
stderr: "",
result: `Already on branch ${branchName}`,
exitCode: 0,
};
}
@ -75,11 +76,12 @@ export async function checkoutBranch(
`Checking if branch 'refs/heads/${branchName}' exists using 'git rev-parse --verify --quiet'`,
);
// Check if branch exists using git rev-parse for robustness
const checkBranchExistsOutput = await sandbox.commands.run(
const checkBranchExistsOutput = await sandbox.process.executeCommand(
`git rev-parse --verify --quiet "refs/heads/${branchName}"`,
{ cwd: absoluteRepoDir },
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
await sandbox.setTimeout(TIMEOUT_MS);
if (checkBranchExistsOutput.exitCode === 0) {
// Branch exists (rev-parse exit code 0 means success)
@ -94,7 +96,7 @@ export async function checkoutBranch(
if (
errorFields &&
errorFields.exitCode === 1 &&
errorFields.stderr === ""
errorFields.result === ""
) {
checkoutCommand = `git checkout -b "${branchName}"`;
} else {
@ -110,9 +112,12 @@ export async function checkoutBranch(
}
try {
const gitCheckoutOutput = await sandbox.commands.run(checkoutCommand, {
cwd: absoluteRepoDir,
});
const gitCheckoutOutput = await sandbox.process.executeCommand(
checkoutCommand,
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
if (gitCheckoutOutput.exitCode !== 0) {
logger.error(`Failed to checkout branch`, {
@ -212,20 +217,24 @@ export async function configureGitUserInRepo(
): Promise<void> {
let needsGitConfig = false;
try {
const nameCheck = await sandbox.commands.run("git config user.name", {
cwd: absoluteRepoDir,
});
await sandbox.setTimeout(TIMEOUT_MS);
const emailCheck = await sandbox.commands.run("git config user.email", {
cwd: absoluteRepoDir,
});
await sandbox.setTimeout(TIMEOUT_MS);
const nameCheck = await sandbox.process.executeCommand(
"git config user.name",
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
const emailCheck = await sandbox.process.executeCommand(
"git config user.email",
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
if (
nameCheck.exitCode !== 0 ||
nameCheck.stdout.trim() === "" ||
nameCheck.result.trim() === "" ||
emailCheck.exitCode !== 0 ||
emailCheck.stdout.trim() === ""
emailCheck.result.trim() === ""
) {
needsGitConfig = true;
}
@ -244,11 +253,12 @@ export async function configureGitUserInRepo(
const { userName, userEmail } = await getGitUserDetailsFromGitHub();
if (userName) {
const configUserNameOutput = await sandbox.commands.run(
const configUserNameOutput = await sandbox.process.executeCommand(
`git config user.name "${userName}"`,
{ cwd: absoluteRepoDir },
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
await sandbox.setTimeout(TIMEOUT_MS);
if (configUserNameOutput.exitCode !== 0) {
logger.error(`Failed to set git user.name`, {
configUserNameOutput,
@ -259,11 +269,12 @@ export async function configureGitUserInRepo(
}
if (userEmail) {
const configUserEmailOutput = await sandbox.commands.run(
const configUserEmailOutput = await sandbox.process.executeCommand(
`git config user.email "${userEmail}"`,
{ cwd: absoluteRepoDir },
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
await sandbox.setTimeout(TIMEOUT_MS);
if (configUserEmailOutput.exitCode !== 0) {
logger.error(`Failed to set git user.email`, {
configUserEmailOutput,
@ -283,13 +294,14 @@ export async function commitAll(
absoluteRepoDir: string,
message: string,
sandbox: Sandbox,
): Promise<CommandResult | false> {
): Promise<ExecuteResponse | false> {
try {
const gitAddOutput = await sandbox.commands.run(
const gitAddOutput = await sandbox.process.executeCommand(
`git add -A && git commit -m "${message}"`,
{ cwd: absoluteRepoDir },
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
await sandbox.setTimeout(TIMEOUT_MS);
if (gitAddOutput.exitCode !== 0) {
logger.error(`Failed to commit all changes to git repository`, {
@ -315,7 +327,7 @@ export async function commitAllAndPush(
absoluteRepoDir: string,
message: string,
sandbox: Sandbox,
): Promise<CommandResult | false> {
): Promise<ExecuteResponse | false> {
try {
const commitOutput = await commitAll(absoluteRepoDir, message, sandbox);
@ -326,10 +338,12 @@ export async function commitAllAndPush(
return false;
}
const gitPushOutput = await sandbox.commands.run(pushCurrentBranchCmd, {
cwd: absoluteRepoDir,
});
await sandbox.setTimeout(TIMEOUT_MS);
const gitPushOutput = await sandbox.process.executeCommand(
pushCurrentBranchCmd,
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
if (gitPushOutput.exitCode !== 0) {
logger.error(`Failed to push changes to git repository`, {
@ -357,9 +371,12 @@ export async function getChangedFilesStatus(
absoluteRepoDir: string,
sandbox: Sandbox,
): Promise<string[]> {
const gitStatusOutput = await sandbox.commands.run("git status --porcelain", {
cwd: absoluteRepoDir,
});
const gitStatusOutput = await sandbox.process.executeCommand(
"git status --porcelain",
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
if (gitStatusOutput.exitCode !== 0) {
logger.error(`Failed to get changed files status`, {
@ -368,7 +385,7 @@ export async function getChangedFilesStatus(
return [];
}
return gitStatusOutput.stdout
return gitStatusOutput.result
.split("\n")
.map((line) => line.trim())
.filter((line) => line !== "");
@ -453,12 +470,14 @@ export async function createPullRequest({
export async function pullLatestChanges(
absoluteRepoDir: string,
sandbox: Sandbox,
): Promise<CommandResult | false> {
): Promise<ExecuteResponse | false> {
try {
const gitPullOutput = await sandbox.commands.run("git pull", {
cwd: absoluteRepoDir,
});
await sandbox.setTimeout(TIMEOUT_MS);
const gitPullOutput = await sandbox.process.executeCommand(
"git pull",
absoluteRepoDir,
undefined,
TIMEOUT_MS,
);
return gitPullOutput;
} catch (e) {
const errorFields = getSandboxErrorFields(e);

View file

@ -1,6 +1,5 @@
import { Sandbox } from "@e2b/code-interpreter";
import { Sandbox } from "@daytonaio/sdk";
import { createLogger, LogLevel } from "./logger.js";
import { TIMEOUT_MS } from "../constants.js";
import { getSandboxErrorFields } from "./sandbox-error-fields.js";
import { traceable } from "langsmith/traceable";
@ -15,9 +14,10 @@ async function handleCreateFile(
) {
try {
const touchCommand = `touch "${filePath}"`;
const touchOutput = await sandbox.commands.run(touchCommand, {
cwd: args?.workDir,
});
const touchOutput = await sandbox.process.executeCommand(
touchCommand,
args?.workDir,
);
return touchOutput;
} catch (e) {
const errorFields = getSandboxErrorFields(e);
@ -44,9 +44,10 @@ async function readFileFunc(
output: string;
}> {
try {
const readOutput = await sandbox.commands.run(`cat "${filePath}"`, {
cwd: args?.workDir,
});
const readOutput = await sandbox.process.executeCommand(
`cat "${filePath}"`,
args?.workDir,
);
if (readOutput.exitCode !== 0) {
logger.error(`Error reading file '${filePath}' from sandbox via cat:`, {
@ -54,13 +55,13 @@ async function readFileFunc(
});
return {
success: false,
output: `FAILED TO READ FILE from sandbox '${filePath}'. Exit code: ${readOutput.exitCode}.\nStderr: ${readOutput.stderr}\nStdout: ${readOutput.stdout}`,
output: `FAILED TO READ FILE from sandbox '${filePath}'. Exit code: ${readOutput.exitCode}.\nResult: ${readOutput.result}\nStdout: ${readOutput.artifacts?.stdout}`,
};
}
return {
success: true,
output: readOutput.stdout,
output: readOutput.result,
};
} catch (e: any) {
if (e instanceof Error && e.message.includes("No such file or directory")) {
@ -87,7 +88,7 @@ async function readFileFunc(
let outputMessage = `FAILED TO EXECUTE READ COMMAND for sandbox '${filePath}'.`;
const errorFields = getSandboxErrorFields(e);
if (errorFields) {
outputMessage += `\nExit code: ${errorFields.exitCode}\nStderr: ${errorFields.stderr}\nStdout: ${errorFields.stdout}`;
outputMessage += `\nExit code: ${errorFields.exitCode}\nResult: ${errorFields.result}\nStdout: ${errorFields.artifacts?.stdout}`;
} else {
outputMessage += ` Error: ${(e as Error).message || String(e)}`;
}
@ -100,17 +101,6 @@ async function readFileFunc(
success: false,
output: outputMessage,
};
} finally {
try {
if (sandbox) {
// Add an extra 5 min timeout to the sandbox.
await sandbox.setTimeout(TIMEOUT_MS);
}
} catch (_) {
logger.warn(
"Failed to set timeout for sandbox inside 'finally' block for read file.",
);
}
}
}
@ -134,9 +124,10 @@ async function writeFileFunc(
const writeCommand = `cat > "${filePath}" << '${delimiter}'
${content}
${delimiter}`;
const writeOutput = await sandbox.commands.run(writeCommand, {
cwd: args?.workDir,
});
const writeOutput = await sandbox.process.executeCommand(
writeCommand,
args?.workDir,
);
if (writeOutput.exitCode !== 0) {
logger.error(`Error writing file '${filePath}' to sandbox via cat:`, {
@ -144,14 +135,9 @@ ${delimiter}`;
});
return {
success: false,
output: `FAILED TO WRITE FILE to sandbox '${filePath}'. Exit code: ${writeOutput.exitCode}\nStderr: ${writeOutput.stderr}\nStdout: ${writeOutput.stdout}`,
output: `FAILED TO WRITE FILE to sandbox '${filePath}'. Exit code: ${writeOutput.exitCode}\nResult: ${writeOutput.result}\nStdout: ${writeOutput.artifacts?.stdout}`,
};
}
if (writeOutput.stderr) {
logger.warn(
`Stderr while writing file '${filePath}' to sandbox via cat: ${writeOutput.stderr}`,
);
}
return {
success: true,
output: `Successfully wrote file '${filePath}' to sandbox via cat.`,
@ -169,7 +155,7 @@ ${delimiter}`;
let outputMessage = `FAILED TO EXECUTE WRITE COMMAND for sandbox '${filePath}'.`;
const errorFields = getSandboxErrorFields(e);
if (errorFields) {
outputMessage += `\nExit code: ${errorFields.exitCode}\nStderr: ${errorFields.stderr}\nStdout: ${errorFields.stdout}`;
outputMessage += `\nExit code: ${errorFields.exitCode}\nResult: ${errorFields.result}\nStdout: ${errorFields.artifacts?.stdout}`;
} else {
outputMessage += ` Error: ${(e as Error).message || String(e)}`;
}
@ -178,17 +164,6 @@ ${delimiter}`;
success: false,
output: outputMessage,
};
} finally {
try {
if (sandbox) {
// Add an extra 5 min timeout to the sandbox.
await sandbox.setTimeout(TIMEOUT_MS);
}
} catch (_) {
logger.warn(
"Failed to set timeout for sandbox inside 'finally' block for write file.",
);
}
}
}

View file

@ -1,8 +1,8 @@
import { CommandResult } from "@e2b/code-interpreter";
import { ExecuteResponse } from "@daytonaio/sdk/dist/types/ExecuteResponse.js";
export function getSandboxErrorFields(
error: unknown,
): CommandResult | undefined {
): ExecuteResponse | undefined {
if (
!error ||
typeof error !== "object" ||
@ -16,5 +16,5 @@ export function getSandboxErrorFields(
return undefined;
}
return error.result as CommandResult;
return error.result as unknown as ExecuteResponse;
}

View file

@ -1,28 +1,52 @@
import { Sandbox } from "@e2b/code-interpreter";
import { Daytona, Sandbox, SandboxState } from "@daytonaio/sdk";
// Singleton instance of Daytona
let daytonaInstance: Daytona | null = null;
/**
* Pauses the sandbox. Either pass an existing sandbox client, or a sandbox session ID.
* Returns a shared Daytona instance
*/
export function daytonaClient(): Daytona {
if (!daytonaInstance) {
daytonaInstance = new Daytona();
}
return daytonaInstance;
}
/**
* Stops the sandbox. Either pass an existing sandbox client, or a sandbox session ID.
* If no sandbox client is provided, the sandbox will be connected to.
* @param sandboxSessionId The ID of the sandbox to pause.
* @param sandbox The sandbox client to pause. If not provided, the sandbox will be connected to.
* @param sandboxSessionId The ID of the sandbox to stop.
* @param sandbox The sandbox client to stop. If not provided, the sandbox will be connected to.
* @returns The sandbox session ID.
*/
export async function pauseSandbox(
sandboxSessionId: string,
sandbox?: Sandbox,
): Promise<string> {
const sandboxClient = sandbox ?? (await Sandbox.connect(sandboxSessionId));
return await sandboxClient.pause();
export async function stopSandbox(sandboxSessionId: string): Promise<string> {
const sandbox = await daytonaClient().get(sandboxSessionId);
if (
sandbox.instance.state == SandboxState.STOPPED ||
sandbox.instance.state == SandboxState.ARCHIVED
) {
return sandboxSessionId;
} else if (sandbox.instance.state == "started") {
await daytonaClient().stop(sandbox);
}
return sandbox.id;
}
/**
* Resumes the sandbox.
* @param sandboxSessionId The ID of the sandbox to resume.
* Starts the sandbox.
* @param sandboxSessionId The ID of the sandbox to start.
* @returns The sandbox client.
*/
export async function resumeSandbox(
sandboxSessionId: string,
): Promise<Sandbox> {
return await Sandbox.resume(sandboxSessionId);
export async function startSandbox(sandboxSessionId: string): Promise<Sandbox> {
const sandbox = await daytonaClient().get(sandboxSessionId);
if (
sandbox.instance.state == SandboxState.STOPPED ||
sandbox.instance.state == SandboxState.ARCHIVED
) {
await daytonaClient().start(sandbox);
}
return sandbox;
}

206
yarn.lock
View file

@ -449,13 +449,6 @@ __metadata:
languageName: node
linkType: hard
"@bufbuild/protobuf@npm:^2.2.2":
version: 2.5.0
resolution: "@bufbuild/protobuf@npm:2.5.0"
checksum: ff7f015e3ab7243461ac480efcc58a6b36473fef036aaa118361eb45871587d97a0bebc9daf77127ef86cdf08f1af990469e51c54b59b1818997c032dd221407
languageName: node
linkType: hard
"@cfworker/json-schema@npm:^4.0.2":
version: 4.1.1
resolution: "@cfworker/json-schema@npm:4.1.1"
@ -479,25 +472,6 @@ __metadata:
languageName: node
linkType: hard
"@connectrpc/connect-web@npm:2.0.0-rc.3":
version: 2.0.0-rc.3
resolution: "@connectrpc/connect-web@npm:2.0.0-rc.3"
peerDependencies:
"@bufbuild/protobuf": ^2.2.0
"@connectrpc/connect": 2.0.0-rc.3
checksum: 2889806b7161507f20d01a472b8173ea8fec4db664e89a3c46b2a04690abafb3b73a7541334d34b70be5d4867b2c4e51558bec7dfb3cead061f66fcc42076f8d
languageName: node
linkType: hard
"@connectrpc/connect@npm:2.0.0-rc.3":
version: 2.0.0-rc.3
resolution: "@connectrpc/connect@npm:2.0.0-rc.3"
peerDependencies:
"@bufbuild/protobuf": ^2.2.0
checksum: 78e993f48b4ff7dd290a919eb34578ca97848f12576a1fb8e988af50a100c6aa790c2cfaa0aca6fae658e56c652c81db5be9cbf650a4a69f18fa0a8a3b94b58d
languageName: node
linkType: hard
"@dabh/diagnostics@npm:^2.0.2":
version: 2.0.3
resolution: "@dabh/diagnostics@npm:2.0.3"
@ -509,12 +483,54 @@ __metadata:
languageName: node
linkType: hard
"@e2b/code-interpreter@npm:1.2.0-beta.4":
version: 1.2.0-beta.4
resolution: "@e2b/code-interpreter@npm:1.2.0-beta.4"
"@daytonaio/api-client@npm:^0.19.1":
version: 0.19.1
resolution: "@daytonaio/api-client@npm:0.19.1"
dependencies:
e2b: 1.2.0-beta.4
checksum: c08fb0381482156301f1147bf5cf71bab7c817afa7c02613bde3dc1b8c7011f9aca14b9edd1e4e9c365cec486818b8d3e5c54b1e7bcc70248bdf722a5b4026bd
axios: ^1.6.1
checksum: fc5402ff535b4299d9e5124795f02e09f86f22d0e07153943452e2771038c803fe11153d52c9b5f1b618bd17d18a4af76aa421547f640b86cbcdc6dec1d4d063
languageName: node
linkType: hard
"@daytonaio/sdk@npm:^0.18.1":
version: 0.18.1
resolution: "@daytonaio/sdk@npm:0.18.1"
dependencies:
"@daytonaio/api-client": ^0.19.1
"@dotenvx/dotenvx": ^1.25.1
axios: ^1.6.0
form-data: ^4.0.0
uuid: ^11.0.3
checksum: a3e0afd4e60a8f631f78579e8defed206dcd97ebe6e005d3bb75bb0e289be7bbf51e68075c255638beba98eea272f642a2c6872dbc6689d0d8bab7ba660fd475
languageName: node
linkType: hard
"@dotenvx/dotenvx@npm:^1.25.1":
version: 1.44.1
resolution: "@dotenvx/dotenvx@npm:1.44.1"
dependencies:
commander: ^11.1.0
dotenv: ^16.4.5
eciesjs: ^0.4.10
execa: ^5.1.1
fdir: ^6.2.0
ignore: ^5.3.0
object-treeify: 1.1.33
picomatch: ^4.0.2
which: ^4.0.0
bin:
dotenvx: src/cli/dotenvx.js
git-dotenvx: src/cli/dotenvx.js
checksum: a868270e4fdb0d3ac307fc44728630dbfb3a17685799de8483fdb3e2e8543cc4eb6f576fe6284804d0aeccbf9cbf5ca4b14906ff554d090960f3223d88db93b5
languageName: node
linkType: hard
"@ecies/ciphers@npm:^0.2.3":
version: 0.2.3
resolution: "@ecies/ciphers@npm:0.2.3"
peerDependencies:
"@noble/ciphers": ^1.0.0
checksum: fdf01fd5e687a74802df60d21ff9be5aa6cdf692dcf671186e94eb17e9c260a4652b64c7127b277af20c35d3ccc733003cefb2fdaee93e8b516fa71d317d503f
languageName: node
linkType: hard
@ -2416,6 +2432,29 @@ __metadata:
languageName: node
linkType: hard
"@noble/ciphers@npm:^1.3.0":
version: 1.3.0
resolution: "@noble/ciphers@npm:1.3.0"
checksum: 19722c35475df9bc78db60d261d0b5ef8a6d722561efc2135453f943eaa421b492195dc666e3e4df2b755bca3739e04f04b9c660198559f5dd05d3cfbf1b9e92
languageName: node
linkType: hard
"@noble/curves@npm:^1.9.1":
version: 1.9.1
resolution: "@noble/curves@npm:1.9.1"
dependencies:
"@noble/hashes": 1.8.0
checksum: 4f3483a1001538d2f55516cdcb19319d1eaef79550633f670e7d570b989cdbc0129952868b72bb67643329746b8ffefe8e4cd791c8cc35574e05a37f873eef42
languageName: node
linkType: hard
"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.8.0":
version: 1.8.0
resolution: "@noble/hashes@npm:1.8.0"
checksum: c94e98b941963676feaba62475b1ccfa8341e3f572adbb3b684ee38b658df44100187fa0ef4220da580b13f8d27e87d5492623c8a02ecc61f23fb9960c7918f5
languageName: node
linkType: hard
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@ -2608,7 +2647,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@open-swe/agent@workspace:apps/open-swe"
dependencies:
"@e2b/code-interpreter": 1.2.0-beta.4
"@daytonaio/sdk": ^0.18.1
"@eslint/eslintrc": ^3.1.0
"@eslint/js": ^9.19.0
"@jest/globals": ^29.7.0
@ -5375,7 +5414,7 @@ __metadata:
languageName: node
linkType: hard
"axios@npm:^1.4.0, axios@npm:^1.6.2":
"axios@npm:^1.4.0, axios@npm:^1.6.0, axios@npm:^1.6.1, axios@npm:^1.6.2":
version: 1.9.0
resolution: "axios@npm:1.9.0"
dependencies:
@ -6240,6 +6279,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^11.1.0":
version: 11.1.0
resolution: "commander@npm:11.1.0"
checksum: fd1a8557c6b5b622c89ecdfde703242ab7db3b628ea5d1755784c79b8e7cb0d74d65b4a262289b533359cd58e1bfc0bf50245dfbcd2954682a6f367c828b79ef
languageName: node
linkType: hard
"commander@npm:^13.0.0":
version: 13.1.0
resolution: "commander@npm:13.1.0"
@ -6254,13 +6300,6 @@ __metadata:
languageName: node
linkType: hard
"compare-versions@npm:^6.1.0":
version: 6.1.1
resolution: "compare-versions@npm:6.1.1"
checksum: 73fe6c4f52d22efe28f0a3be10df2afd704e10b3593360cd963e86b33a7a263c263b41a1361b69c30a0fe68bfa70fef90860c1cf2ef41502629d4402890fcd57
languageName: node
linkType: hard
"concat-map@npm:0.0.1":
version: 0.0.1
resolution: "concat-map@npm:0.0.1"
@ -6876,7 +6915,7 @@ __metadata:
languageName: node
linkType: hard
"dotenv@npm:^16.4.7":
"dotenv@npm:^16.4.5, dotenv@npm:^16.4.7":
version: 16.5.0
resolution: "dotenv@npm:16.5.0"
checksum: 6543fe87b5ddf2d60dd42df6616eec99148a5fc150cb4530fef5bda655db5204a3afa0e6f25f7cd64b20657ace4d79c0ef974bec32fdb462cad18754191e7a90
@ -6894,20 +6933,6 @@ __metadata:
languageName: node
linkType: hard
"e2b@npm:1.2.0-beta.4":
version: 1.2.0-beta.4
resolution: "e2b@npm:1.2.0-beta.4"
dependencies:
"@bufbuild/protobuf": ^2.2.2
"@connectrpc/connect": 2.0.0-rc.3
"@connectrpc/connect-web": 2.0.0-rc.3
compare-versions: ^6.1.0
openapi-fetch: ^0.9.7
platform: ^1.3.6
checksum: 98adde94c74d18313d71fca2fdc8bf99992940238e667f797d99e1b1987887b2ef20cd94d4275cf4033026f74486929a090fb3789def02a20b3361693f6950ae
languageName: node
linkType: hard
"eastasianwidth@npm:^0.2.0":
version: 0.2.0
resolution: "eastasianwidth@npm:0.2.0"
@ -6915,6 +6940,18 @@ __metadata:
languageName: node
linkType: hard
"eciesjs@npm:^0.4.10":
version: 0.4.15
resolution: "eciesjs@npm:0.4.15"
dependencies:
"@ecies/ciphers": ^0.2.3
"@noble/ciphers": ^1.3.0
"@noble/curves": ^1.9.1
"@noble/hashes": ^1.8.0
checksum: 133a0ded69da0579d7082fe89b6a15225cf5cf503f653438355c2106cfc42576ab75835421f6a1ad099b9b802f389fc0077e72a951fd0e7dbc5c1e5220457ac9
languageName: node
linkType: hard
"ee-first@npm:1.1.1":
version: 1.1.1
resolution: "ee-first@npm:1.1.1"
@ -7879,7 +7916,7 @@ __metadata:
languageName: node
linkType: hard
"execa@npm:^5.0.0":
"execa@npm:^5.0.0, execa@npm:^5.1.1":
version: 5.1.1
resolution: "execa@npm:5.1.1"
dependencies:
@ -8189,6 +8226,18 @@ __metadata:
languageName: node
linkType: hard
"fdir@npm:^6.2.0":
version: 6.4.5
resolution: "fdir@npm:6.4.5"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 14efd2d6617a6f9fb314916ccff64e00bdb96216f26542ec9dfa532ed60a7ebb45463f7009aa215c14071566bf43caeb8ba268ccb52a11e6b51e4aaa8cb58d81
languageName: node
linkType: hard
"fdir@npm:^6.4.4":
version: 6.4.4
resolution: "fdir@npm:6.4.4"
@ -9284,7 +9333,7 @@ __metadata:
languageName: node
linkType: hard
"ignore@npm:^5.2.0, ignore@npm:^5.3.1":
"ignore@npm:^5.2.0, ignore@npm:^5.3.0, ignore@npm:^5.3.1":
version: 5.3.2
resolution: "ignore@npm:5.3.2"
checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be
@ -12549,6 +12598,13 @@ __metadata:
languageName: node
linkType: hard
"object-treeify@npm:1.1.33":
version: 1.1.33
resolution: "object-treeify@npm:1.1.33"
checksum: 3af7f889349571ee73f5bdfb5ac478270c85eda8bcba950b454eb598ce41759a1ed6b0b43fbd624cb449080a4eb2df906b602e5138b6186b9563b692231f1694
languageName: node
linkType: hard
"object.assign@npm:^4.1.4, object.assign@npm:^4.1.7":
version: 4.1.7
resolution: "object.assign@npm:4.1.7"
@ -12703,15 +12759,6 @@ __metadata:
languageName: node
linkType: hard
"openapi-fetch@npm:^0.9.7":
version: 0.9.8
resolution: "openapi-fetch@npm:0.9.8"
dependencies:
openapi-typescript-helpers: ^0.0.8
checksum: 32a32698942b12b4c2fe2249eb90f745fa8dfde16d3312966e009fb7aaf7a233f883536ed58b1cdd58b6cafd6b55784e6aa7d13fefaf7c990929868e335d6cbe
languageName: node
linkType: hard
"openapi-types@npm:^12.0.0, openapi-types@npm:^12.1.3":
version: 12.1.3
resolution: "openapi-types@npm:12.1.3"
@ -12719,13 +12766,6 @@ __metadata:
languageName: node
linkType: hard
"openapi-typescript-helpers@npm:^0.0.8":
version: 0.0.8
resolution: "openapi-typescript-helpers@npm:0.0.8"
checksum: e309d7d297cf78308c41c324ffe094c3ef194117bf104b5300e45535d158265f665c69e810a143f1d3db7020575c3041e370d2ee93fc8094f5aabc14a366fc39
languageName: node
linkType: hard
"optionator@npm:^0.9.3":
version: 0.9.4
resolution: "optionator@npm:0.9.4"
@ -13111,13 +13151,6 @@ __metadata:
languageName: node
linkType: hard
"platform@npm:^1.3.6":
version: 1.3.6
resolution: "platform@npm:1.3.6"
checksum: 6f472a09c61d418c7e26c1c16d0bdc029549d512dbec6526216a1e59ec68100d07007d0097dcba69dddad883d6f2a83361b4bdfe0094a3d9a2af24158643d85e
languageName: node
linkType: hard
"pony-cause@npm:^1.1.1":
version: 1.1.1
resolution: "pony-cause@npm:1.1.1"
@ -16184,7 +16217,7 @@ __metadata:
languageName: node
linkType: hard
"uuid@npm:^11.0.5, uuid@npm:^11.1.0":
"uuid@npm:^11.0.3, uuid@npm:^11.0.5, uuid@npm:^11.1.0":
version: 11.1.0
resolution: "uuid@npm:11.1.0"
bin:
@ -16403,6 +16436,17 @@ __metadata:
languageName: node
linkType: hard
"which@npm:^4.0.0":
version: 4.0.0
resolution: "which@npm:4.0.0"
dependencies:
isexe: ^3.1.1
bin:
node-which: bin/which.js
checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651
languageName: node
linkType: hard
"which@npm:^5.0.0":
version: 5.0.0
resolution: "which@npm:5.0.0"