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 # Environment
.env .env
.env.* .env.*
.sandbox-agent-factory/ .sandbox-agent-foundry/
# IDE # IDE
.idea/ .idea/

View file

@ -3,7 +3,7 @@
APP_URL=http://localhost:4173 APP_URL=http://localhost:4173
BETTER_AUTH_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 GITHUB_REDIRECT_URI=http://localhost:4173/api/rivet/app/auth/github/callback
# Fill these in when enabling live GitHub OAuth. # Fill these in when enabling live GitHub OAuth.

6
.gitignore vendored
View file

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

View file

@ -102,7 +102,7 @@
- `research/acp/todo.md` - `research/acp/todo.md`
- `research/friction/rivetkit.md` - `research/friction/rivetkit.md`
- `research/friction/sandbox-agent-sdk.md` - `research/friction/sandbox-agent-sdk.md`
- `research/friction/factory.md` - `research/friction/foundry.md`
## Change Tracking ## Change Tracking
@ -114,7 +114,7 @@
- `research/acp/friction.md` — ACP protocol, migration, and spec issues. - `research/acp/friction.md` — ACP protocol, migration, and spec issues.
- `research/friction/rivetkit.md` — RivetKit runtime, actor model, queues, keys, workflows. - `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/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): - Friction log entry format (all logs use the same template):
``` ```
- Date: - 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) [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 ### CLI

View file

@ -1,15 +1,15 @@
--- ---
title: "Factory Self-Hosting" title: "Foundry Self-Hosting"
description: "Environment, credentials, and deployment setup for Sandbox Agent Factory auth, GitHub, and billing." 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`. It also covers the local-development bootstrap that uses `.env.development` only when `NODE_ENV=development`.
## Local 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 loads `.env.development.local` and `.env.development`
- It does this **only** when `NODE_ENV=development` - It does this **only** when `NODE_ENV=development`
@ -26,7 +26,7 @@ cp .env.development.example .env.development
Run the backend with: Run the backend with:
```bash ```bash
just factory-backend-start just foundry-backend-start
``` ```
That recipe sets `NODE_ENV=development`, which enables the dotenv loader. 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` - `APP_URL=http://localhost:4173`
- `BETTER_AUTH_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` - `GITHUB_REDIRECT_URI=http://localhost:4173/api/rivet/app/auth/github/callback`
These should be treated as development-only values. These should be treated as development-only values.
@ -71,7 +71,7 @@ Use GitHub OAuth for:
## GitHub App ## 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 | | Variable | Required | Notes |
|---|---:|---| |---|---:|---|
@ -139,7 +139,7 @@ Stripe should own:
## Mock Invariant ## 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: That includes:

View file

@ -57,7 +57,7 @@
"icon": "server", "icon": "server",
"pages": [ "pages": [
"deploy/local", "deploy/local",
"deploy/factory-self-hosting", "deploy/foundry-self-hosting",
"deploy/computesdk", "deploy/computesdk",
"deploy/e2b", "deploy/e2b",
"deploy/daytona", "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. **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` - Start the full dev stack (real backend): `just foundry-dev`
- Stop the dev stack: `just factory-dev-down` - Stop the dev stack: `just foundry-dev-down`
- Tail dev logs: `just factory-dev-logs` - Tail dev logs: `just foundry-dev-logs`
- Start the mock dev stack (frontend-only, no backend): `just factory-dev-mock` - Start the mock dev stack (frontend-only, no backend): `just foundry-dev-mock`
- Stop the mock stack: `just factory-dev-mock-down` - Stop the mock stack: `just foundry-dev-mock-down`
- Tail mock logs: `just factory-dev-mock-logs` - Tail mock logs: `just foundry-dev-mock-logs`
- Start the production-build preview stack: `just factory-preview` - Start the production-build preview stack: `just foundry-preview`
- Stop the preview stack: `just factory-preview-down` - Stop the preview stack: `just foundry-preview-down`
- Tail preview logs: `just factory-preview-logs` - 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. 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 ## 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` - Install deps: `pnpm install`
- Full active-workspace validation: `pnpm -w typecheck`, `pnpm -w build`, `pnpm -w test` - 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 ## Loading & Skeleton UI Policy
@ -152,14 +152,14 @@ For all Rivet/RivetKit implementation:
## Workspace + Actor Rules ## Workspace + Actor Rules
- Everything is scoped to a workspace. - 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. - 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`. - 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"`. - Workspace resolution order: `--workspace` flag -> config default -> `"default"`.
- `ControlPlaneActor` is replaced by `WorkspaceActor` (workspace coordinator). - `ControlPlaneActor` is replaced by `WorkspaceActor` (workspace coordinator).
- Every actor key must be prefixed with workspace namespace (`["ws", workspaceId, ...]`). - 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). - 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. - 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. - 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. - 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. - `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. - 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. - 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: - Workflow history divergence policy:
- Production: never auto-delete actor state to resolve `HistoryDivergedError`; ship explicit workflow migrations (`ctx.removed(...)`, step compatibility). - 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 ## 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. - Evolve properties in place; do not move config location.
## Project Guidance ## Project Guidance

View file

@ -5,8 +5,8 @@
1. Clone: 1. Clone:
```bash ```bash
git clone https://github.com/rivet-dev/sandbox-agent-factory.git git clone https://github.com/rivet-dev/sandbox-agent-foundry.git
cd sandbox-agent-factory cd sandbox-agent-foundry
``` ```
2. Install dependencies: 2. Install dependencies:
@ -35,7 +35,7 @@ Build local RivetKit before backend changes that depend on Rivet internals:
cd ../rivet cd ../rivet
pnpm build -F rivetkit pnpm build -F rivetkit
cd /path/to/sandbox-agent-factory cd /path/to/sandbox-agent-foundry
just sync-rivetkit 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: Start the dev backend (hot reload via `bun --watch`) and Vite frontend via Docker Compose:
```bash ```bash
just factory-dev just foundry-dev
``` ```
Stop it: Stop it:
```bash ```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/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/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 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 FROM base AS build
COPY --from=deps /pnpm/store /pnpm/store COPY --from=deps /pnpm/store /pnpm/store
COPY . . COPY . .
RUN pnpm install --frozen-lockfile --prefer-offline --filter @sandbox-agent/factory-backend... RUN pnpm install --frozen-lockfile --prefer-offline --filter @sandbox-agent/foundry-backend...
RUN pnpm --filter @sandbox-agent/factory-shared build RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/factory-backend build RUN pnpm --filter @sandbox-agent/foundry-backend build
RUN pnpm --filter @sandbox-agent/factory-backend deploy --prod --legacy /out RUN pnpm --filter @sandbox-agent/foundry-backend deploy --prod --legacy /out
FROM oven/bun:1.2 AS runtime FROM oven/bun:1.2 AS runtime
ENV NODE_ENV=production 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. 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: services:
backend: backend:
build: build:
context: .. context: ..
dockerfile: factory/docker/backend.dev.Dockerfile dockerfile: foundry/docker/backend.dev.Dockerfile
image: sandbox-agent-factory-backend-dev image: sandbox-agent-foundry-backend-dev
working_dir: /app working_dir: /app
environment: environment:
HF_BACKEND_HOST: "0.0.0.0" HF_BACKEND_HOST: "0.0.0.0"
HF_BACKEND_PORT: "7741" HF_BACKEND_PORT: "7741"
HF_RIVET_MANAGER_PORT: "8750" 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. # Pass through credentials needed for agent execution + PR creation in dev/e2e.
# Do not hardcode secrets; set these in your environment when starting compose. # Do not hardcode secrets; set these in your environment when starting compose.
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" 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. # Reuse the host Codex auth profile for local sandbox-agent Codex sessions in dev.
- "${HOME}/.codex:/root/.codex" - "${HOME}/.codex:/root/.codex"
# Keep backend dependency installs Linux-native instead of using host node_modules. # Keep backend dependency installs Linux-native instead of using host node_modules.
- "sandbox-agent-factory_backend_root_node_modules:/app/node_modules" - "sandbox-agent-foundry_backend_root_node_modules:/app/node_modules"
- "sandbox-agent-factory_backend_backend_node_modules:/app/factory/packages/backend/node_modules" - "sandbox-agent-foundry_backend_backend_node_modules:/app/foundry/packages/backend/node_modules"
- "sandbox-agent-factory_backend_shared_node_modules:/app/factory/packages/shared/node_modules" - "sandbox-agent-foundry_backend_shared_node_modules:/app/foundry/packages/shared/node_modules"
- "sandbox-agent-factory_backend_persist_rivet_node_modules:/app/sdks/persist-rivet/node_modules" - "sandbox-agent-foundry_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-foundry_backend_typescript_node_modules:/app/sdks/typescript/node_modules"
- "sandbox-agent-factory_backend_pnpm_store:/root/.local/share/pnpm/store" - "sandbox-agent-foundry_backend_pnpm_store:/root/.local/share/pnpm/store"
# Persist backend-managed local git clones across container restarts. # 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. # 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: frontend:
build: build:
context: .. context: ..
dockerfile: factory/docker/frontend.dev.Dockerfile dockerfile: foundry/docker/frontend.dev.Dockerfile
working_dir: /app working_dir: /app
depends_on: depends_on:
- backend - backend
@ -60,28 +60,28 @@ services:
- "4173:4173" - "4173:4173"
volumes: volumes:
- "..:/app" - "..:/app"
# Ensure logs in .sandbox-agent-factory/ persist on the host even if we change source mounts later. # Ensure logs in .sandbox-agent-foundry/ persist on the host even if we change source mounts later.
- "./.sandbox-agent-factory:/app/factory/.sandbox-agent-factory" - "./.sandbox-agent-foundry:/app/foundry/.sandbox-agent-foundry"
# Use Linux-native workspace dependencies inside the container instead of host node_modules. # Use Linux-native workspace dependencies inside the container instead of host node_modules.
- "sandbox-agent-factory_node_modules:/app/node_modules" - "sandbox-agent-foundry_node_modules:/app/node_modules"
- "sandbox-agent-factory_client_node_modules:/app/factory/packages/client/node_modules" - "sandbox-agent-foundry_client_node_modules:/app/foundry/packages/client/node_modules"
- "sandbox-agent-factory_frontend_errors_node_modules:/app/factory/packages/frontend-errors/node_modules" - "sandbox-agent-foundry_frontend_errors_node_modules:/app/foundry/packages/frontend-errors/node_modules"
- "sandbox-agent-factory_frontend_node_modules:/app/factory/packages/frontend/node_modules" - "sandbox-agent-foundry_frontend_node_modules:/app/foundry/packages/frontend/node_modules"
- "sandbox-agent-factory_shared_node_modules:/app/factory/packages/shared/node_modules" - "sandbox-agent-foundry_shared_node_modules:/app/foundry/packages/shared/node_modules"
- "sandbox-agent-factory_pnpm_store:/tmp/.local/share/pnpm/store" - "sandbox-agent-foundry_pnpm_store:/tmp/.local/share/pnpm/store"
volumes: volumes:
sandbox-agent-factory_backend_root_node_modules: {} sandbox-agent-foundry_backend_root_node_modules: {}
sandbox-agent-factory_backend_backend_node_modules: {} sandbox-agent-foundry_backend_backend_node_modules: {}
sandbox-agent-factory_backend_shared_node_modules: {} sandbox-agent-foundry_backend_shared_node_modules: {}
sandbox-agent-factory_backend_persist_rivet_node_modules: {} sandbox-agent-foundry_backend_persist_rivet_node_modules: {}
sandbox-agent-factory_backend_typescript_node_modules: {} sandbox-agent-foundry_backend_typescript_node_modules: {}
sandbox-agent-factory_backend_pnpm_store: {} sandbox-agent-foundry_backend_pnpm_store: {}
sandbox-agent-factory_git_repos: {} sandbox-agent-foundry_git_repos: {}
sandbox-agent-factory_rivetkit_storage: {} sandbox-agent-foundry_rivetkit_storage: {}
sandbox-agent-factory_node_modules: {} sandbox-agent-foundry_node_modules: {}
sandbox-agent-factory_client_node_modules: {} sandbox-agent-foundry_client_node_modules: {}
sandbox-agent-factory_frontend_errors_node_modules: {} sandbox-agent-foundry_frontend_errors_node_modules: {}
sandbox-agent-factory_frontend_node_modules: {} sandbox-agent-foundry_frontend_node_modules: {}
sandbox-agent-factory_shared_node_modules: {} sandbox-agent-foundry_shared_node_modules: {}
sandbox-agent-factory_pnpm_store: {} 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: services:
backend: backend:
build: build:
context: .. context: ..
dockerfile: quebec/docker/backend.preview.Dockerfile dockerfile: quebec/docker/backend.preview.Dockerfile
image: sandbox-agent-factory-backend-preview image: sandbox-agent-foundry-backend-preview
environment: environment:
HF_BACKEND_HOST: "0.0.0.0" HF_BACKEND_HOST: "0.0.0.0"
HF_BACKEND_PORT: "7841" HF_BACKEND_PORT: "7841"
HF_RIVET_MANAGER_PORT: "8850" 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:-}" ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
CLAUDE_API_KEY: "${CLAUDE_API_KEY:-${ANTHROPIC_API_KEY:-}}" CLAUDE_API_KEY: "${CLAUDE_API_KEY:-${ANTHROPIC_API_KEY:-}}"
OPENAI_API_KEY: "${OPENAI_API_KEY:-}" OPENAI_API_KEY: "${OPENAI_API_KEY:-}"
@ -26,19 +26,19 @@ services:
- "8850:8850" - "8850:8850"
volumes: volumes:
- "${HOME}/.codex:/root/.codex" - "${HOME}/.codex:/root/.codex"
- "sandbox-agent-factory_preview_git_repos:/root/.local/share/sandbox-agent-factory/repos" - "sandbox-agent-foundry_preview_git_repos:/root/.local/share/sandbox-agent-foundry/repos"
- "sandbox-agent-factory_preview_rivetkit_storage:/root/.local/share/sandbox-agent-factory/rivetkit" - "sandbox-agent-foundry_preview_rivetkit_storage:/root/.local/share/sandbox-agent-foundry/rivetkit"
frontend: frontend:
build: build:
context: .. context: ..
dockerfile: quebec/docker/frontend.preview.Dockerfile dockerfile: quebec/docker/frontend.preview.Dockerfile
image: sandbox-agent-factory-frontend-preview image: sandbox-agent-foundry-frontend-preview
depends_on: depends_on:
- backend - backend
ports: ports:
- "4273:4273" - "4273:4273"
volumes: volumes:
sandbox-agent-factory_preview_git_repos: {} sandbox-agent-foundry_preview_git_repos: {}
sandbox-agent-factory_preview_rivetkit_storage: {} sandbox-agent-foundry_preview_rivetkit_storage: {}

View file

@ -39,4 +39,4 @@ ENV SANDBOX_AGENT_BIN="/root/.local/bin/sandbox-agent"
WORKDIR /app 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 COPY quebec /workspace/quebec
RUN pnpm install --frozen-lockfile RUN pnpm install --frozen-lockfile
RUN pnpm --filter @sandbox-agent/factory-shared build RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/factory-client build RUN pnpm --filter @sandbox-agent/foundry-client build
RUN pnpm --filter @sandbox-agent/factory-backend 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"] 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 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 COPY quebec /workspace/quebec
RUN pnpm install --frozen-lockfile RUN pnpm install --frozen-lockfile
RUN pnpm --filter @sandbox-agent/factory-shared build RUN pnpm --filter @sandbox-agent/foundry-shared build
RUN pnpm --filter @sandbox-agent/factory-client build RUN pnpm --filter @sandbox-agent/foundry-client build
RUN pnpm --filter @sandbox-agent/factory-frontend-errors build RUN pnpm --filter @sandbox-agent/foundry-frontend-errors build
RUN pnpm --filter @sandbox-agent/factory-frontend build RUN pnpm --filter @sandbox-agent/foundry-frontend build
FROM nginx:1.27-alpine 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", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",
@ -17,7 +17,7 @@
"@hono/node-server": "^1.19.7", "@hono/node-server": "^1.19.7",
"@hono/node-ws": "^1.3.0", "@hono/node-ws": "^1.3.0",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@sandbox-agent/factory-shared": "workspace:*", "@sandbox-agent/foundry-shared": "workspace:*",
"@sandbox-agent/persist-rivet": "workspace:*", "@sandbox-agent/persist-rivet": "workspace:*",
"drizzle-orm": "^0.44.5", "drizzle-orm": "^0.44.5",
"hono": "^4.11.9", "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 { BackendDriver } from "../driver.js";
import type { NotificationService } from "../notifications/index.js"; import type { NotificationService } from "../notifications/index.js";
import type { ProviderRegistry } from "../providers/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 { export interface TaskCreatedEvent {
workspaceId: string; workspaceId: string;

View file

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

View file

@ -2,7 +2,7 @@
import { and, desc, eq } from "drizzle-orm"; import { and, desc, eq } from "drizzle-orm";
import { actor, queue } from "rivetkit"; import { actor, queue } from "rivetkit";
import { Loop, workflow } from "rivetkit/workflow"; 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 { selfHistory } from "../handles.js";
import { historyDb } from "./db/db.js"; import { historyDb } from "./db/db.js";
import { events } from "./db/schema.js"; import { events } from "./db/schema.js";

View file

@ -27,5 +27,5 @@ export function logActorWarning(
...(context ?? {}) ...(context ?? {})
}; };
// eslint-disable-next-line no-console // 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, RepoOverview,
RepoStackAction, RepoStackAction,
RepoStackActionResult RepoStackActionResult
} from "@sandbox-agent/factory-shared"; } from "@sandbox-agent/foundry-shared";
import { getActorRuntimeContext } from "../context.js"; import { getActorRuntimeContext } from "../context.js";
import { import {
getTask, getTask,
@ -21,7 +21,7 @@ import {
selfRepo selfRepo
} from "../handles.js"; } from "../handles.js";
import { isActorNotFoundError, logActorWarning, resolveErrorMessage } from "../logging.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 { expectQueueResponse } from "../../services/queue.js";
import { withRepoGitLock } from "../../services/repo-git-lock.js"; import { withRepoGitLock } from "../../services/repo-git-lock.js";
import { branches, taskIndex, prCache, repoMeta } from "./db/schema.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> { async function ensureLocalClone(c: any, remoteUrl: string): Promise<string> {
const { config, driver } = getActorRuntimeContext(); 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); await driver.git.ensureCloned(remoteUrl, localPath);
c.state.localPath = localPath; c.state.localPath = localPath;
return localPath; return localPath;

View file

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

View file

@ -1,6 +1,6 @@
import { actor, queue } from "rivetkit"; import { actor, queue } from "rivetkit";
import { workflow } from "rivetkit/workflow"; 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 { getTask, getSandboxInstance, selfTaskStatusSync } from "../handles.js";
import { logActorWarning, resolveErrorMessage, resolveErrorStack } from "../logging.js"; import { logActorWarning, resolveErrorMessage, resolveErrorStack } from "../logging.js";
import { type PollingControlState, runWorkflowPollingLoop } from "../polling.js"; import { type PollingControlState, runWorkflowPollingLoop } from "../polling.js";

View file

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

View file

@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import { eq } from "drizzle-orm"; 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 { getOrCreateWorkspace } from "../../handles.js";
import { task as taskTable, taskRuntime, taskSandboxes } from "../db/schema.js"; import { task as taskTable, taskRuntime, taskSandboxes } from "../db/schema.js";
import { historyKey } from "../../keys.js"; import { historyKey } from "../../keys.js";

View file

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

View file

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

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