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:
@@ -43,8 +43,8 @@ import type {
|
||||
CodexSandbox,
|
||||
CodexApproval,
|
||||
} from "../../lib/api";
|
||||
import type { Session, Lane, LaneTool } from "../../lib/types";
|
||||
import { LANE_TOOL_LABEL } from "../../lib/types";
|
||||
import type { Session, Lane } from "../../lib/types";
|
||||
import { useAiTools } from "../../hooks/useAiTools";
|
||||
import { TerminalView } from "./TerminalView";
|
||||
import { RunSetup } from "./RunSetup";
|
||||
import { ActiveRunsSwitcher } from "./RunHistory";
|
||||
@@ -69,7 +69,7 @@ export interface LaneConsolePaneProps {
|
||||
showLaneSelector: boolean;
|
||||
onLaneIdChange: (id: number) => void;
|
||||
onLaneCreated: (lane: Lane) => void;
|
||||
binaryStatus: Record<LaneTool, { found: boolean; path: string | null }> | null;
|
||||
binaryStatus: Record<string, { found: boolean; path: string | null }> | null;
|
||||
cwdSuggestions: CwdSuggestion[];
|
||||
activeRuns: RunListResponse | null;
|
||||
/** Active Claude Code sessions started outside the dashboard, listed in the
|
||||
@@ -150,7 +150,8 @@ export function LaneConsolePane({
|
||||
: (externalSessions ?? []).filter((s) => s.cwd && owningLaneId(s.cwd, lanes) === laneId),
|
||||
[externalSessions, laneId, lanes]
|
||||
);
|
||||
const toolLabel = LANE_TOOL_LABEL[laneTool];
|
||||
const aiTools = useAiTools();
|
||||
const toolLabel = aiTools.label(laneTool);
|
||||
|
||||
useEffect(() => {
|
||||
onHasActiveRunChange?.(handle !== null);
|
||||
@@ -313,6 +314,7 @@ export function LaneConsolePane({
|
||||
sandbox: args.sandbox,
|
||||
approval: args.approval,
|
||||
resumeLast: args.resumeLast,
|
||||
bypass: args.bypass,
|
||||
});
|
||||
} catch (laneErr: unknown) {
|
||||
// A code-only check: the server's message is human prose ("lane
|
||||
@@ -566,12 +568,13 @@ export function LaneConsolePane({
|
||||
/>
|
||||
</header>
|
||||
|
||||
{binaryStatus && !binaryStatus[laneTool]?.found && (
|
||||
{/* Not probed yet (a lane on a tool the page has not probed) is not "missing". */}
|
||||
{binaryStatus?.[laneTool] && !binaryStatus[laneTool].found && (
|
||||
<div className="rounded-lg border border-status-danger/40 bg-status-danger/10 px-4 py-3 text-sm text-status-danger flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 flex-shrink-0" />
|
||||
<span>
|
||||
{t("binary.missing", {
|
||||
cli: laneTool === "antigravity" ? "agy" : laneTool,
|
||||
cli: aiTools.byId(laneTool)?.command ?? laneTool,
|
||||
tool: toolLabel,
|
||||
})}
|
||||
</span>
|
||||
@@ -613,7 +616,9 @@ export function LaneConsolePane({
|
||||
onSandboxChange={setSandbox}
|
||||
approval={approval}
|
||||
onApprovalChange={setApproval}
|
||||
onResumeLast={() => onStartFromSetup({ laneId: laneId ?? 0, cwd, resumeLast: true })}
|
||||
onResumeLast={(bypass) =>
|
||||
onStartFromSetup({ laneId: laneId ?? 0, cwd, resumeLast: true, bypass })
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 min-h-0 flex flex-col">
|
||||
|
||||
Reference in New Issue
Block a user