Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions apps/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ function registerIpc(): void {
const out: AgentInfo[] = [];
for (const [id, spec] of Object.entries(INSTALL_SPECS) as Array<[AgentId, AgentInstallSpec | undefined]>) {
const settings = agents.agents?.[id] ?? {};
const bin = settings.bin ?? spec?.bin ?? id;
const detect = id === 'pi' ? PiAdapter.detect(bin) : detectGeneric(bin);
const effectiveBin = settings.bin ?? spec?.bin ?? id;
const detect = id === 'pi' ? PiAdapter.detect(effectiveBin) : detectGeneric(effectiveBin);
const resolved = resolveExecutable(effectiveBin, process.env);
out.push({
id,
displayName: spec?.displayName ?? id,
Expand All @@ -181,6 +182,8 @@ function registerIpc(): void {
providerId: settings.providerId,
model: settings.model,
isDefault: id === defaultId,
bin: settings.bin,
resolvedPath: resolved.resolved,
});
}
return out;
Expand Down
64 changes: 63 additions & 1 deletion apps/desktop/src/renderer/src/Settings/AgentsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ function buildAgentsCfg(agentList: AgentInfo[]): AgentsConfig {
agents: Object.fromEntries(
agentList.map((a) => [
a.id,
{ providerId: a.providerId || undefined, model: a.model || undefined },
{
bin: a.bin || undefined,
providerId: a.providerId || undefined,
model: a.model || undefined,
},
]),
) as AgentsConfig['agents'],
};
Expand Down Expand Up @@ -88,6 +92,27 @@ export function AgentsSection({
}
};

const setAgentBin = (agentId: string, bin: string) => {
const next = agents.map((a) => (a.id === agentId ? { ...a, bin: bin.trim() || undefined } : a));
onAgentsChange(next);
};

const flushAgentBin = () => persistAgents(agents);

const resetAgentBin = (agentId: string) => {
const next = agents.map((a) => (a.id === agentId ? { ...a, bin: undefined } : a));
onAgentsChange(next);
persistAgents(next);
};

const browseAgentBin = async (agentId: string) => {
const picked = await window.tday.pickFile();
if (!picked) return;
const next = agents.map((a) => (a.id === agentId ? { ...a, bin: picked } : a));
onAgentsChange(next);
persistAgents(next);
};

const installAgent = async (agentId: string, action: 'install' | 'update' | 'uninstall') => {
setInstallingId(agentId);
setInstallPct(0);
Expand Down Expand Up @@ -288,6 +313,43 @@ export function AgentsSection({
</label>
</div>

{/* Path */}
<div>
<span className="mb-1 block text-[10px] uppercase tracking-wider text-zinc-500">
Path
</span>
<div className="flex items-center gap-1.5">
<input
className="input flex-1 font-mono text-[11px]"
placeholder={a.resolvedPath ?? ''}
value={a.bin ?? ''}
onChange={(e) => setAgentBin(a.id, e.target.value)}
onBlur={() => flushAgentBin()}
/>
<button
onClick={() => void browseAgentBin(a.id)}
className="shrink-0 rounded-md border border-zinc-700 px-2 py-1 text-[11px] text-zinc-300 hover:bg-zinc-800"
title="Browse for executable"
>
</button>
{a.bin ? (
<button
onClick={() => resetAgentBin(a.id)}
className="shrink-0 rounded-md border border-zinc-800 px-2 py-1 text-[11px] text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300"
title="Reset to default path"
>
Reset
</button>
) : null}
</div>
{a.resolvedPath ? (
<div className="mt-0.5 truncate text-[11px] text-zinc-500" title={a.resolvedPath}>
{a.resolvedPath}
</div>
) : null}
</div>

{/* Default for new tabs */}
<div className="flex items-center justify-end border-b border-zinc-800/40 pb-3">
<label className="flex cursor-pointer items-center gap-2 text-[11px] text-zinc-400">
Expand Down
5 changes: 5 additions & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ export interface AgentInfo {
model?: string;
/** True if this agent is configured as the default for new tabs. */
isDefault?: boolean;
/** User-configured executable name or absolute path override (agents.json bin field).
* Undefined when using the built-in default binary name. */
bin?: string;
/** Resolved absolute path of the effective executable (null when not found on PATH). */
resolvedPath?: string | null;
}

/** Static config loaded from ~/.tday/providers.json (v0.1.0). */
Expand Down