mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 12:04:15 +00:00
Fix Foundry UI bugs: org names, sessions, and repo selection (#250)
* Fix Foundry auth: migrate to Better Auth adapter, fix access token retrieval - Remove @ts-nocheck from better-auth.ts, auth-user/index.ts, app-shell.ts and fix all type errors - Fix getAccessTokenForSession: read GitHub token directly from account record instead of calling Better Auth's internal /get-access-token endpoint which returns 403 on server-side calls - Re-implement workspaceAuth helper functions (workspaceAuthColumn, normalizeAuthValue, workspaceAuthClause, workspaceAuthWhere) that were accidentally deleted - Remove all retry logic (withRetries, isRetryableAppActorError) - Implement CORS origin allowlist from configured environment - Document cachedAppWorkspace singleton pattern - Add inline org sync fallback in buildAppSnapshot for post-OAuth flow - Add no-retry rule to CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add Foundry dev panel from fix-git-data branch Port the dev panel component that was left out when PR #243 was replaced by PR #247. Adapted to remove runtime/mock-debug references that don't exist on the current branch. - Toggle with Shift+D, persists visibility to localStorage - Shows context, session, GitHub sync status sections - Dev-only (import.meta.env.DEV) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add full Docker image defaults, fix actor deadlocks, and improve dev experience - Add Dockerfile.full and --all flag to install-agent CLI for pre-built images - Centralize Docker image constant (FULL_IMAGE) pinned to 0.3.1-full - Remove examples/shared/Dockerfile{,.dev} and daytona snapshot example - Expand Docker docs with full runnable Dockerfile - Fix self-deadlock in createWorkbenchSession (fire-and-forget provisioning) - Audit and convert 12 task actions from wait:true to wait:false - Add bun --hot for dev backend hot reload - Remove --force from pnpm install in dev Dockerfile for faster startup - Add env_file support to compose.dev.yaml for automatic credential loading - Add mock frontend compose config and dev panel - Update CLAUDE.md with wait:true policy and dev environment setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * WIP: async action fixes and interest manager Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix Foundry UI bugs: org names, hanging sessions, and wrong repo creation - Fix org display name using GitHub description instead of name field - Fix createWorkbenchSession hanging when sandbox is provisioning - Fix auto-session creation retry storm on errors - Fix task creation using wrong repo due to React state race conditions - Remove Bun hot-reload from backend Dockerfile (causes port drift) - Add GitHub sync/install status to dev panel Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
58c54156f1
commit
d8b8b49f37
88 changed files with 9252 additions and 1933 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import type { SandboxProcessRecord } from "@sandbox-agent/foundry-client";
|
||||
import { type SandboxProcessRecord, useInterest } from "@sandbox-agent/foundry-client";
|
||||
import { ProcessTerminal } from "@sandbox-agent/react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useStyletron } from "baseui";
|
||||
|
|
@ -7,6 +7,7 @@ import { ChevronDown, ChevronUp, Plus, SquareTerminal, Trash2 } from "lucide-rea
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { SandboxAgent } from "sandbox-agent";
|
||||
import { backendClient } from "../../lib/backend";
|
||||
import { interestManager } from "../../lib/interest";
|
||||
|
||||
interface TerminalPaneProps {
|
||||
workspaceId: string;
|
||||
|
|
@ -135,6 +136,9 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
setProcessTabs((prev) => {
|
||||
const next = [...prev];
|
||||
const [moved] = next.splice(d.fromIdx, 1);
|
||||
if (!moved) {
|
||||
return prev;
|
||||
}
|
||||
next.splice(d.overIdx!, 0, moved);
|
||||
return next;
|
||||
});
|
||||
|
|
@ -180,28 +184,31 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
[listWidth],
|
||||
);
|
||||
|
||||
const taskQuery = useQuery({
|
||||
queryKey: ["mock-layout", "task", workspaceId, taskId],
|
||||
enabled: Boolean(taskId),
|
||||
staleTime: 1_000,
|
||||
refetchOnWindowFocus: true,
|
||||
refetchInterval: (query) => (query.state.data?.activeSandboxId ? false : 2_000),
|
||||
queryFn: async () => {
|
||||
if (!taskId) {
|
||||
throw new Error("Cannot load terminal state without a task.");
|
||||
}
|
||||
return await backendClient.getTask(workspaceId, taskId);
|
||||
},
|
||||
});
|
||||
const workspaceState = useInterest(interestManager, "workspace", { workspaceId });
|
||||
const taskSummary = useMemo(
|
||||
() => (taskId ? (workspaceState.data?.taskSummaries.find((task) => task.id === taskId) ?? null) : null),
|
||||
[taskId, workspaceState.data?.taskSummaries],
|
||||
);
|
||||
const taskState = useInterest(
|
||||
interestManager,
|
||||
"task",
|
||||
taskSummary
|
||||
? {
|
||||
workspaceId,
|
||||
repoId: taskSummary.repoId,
|
||||
taskId: taskSummary.id,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
const activeSandbox = useMemo(() => {
|
||||
const task = taskQuery.data;
|
||||
const task = taskState.data;
|
||||
if (!task?.activeSandboxId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return task.sandboxes.find((sandbox) => sandbox.sandboxId === task.activeSandboxId) ?? null;
|
||||
}, [taskQuery.data]);
|
||||
}, [taskState.data]);
|
||||
|
||||
const connectionQuery = useQuery({
|
||||
queryKey: ["mock-layout", "sandbox-agent-connection", workspaceId, activeSandbox?.providerId ?? "", activeSandbox?.sandboxId ?? ""],
|
||||
|
|
@ -217,30 +224,17 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
},
|
||||
});
|
||||
|
||||
const processesQuery = useQuery({
|
||||
queryKey: ["mock-layout", "sandbox-processes", workspaceId, activeSandbox?.providerId ?? "", activeSandbox?.sandboxId ?? ""],
|
||||
enabled: Boolean(activeSandbox?.sandboxId),
|
||||
staleTime: 0,
|
||||
refetchOnWindowFocus: true,
|
||||
refetchInterval: activeSandbox?.sandboxId ? 3_000 : false,
|
||||
queryFn: async () => {
|
||||
if (!activeSandbox) {
|
||||
throw new Error("Cannot load processes without an active sandbox.");
|
||||
}
|
||||
|
||||
return await backendClient.listSandboxProcesses(workspaceId, activeSandbox.providerId, activeSandbox.sandboxId);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeSandbox?.sandboxId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return backendClient.subscribeSandboxProcesses(workspaceId, activeSandbox.providerId, activeSandbox.sandboxId, () => {
|
||||
void processesQuery.refetch();
|
||||
});
|
||||
}, [activeSandbox?.providerId, activeSandbox?.sandboxId, processesQuery, workspaceId]);
|
||||
const processesState = useInterest(
|
||||
interestManager,
|
||||
"sandboxProcesses",
|
||||
activeSandbox
|
||||
? {
|
||||
workspaceId,
|
||||
providerId: activeSandbox.providerId,
|
||||
sandboxId: activeSandbox.sandboxId,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!connectionQuery.data) {
|
||||
|
|
@ -311,7 +305,7 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
setProcessTabs([]);
|
||||
}, [taskId]);
|
||||
|
||||
const processes = processesQuery.data?.processes ?? [];
|
||||
const processes = processesState.data ?? [];
|
||||
|
||||
const openTerminalTab = useCallback((process: SandboxProcessRecord) => {
|
||||
setProcessTabs((current) => {
|
||||
|
|
@ -357,12 +351,11 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
sandboxId: activeSandbox.sandboxId,
|
||||
request: defaultShellRequest(activeSandbox.cwd),
|
||||
});
|
||||
await processesQuery.refetch();
|
||||
openTerminalTab(created);
|
||||
} finally {
|
||||
setCreatingProcess(false);
|
||||
}
|
||||
}, [activeSandbox, openTerminalTab, processesQuery, workspaceId]);
|
||||
}, [activeSandbox, openTerminalTab, workspaceId]);
|
||||
|
||||
const processTabsById = useMemo(() => new Map(processTabs.map((tab) => [tab.id, tab])), [processTabs]);
|
||||
const activeProcessTab = activeTabId ? (processTabsById.get(activeTabId) ?? null) : null;
|
||||
|
|
@ -462,9 +455,6 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
height: "100%",
|
||||
padding: "18px 16px 14px",
|
||||
}}
|
||||
onExit={() => {
|
||||
void processesQuery.refetch();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -481,7 +471,7 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
);
|
||||
}
|
||||
|
||||
if (taskQuery.isLoading) {
|
||||
if (taskState.status === "loading") {
|
||||
return (
|
||||
<div className={emptyBodyClassName}>
|
||||
<div className={emptyCopyClassName}>
|
||||
|
|
@ -491,12 +481,12 @@ export function TerminalPane({ workspaceId, taskId, isExpanded, onExpand, onColl
|
|||
);
|
||||
}
|
||||
|
||||
if (taskQuery.error) {
|
||||
if (taskState.error) {
|
||||
return (
|
||||
<div className={emptyBodyClassName}>
|
||||
<div className={emptyCopyClassName}>
|
||||
<strong>Could not load task state.</strong>
|
||||
<span>{taskQuery.error.message}</span>
|
||||
<span>{taskState.error.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue