diff --git a/.gitignore b/.gitignore index 07d74c2..b540697 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ Cargo.lock # Example temp files .tmp-upload/ +*.db # CLI binaries (downloaded during npm publish) sdks/cli/platforms/*/bin/ diff --git a/Cargo.toml b/Cargo.toml index 505efb0..ebef66d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["server/packages/*", "gigacode"] [workspace.package] -version = "0.2.0" +version = "0.2.1" edition = "2021" authors = [ "Rivet Gaming, LLC " ] license = "Apache-2.0" @@ -12,13 +12,13 @@ description = "Universal API for automatic coding agents in sandboxes. Supports [workspace.dependencies] # Internal crates -sandbox-agent = { version = "0.2.0", path = "server/packages/sandbox-agent" } -sandbox-agent-error = { version = "0.2.0", path = "server/packages/error" } -sandbox-agent-agent-management = { version = "0.2.0", path = "server/packages/agent-management" } -sandbox-agent-agent-credentials = { version = "0.2.0", path = "server/packages/agent-credentials" } -sandbox-agent-opencode-adapter = { version = "0.2.0", path = "server/packages/opencode-adapter" } -sandbox-agent-opencode-server-manager = { version = "0.2.0", path = "server/packages/opencode-server-manager" } -acp-http-adapter = { version = "0.2.0", path = "server/packages/acp-http-adapter" } +sandbox-agent = { version = "0.2.1", path = "server/packages/sandbox-agent" } +sandbox-agent-error = { version = "0.2.1", path = "server/packages/error" } +sandbox-agent-agent-management = { version = "0.2.1", path = "server/packages/agent-management" } +sandbox-agent-agent-credentials = { version = "0.2.1", path = "server/packages/agent-credentials" } +sandbox-agent-opencode-adapter = { version = "0.2.1", path = "server/packages/opencode-adapter" } +sandbox-agent-opencode-server-manager = { version = "0.2.1", path = "server/packages/opencode-server-manager" } +acp-http-adapter = { version = "0.2.1", path = "server/packages/acp-http-adapter" } # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index f57d233..b7ac39a 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ npm install sandbox-agent@0.2.x ```bash bun add sandbox-agent@0.2.x # Optional: allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()). -bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 +bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 ``` **Setup** @@ -171,7 +171,7 @@ npm install -g @sandbox-agent/cli@0.2.x ```bash # Allow Bun to run postinstall scripts for native binaries. bun add -g @sandbox-agent/cli@0.2.x -bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 +bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 ``` Create a session and send a message: diff --git a/docs/openapi.json b/docs/openapi.json index 01fd073..c6e35f4 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "0.2.0" + "version": "0.2.1" }, "servers": [ { diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index 37eacb9..ad2c2ca 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -120,7 +120,7 @@ icon: "rocket" ```bash bun add -g @sandbox-agent/cli@0.2.x # Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()). - bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 + bun pm -g trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 sandbox-agent server --no-token --host 0.0.0.0 --port 2468 ``` @@ -145,7 +145,7 @@ icon: "rocket" ```bash bun add sandbox-agent@0.2.x # Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()). - bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 + bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 ``` ```typescript diff --git a/docs/sdk-overview.mdx b/docs/sdk-overview.mdx index 8a973a2..215ab82 100644 --- a/docs/sdk-overview.mdx +++ b/docs/sdk-overview.mdx @@ -18,7 +18,7 @@ The TypeScript SDK is centered on `sandbox-agent` and its `SandboxAgent` class. ```bash bun add sandbox-agent@0.2.x # Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()). - bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 + bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 ``` diff --git a/docs/sdks/typescript.mdx b/docs/sdks/typescript.mdx index 9c74214..dccc773 100644 --- a/docs/sdks/typescript.mdx +++ b/docs/sdks/typescript.mdx @@ -18,7 +18,7 @@ The TypeScript SDK is centered on `sandbox-agent` and its `SandboxAgentClient`, ```bash bun add sandbox-agent # Allow Bun to run postinstall scripts for native binaries (required for SandboxAgent.start()). - bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 + bun pm trust @sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64 ``` diff --git a/docs/session-persistence.mdx b/docs/session-persistence.mdx index 43bbcef..676c948 100644 --- a/docs/session-persistence.mdx +++ b/docs/session-persistence.mdx @@ -38,7 +38,7 @@ const sdk = await SandboxAgent.connect({ Recommended for sandbox orchestration with actor state. ```bash -npm install @sandbox-agent/persist-rivet@0.1.x +npm install @sandbox-agent/persist-rivet@0.2.x ``` ```ts diff --git a/examples/persist-memory/package.json b/examples/persist-memory/package.json new file mode 100644 index 0000000..0514ad9 --- /dev/null +++ b/examples/persist-memory/package.json @@ -0,0 +1,18 @@ +{ + "name": "@sandbox-agent/example-persist-memory", + "private": true, + "type": "module", + "scripts": { + "start": "tsx src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@sandbox-agent/example-shared": "workspace:*", + "sandbox-agent": "workspace:*" + }, + "devDependencies": { + "@types/node": "latest", + "tsx": "latest", + "typescript": "latest" + } +} diff --git a/examples/persist-memory/src/index.ts b/examples/persist-memory/src/index.ts new file mode 100644 index 0000000..247f724 --- /dev/null +++ b/examples/persist-memory/src/index.ts @@ -0,0 +1,37 @@ +import { SandboxAgent, InMemorySessionPersistDriver } from "sandbox-agent"; +import { startDockerSandbox } from "@sandbox-agent/example-shared/docker"; +import { detectAgent } from "@sandbox-agent/example-shared"; + +const persist = new InMemorySessionPersistDriver(); + +console.log("Starting sandbox..."); +const sandbox = await startDockerSandbox({ + port: 3000, + setupCommands: [ + "sandbox-agent install-agent claude", + "sandbox-agent install-agent codex", + ], +}); + +const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist }); + +const session = await sdk.createSession({ agent: detectAgent() }); +console.log(`Created session ${session.id}`); + +await session.prompt([{ type: "text", text: "Say hello in one sentence." }]); +console.log("Prompt complete."); + +const sessions = await sdk.listSessions(); +console.log(`\nSessions (${sessions.items.length}):`); +for (const s of sessions.items) { + console.log(` ${s.id} agent=${s.agent}`); +} + +const events = await sdk.getEvents({ sessionId: session.id }); +console.log(`\nSession history (${events.items.length} events):`); +for (const e of events.items) { + console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`); +} + +await sdk.dispose(); +await sandbox.cleanup(); diff --git a/examples/persist-memory/tsconfig.json b/examples/persist-memory/tsconfig.json new file mode 100644 index 0000000..d1c0065 --- /dev/null +++ b/examples/persist-memory/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/examples/persist-postgres/package.json b/examples/persist-postgres/package.json new file mode 100644 index 0000000..8114ffb --- /dev/null +++ b/examples/persist-postgres/package.json @@ -0,0 +1,21 @@ +{ + "name": "@sandbox-agent/example-persist-postgres", + "private": true, + "type": "module", + "scripts": { + "start": "tsx src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@sandbox-agent/example-shared": "workspace:*", + "@sandbox-agent/persist-postgres": "workspace:*", + "pg": "latest", + "sandbox-agent": "workspace:*" + }, + "devDependencies": { + "@types/node": "latest", + "@types/pg": "latest", + "tsx": "latest", + "typescript": "latest" + } +} diff --git a/examples/persist-postgres/src/index.ts b/examples/persist-postgres/src/index.ts new file mode 100644 index 0000000..32eda13 --- /dev/null +++ b/examples/persist-postgres/src/index.ts @@ -0,0 +1,76 @@ +import { execFileSync } from "node:child_process"; +import { randomUUID } from "node:crypto"; +import { Client } from "pg"; +import { setTimeout as delay } from "node:timers/promises"; +import { SandboxAgent } from "sandbox-agent"; +import { PostgresSessionPersistDriver } from "@sandbox-agent/persist-postgres"; +import { startDockerSandbox } from "@sandbox-agent/example-shared/docker"; +import { detectAgent } from "@sandbox-agent/example-shared"; + +// --- Postgres setup (Docker or DATABASE_URL) --- + +let containerId: string | undefined; +let connectionString: string; + +if (process.env.DATABASE_URL) { + connectionString = process.env.DATABASE_URL; +} else { + const name = `persist-example-${randomUUID().slice(0, 8)}`; + containerId = execFileSync("docker", [ + "run", "-d", "--rm", "--name", name, + "-e", "POSTGRES_USER=postgres", "-e", "POSTGRES_PASSWORD=postgres", "-e", "POSTGRES_DB=sandbox", + "-p", "127.0.0.1::5432", "postgres:16-alpine", + ], { encoding: "utf8" }).trim(); + const port = execFileSync("docker", ["port", containerId, "5432/tcp"], { encoding: "utf8" }) + .trim().split("\n")[0]?.match(/:(\d+)$/)?.[1]; + connectionString = `postgres://postgres:postgres@127.0.0.1:${port}/sandbox`; + console.log(`Postgres on port ${port}`); + + const deadline = Date.now() + 30_000; + while (Date.now() < deadline) { + const c = new Client({ connectionString }); + try { await c.connect(); await c.query("SELECT 1"); await c.end(); break; } + catch { try { await c.end(); } catch {} await delay(250); } + } +} + +try { + const persist = new PostgresSessionPersistDriver({ connectionString }); + + console.log("Starting sandbox..."); + const sandbox = await startDockerSandbox({ + port: 3000, + setupCommands: [ + "sandbox-agent install-agent claude", + "sandbox-agent install-agent codex", + ], + }); + + const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist }); + + const session = await sdk.createSession({ agent: detectAgent() }); + console.log(`Created session ${session.id}`); + + await session.prompt([{ type: "text", text: "Say hello in one sentence." }]); + console.log("Prompt complete."); + + const sessions = await sdk.listSessions(); + console.log(`\nSessions (${sessions.items.length}):`); + for (const s of sessions.items) { + console.log(` ${s.id} agent=${s.agent}`); + } + + const events = await sdk.getEvents({ sessionId: session.id }); + console.log(`\nSession history (${events.items.length} events):`); + for (const e of events.items) { + console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`); + } + + await persist.close(); + await sdk.dispose(); + await sandbox.cleanup(); +} finally { + if (containerId) { + try { execFileSync("docker", ["rm", "-f", containerId], { stdio: "ignore" }); } catch {} + } +} diff --git a/examples/persist-postgres/tsconfig.json b/examples/persist-postgres/tsconfig.json new file mode 100644 index 0000000..d1c0065 --- /dev/null +++ b/examples/persist-postgres/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/examples/persist-sqlite/package.json b/examples/persist-sqlite/package.json new file mode 100644 index 0000000..8b7b822 --- /dev/null +++ b/examples/persist-sqlite/package.json @@ -0,0 +1,19 @@ +{ + "name": "@sandbox-agent/example-persist-sqlite", + "private": true, + "type": "module", + "scripts": { + "start": "tsx src/index.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@sandbox-agent/example-shared": "workspace:*", + "@sandbox-agent/persist-sqlite": "workspace:*", + "sandbox-agent": "workspace:*" + }, + "devDependencies": { + "@types/node": "latest", + "tsx": "latest", + "typescript": "latest" + } +} diff --git a/examples/persist-sqlite/src/index.ts b/examples/persist-sqlite/src/index.ts new file mode 100644 index 0000000..9b51024 --- /dev/null +++ b/examples/persist-sqlite/src/index.ts @@ -0,0 +1,39 @@ +import { SandboxAgent } from "sandbox-agent"; +import { SQLiteSessionPersistDriver } from "@sandbox-agent/persist-sqlite"; +import { startDockerSandbox } from "@sandbox-agent/example-shared/docker"; +import { detectAgent } from "@sandbox-agent/example-shared"; + +const persist = new SQLiteSessionPersistDriver({ filename: "./sessions.db" }); + +console.log("Starting sandbox..."); +const sandbox = await startDockerSandbox({ + port: 3000, + setupCommands: [ + "sandbox-agent install-agent claude", + "sandbox-agent install-agent codex", + ], +}); + +const sdk = await SandboxAgent.connect({ baseUrl: sandbox.baseUrl, persist }); + +const session = await sdk.createSession({ agent: detectAgent() }); +console.log(`Created session ${session.id}`); + +await session.prompt([{ type: "text", text: "Say hello in one sentence." }]); +console.log("Prompt complete."); + +const sessions = await sdk.listSessions(); +console.log(`\nSessions (${sessions.items.length}):`); +for (const s of sessions.items) { + console.log(` ${s.id} agent=${s.agent}`); +} + +const events = await sdk.getEvents({ sessionId: session.id }); +console.log(`\nSession history (${events.items.length} events):`); +for (const e of events.items) { + console.log(` [${e.eventIndex}] ${e.sender}: ${JSON.stringify(e.payload).slice(0, 120)}`); +} + +persist.close(); +await sdk.dispose(); +await sandbox.cleanup(); diff --git a/examples/persist-sqlite/tsconfig.json b/examples/persist-sqlite/tsconfig.json new file mode 100644 index 0000000..d1c0065 --- /dev/null +++ b/examples/persist-sqlite/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/frontend/packages/inspector/src/components/chat/ChatMessages.tsx b/frontend/packages/inspector/src/components/chat/ChatMessages.tsx index 75f1ee1..ee40edb 100644 --- a/frontend/packages/inspector/src/components/chat/ChatMessages.tsx +++ b/frontend/packages/inspector/src/components/chat/ChatMessages.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import { getAvatarLabel, getMessageClass } from "./messageUtils"; -import renderContentPart from "./renderContentPart"; import type { TimelineEntry } from "./types"; import { AlertTriangle, Settings, ChevronRight, ChevronDown } from "lucide-react"; @@ -63,81 +62,98 @@ const ChatMessages = ({ ); } - // Other status messages - collapsible + // Other status messages - collapsible only if there's detail + const hasDetail = Boolean(entry.meta?.detail); + if (hasDetail) { + return ( + : } + label={title} + className={isError ? "error" : "system"} + > +
{entry.meta?.detail}
+
+ ); + } + + // No detail - simple non-collapsible message + return ( +
+ {isError ? : } + {title} +
+ ); + } + + if (entry.kind === "reasoning") { return ( : } - label={title} - className={isError ? "error" : "system"} + icon={} + label={`Reasoning${entry.reasoning?.visibility ? ` (${entry.reasoning.visibility})` : ""}`} + className="system" > - {entry.meta?.detail &&
{entry.meta.detail}
} +
{entry.reasoning?.text}
); } - const item = entry.item; - if (!item) return null; - const hasParts = (item.content ?? []).length > 0; - const isInProgress = item.status === "in_progress"; - const isFailed = item.status === "failed"; - const messageClass = getMessageClass(item); - const statusLabel = item.status !== "completed" ? item.status.replace("_", " ") : ""; - const kindLabel = item.kind.replace("_", " "); - const isTool = messageClass === "tool"; + if (entry.kind === "tool") { + const isComplete = entry.toolStatus === "completed" || entry.toolStatus === "failed"; + const isFailed = entry.toolStatus === "failed"; + const statusLabel = entry.toolStatus && entry.toolStatus !== "completed" + ? entry.toolStatus.replace("_", " ") + : ""; - // Tool results - collapsible - if (isTool) { return ( T} - label={`${kindLabel}${statusLabel ? ` (${statusLabel})` : ""}`} - className="tool" + label={`${entry.toolName ?? "tool"}${statusLabel ? ` (${statusLabel})` : ""}`} + className={`tool${isFailed ? " error" : ""}`} > - {hasParts ? ( - (item.content ?? []).map(renderContentPart) - ) : entry.deltaText ? ( - {entry.deltaText} - ) : ( - No content. - )} - - ); - } - - return ( -
- {!isFailed &&
{getAvatarLabel(messageClass)}
} -
- {(item.kind !== "message" || item.status !== "completed") && ( -
- {isFailed && } - {kindLabel} - {statusLabel && ( - - {statusLabel} - - )} + {entry.toolInput && ( +
+
input
+
{entry.toolInput}
)} - {hasParts ? ( - (item.content ?? []).map(renderContentPart) - ) : entry.deltaText ? ( - - {entry.deltaText} - {isInProgress && } - - ) : isInProgress ? ( + {isComplete && entry.toolOutput && ( +
+
output
+
{entry.toolOutput}
+
+ )} + {!isComplete && !entry.toolInput && ( + )} + + ); + } + + // Regular message + const messageClass = getMessageClass(entry); + + return ( +
+
{getAvatarLabel(messageClass)}
+
+ {entry.text ? ( +
{entry.text}
) : ( - No content yet. + + + + + )}
diff --git a/frontend/packages/inspector/src/components/chat/messageUtils.tsx b/frontend/packages/inspector/src/components/chat/messageUtils.tsx index fba8962..9df610e 100644 --- a/frontend/packages/inspector/src/components/chat/messageUtils.tsx +++ b/frontend/packages/inspector/src/components/chat/messageUtils.tsx @@ -1,13 +1,12 @@ -import type { UniversalItem } from "sandbox-agent"; +import type { TimelineEntry } from "./types"; import { Settings, AlertTriangle, User } from "lucide-react"; import type { ReactNode } from "react"; -export const getMessageClass = (item: UniversalItem) => { - if (item.kind === "tool_call" || item.kind === "tool_result") return "tool"; - if (item.kind === "system" || item.kind === "status") return "system"; - if (item.role === "user") return "user"; - if (item.role === "tool") return "tool"; - if (item.role === "system") return "system"; +export const getMessageClass = (entry: TimelineEntry) => { + if (entry.kind === "tool") return "tool"; + if (entry.kind === "meta") return entry.meta?.severity === "error" ? "error" : "system"; + if (entry.kind === "reasoning") return "assistant"; + if (entry.role === "user") return "user"; return "assistant"; }; @@ -16,5 +15,5 @@ export const getAvatarLabel = (messageClass: string): ReactNode => { if (messageClass === "tool") return "T"; if (messageClass === "system") return ; if (messageClass === "error") return ; - return "AI"; + return AI; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e485db..45e190a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ importers: dependencies: '@cloudflare/sandbox': specifier: latest - version: 0.7.1 + version: 0.7.2 react: specifier: ^19.1.0 version: 19.2.4 @@ -240,6 +240,75 @@ importers: specifier: latest version: 5.9.3 + examples/persist-memory: + dependencies: + '@sandbox-agent/example-shared': + specifier: workspace:* + version: link:../shared + sandbox-agent: + specifier: workspace:* + version: link:../../sdks/typescript + devDependencies: + '@types/node': + specifier: latest + version: 25.2.3 + tsx: + specifier: latest + version: 4.21.0 + typescript: + specifier: latest + version: 5.9.3 + + examples/persist-postgres: + dependencies: + '@sandbox-agent/example-shared': + specifier: workspace:* + version: link:../shared + '@sandbox-agent/persist-postgres': + specifier: workspace:* + version: link:../../sdks/persist-postgres + pg: + specifier: latest + version: 8.18.0 + sandbox-agent: + specifier: workspace:* + version: link:../../sdks/typescript + devDependencies: + '@types/node': + specifier: latest + version: 25.2.3 + '@types/pg': + specifier: latest + version: 8.16.0 + tsx: + specifier: latest + version: 4.21.0 + typescript: + specifier: latest + version: 5.9.3 + + examples/persist-sqlite: + dependencies: + '@sandbox-agent/example-shared': + specifier: workspace:* + version: link:../shared + '@sandbox-agent/persist-sqlite': + specifier: workspace:* + version: link:../../sdks/persist-sqlite + sandbox-agent: + specifier: workspace:* + version: link:../../sdks/typescript + devDependencies: + '@types/node': + specifier: latest + version: 25.2.3 + tsx: + specifier: latest + version: 4.21.0 + typescript: + specifier: latest + version: 5.9.3 + examples/shared: dependencies: dockerode: @@ -370,6 +439,9 @@ importers: '@astrojs/react': specifier: ^4.2.0 version: 4.4.2(@types/node@25.2.3)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2) + '@astrojs/sitemap': + specifier: ^3.2.0 + version: 3.7.0 '@astrojs/tailwind': specifier: ^6.0.0 version: 6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)) @@ -648,10 +720,16 @@ importers: sdks/persist-sqlite: dependencies: + better-sqlite3: + specifier: ^11.0.0 + version: 11.10.0 sandbox-agent: specifier: workspace:* version: link:../typescript devDependencies: + '@types/better-sqlite3': + specifier: ^7.0.0 + version: 7.6.13 '@types/node': specifier: ^22.0.0 version: 22.19.7 @@ -732,6 +810,9 @@ packages: react: ^17.0.2 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + '@astrojs/sitemap@3.7.0': + resolution: {integrity: sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA==} + '@astrojs/tailwind@6.0.2': resolution: {integrity: sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg==} peerDependencies: @@ -1061,8 +1142,8 @@ packages: resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} - '@cloudflare/sandbox@0.7.1': - resolution: {integrity: sha512-suDuYa+/rLmvaTqRwETsRO7gyN7XGAfxy+Y/UmCCQJzPL5FHBojV5NUw7xwe6mIznSscz2uk1iPHrF7sCecCBA==} + '@cloudflare/sandbox@0.7.2': + resolution: {integrity: sha512-q0dn6RUBLZ8CD4g99sVJbxoQK/BjJ3luw0T03mvTrUbRicjyzdFWP7dlEYzhvqpyd6TWpTsYr+TSHf/95bVuWw==} peerDependencies: '@openai/agents': ^0.3.3 '@opencode-ai/sdk': ^1.0.137 @@ -2651,6 +2732,9 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -2684,6 +2768,9 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} @@ -2713,6 +2800,9 @@ packages: '@types/retry@0.12.2': resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} @@ -2906,10 +2996,16 @@ packages: bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + better-sqlite3@11.10.0: + resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -3184,10 +3280,18 @@ packages: decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -3388,6 +3492,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expand-tilde@2.0.2: resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} engines: {node: '>=0.10.0'} @@ -3442,6 +3550,9 @@ packages: picomatch: optional: true + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -3551,6 +3662,9 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -3663,6 +3777,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -4016,6 +4133,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + miniflare@4.20260210.0: resolution: {integrity: sha512-HXR6m53IOqEzq52DuGF1x7I1K6lSIqzhbCbQXv/cTmPnPJmNkr7EBtLDm4nfSkOvlDtnwDCLUjWII5fyGJI5Tw==} engines: {node: '>=18.0.0'} @@ -4029,6 +4150,9 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -4074,6 +4198,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -4085,6 +4212,10 @@ packages: nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + node-abi@3.87.0: + resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} + engines: {node: '>=10'} + node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} @@ -4384,6 +4515,11 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -4433,6 +4569,10 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: @@ -4671,9 +4811,20 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + sitemap@8.0.2: + resolution: {integrity: sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + smol-toml@1.6.0: resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} engines: {node: '>= 18'} @@ -4716,6 +4867,9 @@ packages: stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -4753,6 +4907,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} @@ -4910,6 +5068,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + turbo-darwin-64@2.7.6: resolution: {integrity: sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw==} cpu: [x64] @@ -5469,6 +5630,12 @@ snapshots: - tsx - yaml + '@astrojs/sitemap@3.7.0': + dependencies: + sitemap: 8.0.2 + stream-replace-string: 2.0.0 + zod: 3.25.76 + '@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))': dependencies: astro: 5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) @@ -6177,7 +6344,7 @@ snapshots: '@cloudflare/kv-asset-handler@0.4.2': {} - '@cloudflare/sandbox@0.7.1': + '@cloudflare/sandbox@0.7.2': dependencies: '@cloudflare/containers': 0.0.30 @@ -7597,6 +7764,10 @@ snapshots: dependencies: '@babel/types': 7.28.6 + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 25.2.3 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -7637,6 +7808,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/node@17.0.45': {} + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 @@ -7672,6 +7845,10 @@ snapshots: '@types/retry@0.12.2': {} + '@types/sax@1.2.7': + dependencies: + '@types/node': 25.2.3 + '@types/semver@7.7.1': {} '@types/ssh2@1.15.5': @@ -7731,6 +7908,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@22.19.7))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.19.7) + '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.2.3))': dependencies: '@vitest/spy': 3.2.4 @@ -7971,8 +8156,17 @@ snapshots: dependencies: tweetnacl: 0.14.5 + better-sqlite3@11.10.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + binary-extensions@2.3.0: {} + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -8245,8 +8439,14 @@ snapshots: dependencies: character-entities: 2.0.2 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} + defu@6.1.4: {} delayed-stream@1.0.0: {} @@ -8538,6 +8738,8 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + expand-template@2.0.3: {} + expand-tilde@2.0.2: dependencies: homedir-polyfill: 1.0.3 @@ -8612,6 +8814,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -8714,6 +8918,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + github-from-package@0.0.0: {} + github-slugger@2.0.0: {} glob-parent@5.1.2: @@ -8892,6 +9098,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -9372,6 +9580,8 @@ snapshots: mimic-fn@4.0.0: {} + mimic-response@3.1.0: {} + miniflare@4.20260210.0: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -9392,6 +9602,8 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimist@1.2.8: {} + minipass@7.1.2: {} minizlib@3.1.0: @@ -9432,6 +9644,8 @@ snapshots: nanoid@3.3.11: {} + napi-build-utils@2.0.0: {} + negotiator@1.0.0: {} neotraverse@0.6.18: {} @@ -9440,6 +9654,10 @@ snapshots: dependencies: '@types/nlcst': 2.0.3 + node-abi@3.87.0: + dependencies: + semver: 7.7.3 + node-fetch-native@1.6.7: {} node-gyp-build-optional-packages@5.1.1: @@ -9713,6 +9931,21 @@ snapshots: dependencies: xtend: 4.0.2 + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.87.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + prismjs@1.30.0: {} process-warning@5.0.0: {} @@ -9770,6 +10003,13 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 @@ -10134,8 +10374,23 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + sisteransi@1.0.5: {} + sitemap@8.0.2: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.4 + smol-toml@1.6.0: {} sonic-boom@4.2.1: @@ -10171,6 +10426,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + stream-replace-string@2.0.0: {} + streamsearch@1.1.0: {} streamx@2.23.0: @@ -10219,6 +10476,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-json-comments@2.0.1: {} + strip-literal@3.1.0: dependencies: js-tokens: 9.0.1 @@ -10421,6 +10680,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + turbo-darwin-64@2.7.6: optional: true @@ -10684,7 +10947,7 @@ snapshots: dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.2.3)) + '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@22.19.7)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 diff --git a/scripts/release/main.ts b/scripts/release/main.ts index 1a8c3a3..426b322 100755 --- a/scripts/release/main.ts +++ b/scripts/release/main.ts @@ -13,7 +13,7 @@ import { createGitHubRelease, validateGit, } from "./git"; -import { publishCrates, publishNpmCli, publishNpmCliShared, publishNpmSdk } from "./sdk"; +import { publishCrates, publishNpmCli, publishNpmLibraries } from "./sdk"; import { updateVersion } from "./update_version"; import { assert, assertEquals, fetchGitRef, versionOrCommitToRef } from "./utils"; @@ -282,8 +282,7 @@ const STEPS = [ "run-ci-checks", "build-js-artifacts", "publish-crates", - "publish-npm-cli-shared", - "publish-npm-sdk", + "publish-npm-libraries", "publish-npm-cli", "tag-docker", "promote-artifacts", @@ -324,8 +323,7 @@ const PHASE_MAP: Record = { "complete-ci": [ "update-version", "publish-crates", - "publish-npm-cli-shared", - "publish-npm-sdk", + "publish-npm-libraries", "publish-npm-cli", "tag-docker", "promote-artifacts", @@ -604,14 +602,9 @@ async function main() { await publishCrates(releaseOpts); } - if (shouldRunStep("publish-npm-cli-shared")) { - console.log("==> Publishing NPM CLI Shared"); - await publishNpmCliShared(releaseOpts); - } - - if (shouldRunStep("publish-npm-sdk")) { - console.log("==> Publishing NPM SDK"); - await publishNpmSdk(releaseOpts); + if (shouldRunStep("publish-npm-libraries")) { + console.log("==> Publishing NPM Libraries"); + await publishNpmLibraries(releaseOpts); } if (shouldRunStep("publish-npm-cli")) { diff --git a/scripts/release/sdk.ts b/scripts/release/sdk.ts index 66caa51..b5f54b0 100644 --- a/scripts/release/sdk.ts +++ b/scripts/release/sdk.ts @@ -1,38 +1,13 @@ import { $ } from "execa"; import * as fs from "node:fs/promises"; -import { join } from "node:path"; +import { dirname, join, relative } from "node:path"; +import { glob } from "glob"; import type { ReleaseOpts } from "./main"; import { downloadFromReleases, PREFIX } from "./utils"; -// Crates to publish in dependency order -const CRATES = [ - "error", - "agent-credentials", - "agent-management", - "opencode-server-manager", - "opencode-adapter", - "acp-http-adapter", - "sandbox-agent", - "gigacode", -] as const; - -// NPM CLI packages -const CLI_PACKAGES = [ - "@sandbox-agent/cli", - "@sandbox-agent/cli-linux-x64", - "@sandbox-agent/cli-linux-arm64", - "@sandbox-agent/cli-win32-x64", - "@sandbox-agent/cli-darwin-x64", - "@sandbox-agent/cli-darwin-arm64", - "@sandbox-agent/gigacode", - "@sandbox-agent/gigacode-linux-x64", - "@sandbox-agent/gigacode-linux-arm64", - "@sandbox-agent/gigacode-win32-x64", - "@sandbox-agent/gigacode-darwin-x64", - "@sandbox-agent/gigacode-darwin-arm64", -] as const; - -// Mapping from npm package name to Rust target and binary extension +// ─── Platform binary mapping (npm package → Rust target) ──────────────────── +// Maps CLI platform packages to their Rust build targets. +// Keep in sync when adding new target platforms. const CLI_PLATFORM_MAP: Record< string, { target: string; binaryExt: string; binaryName: string } @@ -89,6 +64,8 @@ const CLI_PLATFORM_MAP: Record< }, }; +// ─── Shared helpers ───────────────────────────────────────────────────────── + async function npmVersionExists( packageName: string, version: string, @@ -104,16 +81,16 @@ async function npmVersionExists( return true; } catch (error: any) { if (error.stderr) { - if ( - !error.stderr.includes( - `No match found for version ${version}`, - ) && - !error.stderr.includes( - `'${packageName}@${version}' is not in this registry.`, - ) - ) { + const stderr = error.stderr; + // Expected errors when version or package doesn't exist + const expected = + stderr.includes(`No match found for version ${version}`) || + stderr.includes(`'${packageName}@${version}' is not in this registry.`) || + stderr.includes("404 Not Found") || + stderr.includes("is not in the npm registry"); + if (!expected) { throw new Error( - `unexpected npm view version output: ${error.stderr}`, + `unexpected npm view version output: ${stderr}`, ); } } @@ -131,7 +108,6 @@ async function crateVersionExists( stdout: "pipe", stderr: "pipe", })`cargo search ${crateName} --limit 1`; - // cargo search output format: "cratename = \"version\" # description" const output = result.stdout; const match = output.match(new RegExp(`^${crateName}\\s*=\\s*"([^"]+)"`)); if (match && match[1] === version) { @@ -139,62 +115,148 @@ async function crateVersionExists( } return false; } catch (error: any) { - // If cargo search fails, assume crate doesn't exist return false; } } +// ─── Package discovery ────────────────────────────────────────────────────── + +interface NpmPackageInfo { + name: string; + dir: string; + hasBuildScript: boolean; + localDeps: string[]; +} + +/** + * Discover non-private npm packages matching the given glob patterns. + */ +async function discoverNpmPackages(root: string, patterns: string[]): Promise { + const packages: NpmPackageInfo[] = []; + + for (const pattern of patterns) { + const matches = await glob(pattern, { cwd: root }); + for (const match of matches) { + const fullPath = join(root, match); + const pkg = JSON.parse(await fs.readFile(fullPath, "utf-8")); + if (pkg.private) continue; + + const allDeps = { ...pkg.dependencies, ...pkg.peerDependencies }; + const localDeps = Object.entries(allDeps || {}) + .filter(([_, v]) => String(v).startsWith("workspace:")) + .map(([k]) => k); + + packages.push({ + name: pkg.name, + dir: dirname(fullPath), + hasBuildScript: !!pkg.scripts?.build, + localDeps, + }); + } + } + + return packages; +} + +/** + * Topologically sort packages so dependencies are published before dependents. + */ +function topoSort(packages: NpmPackageInfo[]): NpmPackageInfo[] { + const byName = new Map(packages.map(p => [p.name, p])); + const visited = new Set(); + const result: NpmPackageInfo[] = []; + + function visit(pkg: NpmPackageInfo) { + if (visited.has(pkg.name)) return; + visited.add(pkg.name); + for (const dep of pkg.localDeps) { + const d = byName.get(dep); + if (d) visit(d); + } + result.push(pkg); + } + + for (const pkg of packages) visit(pkg); + return result; +} + +interface CrateInfo { + name: string; + dir: string; +} + +/** + * Discover workspace crates via `cargo metadata` and return them in dependency order. + */ +async function discoverCrates(root: string): Promise { + const result = await $({ cwd: root, stdout: "pipe" })`cargo metadata --no-deps --format-version 1`; + const metadata = JSON.parse(result.stdout); + + const memberIds = new Set(metadata.workspace_members); + const workspacePackages = metadata.packages.filter((p: any) => memberIds.has(p.id)); + + // Build name→package map for topo sort + const byName = new Map(workspacePackages.map((p: any) => [p.name, p])); + + const visited = new Set(); + const sorted: CrateInfo[] = []; + + function visit(pkg: any) { + if (visited.has(pkg.name)) return; + visited.add(pkg.name); + for (const dep of pkg.dependencies) { + const internal = byName.get(dep.name); + if (internal) visit(internal); + } + sorted.push({ + name: pkg.name, + dir: dirname(pkg.manifest_path), + }); + } + + for (const pkg of workspacePackages) visit(pkg); + return sorted; +} + +// ─── Crate publishing ─────────────────────────────────────────────────────── + export async function publishCrates(opts: ReleaseOpts) { - console.log("==> Publishing crates to crates.io"); + console.log("==> Discovering workspace crates"); + const crates = await discoverCrates(opts.root); - for (const crate of CRATES) { - const cratePath = crate === "gigacode" - ? join(opts.root, "gigacode") - : join(opts.root, "server/packages", crate); + console.log(`Found ${crates.length} crates to publish:`); + for (const c of crates) console.log(` - ${c.name}`); - // Read Cargo.toml to get the actual crate name - const cargoTomlPath = join(cratePath, "Cargo.toml"); - const cargoToml = await fs.readFile(cargoTomlPath, "utf-8"); - const nameMatch = cargoToml.match(/^name\s*=\s*"([^"]+)"/m); - const crateName = nameMatch ? nameMatch[1] : `sandbox-agent-${crate}`; - - // Check if version already exists - const versionExists = await crateVersionExists(crateName, opts.version); + for (const crate of crates) { + const versionExists = await crateVersionExists(crate.name, opts.version); if (versionExists) { console.log( - `Version ${opts.version} of ${crateName} already exists on crates.io. Skipping...`, + `Version ${opts.version} of ${crate.name} already exists on crates.io. Skipping...`, ); continue; } - // Publish - // Use --no-verify to skip the verification step because: - // 1. Code was already built/checked in the setup phase - // 2. Verification downloads published dependencies which may not have the latest - // changes yet (crates.io indexing takes time) - console.log(`==> Publishing to crates.io: ${crateName}@${opts.version}`); + console.log(`==> Publishing to crates.io: ${crate.name}@${opts.version}`); try { await $({ stdout: "pipe", stderr: "pipe", - cwd: cratePath, + cwd: crate.dir, })`cargo publish --allow-dirty --no-verify`; - console.log(`✅ Published ${crateName}@${opts.version}`); + console.log(`✅ Published ${crate.name}@${opts.version}`); } catch (err: any) { - // Check if error is because crate already exists (from a previous partial run) if (err.stderr?.includes("already exists")) { console.log( - `Version ${opts.version} of ${crateName} already exists on crates.io. Skipping...`, + `Version ${opts.version} of ${crate.name} already exists on crates.io. Skipping...`, ); continue; } - console.error(`❌ Failed to publish ${crateName}`); + console.error(`❌ Failed to publish ${crate.name}`); console.error(err.stderr || err.message); throw err; } - // Wait a bit for crates.io to index the new version (needed for dependency resolution) console.log("Waiting for crates.io to index..."); await new Promise((resolve) => setTimeout(resolve, 30000)); } @@ -202,108 +264,68 @@ export async function publishCrates(opts: ReleaseOpts) { console.log("✅ All crates published"); } -export async function publishNpmCliShared(opts: ReleaseOpts) { - const cliSharedPath = join(opts.root, "sdks/cli-shared"); - const packageJsonPath = join(cliSharedPath, "package.json"); - const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")); - const name = packageJson.name; +// ─── NPM library publishing ──────────────────────────────────────────────── - // Check if version already exists - const versionExists = await npmVersionExists(name, opts.version); - if (versionExists) { - console.log( - `Version ${opts.version} of ${name} already exists. Skipping...`, - ); - return; - } +/** + * Discover and publish all non-private library packages under sdks/. + * Excludes CLI/gigacode wrapper and platform packages (handled by publishNpmCli). + * Publishes in dependency order via topological sort. + */ +export async function publishNpmLibraries(opts: ReleaseOpts) { + console.log("==> Discovering library packages"); + const all = await discoverNpmPackages(opts.root, ["sdks/*/package.json"]); - // Build cli-shared - console.log(`==> Building @sandbox-agent/cli-shared`); - await $({ - stdio: "inherit", - cwd: opts.root, - })`pnpm --filter @sandbox-agent/cli-shared build`; + // Exclude CLI and gigacode directories (handled by publishNpmCli) + const libraries = all.filter(p => { + const rel = relative(opts.root, p.dir); + return !rel.startsWith("sdks/cli") && !rel.startsWith("sdks/gigacode"); + }); - // Publish - console.log(`==> Publishing to NPM: ${name}@${opts.version}`); + const sorted = topoSort(libraries); + + console.log(`Found ${sorted.length} library packages to publish:`); + for (const pkg of sorted) console.log(` - ${pkg.name}`); - // Add --tag flag for release candidates const isReleaseCandidate = opts.version.includes("-rc."); const tag = isReleaseCandidate ? "rc" : (opts.latest ? "latest" : opts.minorVersionChannel); - await $({ - stdio: "inherit", - cwd: cliSharedPath, - })`pnpm publish --access public --tag ${tag} --no-git-checks`; - - console.log(`✅ Published ${name}@${opts.version}`); -} - -export async function publishNpmSdk(opts: ReleaseOpts) { - const isReleaseCandidate = opts.version.includes("-rc."); - const tag = isReleaseCandidate ? "rc" : (opts.latest ? "latest" : opts.minorVersionChannel); - - // Publish acp-http-client (dependency of the SDK) - { - const acpHttpClientPath = join(opts.root, "sdks/acp-http-client"); - const packageJsonPath = join(acpHttpClientPath, "package.json"); - const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")); - const name = packageJson.name; - - const versionExists = await npmVersionExists(name, opts.version); + for (const pkg of sorted) { + const versionExists = await npmVersionExists(pkg.name, opts.version); if (versionExists) { - console.log( - `Version ${opts.version} of ${name} already exists. Skipping...`, - ); - } else { - console.log(`==> Building acp-http-client`); - await $({ - stdio: "inherit", - cwd: opts.root, - })`pnpm --filter acp-http-client build`; - - console.log(`==> Publishing to NPM: ${name}@${opts.version}`); - await $({ - stdio: "inherit", - cwd: acpHttpClientPath, - })`pnpm publish --access public --tag ${tag} --no-git-checks`; - - console.log(`✅ Published ${name}@${opts.version}`); + console.log(`Version ${opts.version} of ${pkg.name} already exists. Skipping...`); + continue; } + + if (pkg.hasBuildScript) { + console.log(`==> Building ${pkg.name}`); + await $({ stdio: "inherit", cwd: opts.root })`pnpm --filter ${pkg.name} build`; + } + + console.log(`==> Publishing to NPM: ${pkg.name}@${opts.version}`); + await $({ stdio: "inherit", cwd: pkg.dir })`pnpm publish --access public --tag ${tag} --no-git-checks`; + console.log(`✅ Published ${pkg.name}@${opts.version}`); } - // Publish SDK - const sdkPath = join(opts.root, "sdks/typescript"); - const packageJsonPath = join(sdkPath, "package.json"); - const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8")); - const name = packageJson.name; - - const versionExists = await npmVersionExists(name, opts.version); - if (versionExists) { - console.log( - `Version ${opts.version} of ${name} already exists. Skipping...`, - ); - return; - } - - // Build the SDK (cli-shared should already be built by publishNpmCliShared) - console.log(`==> Building TypeScript SDK`); - await $({ - stdio: "inherit", - cwd: opts.root, - })`pnpm --filter sandbox-agent build`; - - console.log(`==> Publishing to NPM: ${name}@${opts.version}`); - await $({ - stdio: "inherit", - cwd: sdkPath, - })`pnpm publish --access public --tag ${tag} --no-git-checks`; - - console.log(`✅ Published ${name}@${opts.version}`); + console.log("✅ All library packages published"); } +// ─── NPM CLI publishing ──────────────────────────────────────────────────── + +/** + * Discover and publish CLI wrapper and platform packages. + * Platform packages get their binaries downloaded from R2 before publishing. + */ export async function publishNpmCli(opts: ReleaseOpts) { - console.log("==> Publishing CLI packages to NPM"); + console.log("==> Discovering CLI packages"); + const packages = await discoverNpmPackages(opts.root, [ + "sdks/cli/package.json", + "sdks/cli/platforms/*/package.json", + "sdks/gigacode/package.json", + "sdks/gigacode/platforms/*/package.json", + ]); + + console.log(`Found ${packages.length} CLI packages to publish:`); + for (const pkg of packages) console.log(` - ${pkg.name}`); // Determine which commit to use for downloading binaries let sourceCommit = opts.commit; @@ -316,65 +338,41 @@ export async function publishNpmCli(opts: ReleaseOpts) { console.log(`Using binaries from commit: ${sourceCommit}`); } - for (const packageName of CLI_PACKAGES) { - // Check if version already exists - const versionExists = await npmVersionExists(packageName, opts.version); + for (const pkg of packages) { + const versionExists = await npmVersionExists(pkg.name, opts.version); if (versionExists) { console.log( - `Version ${opts.version} of ${packageName} already exists. Skipping...`, + `Version ${opts.version} of ${pkg.name} already exists. Skipping...`, ); continue; } - // Determine package path - let packagePath: string; - if (packageName === "@sandbox-agent/cli") { - packagePath = join(opts.root, "sdks/cli"); - } else if (packageName === "@sandbox-agent/gigacode") { - packagePath = join(opts.root, "sdks/gigacode"); - } else if (packageName.startsWith("@sandbox-agent/cli-")) { - // Platform-specific packages: @sandbox-agent/cli-linux-x64 -> sdks/cli/platforms/linux-x64 - const platform = packageName.replace("@sandbox-agent/cli-", ""); - packagePath = join(opts.root, "sdks/cli/platforms", platform); - } else if (packageName.startsWith("@sandbox-agent/gigacode-")) { - // Platform-specific packages: @sandbox-agent/gigacode-linux-x64 -> sdks/gigacode/platforms/linux-x64 - const platform = packageName.replace("@sandbox-agent/gigacode-", ""); - packagePath = join(opts.root, "sdks/gigacode/platforms", platform); - } else { - throw new Error(`Unknown CLI package: ${packageName}`); - } - - // Download binary from R2 for platform-specific packages - const platformInfo = CLI_PLATFORM_MAP[packageName]; + // Download binary for platform-specific packages + const platformInfo = CLI_PLATFORM_MAP[pkg.name]; if (platformInfo) { - const binDir = join(packagePath, "bin"); + const binDir = join(pkg.dir, "bin"); const binaryName = `${platformInfo.binaryName}${platformInfo.binaryExt}`; const localBinaryPath = join(binDir, binaryName); const remoteBinaryPath = `${PREFIX}/${sourceCommit}/binaries/${platformInfo.binaryName}-${platformInfo.target}${platformInfo.binaryExt}`; - console.log(`==> Downloading binary for ${packageName}`); + console.log(`==> Downloading binary for ${pkg.name}`); console.log(` From: ${remoteBinaryPath}`); console.log(` To: ${localBinaryPath}`); - // Create bin directory await fs.mkdir(binDir, { recursive: true }); - - // Download binary await downloadFromReleases(remoteBinaryPath, localBinaryPath); - // Make binary executable (not needed on Windows) if (!platformInfo.binaryExt) { await fs.chmod(localBinaryPath, 0o755); } } // Publish - console.log(`==> Publishing to NPM: ${packageName}@${opts.version}`); + console.log(`==> Publishing to NPM: ${pkg.name}@${opts.version}`); - // Add --tag flag for release candidates const isReleaseCandidate = opts.version.includes("-rc."); const tag = getCliPackageNpmTag({ - packageName, + packageName: pkg.name, isReleaseCandidate, latest: opts.latest, minorVersionChannel: opts.minorVersionChannel, @@ -383,12 +381,11 @@ export async function publishNpmCli(opts: ReleaseOpts) { try { await $({ stdio: "inherit", - cwd: packagePath, + cwd: pkg.dir, })`pnpm publish --access public --tag ${tag} --no-git-checks`; - console.log(`✅ Published ${packageName}@${opts.version}`); - + console.log(`✅ Published ${pkg.name}@${opts.version}`); } catch (err) { - console.error(`❌ Failed to publish ${packageName}`); + console.error(`❌ Failed to publish ${pkg.name}`); throw err; } } @@ -412,4 +409,3 @@ function getCliPackageNpmTag(opts: { return opts.minorVersionChannel; } - diff --git a/scripts/release/update_version.ts b/scripts/release/update_version.ts index 67df56d..a22e0f9 100644 --- a/scripts/release/update_version.ts +++ b/scripts/release/update_version.ts @@ -1,4 +1,5 @@ import * as fs from "node:fs/promises"; +import { join } from "node:path"; import { $ } from "execa"; import { glob } from "glob"; import type { ReleaseOpts } from "./main"; @@ -10,70 +11,36 @@ function assert(condition: any, message?: string): asserts condition { } export async function updateVersion(opts: ReleaseOpts) { - // Define substitutions - const findReplace = [ - { - path: "Cargo.toml", - find: /\[workspace\.package\]\nversion = ".*"/, - replace: `[workspace.package]\nversion = "${opts.version}"`, - }, - { - path: "sdks/cli-shared/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/acp-http-client/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/typescript/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/cli/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/gigacode/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/cli/platforms/*/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - { - path: "sdks/gigacode/platforms/*/package.json", - find: /"version": ".*"/, - replace: `"version": "${opts.version}"`, - }, - ]; - - // Update internal crate versions in workspace dependencies - // These need to match the new version so cargo publish can resolve them correctly - const internalCrates = [ - "sandbox-agent", - "sandbox-agent-error", - "sandbox-agent-agent-management", - "sandbox-agent-agent-credentials", - "sandbox-agent-opencode-adapter", - "sandbox-agent-opencode-server-manager", - "acp-http-adapter", - ]; - - const cargoTomlPath = `${opts.root}/Cargo.toml`; + // 1. Update workspace version and internal crate versions in root Cargo.toml + const cargoTomlPath = join(opts.root, "Cargo.toml"); let cargoContent = await fs.readFile(cargoTomlPath, "utf-8"); + // Update [workspace.package] version + assert( + /\[workspace\.package\]\nversion = ".*"/.test(cargoContent), + "Could not find workspace.package version in Cargo.toml", + ); + cargoContent = cargoContent.replace( + /\[workspace\.package\]\nversion = ".*"/, + `[workspace.package]\nversion = "${opts.version}"`, + ); + + // Discover internal crates from [workspace.dependencies] by matching + // lines with both `version = "..."` and `path = "..."` (internal path deps) + const internalCratePattern = /^(\S+)\s*=\s*\{[^}]*version\s*=\s*"[^"]+"\s*,[^}]*path\s*=/gm; + let match; + const internalCrates: string[] = []; + while ((match = internalCratePattern.exec(cargoContent)) !== null) { + internalCrates.push(match[1]); + } + + console.log(`Discovered ${internalCrates.length} internal crates to version-bump:`); + for (const crate of internalCrates) console.log(` - ${crate}`); + for (const crate of internalCrates) { - // Match: crate-name = { version = "x.y.z", path = "..." } const pattern = new RegExp( `(${crate.replace(/-/g, "-")} = \\{ version = ")[^"]+(",)`, - "g" + "g", ); cargoContent = cargoContent.replace(pattern, `$1${opts.version}$2`); } @@ -81,18 +48,34 @@ export async function updateVersion(opts: ReleaseOpts) { await fs.writeFile(cargoTomlPath, cargoContent); await $({ cwd: opts.root })`git add Cargo.toml`; - // Substitute all files - for (const { path: globPath, find, replace } of findReplace) { - const paths = await glob(globPath, { cwd: opts.root }); - assert(paths.length > 0, `no paths matched: ${globPath}`); - for (const path of paths) { - const fullPath = `${opts.root}/${path}`; - const file = await fs.readFile(fullPath, "utf-8"); - assert(find.test(file), `file does not match ${find}: ${fullPath}`); - const newFile = file.replace(find, replace); - await fs.writeFile(fullPath, newFile); + // 2. Discover and update all non-private SDK package.json versions + const packageJsonPaths = await glob("sdks/**/package.json", { + cwd: opts.root, + ignore: ["**/node_modules/**"], + }); - await $({ cwd: opts.root })`git add ${path}`; - } + // Filter to non-private packages only + const toUpdate: string[] = []; + for (const relPath of packageJsonPaths) { + const fullPath = join(opts.root, relPath); + const content = await fs.readFile(fullPath, "utf-8"); + const pkg = JSON.parse(content); + if (pkg.private) continue; + toUpdate.push(relPath); + } + + console.log(`Discovered ${toUpdate.length} SDK package.json files to version-bump:`); + for (const relPath of toUpdate) console.log(` - ${relPath}`); + + for (const relPath of toUpdate) { + const fullPath = join(opts.root, relPath); + const content = await fs.readFile(fullPath, "utf-8"); + + const versionPattern = /"version": ".*"/; + assert(versionPattern.test(content), `No version field in ${relPath}`); + + const updated = content.replace(versionPattern, `"version": "${opts.version}"`); + await fs.writeFile(fullPath, updated); + await $({ cwd: opts.root })`git add ${relPath}`; } } diff --git a/sdks/acp-http-client/package.json b/sdks/acp-http-client/package.json index 6eb419a..1fd4fb6 100644 --- a/sdks/acp-http-client/package.json +++ b/sdks/acp-http-client/package.json @@ -1,6 +1,6 @@ { "name": "acp-http-client", - "version": "0.1.0", + "version": "0.2.1", "description": "Protocol-faithful ACP JSON-RPC over streamable HTTP client.", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli-shared/package.json b/sdks/cli-shared/package.json index 4317604..d878151 100644 --- a/sdks/cli-shared/package.json +++ b/sdks/cli-shared/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-shared", - "version": "0.2.0", + "version": "0.2.1", "description": "Shared helpers for sandbox-agent CLI and SDK", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/package.json b/sdks/cli/package.json index 90ad966..e79e1cc 100644 --- a/sdks/cli/package.json +++ b/sdks/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli", - "version": "0.2.0", + "version": "0.2.1", "description": "CLI for sandbox-agent - run AI coding agents in sandboxes", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/darwin-arm64/package.json b/sdks/cli/platforms/darwin-arm64/package.json index 462abb3..74deca6 100644 --- a/sdks/cli/platforms/darwin-arm64/package.json +++ b/sdks/cli/platforms/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-darwin-arm64", - "version": "0.2.0", + "version": "0.2.1", "description": "sandbox-agent CLI binary for macOS ARM64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/darwin-x64/package.json b/sdks/cli/platforms/darwin-x64/package.json index 20b2644..d179804 100644 --- a/sdks/cli/platforms/darwin-x64/package.json +++ b/sdks/cli/platforms/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-darwin-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "sandbox-agent CLI binary for macOS x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/linux-arm64/package.json b/sdks/cli/platforms/linux-arm64/package.json index 5f7439f..684a4da 100644 --- a/sdks/cli/platforms/linux-arm64/package.json +++ b/sdks/cli/platforms/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-linux-arm64", - "version": "0.2.0", + "version": "0.2.1", "description": "sandbox-agent CLI binary for Linux arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/linux-x64/package.json b/sdks/cli/platforms/linux-x64/package.json index 15bc1a6..7c004fc 100644 --- a/sdks/cli/platforms/linux-x64/package.json +++ b/sdks/cli/platforms/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-linux-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "sandbox-agent CLI binary for Linux x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/cli/platforms/win32-x64/package.json b/sdks/cli/platforms/win32-x64/package.json index 3890155..9166aee 100644 --- a/sdks/cli/platforms/win32-x64/package.json +++ b/sdks/cli/platforms/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/cli-win32-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "sandbox-agent CLI binary for Windows x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/package.json b/sdks/gigacode/package.json index e16e70e..3c9c614 100644 --- a/sdks/gigacode/package.json +++ b/sdks/gigacode/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode", - "version": "0.2.0", + "version": "0.2.1", "description": "Gigacode CLI (sandbox-agent with OpenCode attach by default)", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/darwin-arm64/package.json b/sdks/gigacode/platforms/darwin-arm64/package.json index 2324291..ed7aea7 100644 --- a/sdks/gigacode/platforms/darwin-arm64/package.json +++ b/sdks/gigacode/platforms/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-darwin-arm64", - "version": "0.2.0", + "version": "0.2.1", "description": "gigacode CLI binary for macOS arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/darwin-x64/package.json b/sdks/gigacode/platforms/darwin-x64/package.json index 6b94c06..2a14864 100644 --- a/sdks/gigacode/platforms/darwin-x64/package.json +++ b/sdks/gigacode/platforms/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-darwin-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "gigacode CLI binary for macOS x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/linux-arm64/package.json b/sdks/gigacode/platforms/linux-arm64/package.json index 9b82cac..a97c711 100644 --- a/sdks/gigacode/platforms/linux-arm64/package.json +++ b/sdks/gigacode/platforms/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-linux-arm64", - "version": "0.2.0", + "version": "0.2.1", "description": "gigacode CLI binary for Linux arm64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/linux-x64/package.json b/sdks/gigacode/platforms/linux-x64/package.json index b9ac143..3ba6cd1 100644 --- a/sdks/gigacode/platforms/linux-x64/package.json +++ b/sdks/gigacode/platforms/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-linux-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "gigacode CLI binary for Linux x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/gigacode/platforms/win32-x64/package.json b/sdks/gigacode/platforms/win32-x64/package.json index ae287f0..1e48389 100644 --- a/sdks/gigacode/platforms/win32-x64/package.json +++ b/sdks/gigacode/platforms/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/gigacode-win32-x64", - "version": "0.2.0", + "version": "0.2.1", "description": "gigacode CLI binary for Windows x64", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-indexeddb/package.json b/sdks/persist-indexeddb/package.json index dc204ca..ba6706d 100644 --- a/sdks/persist-indexeddb/package.json +++ b/sdks/persist-indexeddb/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-indexeddb", - "version": "0.1.0", + "version": "0.2.1", "description": "IndexedDB persistence driver for the Sandbox Agent TypeScript SDK", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-postgres/package.json b/sdks/persist-postgres/package.json index 0ad277d..245d820 100644 --- a/sdks/persist-postgres/package.json +++ b/sdks/persist-postgres/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-postgres", - "version": "0.1.0", + "version": "0.2.1", "description": "PostgreSQL persistence driver for the Sandbox Agent TypeScript SDK", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-rivet/package.json b/sdks/persist-rivet/package.json index 44baf47..4eb1791 100644 --- a/sdks/persist-rivet/package.json +++ b/sdks/persist-rivet/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-rivet", - "version": "0.1.0", + "version": "0.2.1", "description": "Rivet Actor persistence driver for the Sandbox Agent TypeScript SDK", "license": "Apache-2.0", "repository": { diff --git a/sdks/persist-sqlite/package.json b/sdks/persist-sqlite/package.json index 46f1e58..80623a1 100644 --- a/sdks/persist-sqlite/package.json +++ b/sdks/persist-sqlite/package.json @@ -1,6 +1,6 @@ { "name": "@sandbox-agent/persist-sqlite", - "version": "0.1.0", + "version": "0.2.1", "description": "SQLite persistence driver for the Sandbox Agent TypeScript SDK", "license": "Apache-2.0", "repository": { @@ -17,6 +17,7 @@ } }, "dependencies": { + "better-sqlite3": "^11.0.0", "sandbox-agent": "workspace:*" }, "files": [ @@ -29,6 +30,7 @@ "test:watch": "vitest" }, "devDependencies": { + "@types/better-sqlite3": "^7.0.0", "@types/node": "^22.0.0", "tsup": "^8.0.0", "typescript": "^5.7.0", diff --git a/sdks/persist-sqlite/src/index.ts b/sdks/persist-sqlite/src/index.ts index 867e83f..73f4f48 100644 --- a/sdks/persist-sqlite/src/index.ts +++ b/sdks/persist-sqlite/src/index.ts @@ -1,4 +1,4 @@ -import { DatabaseSync } from "node:sqlite"; +import Database from "better-sqlite3"; import type { ListEventsRequest, ListPage, @@ -12,16 +12,13 @@ const DEFAULT_LIST_LIMIT = 100; export interface SQLiteSessionPersistDriverOptions { filename?: string; - open?: boolean; } export class SQLiteSessionPersistDriver implements SessionPersistDriver { - private readonly db: DatabaseSync; + private readonly db: Database.Database; constructor(options: SQLiteSessionPersistDriverOptions = {}) { - this.db = new DatabaseSync(options.filename ?? ":memory:", { - open: options.open ?? true, - }); + this.db = new Database(options.filename ?? ":memory:"); this.initialize(); } diff --git a/sdks/persist-sqlite/tsup.config.ts b/sdks/persist-sqlite/tsup.config.ts index 70a8b7e..e896e72 100644 --- a/sdks/persist-sqlite/tsup.config.ts +++ b/sdks/persist-sqlite/tsup.config.ts @@ -7,4 +7,5 @@ export default defineConfig({ sourcemap: true, clean: true, target: "es2022", + external: ["better-sqlite3"], }); diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json index 2df4050..8997de1 100644 --- a/sdks/typescript/package.json +++ b/sdks/typescript/package.json @@ -1,6 +1,6 @@ { "name": "sandbox-agent", - "version": "0.2.0", + "version": "0.2.1", "description": "Universal API for automatic coding agents in sandboxes. Supports Claude Code, Codex, OpenCode, and Amp.", "license": "Apache-2.0", "repository": {