Skip to content

Commit 8fb19b5

Browse files
authored
Remove frontend errors and app passthrough (#251)
1 parent d8b8b49 commit 8fb19b5

23 files changed

Lines changed: 146 additions & 1119 deletions

foundry/CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ Use `pnpm` workspaces and Turborepo.
5050
- `compose.dev.yaml` loads `foundry/.env` (optional) for credentials needed by the backend (GitHub OAuth, Stripe, Daytona, API keys, etc.).
5151
- The canonical source for these credentials is `~/misc/the-foundry.env`. If `foundry/.env` does not exist, copy it: `cp ~/misc/the-foundry.env foundry/.env`
5252
- `foundry/.env` is gitignored and must never be committed.
53+
- If your changes affect the dev server, mock server, frontend runtime, backend runtime, Vite wiring, compose files, or other server-startup/runtime behavior, you must start or restart the relevant stack before finishing the task.
54+
- Use the matching stack for verification:
55+
- real backend + frontend changes: `just foundry-dev` or restart with `just foundry-dev-down && just foundry-dev`
56+
- mock frontend changes: `just foundry-mock` or restart with `just foundry-mock-down && just foundry-mock`
57+
- local frontend-only work outside Docker: restart `pnpm --filter @sandbox-agent/foundry-frontend dev` or `just foundry-dev-mock` as appropriate
5358
- The backend does **not** hot reload. Bun's `--hot` flag causes the server to re-bind on a different port (e.g. 6421 instead of 6420), breaking all client connections while the container still exposes the original port. After backend code changes, restart the backend container: `just foundry-dev-down && just foundry-dev`.
5459

5560
## Railway Logs

foundry/compose.dev.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ services:
8484
# Use Linux-native workspace dependencies inside the container instead of host node_modules.
8585
- "foundry_node_modules:/app/node_modules"
8686
- "foundry_client_node_modules:/app/foundry/packages/client/node_modules"
87-
- "foundry_frontend_errors_node_modules:/app/foundry/packages/frontend-errors/node_modules"
8887
- "foundry_frontend_node_modules:/app/foundry/packages/frontend/node_modules"
8988
- "foundry_shared_node_modules:/app/foundry/packages/shared/node_modules"
9089
- "foundry_pnpm_store:/tmp/.local/share/pnpm/store"
@@ -100,7 +99,6 @@ volumes:
10099
foundry_rivetkit_storage: {}
101100
foundry_node_modules: {}
102101
foundry_client_node_modules: {}
103-
foundry_frontend_errors_node_modules: {}
104102
foundry_frontend_node_modules: {}
105103
foundry_shared_node_modules: {}
106104
foundry_pnpm_store: {}

foundry/compose.mock.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@ services:
1818
- "../../../task/rivet-checkout:/task/rivet-checkout:ro"
1919
- "mock_node_modules:/app/node_modules"
2020
- "mock_client_node_modules:/app/foundry/packages/client/node_modules"
21-
- "mock_frontend_errors_node_modules:/app/foundry/packages/frontend-errors/node_modules"
2221
- "mock_frontend_node_modules:/app/foundry/packages/frontend/node_modules"
2322
- "mock_shared_node_modules:/app/foundry/packages/shared/node_modules"
2423
- "mock_pnpm_store:/tmp/.local/share/pnpm/store"
2524

2625
volumes:
2726
mock_node_modules: {}
2827
mock_client_node_modules: {}
29-
mock_frontend_errors_node_modules: {}
3028
mock_frontend_node_modules: {}
3129
mock_shared_node_modules: {}
3230
mock_pnpm_store: {}

foundry/docker/frontend.Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ RUN pnpm --filter @sandbox-agent/cli-shared build
1515
RUN SKIP_OPENAPI_GEN=1 pnpm --filter sandbox-agent build
1616
RUN pnpm --filter @sandbox-agent/react build
1717
RUN pnpm --filter @sandbox-agent/foundry-client build
18-
RUN pnpm --filter @sandbox-agent/foundry-frontend-errors build
1918
ENV FOUNDRY_FRONTEND_CLIENT_MODE=remote
2019
RUN pnpm --filter @sandbox-agent/foundry-frontend build
2120

foundry/docker/frontend.mock.Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ RUN pnpm --filter @sandbox-agent/cli-shared build
1515
RUN SKIP_OPENAPI_GEN=1 pnpm --filter sandbox-agent build
1616
RUN pnpm --filter @sandbox-agent/react build
1717
RUN pnpm --filter @sandbox-agent/foundry-client build
18-
RUN pnpm --filter @sandbox-agent/foundry-frontend-errors build
1918
ENV FOUNDRY_FRONTEND_CLIENT_MODE=mock
2019
RUN pnpm --filter @sandbox-agent/foundry-frontend build
2120

foundry/docker/frontend.preview.Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ COPY rivet-checkout /workspace/rivet-checkout
1212
RUN pnpm install --frozen-lockfile
1313
RUN pnpm --filter @sandbox-agent/foundry-shared build
1414
RUN pnpm --filter @sandbox-agent/foundry-client build
15-
RUN pnpm --filter @sandbox-agent/foundry-frontend-errors build
1615
RUN pnpm --filter @sandbox-agent/foundry-frontend build
1716

1817
FROM nginx:1.27-alpine

foundry/packages/backend/src/index.ts

Lines changed: 0 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { createBackends, createNotificationService } from "./notifications/index
99
import { createDefaultDriver } from "./driver.js";
1010
import { createProviderRegistry } from "./providers/index.js";
1111
import { createClient } from "rivetkit/client";
12-
import type { FoundryBillingPlanId } from "@sandbox-agent/foundry-shared";
1312
import { initBetterAuthService } from "./services/better-auth.js";
1413
import { createDefaultAppShellServices } from "./services/app-shell-runtime.js";
1514
import { APP_SHELL_WORKSPACE_ID } from "./actors/workspace/app-shell.js";
@@ -205,23 +204,6 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
205204
}
206205
};
207206

208-
const appWorkspaceAction = async <T>(action: string, run: (workspace: any) => Promise<T>, context: AppWorkspaceLogContext = {}): Promise<T> => {
209-
try {
210-
return await run(await appWorkspace({ ...context, action }));
211-
} catch (error) {
212-
logger.error(
213-
{
214-
...context,
215-
action,
216-
errorMessage: error instanceof Error ? error.message : String(error),
217-
errorStack: error instanceof Error ? error.stack : undefined,
218-
},
219-
"app_workspace_action_failed",
220-
);
221-
throw error;
222-
}
223-
};
224-
225207
const requestLogContext = (c: any, sessionId?: string): AppWorkspaceLogContext => ({
226208
...requestHeaderContext(c),
227209
method: c.req.method,
@@ -235,30 +217,6 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
235217
return session?.session?.id ?? null;
236218
};
237219

238-
app.get("/v1/app/snapshot", async (c) => {
239-
const sessionId = await resolveSessionId(c);
240-
if (!sessionId) {
241-
return c.json({
242-
auth: { status: "signed_out", currentUserId: null },
243-
activeOrganizationId: null,
244-
onboarding: {
245-
starterRepo: {
246-
repoFullName: "rivet-dev/sandbox-agent",
247-
repoUrl: "https://github.com/rivet-dev/sandbox-agent",
248-
status: "pending",
249-
starredAt: null,
250-
skippedAt: null,
251-
},
252-
},
253-
users: [],
254-
organizations: [],
255-
});
256-
}
257-
return c.json(
258-
await appWorkspaceAction("getAppSnapshot", async (workspace) => await workspace.getAppSnapshot({ sessionId }), requestLogContext(c, sessionId)),
259-
);
260-
});
261-
262220
app.all("/v1/auth/*", async (c) => {
263221
return await betterAuth.auth.handler(c.req.raw);
264222
});
@@ -289,126 +247,6 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
289247
});
290248
});
291249

292-
app.post("/v1/app/onboarding/starter-repo/skip", async (c) => {
293-
const sessionId = await resolveSessionId(c);
294-
if (!sessionId) {
295-
return c.text("Unauthorized", 401);
296-
}
297-
return c.json(
298-
await appWorkspaceAction("skipAppStarterRepo", async (workspace) => await workspace.skipAppStarterRepo({ sessionId }), requestLogContext(c, sessionId)),
299-
);
300-
});
301-
302-
app.post("/v1/app/organizations/:organizationId/starter-repo/star", async (c) => {
303-
const sessionId = await resolveSessionId(c);
304-
if (!sessionId) {
305-
return c.text("Unauthorized", 401);
306-
}
307-
return c.json(
308-
await appWorkspaceAction(
309-
"starAppStarterRepo",
310-
async (workspace) =>
311-
await workspace.starAppStarterRepo({
312-
sessionId,
313-
organizationId: c.req.param("organizationId"),
314-
}),
315-
requestLogContext(c, sessionId),
316-
),
317-
);
318-
});
319-
320-
app.post("/v1/app/organizations/:organizationId/select", async (c) => {
321-
const sessionId = await resolveSessionId(c);
322-
if (!sessionId) {
323-
return c.text("Unauthorized", 401);
324-
}
325-
return c.json(
326-
await appWorkspaceAction(
327-
"selectAppOrganization",
328-
async (workspace) =>
329-
await workspace.selectAppOrganization({
330-
sessionId,
331-
organizationId: c.req.param("organizationId"),
332-
}),
333-
requestLogContext(c, sessionId),
334-
),
335-
);
336-
});
337-
338-
app.patch("/v1/app/organizations/:organizationId/profile", async (c) => {
339-
const sessionId = await resolveSessionId(c);
340-
if (!sessionId) {
341-
return c.text("Unauthorized", 401);
342-
}
343-
const body = await c.req.json();
344-
return c.json(
345-
await appWorkspaceAction(
346-
"updateAppOrganizationProfile",
347-
async (workspace) =>
348-
await workspace.updateAppOrganizationProfile({
349-
sessionId,
350-
organizationId: c.req.param("organizationId"),
351-
displayName: typeof body?.displayName === "string" ? body.displayName : "",
352-
slug: typeof body?.slug === "string" ? body.slug : "",
353-
primaryDomain: typeof body?.primaryDomain === "string" ? body.primaryDomain : "",
354-
}),
355-
requestLogContext(c, sessionId),
356-
),
357-
);
358-
});
359-
360-
app.post("/v1/app/organizations/:organizationId/import", async (c) => {
361-
const sessionId = await resolveSessionId(c);
362-
if (!sessionId) {
363-
return c.text("Unauthorized", 401);
364-
}
365-
return c.json(
366-
await appWorkspaceAction(
367-
"triggerAppRepoImport",
368-
async (workspace) =>
369-
await workspace.triggerAppRepoImport({
370-
sessionId,
371-
organizationId: c.req.param("organizationId"),
372-
}),
373-
requestLogContext(c, sessionId),
374-
),
375-
);
376-
});
377-
378-
app.post("/v1/app/organizations/:organizationId/reconnect", async (c) => {
379-
const sessionId = await resolveSessionId(c);
380-
if (!sessionId) {
381-
return c.text("Unauthorized", 401);
382-
}
383-
return c.json(
384-
await appWorkspaceAction(
385-
"beginAppGithubInstall",
386-
async (workspace) =>
387-
await workspace.beginAppGithubInstall({
388-
sessionId,
389-
organizationId: c.req.param("organizationId"),
390-
}),
391-
requestLogContext(c, sessionId),
392-
),
393-
);
394-
});
395-
396-
app.post("/v1/app/organizations/:organizationId/billing/checkout", async (c) => {
397-
const sessionId = await resolveSessionId(c);
398-
if (!sessionId) {
399-
return c.text("Unauthorized", 401);
400-
}
401-
const body = await c.req.json().catch(() => ({}));
402-
const planId = body?.planId === "free" || body?.planId === "team" ? (body.planId as FoundryBillingPlanId) : "team";
403-
return c.json(
404-
await (await appWorkspace(requestLogContext(c, sessionId))).createAppCheckoutSession({
405-
sessionId,
406-
organizationId: c.req.param("organizationId"),
407-
planId,
408-
}),
409-
);
410-
});
411-
412250
app.get("/v1/billing/checkout/complete", async (c) => {
413251
const organizationId = c.req.query("organizationId");
414252
const checkoutSessionId = c.req.query("session_id");
@@ -427,58 +265,6 @@ export async function startBackend(options: BackendStartOptions = {}): Promise<v
427265
return Response.redirect(result.redirectTo, 302);
428266
});
429267

430-
app.post("/v1/app/organizations/:organizationId/billing/portal", async (c) => {
431-
const sessionId = await resolveSessionId(c);
432-
if (!sessionId) {
433-
return c.text("Unauthorized", 401);
434-
}
435-
return c.json(
436-
await (await appWorkspace(requestLogContext(c, sessionId))).createAppBillingPortalSession({
437-
sessionId,
438-
organizationId: c.req.param("organizationId"),
439-
}),
440-
);
441-
});
442-
443-
app.post("/v1/app/organizations/:organizationId/billing/cancel", async (c) => {
444-
const sessionId = await resolveSessionId(c);
445-
if (!sessionId) {
446-
return c.text("Unauthorized", 401);
447-
}
448-
return c.json(
449-
await (await appWorkspace(requestLogContext(c, sessionId))).cancelAppScheduledRenewal({
450-
sessionId,
451-
organizationId: c.req.param("organizationId"),
452-
}),
453-
);
454-
});
455-
456-
app.post("/v1/app/organizations/:organizationId/billing/resume", async (c) => {
457-
const sessionId = await resolveSessionId(c);
458-
if (!sessionId) {
459-
return c.text("Unauthorized", 401);
460-
}
461-
return c.json(
462-
await (await appWorkspace(requestLogContext(c, sessionId))).resumeAppSubscription({
463-
sessionId,
464-
organizationId: c.req.param("organizationId"),
465-
}),
466-
);
467-
});
468-
469-
app.post("/v1/app/workspaces/:workspaceId/seat-usage", async (c) => {
470-
const sessionId = await resolveSessionId(c);
471-
if (!sessionId) {
472-
return c.text("Unauthorized", 401);
473-
}
474-
return c.json(
475-
await (await appWorkspace(requestLogContext(c, sessionId))).recordAppSeatUsage({
476-
sessionId,
477-
workspaceId: c.req.param("workspaceId"),
478-
}),
479-
);
480-
});
481-
482268
const handleStripeWebhook = async (c: any) => {
483269
const payload = await c.req.text();
484270
await (await appWorkspace(requestLogContext(c))).handleAppStripeWebhook({

0 commit comments

Comments
 (0)