Rename Factory to Foundry

This commit is contained in:
Nathan Flurry 2026-03-10 22:01:39 -07:00
parent 0a8fda040b
commit 324de36577
256 changed files with 605 additions and 603 deletions

View file

@ -17,7 +17,7 @@ coverage/
# Environment
.env
.env.*
.sandbox-agent-factory/
.sandbox-agent-foundry/
# IDE
.idea/

View file

@ -3,7 +3,7 @@
APP_URL=http://localhost:4173
BETTER_AUTH_URL=http://localhost:4173
BETTER_AUTH_SECRET=sandbox-agent-factory-development-only-change-me
BETTER_AUTH_SECRET=sandbox-agent-foundry-development-only-change-me
GITHUB_REDIRECT_URI=http://localhost:4173/api/rivet/app/auth/github/callback
# Fill these in when enabling live GitHub OAuth.

6
.gitignore vendored
View file

@ -49,12 +49,14 @@ Cargo.lock
.claude/
.opencode/
.context/
factory/.context/
foundry/.context/
**/.sandbox-agent-factory/
**/.sandbox-agent-foundry/
# Example temp files
.tmp-upload/
*.db
.sandbox-agent-factory/
.sandbox-agent-foundry/
.openhandoff/
# CLI binaries (downloaded during npm publish)

View file

@ -102,7 +102,7 @@
- `research/acp/todo.md`
- `research/friction/rivetkit.md`
- `research/friction/sandbox-agent-sdk.md`
- `research/friction/factory.md`
- `research/friction/foundry.md`
## Change Tracking
@ -114,7 +114,7 @@
- `research/acp/friction.md` — ACP protocol, migration, and spec issues.
- `research/friction/rivetkit.md` — RivetKit runtime, actor model, queues, keys, workflows.
- `research/friction/sandbox-agent-sdk.md` — Sandbox Agent SDK/API, TypeScript clients, ACP HTTP client.
- `research/friction/factory.md` — Factory product development, frontend, backend, client.
- `research/friction/foundry.md` — Foundry product development, frontend, backend, client.
- Friction log entry format (all logs use the same template):
```
- Date:

View file

@ -157,7 +157,7 @@ sandbox-agent server --no-token --host 127.0.0.1 --port 2468
[Quickstart](https://sandboxagent.dev/docs/quickstart) — [Deployment guides](https://sandboxagent.dev/docs/deploy)
Factory self-hosting setup for auth, GitHub, and billing: [docs/deploy/factory-self-hosting.mdx](docs/deploy/factory-self-hosting.mdx)
Foundry self-hosting setup for auth, GitHub, and billing: [docs/deploy/foundry-self-hosting.mdx](docs/deploy/foundry-self-hosting.mdx)
### CLI

View file

@ -1,15 +1,15 @@
---
title: "Factory Self-Hosting"
description: "Environment, credentials, and deployment setup for Sandbox Agent Factory auth, GitHub, and billing."
title: "Foundry Self-Hosting"
description: "Environment, credentials, and deployment setup for Sandbox Agent Foundry auth, GitHub, and billing."
---
This guide documents the deployment contract for the Factory product surface: app auth, GitHub onboarding, repository import, and billing.
This guide documents the deployment contract for the Foundry product surface: app auth, GitHub onboarding, repository import, and billing.
It also covers the local-development bootstrap that uses `.env.development` only when `NODE_ENV=development`.
## Local Development
For backend local development, the Factory backend now supports a development-only dotenv bootstrap:
For backend local development, the Foundry backend now supports a development-only dotenv bootstrap:
- It loads `.env.development.local` and `.env.development`
- It does this **only** when `NODE_ENV=development`
@ -26,7 +26,7 @@ cp .env.development.example .env.development
Run the backend with:
```bash
just factory-backend-start
just foundry-backend-start
```
That recipe sets `NODE_ENV=development`, which enables the dotenv loader.
@ -37,7 +37,7 @@ These values can be safely defaulted for local development:
- `APP_URL=http://localhost:4173`
- `BETTER_AUTH_URL=http://localhost:4173`
- `BETTER_AUTH_SECRET=sandbox-agent-factory-development-only-change-me`
- `BETTER_AUTH_SECRET=sandbox-agent-foundry-development-only-change-me`
- `GITHUB_REDIRECT_URI=http://localhost:4173/api/rivet/app/auth/github/callback`
These should be treated as development-only values.
@ -71,7 +71,7 @@ Use GitHub OAuth for:
## GitHub App
If your Factory deployment uses GitHub App-backed organization install and repo import, also configure:
If your Foundry deployment uses GitHub App-backed organization install and repo import, also configure:
| Variable | Required | Notes |
|---|---:|---|
@ -139,7 +139,7 @@ Stripe should own:
## Mock Invariant
Factorys mock client path should continue to work end to end even when the real auth/GitHub/Stripe path exists.
Foundrys mock client path should continue to work end to end even when the real auth/GitHub/Stripe path exists.
That includes:

View file

@ -57,7 +57,7 @@
"icon": "server",
"pages": [
"deploy/local",
"deploy/factory-self-hosting",
"deploy/foundry-self-hosting",
"deploy/computesdk",
"deploy/e2b",
"deploy/daytona",

View file

@ -1,31 +0,0 @@
name: sandbox-agent-factory-mock
services:
frontend:
build:
context: ..
dockerfile: factory/docker/frontend.dev.Dockerfile
working_dir: /app
environment:
HOME: "/tmp"
FACTORY_FRONTEND_CLIENT_MODE: "mock"
ports:
- "4174:4173"
volumes:
- "..:/app"
- "./.sandbox-agent-factory:/app/factory/.sandbox-agent-factory"
# Use Linux-native workspace dependencies inside the container instead of host node_modules.
- "sandbox-agent-factory-mock_node_modules:/app/node_modules"
- "sandbox-agent-factory-mock_client_node_modules:/app/factory/packages/client/node_modules"
- "sandbox-agent-factory-mock_frontend_errors_node_modules:/app/factory/packages/frontend-errors/node_modules"
- "sandbox-agent-factory-mock_frontend_node_modules:/app/factory/packages/frontend/node_modules"
- "sandbox-agent-factory-mock_shared_node_modules:/app/factory/packages/shared/node_modules"
- "sandbox-agent-factory-mock_pnpm_store:/tmp/.local/share/pnpm/store"
volumes:
sandbox-agent-factory-mock_node_modules: {}
sandbox-agent-factory-mock_client_node_modules: {}
sandbox-agent-factory-mock_frontend_errors_node_modules: {}
sandbox-agent-factory-mock_frontend_node_modules: {}
sandbox-agent-factory-mock_shared_node_modules: {}
sandbox-agent-factory-mock_pnpm_store: {}

View file

@ -1,7 +0,0 @@
declare module "@sandbox-agent/factory-client/view-model" {
export {
HANDOFF_STATUS_GROUPS,
groupTaskStatus,
} from "@sandbox-agent/factory-client";
export type { TaskStatusGroup } from "@sandbox-agent/factory-client";
}

View file

@ -30,19 +30,19 @@ Use `pnpm` workspaces and Turborepo.
**Always use Docker Compose to run dev servers.** Do not start the backend, frontend, or any other long-running service directly via `bun`, `pnpm dev`, Vite, or tmux. All dev services must run through the Compose stack so that networking, environment variables, and service dependencies are consistent.
- Start the full dev stack (real backend): `just factory-dev`
- Stop the dev stack: `just factory-dev-down`
- Tail dev logs: `just factory-dev-logs`
- Start the mock dev stack (frontend-only, no backend): `just factory-dev-mock`
- Stop the mock stack: `just factory-dev-mock-down`
- Tail mock logs: `just factory-dev-mock-logs`
- Start the production-build preview stack: `just factory-preview`
- Stop the preview stack: `just factory-preview-down`
- Tail preview logs: `just factory-preview-logs`
- Start the full dev stack (real backend): `just foundry-dev`
- Stop the dev stack: `just foundry-dev-down`
- Tail dev logs: `just foundry-dev-logs`
- Start the mock dev stack (frontend-only, no backend): `just foundry-dev-mock`
- Stop the mock stack: `just foundry-dev-mock-down`
- Tail mock logs: `just foundry-dev-mock-logs`
- Start the production-build preview stack: `just foundry-preview`
- Stop the preview stack: `just foundry-preview-down`
- Tail preview logs: `just foundry-preview-logs`
The real dev server runs on port 4173 (frontend) + 7741 (backend). The mock dev server runs on port 4174 (frontend only). Both can run simultaneously.
When making code changes, restart or recreate the relevant Compose services so the running app reflects the latest code (e.g. `docker compose -f factory/compose.dev.yaml up -d --build backend`).
When making code changes, restart or recreate the relevant Compose services so the running app reflects the latest code (e.g. `docker compose -f foundry/compose.dev.yaml up -d --build backend`).
## Mock vs Real Backend — UI Change Policy
@ -60,7 +60,7 @@ When making code changes, restart or recreate the relevant Compose services so t
- Install deps: `pnpm install`
- Full active-workspace validation: `pnpm -w typecheck`, `pnpm -w build`, `pnpm -w test`
- Start the frontend against the mock workbench client (no backend needed): `FACTORY_FRONTEND_CLIENT_MODE=mock pnpm --filter @sandbox-agent/factory-frontend dev`
- Start the frontend against the mock workbench client (no backend needed): `FOUNDRY_FRONTEND_CLIENT_MODE=mock pnpm --filter @sandbox-agent/foundry-frontend dev`
## Loading & Skeleton UI Policy
@ -152,14 +152,14 @@ For all Rivet/RivetKit implementation:
## Workspace + Actor Rules
- Everything is scoped to a workspace.
- All durable Factory data must live inside actors.
- All durable Foundry data must live inside actors.
- App-shell/auth/session/org/billing data is actor-owned data too; do not introduce backend-global stores for it.
- Do not add standalone SQLite files, JSON stores, in-memory singleton stores, or any other non-actor persistence for Factory product state.
- Do not add standalone SQLite files, JSON stores, in-memory singleton stores, or any other non-actor persistence for Foundry product state.
- If data needs durable persistence, store it in actor `c.state` or the owning actor's SQLite DB via `c.db`.
- Workspace resolution order: `--workspace` flag -> config default -> `"default"`.
- `ControlPlaneActor` is replaced by `WorkspaceActor` (workspace coordinator).
- Every actor key must be prefixed with workspace namespace (`["ws", workspaceId, ...]`).
- Product surfaces must use `@sandbox-agent/factory-client` (`packages/client`) for backend access; `rivetkit/client` imports are only allowed inside `packages/client`.
- Product surfaces must use `@sandbox-agent/foundry-client` (`packages/client`) for backend access; `rivetkit/client` imports are only allowed inside `packages/client`.
- Do not add custom backend REST endpoints (no `/v1/*` shim layer).
- We own the sandbox-agent project; treat sandbox-agent defects as first-party bugs and fix them instead of working around them.
- Keep strict single-writer ownership: each table/row has exactly one actor writer.
@ -171,7 +171,7 @@ For all Rivet/RivetKit implementation:
- Use create semantics only on explicit provisioning/create paths where creating a new actor instance is intended.
- `getOrCreate` is a last resort for create paths when an explicit create API is unavailable; never use it in read/command paths.
- For long-lived cross-actor links (for example sandbox/session runtime access), persist actor identity (`actorId`) and keep a fallback lookup path by actor id.
- Docker dev: `compose.dev.yaml` mounts a named volume at `/root/.local/share/sandbox-agent-factory/repos` to persist backend-managed git clones across restarts. Code must still work if this volume is not present (create directories as needed).
- Docker dev: `compose.dev.yaml` mounts a named volume at `/root/.local/share/sandbox-agent-foundry/repos` to persist backend-managed git clones across restarts. Code must still work if this volume is not present (create directories as needed).
- RivetKit actor `c.state` is durable, but in Docker it is stored under `/root/.local/share/rivetkit`. If that path is not persisted, actor state-derived indexes (for example, in `project` actor state) can be lost after container recreation even when other data still exists.
- Workflow history divergence policy:
- Production: never auto-delete actor state to resolve `HistoryDivergedError`; ship explicit workflow migrations (`ctx.removed(...)`, step compatibility).
@ -194,7 +194,7 @@ For all Rivet/RivetKit implementation:
## Config
- Keep config path at `~/.config/sandbox-agent-factory/config.toml`.
- Keep config path at `~/.config/sandbox-agent-foundry/config.toml`.
- Evolve properties in place; do not move config location.
## Project Guidance

View file

@ -5,8 +5,8 @@
1. Clone:
```bash
git clone https://github.com/rivet-dev/sandbox-agent-factory.git
cd sandbox-agent-factory
git clone https://github.com/rivet-dev/sandbox-agent-foundry.git
cd sandbox-agent-foundry
```
2. Install dependencies:
@ -35,7 +35,7 @@ Build local RivetKit before backend changes that depend on Rivet internals:
cd ../rivet
pnpm build -F rivetkit
cd /path/to/sandbox-agent-factory
cd /path/to/sandbox-agent-foundry
just sync-rivetkit
```
@ -54,11 +54,11 @@ pnpm -w test
Start the dev backend (hot reload via `bun --watch`) and Vite frontend via Docker Compose:
```bash
just factory-dev
just foundry-dev
```
Stop it:
```bash
just factory-dev-down
just foundry-dev-down
```

View file

@ -22,15 +22,15 @@ COPY packages/rivetkit-vendor/sqlite-vfs-win32-x64/package.json packages/rivetki
COPY packages/rivetkit-vendor/runner/package.json packages/rivetkit-vendor/runner/package.json
COPY packages/rivetkit-vendor/runner-protocol/package.json packages/rivetkit-vendor/runner-protocol/package.json
COPY packages/rivetkit-vendor/virtual-websocket/package.json packages/rivetkit-vendor/virtual-websocket/package.json
RUN pnpm fetch --frozen-lockfile --filter @sandbox-agent/factory-backend...
RUN pnpm fetch --frozen-lockfile --filter @sandbox-agent/foundry-backend...
FROM base AS build
COPY --from=deps /pnpm/store /pnpm/store
COPY . .
RUN pnpm install --frozen-lockfile --prefer-offline --filter @sandbox-agent/factory-backend...
RUN pnpm --filter @sandbox-agent/factory-shared build
RUN pnpm --filter @sandbox-agent/factory-backend build
RUN pnpm --filter @sandbox-agent/factory-backend deploy --prod --legacy /out
RUN pnpm install --frozen-lockfile --prefer-offline --filter @sandbox-agent/foundry-backend...
RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/foundry-backend build
RUN pnpm --filter @sandbox-agent/foundry-backend deploy --prod --legacy /out
FROM oven/bun:1.2 AS runtime
ENV NODE_ENV=production

View file

@ -1,4 +1,4 @@
# Sandbox Agent Factory
# Sandbox Agent Foundry
TypeScript workspace task system powered by RivetKit actors and SQLite/Drizzle state.

View file

@ -1,17 +1,17 @@
name: sandbox-agent-factory
name: sandbox-agent-foundry
services:
backend:
build:
context: ..
dockerfile: factory/docker/backend.dev.Dockerfile
image: sandbox-agent-factory-backend-dev
dockerfile: foundry/docker/backend.dev.Dockerfile
image: sandbox-agent-foundry-backend-dev
working_dir: /app
environment:
HF_BACKEND_HOST: "0.0.0.0"
HF_BACKEND_PORT: "7741"
HF_RIVET_MANAGER_PORT: "8750"
RIVETKIT_STORAGE_PATH: "/root/.local/share/sandbox-agent-factory/rivetkit"
RIVETKIT_STORAGE_PATH: "/root/.local/share/sandbox-agent-foundry/rivetkit"
# Pass through credentials needed for agent execution + PR creation in dev/e2e.
# Do not hardcode secrets; set these in your environment when starting compose.
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
@ -35,21 +35,21 @@ services:
# Reuse the host Codex auth profile for local sandbox-agent Codex sessions in dev.
- "${HOME}/.codex:/root/.codex"
# Keep backend dependency installs Linux-native instead of using host node_modules.
- "sandbox-agent-factory_backend_root_node_modules:/app/node_modules"
- "sandbox-agent-factory_backend_backend_node_modules:/app/factory/packages/backend/node_modules"
- "sandbox-agent-factory_backend_shared_node_modules:/app/factory/packages/shared/node_modules"
- "sandbox-agent-factory_backend_persist_rivet_node_modules:/app/sdks/persist-rivet/node_modules"
- "sandbox-agent-factory_backend_typescript_node_modules:/app/sdks/typescript/node_modules"
- "sandbox-agent-factory_backend_pnpm_store:/root/.local/share/pnpm/store"
- "sandbox-agent-foundry_backend_root_node_modules:/app/node_modules"
- "sandbox-agent-foundry_backend_backend_node_modules:/app/foundry/packages/backend/node_modules"
- "sandbox-agent-foundry_backend_shared_node_modules:/app/foundry/packages/shared/node_modules"
- "sandbox-agent-foundry_backend_persist_rivet_node_modules:/app/sdks/persist-rivet/node_modules"
- "sandbox-agent-foundry_backend_typescript_node_modules:/app/sdks/typescript/node_modules"
- "sandbox-agent-foundry_backend_pnpm_store:/root/.local/share/pnpm/store"
# Persist backend-managed local git clones across container restarts.
- "sandbox-agent-factory_git_repos:/root/.local/share/sandbox-agent-factory/repos"
- "sandbox-agent-foundry_git_repos:/root/.local/share/sandbox-agent-foundry/repos"
# Persist RivetKit local storage across container restarts.
- "sandbox-agent-factory_rivetkit_storage:/root/.local/share/sandbox-agent-factory/rivetkit"
- "sandbox-agent-foundry_rivetkit_storage:/root/.local/share/sandbox-agent-foundry/rivetkit"
frontend:
build:
context: ..
dockerfile: factory/docker/frontend.dev.Dockerfile
dockerfile: foundry/docker/frontend.dev.Dockerfile
working_dir: /app
depends_on:
- backend
@ -60,28 +60,28 @@ services:
- "4173:4173"
volumes:
- "..:/app"
# Ensure logs in .sandbox-agent-factory/ persist on the host even if we change source mounts later.
- "./.sandbox-agent-factory:/app/factory/.sandbox-agent-factory"
# Ensure logs in .sandbox-agent-foundry/ persist on the host even if we change source mounts later.
- "./.sandbox-agent-foundry:/app/foundry/.sandbox-agent-foundry"
# Use Linux-native workspace dependencies inside the container instead of host node_modules.
- "sandbox-agent-factory_node_modules:/app/node_modules"
- "sandbox-agent-factory_client_node_modules:/app/factory/packages/client/node_modules"
- "sandbox-agent-factory_frontend_errors_node_modules:/app/factory/packages/frontend-errors/node_modules"
- "sandbox-agent-factory_frontend_node_modules:/app/factory/packages/frontend/node_modules"
- "sandbox-agent-factory_shared_node_modules:/app/factory/packages/shared/node_modules"
- "sandbox-agent-factory_pnpm_store:/tmp/.local/share/pnpm/store"
- "sandbox-agent-foundry_node_modules:/app/node_modules"
- "sandbox-agent-foundry_client_node_modules:/app/foundry/packages/client/node_modules"
- "sandbox-agent-foundry_frontend_errors_node_modules:/app/foundry/packages/frontend-errors/node_modules"
- "sandbox-agent-foundry_frontend_node_modules:/app/foundry/packages/frontend/node_modules"
- "sandbox-agent-foundry_shared_node_modules:/app/foundry/packages/shared/node_modules"
- "sandbox-agent-foundry_pnpm_store:/tmp/.local/share/pnpm/store"
volumes:
sandbox-agent-factory_backend_root_node_modules: {}
sandbox-agent-factory_backend_backend_node_modules: {}
sandbox-agent-factory_backend_shared_node_modules: {}
sandbox-agent-factory_backend_persist_rivet_node_modules: {}
sandbox-agent-factory_backend_typescript_node_modules: {}
sandbox-agent-factory_backend_pnpm_store: {}
sandbox-agent-factory_git_repos: {}
sandbox-agent-factory_rivetkit_storage: {}
sandbox-agent-factory_node_modules: {}
sandbox-agent-factory_client_node_modules: {}
sandbox-agent-factory_frontend_errors_node_modules: {}
sandbox-agent-factory_frontend_node_modules: {}
sandbox-agent-factory_shared_node_modules: {}
sandbox-agent-factory_pnpm_store: {}
sandbox-agent-foundry_backend_root_node_modules: {}
sandbox-agent-foundry_backend_backend_node_modules: {}
sandbox-agent-foundry_backend_shared_node_modules: {}
sandbox-agent-foundry_backend_persist_rivet_node_modules: {}
sandbox-agent-foundry_backend_typescript_node_modules: {}
sandbox-agent-foundry_backend_pnpm_store: {}
sandbox-agent-foundry_git_repos: {}
sandbox-agent-foundry_rivetkit_storage: {}
sandbox-agent-foundry_node_modules: {}
sandbox-agent-foundry_client_node_modules: {}
sandbox-agent-foundry_frontend_errors_node_modules: {}
sandbox-agent-foundry_frontend_node_modules: {}
sandbox-agent-foundry_shared_node_modules: {}
sandbox-agent-foundry_pnpm_store: {}

31
foundry/compose.mock.yaml Normal file
View file

@ -0,0 +1,31 @@
name: sandbox-agent-foundry-mock
services:
frontend:
build:
context: ..
dockerfile: foundry/docker/frontend.dev.Dockerfile
working_dir: /app
environment:
HOME: "/tmp"
FOUNDRY_FRONTEND_CLIENT_MODE: "mock"
ports:
- "4174:4173"
volumes:
- "..:/app"
- "./.sandbox-agent-foundry:/app/foundry/.sandbox-agent-foundry"
# Use Linux-native workspace dependencies inside the container instead of host node_modules.
- "sandbox-agent-foundry-mock_node_modules:/app/node_modules"
- "sandbox-agent-foundry-mock_client_node_modules:/app/foundry/packages/client/node_modules"
- "sandbox-agent-foundry-mock_frontend_errors_node_modules:/app/foundry/packages/frontend-errors/node_modules"
- "sandbox-agent-foundry-mock_frontend_node_modules:/app/foundry/packages/frontend/node_modules"
- "sandbox-agent-foundry-mock_shared_node_modules:/app/foundry/packages/shared/node_modules"
- "sandbox-agent-foundry-mock_pnpm_store:/tmp/.local/share/pnpm/store"
volumes:
sandbox-agent-foundry-mock_node_modules: {}
sandbox-agent-foundry-mock_client_node_modules: {}
sandbox-agent-foundry-mock_frontend_errors_node_modules: {}
sandbox-agent-foundry-mock_frontend_node_modules: {}
sandbox-agent-foundry-mock_shared_node_modules: {}
sandbox-agent-foundry-mock_pnpm_store: {}

View file

@ -1,16 +1,16 @@
name: sandbox-agent-factory-preview
name: sandbox-agent-foundry-preview
services:
backend:
build:
context: ..
dockerfile: quebec/docker/backend.preview.Dockerfile
image: sandbox-agent-factory-backend-preview
image: sandbox-agent-foundry-backend-preview
environment:
HF_BACKEND_HOST: "0.0.0.0"
HF_BACKEND_PORT: "7841"
HF_RIVET_MANAGER_PORT: "8850"
RIVETKIT_STORAGE_PATH: "/root/.local/share/sandbox-agent-factory/rivetkit"
RIVETKIT_STORAGE_PATH: "/root/.local/share/sandbox-agent-foundry/rivetkit"
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
CLAUDE_API_KEY: "${CLAUDE_API_KEY:-${ANTHROPIC_API_KEY:-}}"
OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
@ -26,19 +26,19 @@ services:
- "8850:8850"
volumes:
- "${HOME}/.codex:/root/.codex"
- "sandbox-agent-factory_preview_git_repos:/root/.local/share/sandbox-agent-factory/repos"
- "sandbox-agent-factory_preview_rivetkit_storage:/root/.local/share/sandbox-agent-factory/rivetkit"
- "sandbox-agent-foundry_preview_git_repos:/root/.local/share/sandbox-agent-foundry/repos"
- "sandbox-agent-foundry_preview_rivetkit_storage:/root/.local/share/sandbox-agent-foundry/rivetkit"
frontend:
build:
context: ..
dockerfile: quebec/docker/frontend.preview.Dockerfile
image: sandbox-agent-factory-frontend-preview
image: sandbox-agent-foundry-frontend-preview
depends_on:
- backend
ports:
- "4273:4273"
volumes:
sandbox-agent-factory_preview_git_repos: {}
sandbox-agent-factory_preview_rivetkit_storage: {}
sandbox-agent-foundry_preview_git_repos: {}
sandbox-agent-foundry_preview_rivetkit_storage: {}

View file

@ -39,4 +39,4 @@ ENV SANDBOX_AGENT_BIN="/root/.local/bin/sandbox-agent"
WORKDIR /app
CMD ["bash", "-lc", "git config --global --add safe.directory /app >/dev/null 2>&1 || true; pnpm install --force --frozen-lockfile --filter @sandbox-agent/factory-backend... && exec bun factory/packages/backend/src/index.ts start --host 0.0.0.0 --port 7741"]
CMD ["bash", "-lc", "git config --global --add safe.directory /app >/dev/null 2>&1 || true; pnpm install --force --frozen-lockfile --filter @sandbox-agent/foundry-backend... && exec bun foundry/packages/backend/src/index.ts start --host 0.0.0.0 --port 7741"]

View file

@ -41,8 +41,8 @@ WORKDIR /workspace/quebec
COPY quebec /workspace/quebec
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @sandbox-agent/factory-shared build
RUN pnpm --filter @sandbox-agent/factory-client build
RUN pnpm --filter @sandbox-agent/factory-backend build
RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/foundry-client build
RUN pnpm --filter @sandbox-agent/foundry-backend build
CMD ["bash", "-lc", "git config --global --add safe.directory /workspace/quebec >/dev/null 2>&1 || true; exec bun packages/backend/dist/index.js start --host 0.0.0.0 --port 7841"]

View file

@ -8,4 +8,4 @@ RUN npm install -g pnpm@10.28.2
WORKDIR /app
CMD ["bash", "-lc", "pnpm install --force --frozen-lockfile --filter @sandbox-agent/factory-frontend... && cd factory/packages/frontend && exec pnpm vite --host 0.0.0.0 --port 4173"]
CMD ["bash", "-lc", "pnpm install --force --frozen-lockfile --filter @sandbox-agent/foundry-frontend... && cd foundry/packages/frontend && exec pnpm vite --host 0.0.0.0 --port 4173"]

View file

@ -9,10 +9,10 @@ WORKDIR /workspace/quebec
COPY quebec /workspace/quebec
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @sandbox-agent/factory-shared build
RUN pnpm --filter @sandbox-agent/factory-client build
RUN pnpm --filter @sandbox-agent/factory-frontend-errors build
RUN pnpm --filter @sandbox-agent/factory-frontend build
RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/foundry-client build
RUN pnpm --filter @sandbox-agent/foundry-frontend-errors build
RUN pnpm --filter @sandbox-agent/foundry-frontend build
FROM nginx:1.27-alpine

View file

@ -1,5 +1,5 @@
{
"name": "@sandbox-agent/factory-backend",
"name": "@sandbox-agent/foundry-backend",
"version": "0.1.0",
"private": true,
"type": "module",
@ -17,7 +17,7 @@
"@hono/node-server": "^1.19.7",
"@hono/node-ws": "^1.3.0",
"@iarna/toml": "^2.2.5",
"@sandbox-agent/factory-shared": "workspace:*",
"@sandbox-agent/foundry-shared": "workspace:*",
"@sandbox-agent/persist-rivet": "workspace:*",
"drizzle-orm": "^0.44.5",
"hono": "^4.11.9",

View file

@ -1,4 +1,4 @@
import type { AppConfig } from "@sandbox-agent/factory-shared";
import type { AppConfig } from "@sandbox-agent/foundry-shared";
import type { BackendDriver } from "../driver.js";
import type { NotificationService } from "../notifications/index.js";
import type { ProviderRegistry } from "../providers/index.js";

View file

@ -1,4 +1,4 @@
import type { TaskStatus, ProviderId } from "@sandbox-agent/factory-shared";
import type { TaskStatus, ProviderId } from "@sandbox-agent/foundry-shared";
export interface TaskCreatedEvent {
workspaceId: string;

View file

@ -8,7 +8,7 @@ import {
taskStatusSyncKey,
workspaceKey,
} from "./keys.js";
import type { ProviderId } from "@sandbox-agent/factory-shared";
import type { ProviderId } from "@sandbox-agent/foundry-shared";
export function actorClient(c: any) {
return c.client();

View file

@ -2,7 +2,7 @@
import { and, desc, eq } from "drizzle-orm";
import { actor, queue } from "rivetkit";
import { Loop, workflow } from "rivetkit/workflow";
import type { HistoryEvent } from "@sandbox-agent/factory-shared";
import type { HistoryEvent } from "@sandbox-agent/foundry-shared";
import { selfHistory } from "../handles.js";
import { historyDb } from "./db/db.js";
import { events } from "./db/schema.js";

View file

@ -27,5 +27,5 @@ export function logActorWarning(
...(context ?? {})
};
// eslint-disable-next-line no-console
console.warn("[factory][actor:warn]", payload);
console.warn("[foundry][actor:warn]", payload);
}

View file

@ -10,7 +10,7 @@ import type {
RepoOverview,
RepoStackAction,
RepoStackActionResult
} from "@sandbox-agent/factory-shared";
} from "@sandbox-agent/foundry-shared";
import { getActorRuntimeContext } from "../context.js";
import {
getTask,
@ -21,7 +21,7 @@ import {
selfRepo
} from "../handles.js";
import { isActorNotFoundError, logActorWarning, resolveErrorMessage } from "../logging.js";
import { factoryRepoClonePath } from "../../services/factory-paths.js";
import { foundryRepoClonePath } from "../../services/foundry-paths.js";
import { expectQueueResponse } from "../../services/queue.js";
import { withRepoGitLock } from "../../services/repo-git-lock.js";
import { branches, taskIndex, prCache, repoMeta } from "./db/schema.js";
@ -132,7 +132,7 @@ export function repoWorkflowQueueName(name: RepoQueueName): RepoQueueName {
async function ensureLocalClone(c: any, remoteUrl: string): Promise<string> {
const { config, driver } = getActorRuntimeContext();
const localPath = factoryRepoClonePath(config, c.state.workspaceId, c.state.repoId);
const localPath = foundryRepoClonePath(config, c.state.workspaceId, c.state.repoId);
await driver.git.ensureCloned(remoteUrl, localPath);
c.state.localPath = localPath;
return localPath;

View file

@ -2,7 +2,7 @@ import { setTimeout as delay } from "node:timers/promises";
import { eq } from "drizzle-orm";
import { actor, queue } from "rivetkit";
import { Loop, workflow } from "rivetkit/workflow";
import type { ProviderId } from "@sandbox-agent/factory-shared";
import type { ProviderId } from "@sandbox-agent/foundry-shared";
import type { SessionEvent, SessionRecord } from "sandbox-agent";
import { sandboxInstanceDb } from "./db/db.js";
import { sandboxInstance as sandboxInstanceTable } from "./db/schema.js";

View file

@ -1,6 +1,6 @@
import { actor, queue } from "rivetkit";
import { workflow } from "rivetkit/workflow";
import type { ProviderId } from "@sandbox-agent/factory-shared";
import type { ProviderId } from "@sandbox-agent/foundry-shared";
import { getTask, getSandboxInstance, selfTaskStatusSync } from "../handles.js";
import { logActorWarning, resolveErrorMessage, resolveErrorStack } from "../logging.js";
import { type PollingControlState, runWorkflowPollingLoop } from "../polling.js";

View file

@ -10,7 +10,7 @@ import type {
TaskWorkbenchSendMessageInput,
TaskWorkbenchUpdateDraftInput,
ProviderId
} from "@sandbox-agent/factory-shared";
} from "@sandbox-agent/foundry-shared";
import { expectQueueResponse } from "../../services/queue.js";
import { selfTask } from "../handles.js";
import { taskDb } from "./db/db.js";

View file

@ -1,6 +1,6 @@
// @ts-nocheck
import { eq } from "drizzle-orm";
import type { TaskRecord, TaskStatus } from "@sandbox-agent/factory-shared";
import type { TaskRecord, TaskStatus } from "@sandbox-agent/foundry-shared";
import { getOrCreateWorkspace } from "../../handles.js";
import { task as taskTable, taskRuntime, taskSandboxes } from "../db/schema.js";
import { historyKey } from "../../keys.js";

View file

@ -31,7 +31,7 @@ import type {
TaskWorkbenchCreateTaskInput,
TaskWorkbenchSnapshot,
WorkspaceUseInput
} from "@sandbox-agent/factory-shared";
} from "@sandbox-agent/foundry-shared";
import { getActorRuntimeContext } from "../context.js";
import { getTask, getOrCreateHistory, getOrCreateRepo, selfWorkspace } from "../handles.js";
import { logActorWarning, resolveErrorMessage } from "../logging.js";

View file

@ -2,14 +2,14 @@
import { desc, eq } from "drizzle-orm";
import { randomUUID } from "node:crypto";
import type {
FactoryAppSnapshot,
FactoryBillingPlanId,
FactoryBillingState,
FactoryOrganization,
FactoryOrganizationMember,
FactoryUser,
UpdateFactoryOrganizationProfileInput,
} from "@sandbox-agent/factory-shared";
FoundryAppSnapshot,
FoundryBillingPlanId,
FoundryBillingState,
FoundryOrganization,
FoundryOrganizationMember,
FoundryUser,
UpdateFoundryOrganizationProfileInput,
} from "@sandbox-agent/foundry-shared";
import { getActorRuntimeContext } from "../context.js";
import { getOrCreateWorkspace } from "../handles.js";
import { GitHubAppError } from "../../services/app-github.js";
@ -53,7 +53,7 @@ function personalWorkspaceId(login: string): string {
return `personal-${slugify(login)}`;
}
function organizationWorkspaceId(kind: FactoryOrganization["kind"], login: string): string {
function organizationWorkspaceId(kind: FoundryOrganization["kind"], login: string): string {
return kind === "personal" ? personalWorkspaceId(login) : slugify(login);
}
@ -95,7 +95,7 @@ function decodeOauthState(value: string): { sessionId: string; nonce: string } {
};
}
function seatsIncludedForPlan(planId: FactoryBillingPlanId): number {
function seatsIncludedForPlan(planId: FoundryBillingPlanId): number {
switch (planId) {
case "free":
return 1;
@ -107,7 +107,7 @@ function seatsIncludedForPlan(planId: FactoryBillingPlanId): number {
function stripeStatusToBillingStatus(
stripeStatus: string,
cancelAtPeriodEnd: boolean,
): FactoryBillingState["status"] {
): FoundryBillingState["status"] {
if (cancelAtPeriodEnd) {
return "scheduled_cancel";
}
@ -124,7 +124,7 @@ function formatUnixDate(value: number): string {
return new Date(value * 1000).toISOString().slice(0, 10);
}
function legacyRepoImportStatusToGithubSyncStatus(value: string | null | undefined): FactoryOrganization["github"]["syncStatus"] {
function legacyRepoImportStatusToGithubSyncStatus(value: string | null | undefined): FoundryOrganization["github"]["syncStatus"] {
switch (value) {
case "ready":
return "synced";
@ -230,12 +230,12 @@ async function getOrganizationState(workspace: any) {
return await workspace.getOrganizationShellState({});
}
async function buildAppSnapshot(c: any, sessionId: string): Promise<FactoryAppSnapshot> {
async function buildAppSnapshot(c: any, sessionId: string): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireAppSessionRow(c, sessionId);
const eligibleOrganizationIds = parseEligibleOrganizationIds(session.eligibleOrganizationIdsJson);
const organizations: FactoryOrganization[] = [];
const organizations: FoundryOrganization[] = [];
for (const organizationId of eligibleOrganizationIds) {
try {
const workspace = await getOrCreateWorkspace(c, organizationId);
@ -249,7 +249,7 @@ async function buildAppSnapshot(c: any, sessionId: string): Promise<FactoryAppSn
}
}
const currentUser: FactoryUser | null = session.currentUserId
const currentUser: FoundryUser | null = session.currentUserId
? {
id: session.currentUserId,
name: session.currentUserName ?? session.currentUserGithubLogin ?? "GitHub user",
@ -452,7 +452,7 @@ async function syncGithubSessionFromToken(
return {
sessionId: session.id,
redirectTo: `${appShell.appUrl}/organizations?factorySession=${encodeURIComponent(session.id)}`,
redirectTo: `${appShell.appUrl}/organizations?foundrySession=${encodeURIComponent(session.id)}`,
};
}
@ -473,7 +473,7 @@ async function requireOrganizationProfileRow(c: any) {
return row;
}
async function listOrganizationMembers(c: any): Promise<FactoryOrganizationMember[]> {
async function listOrganizationMembers(c: any): Promise<FoundryOrganizationMember[]> {
assertOrganizationWorkspace(c);
const rows = await c.db
.select()
@ -499,7 +499,7 @@ async function listOrganizationSeatAssignments(c: any): Promise<string[]> {
return rows.map((row) => row.email);
}
async function listOrganizationInvoices(c: any): Promise<FactoryBillingState["invoices"]> {
async function listOrganizationInvoices(c: any): Promise<FoundryBillingState["invoices"]> {
assertOrganizationWorkspace(c);
const rows = await c.db
.select()
@ -591,7 +591,7 @@ async function applySubscriptionState(
trialEnd: number | null;
defaultPaymentMethodLabel: string;
},
fallbackPlanId: FactoryBillingPlanId,
fallbackPlanId: FoundryBillingPlanId,
): Promise<void> {
await workspace.applyOrganizationStripeSubscription({
subscription,
@ -605,7 +605,7 @@ export const workspaceAppActions = {
return { sessionId };
},
async getAppSnapshot(c: any, input: { sessionId: string }): Promise<FactoryAppSnapshot> {
async getAppSnapshot(c: any, input: { sessionId: string }): Promise<FoundryAppSnapshot> {
return await buildAppSnapshot(c, input.sessionId);
},
@ -651,7 +651,7 @@ export const workspaceAppActions = {
return await syncGithubSessionFromToken(c, sessionId, input.accessToken);
},
async signOutApp(c: any, input: { sessionId: string }): Promise<FactoryAppSnapshot> {
async signOutApp(c: any, input: { sessionId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const sessionId = await ensureAppSession(c, input.sessionId);
await updateAppSession(c, sessionId, {
@ -670,7 +670,7 @@ export const workspaceAppActions = {
return await buildAppSnapshot(c, sessionId);
},
async selectAppOrganization(c: any, input: { sessionId: string; organizationId: string }): Promise<FactoryAppSnapshot> {
async selectAppOrganization(c: any, input: { sessionId: string; organizationId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.organizationId);
@ -688,8 +688,8 @@ export const workspaceAppActions = {
async updateAppOrganizationProfile(
c: any,
input: { sessionId: string; organizationId: string } & UpdateFactoryOrganizationProfileInput,
): Promise<FactoryAppSnapshot> {
input: { sessionId: string; organizationId: string } & UpdateFoundryOrganizationProfileInput,
): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.organizationId);
@ -702,7 +702,7 @@ export const workspaceAppActions = {
return await buildAppSnapshot(c, input.sessionId);
},
async triggerAppRepoImport(c: any, input: { sessionId: string; organizationId: string }): Promise<FactoryAppSnapshot> {
async triggerAppRepoImport(c: any, input: { sessionId: string; organizationId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.organizationId);
@ -753,7 +753,7 @@ export const workspaceAppActions = {
const organization = await getOrganizationState(workspace);
if (organization.snapshot.kind !== "organization") {
return {
url: `${appShell.appUrl}/workspaces/${input.organizationId}?factorySession=${encodeURIComponent(input.sessionId)}`,
url: `${appShell.appUrl}/workspaces/${input.organizationId}?foundrySession=${encodeURIComponent(input.sessionId)}`,
};
}
return {
@ -763,7 +763,7 @@ export const workspaceAppActions = {
async createAppCheckoutSession(
c: any,
input: { sessionId: string; organizationId: string; planId: FactoryBillingPlanId },
input: { sessionId: string; organizationId: string; planId: FoundryBillingPlanId },
): Promise<{ url: string }> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
@ -775,7 +775,7 @@ export const workspaceAppActions = {
if (input.planId === "free") {
await workspace.applyOrganizationFreePlan({ clearSubscription: false });
return {
url: `${appShell.appUrl}/organizations/${input.organizationId}/billing?factorySession=${encodeURIComponent(input.sessionId)}`,
url: `${appShell.appUrl}/organizations/${input.organizationId}/billing?foundrySession=${encodeURIComponent(input.sessionId)}`,
};
}
@ -804,8 +804,8 @@ export const workspaceAppActions = {
planId: input.planId,
successUrl: `${appShell.appUrl}/api/rivet/app/billing/checkout/complete?organizationId=${encodeURIComponent(
input.organizationId,
)}&factorySession=${encodeURIComponent(input.sessionId)}&session_id={CHECKOUT_SESSION_ID}`,
cancelUrl: `${appShell.appUrl}/organizations/${input.organizationId}/billing?factorySession=${encodeURIComponent(input.sessionId)}`,
)}&foundrySession=${encodeURIComponent(input.sessionId)}&session_id={CHECKOUT_SESSION_ID}`,
cancelUrl: `${appShell.appUrl}/organizations/${input.organizationId}/billing?foundrySession=${encodeURIComponent(input.sessionId)}`,
}).then((checkout) => checkout.url),
};
},
@ -837,7 +837,7 @@ export const workspaceAppActions = {
}
return {
redirectTo: `${appShell.appUrl}/organizations/${input.organizationId}/billing?factorySession=${encodeURIComponent(input.sessionId)}`,
redirectTo: `${appShell.appUrl}/organizations/${input.organizationId}/billing?foundrySession=${encodeURIComponent(input.sessionId)}`,
};
},
@ -856,12 +856,12 @@ export const workspaceAppActions = {
}
const portal = await appShell.stripe.createPortalSession({
customerId: organization.stripeCustomerId,
returnUrl: `${appShell.appUrl}/organizations/${input.organizationId}/billing?factorySession=${encodeURIComponent(input.sessionId)}`,
returnUrl: `${appShell.appUrl}/organizations/${input.organizationId}/billing?foundrySession=${encodeURIComponent(input.sessionId)}`,
});
return { url: portal.url };
},
async cancelAppScheduledRenewal(c: any, input: { sessionId: string; organizationId: string }): Promise<FactoryAppSnapshot> {
async cancelAppScheduledRenewal(c: any, input: { sessionId: string; organizationId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.organizationId);
@ -880,7 +880,7 @@ export const workspaceAppActions = {
return await buildAppSnapshot(c, input.sessionId);
},
async resumeAppSubscription(c: any, input: { sessionId: string; organizationId: string }): Promise<FactoryAppSnapshot> {
async resumeAppSubscription(c: any, input: { sessionId: string; organizationId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.organizationId);
@ -899,7 +899,7 @@ export const workspaceAppActions = {
return await buildAppSnapshot(c, input.sessionId);
},
async recordAppSeatUsage(c: any, input: { sessionId: string; workspaceId: string }): Promise<FactoryAppSnapshot> {
async recordAppSeatUsage(c: any, input: { sessionId: string; workspaceId: string }): Promise<FoundryAppSnapshot> {
assertAppWorkspace(c);
const session = await requireSignedInSession(c, input.sessionId);
requireEligibleOrganization(session, input.workspaceId);
@ -997,7 +997,7 @@ export const workspaceAppActions = {
return { ok: true };
}
const kind: FactoryOrganization["kind"] = accountType === "User" ? "personal" : "organization";
const kind: FoundryOrganization["kind"] = accountType === "User" ? "personal" : "organization";
const organizationId = organizationWorkspaceId(kind, accountLogin);
if (event === "installation" && (body.action === "created" || body.action === "deleted" || body.action === "suspend" || body.action === "unsuspend")) {
@ -1049,7 +1049,7 @@ export const workspaceAppActions = {
githubAccountId: string;
githubLogin: string;
githubAccountType: string;
kind: FactoryOrganization["kind"];
kind: FoundryOrganization["kind"];
displayName: string;
installationId: number | null;
appConfigured: boolean;
@ -1176,7 +1176,7 @@ export const workspaceAppActions = {
async updateOrganizationShellProfile(
c: any,
input: Pick<UpdateFactoryOrganizationProfileInput, "displayName" | "slug" | "primaryDomain">,
input: Pick<UpdateFoundryOrganizationProfileInput, "displayName" | "slug" | "primaryDomain">,
): Promise<void> {
assertOrganizationWorkspace(c);
const existing = await requireOrganizationProfileRow(c);
@ -1209,7 +1209,7 @@ export const workspaceAppActions = {
c: any,
input: {
repositories: Array<{ fullName: string; cloneUrl: string; private: boolean }>;
installationStatus: FactoryOrganization["github"]["installationStatus"];
installationStatus: FoundryOrganization["github"]["installationStatus"];
lastSyncLabel: string;
},
): Promise<void> {
@ -1249,7 +1249,7 @@ export const workspaceAppActions = {
async markOrganizationSyncFailed(
c: any,
input: { message: string; installationStatus: FactoryOrganization["github"]["installationStatus"] },
input: { message: string; installationStatus: FoundryOrganization["github"]["installationStatus"] },
): Promise<void> {
assertOrganizationWorkspace(c);
await c.db
@ -1289,7 +1289,7 @@ export const workspaceAppActions = {
trialEnd: number | null;
defaultPaymentMethodLabel: string;
};
fallbackPlanId: FactoryBillingPlanId;
fallbackPlanId: FoundryBillingPlanId;
},
): Promise<void> {
assertOrganizationWorkspace(c);
@ -1347,7 +1347,7 @@ export const workspaceAppActions = {
.run();
},
async setOrganizationBillingStatus(c: any, input: { status: FactoryBillingState["status"] }): Promise<void> {
async setOrganizationBillingStatus(c: any, input: { status: FoundryBillingState["status"] }): Promise<void> {
assertOrganizationWorkspace(c);
await c.db
.update(organizationProfile)

Some files were not shown because too many files have changed in this diff Show more