feat(ai-tools): Settings → AI Tools, Default/Other lane picker, per-run bypass switch (#40 A1)
Settings → Dashboard gains an AI Tools section (installed tools with Enabled/Disabled and Set default, available ones with a homepage link, Refresh). useAiTools shares one catalog copy across the app and replaces LANE_TOOL_LABEL. The Add-lane modal picks Default (<tool>) or Other…; the router row reads Native (<tool>) and disables AI Router profiles for tools that cannot use one. The run form gives catalog tools resume and a red per-run Bypass permissions switch (Codex too). Claude Code / Codex settings tabs hide while that tool is disabled. The Workspace probes each lane tool's CLI fresh. Docs: CLAUDE.md tool/bypass rule, AGENTS.md, LANES.md, API.md, CLI.md, server/README.md, OpenAPI fragment + regenerated openapi.yaml.
This commit is contained in:
@@ -56,7 +56,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { api } from "../lib/api";
|
||||
import type { CwdSuggestion, RunListResponse } from "../lib/api";
|
||||
import type { Lane, LaneFeature, ProofFeature, Session, WSMessage, LaneTool } from "../lib/types";
|
||||
import type { Lane, LaneFeature, ProofFeature, Session, WSMessage } from "../lib/types";
|
||||
import { eventBus } from "../lib/eventBus";
|
||||
import { keepIfSame } from "../lib/keepIfSame";
|
||||
import {
|
||||
@@ -175,7 +175,7 @@ function ConsoleArea({
|
||||
selectedLaneId: number | null;
|
||||
splitView: SplitViewState;
|
||||
setLayout: (layout: SplitLayout) => void;
|
||||
binaryStatus: Record<LaneTool, { found: boolean; path: string | null }> | null;
|
||||
binaryStatus: Record<string, { found: boolean; path: string | null }> | null;
|
||||
cwdSuggestions: CwdSuggestion[];
|
||||
activeRuns: RunListResponse | null;
|
||||
externalSessions: Session[];
|
||||
@@ -296,7 +296,7 @@ export function Workspace() {
|
||||
// would otherwise show nothing while two agents are working.
|
||||
const [externalSessions, setExternalSessions] = useState<Session[]>([]);
|
||||
const [binaryStatus, setBinaryStatus] = useState<Record<
|
||||
LaneTool,
|
||||
string,
|
||||
{ found: boolean; path: string | null }
|
||||
> | null>(null);
|
||||
const [cwdSuggestions, setCwdSuggestions] = useState<CwdSuggestion[]>([]);
|
||||
@@ -394,13 +394,25 @@ export function Workspace() {
|
||||
);
|
||||
}, [lanes, searchParams, setSearchParams]);
|
||||
|
||||
// Probe the CLI of every tool a lane uses — fresh on the server, so a CLI
|
||||
// installed since the page loaded clears its missing-binary banner. Claude is
|
||||
// always probed: the console's no-lane state runs it.
|
||||
const laneToolsKey = Array.from(new Set(["claude", ...lanes.map((l) => l.tool || "claude")]))
|
||||
.sort()
|
||||
.join(",");
|
||||
useEffect(() => {
|
||||
Promise.all([api.run.binary("claude"), api.run.binary("codex"), api.run.binary("antigravity")])
|
||||
.then(([claude, codex, antigravity]) => setBinaryStatus({ claude, codex, antigravity }))
|
||||
const tools = laneToolsKey.split(",");
|
||||
Promise.all(tools.map((tool) => api.run.binary(tool)))
|
||||
.then((found) =>
|
||||
setBinaryStatus(Object.fromEntries(tools.map((tool, i) => [tool, found[i]!])))
|
||||
)
|
||||
// Fetch failure (server unreachable, proxy misrouted, etc.) isn't proof
|
||||
// `claude` or `codex` is missing from PATH — leave the probe unresolved rather than
|
||||
// a CLI is missing from PATH — leave the probe unresolved rather than
|
||||
// showing a misleading "binary missing" banner for an unrelated fault.
|
||||
.catch(() => undefined);
|
||||
}, [laneToolsKey]);
|
||||
|
||||
useEffect(() => {
|
||||
api.lanes
|
||||
.pipelines()
|
||||
.then((r) => setPipelineTemplates(r.pipelines))
|
||||
@@ -860,7 +872,7 @@ export function Workspace() {
|
||||
</select>
|
||||
)}
|
||||
{/* `agy` has no endpoint to point at a router. */}
|
||||
{currentLane.tool !== "antigravity" && <LaneRouterRow lane={currentLane} />}
|
||||
<LaneRouterRow lane={currentLane} />
|
||||
{/* No stage chip and no "auto: <stage>" chip here: the
|
||||
PipelineMap right below this header already draws both
|
||||
— the declared node as the current ring, the detected
|
||||
|
||||
Reference in New Issue
Block a user