refactor: eliminate ~400 no-explicit-any warnings across the workspace
Parallel subagent pass: four typescript-pro agents on non-overlapping scopes.
Patterns applied:
- catch (err: any) { ... err.message ... } → catch (err) { ... getErrorMessage(err) ... }
using the new @fusion/core helper. Bare catch {} where the error was unused.
- SQLite row types: defined typed XxxRow interfaces per table and cast
.all()/.get() results via `as unknown as XxxRow[]` (the double cast is
required because better-sqlite3 returns Record<string, SQLOutputValue>).
- rowToX(row: any) converters: typed argument with the matching row interface.
- Dynamic settings key writes: (settings as Record<string, unknown>)[key].
- React event handlers and setState callbacks: inferred types or concrete
React.{Mouse,Change,Form}Event<...> where needed.
- pi-claude-cli: local PiMessage / PiContext duck types to avoid re-typing
pi-ai concrete shapes; typed Claude stream event message fields.
72 files changed, ~400 anys eliminated. Typecheck passes across the workspace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,9 +192,11 @@ export function withTokenHeader(init?: HeadersInit): HeadersInit | undefined {
|
||||
* through the `api()` helper without requiring us to touch each one.
|
||||
*/
|
||||
export function installAuthFetch(): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- property sentinel on global window object, no better type exists
|
||||
if (typeof window === "undefined" || (window as any).__fnAuthFetchInstalled) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- property sentinel on global window object, no better type exists
|
||||
(window as any).__fnAuthFetchInstalled = true;
|
||||
|
||||
// Ensure token is captured-from-URL before the first fetch fires.
|
||||
|
||||
@@ -7,10 +7,11 @@ import {
|
||||
} from "lucide-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import type { AgentDetail, AgentState, AgentHeartbeatRun, AgentBudgetStatus, ModelInfo, MemoryFileInfo } from "../api";
|
||||
import type { AgentDetail, AgentState, AgentHeartbeatRun, AgentBudgetStatus, ModelInfo, MemoryFileInfo, AgentCapability } from "../api";
|
||||
import { fetchAgent, updateAgent, updateAgentState, deleteAgent, fetchAgentLogsWithMeta, fetchAgentRunLogs, fetchAgentChildren, fetchAgentRuns, fetchAgentRunDetail, startAgentRun, stopAgentRun, updateAgentInstructions, updateAgentSoul, updateAgentMemory, fetchAgentMemoryFiles, fetchAgentMemoryFile, saveAgentMemoryFile, fetchAgentTasks, fetchChainOfCommand, fetchAgentBudgetStatus, resetAgentBudget, fetchWorkspaceFileContent, saveWorkspaceFileContent, fetchModels, fetchAgents } from "../api";
|
||||
import type { Agent } from "../api";
|
||||
import type { AgentLogEntry, Task } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { AgentLogViewer } from "./AgentLogViewer";
|
||||
import { AgentReflectionsTab } from "./AgentReflectionsTab";
|
||||
import { getAgentHealthStatus } from "../utils/agentHealth";
|
||||
@@ -146,8 +147,8 @@ export function AgentDetailView({ agentId, projectId, onClose, addToast, onChild
|
||||
try {
|
||||
const data = await fetchAgent(agentId, projectId);
|
||||
setAgent(data);
|
||||
} catch (err: any) {
|
||||
addToastRef.current(`Failed to load agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToastRef.current(`Failed to load agent: ${getErrorMessage(err)}`, "error");
|
||||
onCloseRef.current();
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -174,7 +175,7 @@ export function AgentDetailView({ agentId, projectId, onClose, addToast, onChild
|
||||
return;
|
||||
}
|
||||
setLogs(result.entries);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Reject stale error: check context version and current IDs
|
||||
if (contextVersionRef.current !== contextVersionAtCapture ||
|
||||
agentId !== currentAgentId ||
|
||||
@@ -310,8 +311,8 @@ export function AgentDetailView({ agentId, projectId, onClose, addToast, onChild
|
||||
await updateAgentState(agentId, newState, projectId);
|
||||
addToast(`Agent state updated to ${newState}`, "success");
|
||||
void loadAgent();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update state: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update state: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -321,8 +322,8 @@ export function AgentDetailView({ agentId, projectId, onClose, addToast, onChild
|
||||
await deleteAgent(agentId, projectId);
|
||||
addToast(`Agent "${agent.name}" deleted`, "success");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to delete agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to delete agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -674,7 +675,7 @@ function DashboardTab({
|
||||
}, [agent.id, projectId]);
|
||||
|
||||
const stats = useMemo(() => {
|
||||
const runs = (agent as any).completedRuns || [];
|
||||
const runs = agent.completedRuns || [];
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
@@ -1049,8 +1050,8 @@ function RunsTab({
|
||||
try {
|
||||
const data = await fetchAgentRuns(agentId, 50, projectId);
|
||||
setRuns(data);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load runs: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load runs: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsLoadingRuns(false);
|
||||
}
|
||||
@@ -1090,8 +1091,8 @@ function RunsTab({
|
||||
]);
|
||||
setRunLogs(logs);
|
||||
setDetailRun(detail);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load run details: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load run details: ${getErrorMessage(err)}`, "error");
|
||||
setRunLogs([]);
|
||||
setDetailRun(null);
|
||||
} finally {
|
||||
@@ -1106,8 +1107,8 @@ function RunsTab({
|
||||
addToast(`Heartbeat run started for ${agentName ?? agentId}`, "success");
|
||||
setIsLoadingRuns(true);
|
||||
void loadRuns();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to start heartbeat run: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to start heartbeat run: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1121,8 +1122,8 @@ function RunsTab({
|
||||
addToast("Run stopped", "success");
|
||||
setIsLoadingRuns(true);
|
||||
void loadRuns();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to stop run: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to stop run: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1479,10 +1480,10 @@ function TasksTab({
|
||||
setTasks(assignedTasks);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
.catch((err) => {
|
||||
if (!cancelled) {
|
||||
setTasks([]);
|
||||
addToast(`Failed to load assigned tasks: ${err.message}`, "error");
|
||||
addToast(`Failed to load assigned tasks: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -1660,8 +1661,8 @@ function SoulTab({
|
||||
setJustSaved(true);
|
||||
setTimeout(() => setJustSaved(false), 3000);
|
||||
await onSaved();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save soul: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save soul: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -1810,8 +1811,8 @@ function MemoryTab({
|
||||
setSelectedFileContent(result.content);
|
||||
setSelectedFileDirty(false);
|
||||
setSelectedFileJustSaved(false);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load agent memory file: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load agent memory file: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setSelectedFileLoading(false);
|
||||
}
|
||||
@@ -1832,8 +1833,8 @@ function MemoryTab({
|
||||
|
||||
const nextPath = pickDefaultAgentMemoryPath(files, preferredPath);
|
||||
await loadSelectedMemoryFile(nextPath);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load memory files: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load memory files: ${getErrorMessage(err)}`, "error");
|
||||
setMemoryFiles([]);
|
||||
setSelectedFilePath("");
|
||||
setSelectedFileContent("");
|
||||
@@ -1865,8 +1866,8 @@ function MemoryTab({
|
||||
setJustSaved(true);
|
||||
setTimeout(() => setJustSaved(false), 3000);
|
||||
await onSaved();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save memory: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save memory: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -1899,8 +1900,8 @@ function MemoryTab({
|
||||
setFileSwitchHint("");
|
||||
await loadMemoryFiles(selectedFilePath);
|
||||
addToast("Agent memory file saved", "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save agent memory file: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save agent memory file: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setSavingSelectedFile(false);
|
||||
}
|
||||
@@ -2150,13 +2151,14 @@ function InstructionsTab({
|
||||
setFileContent(data.content);
|
||||
setFileContentDirty(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
.catch((err) => {
|
||||
// ENOENT means file doesn't exist yet - treat as empty "new file" state
|
||||
if (err.message?.includes("ENOENT") || err.message?.includes("Not found") || err.message?.includes("not found")) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg.includes("ENOENT") || msg.includes("Not found") || msg.includes("not found")) {
|
||||
setFileContent("");
|
||||
setFileContentDirty(false);
|
||||
} else {
|
||||
addToast(`Failed to load instructions file: ${err.message}`, "error");
|
||||
addToast(`Failed to load instructions file: ${msg}`, "error");
|
||||
setFileContent("");
|
||||
}
|
||||
})
|
||||
@@ -2197,8 +2199,8 @@ function InstructionsTab({
|
||||
setJustSaved(true);
|
||||
setTimeout(() => setJustSaved(false), 3000);
|
||||
await onSaved();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save instructions: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save instructions: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -2218,8 +2220,8 @@ function InstructionsTab({
|
||||
setFileContentDirty(false);
|
||||
setJustSavedFile(true);
|
||||
setTimeout(() => setJustSavedFile(false), 3000);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save instructions file: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save instructions file: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSavingFile(false);
|
||||
}
|
||||
@@ -2437,8 +2439,8 @@ function PerformanceTab({
|
||||
]);
|
||||
setSummary(summaryData);
|
||||
setRatings(ratingsData);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load ratings: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load ratings: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -2466,8 +2468,8 @@ function PerformanceTab({
|
||||
setNewComment("");
|
||||
addToast("Rating added", "success");
|
||||
await loadData();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to add rating: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to add rating: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -2479,8 +2481,8 @@ function PerformanceTab({
|
||||
await deleteAgentRating(agentId, ratingId, projectId);
|
||||
addToast("Rating deleted", "success");
|
||||
await loadData();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to delete rating: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to delete rating: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2824,8 +2826,8 @@ function ConfigTab({
|
||||
// Refresh budget status
|
||||
const status = await fetchAgentBudgetStatus(agent.id, projectId);
|
||||
setBudgetStatus(status);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to reset budget: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to reset budget: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsResettingBudget(false);
|
||||
}
|
||||
@@ -3160,7 +3162,7 @@ function ConfigTab({
|
||||
try {
|
||||
await updateAgent(agent.id, {
|
||||
name: nameValue.trim() || undefined,
|
||||
role: roleValue as any,
|
||||
role: roleValue,
|
||||
title: titleValue.trim() || undefined,
|
||||
icon: iconValue.trim() || undefined,
|
||||
reportsTo: reportsToValue.trim() || undefined,
|
||||
@@ -3176,8 +3178,8 @@ function ConfigTab({
|
||||
}
|
||||
justSavedTimeoutRef.current = setTimeout(() => setJustSaved(false), 3000);
|
||||
await onSaved();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save settings: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save settings: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -3209,7 +3211,7 @@ function ConfigTab({
|
||||
id="agent-role"
|
||||
className="select"
|
||||
value={roleValue}
|
||||
onChange={(e) => setRoleValue(e.target.value as any)}
|
||||
onChange={(e) => setRoleValue(e.target.value as AgentCapability)}
|
||||
>
|
||||
<option value="triage">Triage</option>
|
||||
<option value="executor">Executor</option>
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { Agent, AgentCapability, AgentState } from "../api";
|
||||
import { fetchAgents, createAgent, updateAgent, updateAgentState, deleteAgent } from "../api";
|
||||
import { getScopedItem, setScopedItem } from "../utils/projectStorage";
|
||||
import { getAgentHealthStatus } from "../utils/agentHealth";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { AgentHealthStatus } from "../utils/agentHealth";
|
||||
|
||||
interface AgentListModalProps {
|
||||
@@ -68,8 +69,8 @@ export function AgentListModal({ isOpen, onClose, addToast, projectId }: AgentLi
|
||||
const filter = filterState !== "all" ? { state: filterState } : undefined;
|
||||
const data = await fetchAgents(filter, projectId);
|
||||
setAgents(data);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load agents: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load agents: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -103,8 +104,8 @@ export function AgentListModal({ isOpen, onClose, addToast, projectId }: AgentLi
|
||||
setNewAgentName("");
|
||||
setIsCreating(false);
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to create agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to create agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,8 +114,8 @@ export function AgentListModal({ isOpen, onClose, addToast, projectId }: AgentLi
|
||||
await updateAgentState(agentId, newState, projectId);
|
||||
addToast(`Agent state updated to ${newState}`, "success");
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update state: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update state: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,8 +125,8 @@ export function AgentListModal({ isOpen, onClose, addToast, projectId }: AgentLi
|
||||
await deleteAgent(agentId, projectId);
|
||||
addToast(`Agent "${agentName}" deleted`, "success");
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to delete agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to delete agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,8 +145,8 @@ export function AgentListModal({ isOpen, onClose, addToast, projectId }: AgentLi
|
||||
addToast(`Agent role updated to ${AGENT_ROLES.find(r => r.value === newRole)?.label ?? newRole}`, "success");
|
||||
setEditingRoleForAgent(null);
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update role: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update role: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ export function AgentReflectionsTab({ agentId, projectId, addToast }: AgentRefle
|
||||
]);
|
||||
setReflections(reflectionsData);
|
||||
setPerformance(performanceData);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load reflections: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load reflections: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
MIN_HEARTBEAT_INTERVAL_MS,
|
||||
HEARTBEAT_INTERVAL_PRESETS,
|
||||
} from "../utils/heartbeatIntervals";
|
||||
import { isEphemeralAgent } from "@fusion/core";
|
||||
import { isEphemeralAgent, getErrorMessage } from "@fusion/core";
|
||||
|
||||
export interface AgentsViewProps {
|
||||
addToast: (message: string, type?: "success" | "error") => void;
|
||||
@@ -313,8 +313,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
try {
|
||||
await updateSettings({ heartbeatMultiplier: clampedValue }, projectId);
|
||||
addToast(`Heartbeat speed set to ×${clampedValue.toFixed(1)}`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save heartbeat multiplier: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save heartbeat multiplier: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsSavingMultiplier(false);
|
||||
}
|
||||
@@ -356,8 +356,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
const filter = filterState !== "all" ? { state: filterState } : undefined;
|
||||
const data = await fetchAgents({ ...filter, includeEphemeral: showSystemAgents }, projectId);
|
||||
setAgents(data);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load agents: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load agents: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -378,9 +378,9 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
setOrgTree(data);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
.catch((err) => {
|
||||
if (!cancelled) {
|
||||
addToast(`Failed to load org chart: ${err.message}`, "error");
|
||||
addToast(`Failed to load org chart: ${getErrorMessage(err)}`, "error");
|
||||
setOrgTree([]);
|
||||
}
|
||||
})
|
||||
@@ -435,16 +435,16 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
if (newState === "active") {
|
||||
try {
|
||||
await startAgentRun(agentId, projectId);
|
||||
} catch (runErr: any) {
|
||||
addToast(`Agent activated, but failed to start run: ${runErr.message}`, "error");
|
||||
} catch (runErr) {
|
||||
addToast(`Agent activated, but failed to start run: ${getErrorMessage(runErr)}`, "error");
|
||||
}
|
||||
}
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (previousAgent) {
|
||||
setAgents(prev => prev.map(a => a.id === agentId ? previousAgent : a));
|
||||
}
|
||||
addToast(`Failed to update state: ${err.message}`, "error");
|
||||
addToast(`Failed to update state: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setTransitioningAgentIds(prev => { const next = new Set(prev); next.delete(agentId); return next; });
|
||||
}
|
||||
@@ -456,8 +456,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
await deleteAgent(agentId, projectId);
|
||||
addToast(`Agent "${agentName}" deleted`, "success");
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to delete agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to delete agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -476,8 +476,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
addToast(`Agent role updated to ${AGENT_ROLES.find(r => r.value === newRole)?.label ?? newRole}`, "success");
|
||||
setEditingRoleForAgent(null);
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update role: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update role: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -512,8 +512,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
);
|
||||
addToast(`Heartbeat interval updated to ${formatHeartbeatInterval(newIntervalMs)} for ${agent.name}`, "success");
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update heartbeat interval: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update heartbeat interval: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setUpdatingHeartbeatAgentId(null);
|
||||
}
|
||||
@@ -572,8 +572,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
return next;
|
||||
});
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update heartbeat interval: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update heartbeat interval: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setUpdatingHeartbeatAgentId(null);
|
||||
}
|
||||
@@ -602,8 +602,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
return next;
|
||||
});
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update heartbeat interval: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update heartbeat interval: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
setUpdatingHeartbeatAgentId(null);
|
||||
}
|
||||
@@ -634,8 +634,8 @@ export function AgentsView({ addToast, projectId }: AgentsViewProps) {
|
||||
await startAgentRun(agentId, projectId, { source: "on_demand", triggerDetail: "Triggered from dashboard" });
|
||||
addToast(`Heartbeat run started for ${agentName}`, "success");
|
||||
void loadAgents();
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to start heartbeat run: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to start heartbeat run: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo, useMemo, useState, useCallback, useEffect } from "react";
|
||||
import { useFlashOnIncrease } from "../hooks/useFlashOnIncrease";
|
||||
import type { Task, TaskDetail, Column as ColumnType, TaskCreateInput } from "@fusion/core";
|
||||
import { COLUMN_LABELS, COLUMN_DESCRIPTIONS } from "@fusion/core";
|
||||
import { COLUMN_LABELS, COLUMN_DESCRIPTIONS, getErrorMessage } from "@fusion/core";
|
||||
import { TaskCard } from "./TaskCard";
|
||||
import { WorktreeGroup } from "./WorktreeGroup";
|
||||
import { QuickEntryBox } from "./QuickEntryBox";
|
||||
@@ -113,8 +113,8 @@ function ColumnComponent({ column, tasks, projectId, maxConcurrent, onMoveTask,
|
||||
|
||||
try {
|
||||
await onMoveTask(taskId, column);
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [column, onMoveTask, addToast, tasks]);
|
||||
|
||||
@@ -144,8 +144,8 @@ function ColumnComponent({ column, tasks, projectId, maxConcurrent, onMoveTask,
|
||||
try {
|
||||
const archived = await onArchiveAllDone();
|
||||
addToast(`Archived ${archived.length} tasks`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to archive tasks", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to archive tasks", "error");
|
||||
}
|
||||
}, [onArchiveAllDone, tasks.length, addToast]);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import { FileCode, ChevronDown, ChevronRight, AlertCircle, GitCommit } from "lucide-react";
|
||||
import type { MergeDetails } from "@fusion/core";
|
||||
import { fetchCommitDiff } from "../api";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { highlightDiff } from "../utils/highlightDiff";
|
||||
|
||||
interface CommitDiffTabProps {
|
||||
@@ -92,8 +93,8 @@ export function CommitDiffTab({ commitSha, mergeDetails }: CommitDiffTabProps) {
|
||||
if (parsed.length > 0) {
|
||||
setExpandedFiles(new Set([parsed[0].path]));
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to load commit diff");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to load commit diff");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Folder, File, ChevronRight, Loader2, Copy, Move, Trash2, Pencil, Downlo
|
||||
import type { FileNode } from "../api";
|
||||
import { copyFile, moveFile, deleteFile, renameFile, downloadFileUrl, downloadZipUrl } from "../api";
|
||||
import { appendTokenQuery } from "../auth";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
|
||||
interface FileBrowserProps {
|
||||
entries: FileNode[];
|
||||
@@ -492,8 +493,8 @@ export function FileBrowser({
|
||||
|
||||
setDialog(INITIAL_DIALOG);
|
||||
onRefresh?.();
|
||||
} catch (err: any) {
|
||||
setOperationError(err.message || "Operation failed");
|
||||
} catch (err) {
|
||||
setOperationError(getErrorMessage(err) || "Operation failed");
|
||||
} finally {
|
||||
setOperationLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import type { Task } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
apiFetchGitHubIssues,
|
||||
apiImportGitHubIssue,
|
||||
@@ -164,8 +165,8 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
|
||||
if (fetchedIssues.length === 0) {
|
||||
setError("No open issues found");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to fetch issues");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to fetch issues");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -189,8 +190,8 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
|
||||
if (fetchedPulls.length === 0) {
|
||||
setError("No open pull requests found");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to fetch pull requests");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to fetch pull requests");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -280,11 +281,12 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
|
||||
const task = await apiImportGitHubIssue(owner.trim(), repo.trim(), selectedIssueNumber, projectId);
|
||||
onImport(task);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("already imported")) {
|
||||
setError(err.message);
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg?.includes("already imported")) {
|
||||
setError(msg);
|
||||
} else {
|
||||
setError(err.message || "Failed to import issue");
|
||||
setError(msg || "Failed to import issue");
|
||||
}
|
||||
} finally {
|
||||
setImporting(false);
|
||||
@@ -299,11 +301,12 @@ export function GitHubImportModal({ isOpen, onClose, onImport, tasks, projectId
|
||||
const task = await apiImportGitHubPull(owner.trim(), repo.trim(), selectedPullNumber, projectId);
|
||||
onImport(task);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("already imported")) {
|
||||
setError(err.message);
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg?.includes("already imported")) {
|
||||
setError(msg);
|
||||
} else {
|
||||
setError(err.message || "Failed to import pull request");
|
||||
setError(msg || "Failed to import pull request");
|
||||
}
|
||||
} finally {
|
||||
setImporting(false);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
||||
import type { Task } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import type {
|
||||
GitStatus,
|
||||
@@ -265,9 +266,9 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
setSectionError(err.message || "Failed to fetch git data");
|
||||
addToast(err.message || "Failed to fetch git data", "error");
|
||||
} catch (err) {
|
||||
setSectionError(getErrorMessage(err) || "Failed to fetch git data");
|
||||
addToast(getErrorMessage(err) || "Failed to fetch git data", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -312,8 +313,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
const changes = await fetchFileChanges(projectId);
|
||||
setFileChanges(changes);
|
||||
setSelectedFiles(new Set());
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to stage files", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to stage files", "error");
|
||||
}
|
||||
}, [addToast, projectId]);
|
||||
|
||||
@@ -324,8 +325,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
const changes = await fetchFileChanges(projectId);
|
||||
setFileChanges(changes);
|
||||
setSelectedFiles(new Set());
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to unstage files", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to unstage files", "error");
|
||||
}
|
||||
}, [addToast, projectId]);
|
||||
|
||||
@@ -338,8 +339,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
setFileChanges(changes);
|
||||
setStatus(statusData);
|
||||
setSelectedFiles(new Set());
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to discard changes", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to discard changes", "error");
|
||||
}
|
||||
}, [addToast, projectId]);
|
||||
|
||||
@@ -355,8 +356,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
const [changes, statusData] = await Promise.all([fetchFileChanges(projectId), fetchGitStatus(projectId)]);
|
||||
setFileChanges(changes);
|
||||
setStatus(statusData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to commit", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to commit", "error");
|
||||
} finally {
|
||||
setCommitting(false);
|
||||
}
|
||||
@@ -376,8 +377,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
const [changes, statusData] = await Promise.all([fetchFileChanges(projectId), fetchGitStatus(projectId)]);
|
||||
setFileChanges(changes);
|
||||
setStatus(statusData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to commit", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to commit", "error");
|
||||
} finally {
|
||||
setCommitting(false);
|
||||
}
|
||||
@@ -388,8 +389,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
try {
|
||||
const diff = await fetchUnstagedDiff(projectId);
|
||||
setChangeDiff(diff);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load diff", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load diff", "error");
|
||||
} finally {
|
||||
setLoadingChangeDiff(false);
|
||||
}
|
||||
@@ -420,8 +421,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
try {
|
||||
const diff = await fetchCommitDiff(hash, projectId);
|
||||
setCommitDiff(diff);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load diff", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load diff", "error");
|
||||
setCommitDiff(null);
|
||||
} finally {
|
||||
setLoadingDiff(false);
|
||||
@@ -456,8 +457,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
setBranchBase("");
|
||||
const branchesData = await fetchGitBranches(projectId);
|
||||
setBranches(branchesData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create branch", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create branch", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -471,8 +472,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
const [statusData, branchesData] = await Promise.all([fetchGitStatus(projectId), fetchGitBranches(projectId)]);
|
||||
setStatus(statusData);
|
||||
setBranches(branchesData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to checkout branch", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to checkout branch", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -486,20 +487,20 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
addToast(`Deleted branch ${name}`, "success");
|
||||
const branchesData = await fetchGitBranches(projectId);
|
||||
setBranches(branchesData);
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("not fully merged")) {
|
||||
} catch (err) {
|
||||
if (getErrorMessage(err).includes("not fully merged")) {
|
||||
if (confirm("Branch has unmerged commits. Force delete?")) {
|
||||
try {
|
||||
await deleteBranch(name, true, projectId);
|
||||
addToast(`Force deleted branch ${name}`, "success");
|
||||
const branchesData = await fetchGitBranches(projectId);
|
||||
setBranches(branchesData);
|
||||
} catch (forceErr: any) {
|
||||
addToast(forceErr.message || "Failed to delete branch", "error");
|
||||
} catch (forceErr) {
|
||||
addToast(getErrorMessage(forceErr) || "Failed to delete branch", "error");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addToast(err.message || "Failed to delete branch", "error");
|
||||
addToast(getErrorMessage(err) || "Failed to delete branch", "error");
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -578,8 +579,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
setStashMessage("");
|
||||
const stashesData = await fetchGitStashList(projectId);
|
||||
setStashes(stashesData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to stash changes", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to stash changes", "error");
|
||||
} finally {
|
||||
setStashLoading(null);
|
||||
}
|
||||
@@ -592,8 +593,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
addToast(drop ? "Stash popped" : "Stash applied", "success");
|
||||
const stashesData = await fetchGitStashList(projectId);
|
||||
setStashes(stashesData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to apply stash", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to apply stash", "error");
|
||||
} finally {
|
||||
setStashLoading(null);
|
||||
}
|
||||
@@ -607,8 +608,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
addToast("Stash dropped", "success");
|
||||
const stashesData = await fetchGitStashList(projectId);
|
||||
setStashes(stashesData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to drop stash", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to drop stash", "error");
|
||||
} finally {
|
||||
setStashLoading(null);
|
||||
}
|
||||
@@ -624,8 +625,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
addToast(result.message || "Fetch completed", result.fetched ? "success" : "info");
|
||||
const statusData = await fetchGitStatus(projectId);
|
||||
setStatus(statusData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Fetch failed", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Fetch failed", "error");
|
||||
} finally {
|
||||
setRemoteLoading(null);
|
||||
}
|
||||
@@ -643,8 +644,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
}
|
||||
const statusData = await fetchGitStatus(projectId);
|
||||
setStatus(statusData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Pull failed", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Pull failed", "error");
|
||||
} finally {
|
||||
setRemoteLoading(null);
|
||||
}
|
||||
@@ -658,8 +659,8 @@ export function GitManagerModal({ isOpen, onClose, tasks: _tasks, addToast, proj
|
||||
addToast(result.message || "Push completed", "success");
|
||||
const statusData = await fetchGitStatus(projectId);
|
||||
setStatus(statusData);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Push failed", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Push failed", "error");
|
||||
} finally {
|
||||
setRemoteLoading(null);
|
||||
}
|
||||
@@ -1747,8 +1748,8 @@ function RemotesPanel({
|
||||
try {
|
||||
const data = await fetchGitRemotesDetailed(projectId);
|
||||
setRemotes(data);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load remotes", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load remotes", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -1773,8 +1774,8 @@ function RemotesPanel({
|
||||
try {
|
||||
const commits = await fetchRemoteCommits(remoteName, undefined, 10, projectId);
|
||||
setRemoteCommits(commits);
|
||||
} catch (err: any) {
|
||||
setRemoteCommitsError(err.message || "Failed to load remote commits");
|
||||
} catch (err) {
|
||||
setRemoteCommitsError(getErrorMessage(err) || "Failed to load remote commits");
|
||||
setRemoteCommits([]);
|
||||
} finally {
|
||||
setLoadingRemoteCommits(false);
|
||||
@@ -1793,8 +1794,8 @@ function RemotesPanel({
|
||||
setNewRemoteUrl("");
|
||||
setShowAddForm(false);
|
||||
await loadRemotes();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to add remote", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to add remote", "error");
|
||||
} finally {
|
||||
setRemoteActionLoading(null);
|
||||
}
|
||||
@@ -1808,8 +1809,8 @@ function RemotesPanel({
|
||||
await removeGitRemote(name, projectId);
|
||||
addToast(`Remote '${name}' removed`, "success");
|
||||
await loadRemotes();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to remove remote", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to remove remote", "error");
|
||||
} finally {
|
||||
setRemoteActionLoading(null);
|
||||
}
|
||||
@@ -1825,8 +1826,8 @@ function RemotesPanel({
|
||||
setEditingRemote(null);
|
||||
setEditNameValue("");
|
||||
await loadRemotes();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to rename remote", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to rename remote", "error");
|
||||
} finally {
|
||||
setRemoteActionLoading(null);
|
||||
}
|
||||
@@ -1842,8 +1843,8 @@ function RemotesPanel({
|
||||
setEditingRemote(null);
|
||||
setEditUrlValue("");
|
||||
await loadRemotes();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to update remote URL", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to update remote URL", "error");
|
||||
} finally {
|
||||
setRemoteActionLoading(null);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Brain, Link, Lightbulb, ListTree, Zap, ChevronDown, ChevronUp, Bot, Maximize2, Minimize2 } from "lucide-react";
|
||||
import type { Task, TaskCreateInput, Settings } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import { fetchModels, uploadAttachment, fetchSettings, updateGlobalSettings, fetchAgents } from "../api";
|
||||
import type { ModelInfo, Agent } from "../api";
|
||||
@@ -140,8 +141,8 @@ export function InlineCreateCard({
|
||||
setLoadedModels(response.models);
|
||||
setFavoriteProviders(response.favoriteProviders);
|
||||
setFavoriteModels(response.favoriteModels);
|
||||
} catch (err: any) {
|
||||
setModelsError(err?.message || "Failed to load models");
|
||||
} catch (err) {
|
||||
setModelsError(getErrorMessage(err) || "Failed to load models");
|
||||
} finally {
|
||||
setModelsLoading(false);
|
||||
}
|
||||
@@ -362,8 +363,8 @@ export function InlineCreateCard({
|
||||
if (typeof window !== "undefined") {
|
||||
removeScopedItem(STORAGE_KEY, projectId);
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -472,8 +473,9 @@ export function InlineCreateCard({
|
||||
const result = await fetchAgents(undefined, projectId);
|
||||
setAgents(result);
|
||||
setShowAgentPicker(true);
|
||||
} catch (err: any) {
|
||||
addToast(err?.message ? `Failed to load agents: ${err.message}` : "Failed to load agents", "error");
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
addToast(msg ? `Failed to load agents: ${msg}` : "Failed to load agents", "error");
|
||||
setShowAgentPicker(false);
|
||||
} finally {
|
||||
setAgentsLoading(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback, useMemo, Fragment, useEffect, useRef } from "react";
|
||||
import { ArrowUpDown, ArrowUp, ArrowDown, Link, Columns3, EyeOff, Eye, ChevronRight } from "lucide-react";
|
||||
import type { Task, TaskDetail, Column, TaskCreateInput } from "@fusion/core";
|
||||
import { COLUMN_LABELS, COLUMNS } from "@fusion/core";
|
||||
import { COLUMN_LABELS, COLUMNS, getErrorMessage } from "@fusion/core";
|
||||
import { batchUpdateTaskModels } from "../api";
|
||||
import type { ModelInfo } from "../api";
|
||||
import { QuickEntryBox } from "./QuickEntryBox";
|
||||
@@ -522,8 +522,8 @@ export function ListView({
|
||||
clearSelection();
|
||||
setExecutorModel("__no_change__");
|
||||
setValidatorModel("__no_change__");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to update models", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to update models", "error");
|
||||
} finally {
|
||||
setIsApplying(false);
|
||||
}
|
||||
@@ -582,8 +582,8 @@ export function ListView({
|
||||
|
||||
try {
|
||||
await onMoveTask(taskId, column);
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
},
|
||||
[onMoveTask, addToast]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { X, Send, Loader2, Bot, AlertCircle } from "lucide-react";
|
||||
import type { ParticipantType, MessageType } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { sendMessage } from "../api";
|
||||
import type { Agent } from "../api";
|
||||
|
||||
@@ -66,8 +67,8 @@ export function MessageComposer({
|
||||
projectId,
|
||||
);
|
||||
onSend();
|
||||
} catch (err: any) {
|
||||
const msg = err?.message ?? "Failed to send message";
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err) || "Failed to send message";
|
||||
setError(msg);
|
||||
addToast?.(msg, "error");
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import type { PlanningQuestion } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
startMilestoneInterview,
|
||||
startSliceInterview,
|
||||
@@ -210,9 +211,9 @@ export function MilestoneSliceInterviewModal({
|
||||
currentSessionIdRef.current = sessionId;
|
||||
setLockSessionId(sessionId);
|
||||
connectToInterviewStream(sessionId);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setIsReconnecting(false);
|
||||
setError(err.message || `Failed to start ${targetLabel.toLowerCase()} interview`);
|
||||
setError(getErrorMessage(err) || `Failed to start ${targetLabel.toLowerCase()} interview`);
|
||||
setView({ type: "initial" });
|
||||
currentSessionIdRef.current = null;
|
||||
setLockSessionId(null);
|
||||
@@ -227,8 +228,8 @@ export function MilestoneSliceInterviewModal({
|
||||
await skipInterview(targetId, projectId);
|
||||
onApplied();
|
||||
setView({ type: "applied" });
|
||||
} catch (err: any) {
|
||||
setError(err.message || `Failed to skip ${targetLabel.toLowerCase()} interview`);
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || `Failed to skip ${targetLabel.toLowerCase()} interview`);
|
||||
setIsApplying(false);
|
||||
}
|
||||
}, [onApplied, projectId, skipInterview, targetId, targetLabel]);
|
||||
@@ -395,10 +396,10 @@ export function MilestoneSliceInterviewModal({
|
||||
try {
|
||||
connectToInterviewStream(sessionId);
|
||||
await respondToInterview(sessionId, responses, projectId, sessionTabId);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
streamConnectionRef.current?.close();
|
||||
streamConnectionRef.current = null;
|
||||
setError(err.message || "Failed to submit response");
|
||||
setError(getErrorMessage(err) || "Failed to submit response");
|
||||
setView({ type: "question", sessionId, question: view.question });
|
||||
}
|
||||
},
|
||||
@@ -415,8 +416,8 @@ export function MilestoneSliceInterviewModal({
|
||||
await applyInterview(view.sessionId, editedSummary || undefined, projectId);
|
||||
onApplied();
|
||||
setView({ type: "applied" });
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to apply interview results");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to apply interview results");
|
||||
setIsApplying(false);
|
||||
}
|
||||
}, [applyInterview, editedSummary, onApplied, projectId, view]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import type { PlanningQuestion } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
startMissionInterview,
|
||||
respondToMissionInterview,
|
||||
@@ -158,8 +159,8 @@ export function MissionInterviewModal({
|
||||
setLoadedModels(resp.models);
|
||||
setFavoriteProviders(resp.favoriteProviders);
|
||||
setFavoriteModels(resp.favoriteModels);
|
||||
} catch (err: any) {
|
||||
setModelsError(err.message || "Failed to load models");
|
||||
} catch (err) {
|
||||
setModelsError(getErrorMessage(err) || "Failed to load models");
|
||||
} finally {
|
||||
setModelsLoading(false);
|
||||
}
|
||||
@@ -322,9 +323,9 @@ export function MissionInterviewModal({
|
||||
|
||||
connectToMissionInterviewStream(sessionId);
|
||||
setResponseHistory([]);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setIsReconnecting(false);
|
||||
setError(err.message || "Failed to start interview session");
|
||||
setError(getErrorMessage(err) || "Failed to start interview session");
|
||||
setView({ type: "initial" });
|
||||
currentSessionIdRef.current = null;
|
||||
setLockSessionId(null);
|
||||
@@ -593,10 +594,10 @@ export function MissionInterviewModal({
|
||||
connectToMissionInterviewStream(sessionId);
|
||||
await respondToMissionInterview(sessionId, responses, projectId, sessionTabId);
|
||||
setHasProgress(true);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
streamConnectionRef.current?.close();
|
||||
streamConnectionRef.current = null;
|
||||
setError(err.message || "Failed to submit response");
|
||||
setError(getErrorMessage(err) || "Failed to submit response");
|
||||
setView({ type: "question", sessionId, question: view.question });
|
||||
}
|
||||
},
|
||||
@@ -619,9 +620,9 @@ export function MissionInterviewModal({
|
||||
currentSessionIdRef.current = retrySessionId;
|
||||
setLockSessionId(retrySessionId);
|
||||
await retryMissionInterviewSession(retrySessionId, projectId, sessionTabId);
|
||||
} catch (err: any) {
|
||||
let retryError = err;
|
||||
const retryErrorMessage = err?.message || "";
|
||||
} catch (err) {
|
||||
let retryError: unknown = err;
|
||||
const retryErrorMessage = getErrorMessage(err) || "";
|
||||
|
||||
if (retryErrorMessage.includes("not in an error state")) {
|
||||
try {
|
||||
@@ -678,7 +679,7 @@ export function MissionInterviewModal({
|
||||
|
||||
setIsReconnecting(false);
|
||||
return;
|
||||
} catch (sessionRefreshError: any) {
|
||||
} catch (sessionRefreshError) {
|
||||
retryError = sessionRefreshError;
|
||||
}
|
||||
}
|
||||
@@ -688,7 +689,7 @@ export function MissionInterviewModal({
|
||||
setView({
|
||||
type: "error",
|
||||
sessionId: retrySessionId,
|
||||
errorMessage: retryError?.message || "Retry failed. Please try again.",
|
||||
errorMessage: getErrorMessage(retryError) || "Retry failed. Please try again.",
|
||||
});
|
||||
setIsReconnecting(false);
|
||||
} finally {
|
||||
@@ -723,8 +724,8 @@ export function MissionInterviewModal({
|
||||
currentSessionIdRef.current = null;
|
||||
setLockSessionId(null);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to create mission");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to create mission");
|
||||
setIsCreating(false);
|
||||
}
|
||||
}, [view, editedSummary, onMissionCreated, onClose, projectId]);
|
||||
@@ -867,8 +868,8 @@ export function MissionInterviewModal({
|
||||
setFavoriteProviders(resp.favoriteProviders);
|
||||
setFavoriteModels(resp.favoriteModels);
|
||||
setModelsError(null);
|
||||
} catch (err: any) {
|
||||
setModelsError(err.message || "Failed to load models");
|
||||
} catch (err) {
|
||||
setModelsError(getErrorMessage(err) || "Failed to load models");
|
||||
} finally {
|
||||
setModelsLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
X,
|
||||
Plus,
|
||||
@@ -659,8 +660,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
const data = await fetchMissions(projectId);
|
||||
setMissions(data);
|
||||
void loadMissionHealth(data);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load missions", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load missions", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -699,8 +700,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
setSelectedMilestoneId(null);
|
||||
setValidationTelemetry(null);
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load mission details", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load mission details", "error");
|
||||
} finally {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
@@ -816,8 +817,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
scrollActivityToLatest("auto");
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load mission activity", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load mission activity", "error");
|
||||
} finally {
|
||||
if (!append) {
|
||||
setEventsLoading(false);
|
||||
@@ -1170,8 +1171,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
}
|
||||
await loadMissions();
|
||||
handleCancelMission();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to save mission", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save mission", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1186,8 +1187,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
}
|
||||
await loadMissions();
|
||||
setDeleteConfirmId(null);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete mission", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete mission", "error");
|
||||
}
|
||||
}, [addToast, loadMissions, selectedMission, projectId]);
|
||||
|
||||
@@ -1241,8 +1242,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
}
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
handleCancelMilestone();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to save milestone", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save milestone", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1254,8 +1255,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Milestone deleted", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
setDeleteConfirmId(null);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete milestone", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete milestone", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1338,8 +1339,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
}
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
handleCancelSlice();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to save slice", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save slice", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1351,8 +1352,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Slice deleted", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
setDeleteConfirmId(null);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete slice", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete slice", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1361,8 +1362,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await activateSlice(sliceId, projectId);
|
||||
addToast("Slice activated", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to activate slice", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to activate slice", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1430,8 +1431,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
}
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
handleCancelFeature();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to save feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save feature", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1443,8 +1444,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Feature deleted", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
setDeleteConfirmId(null);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete feature", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1460,8 +1461,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
setLinkTaskFeatureId(null);
|
||||
setSelectedTaskId("");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to link feature to task", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to link feature to task", "error");
|
||||
}
|
||||
}, [linkTaskFeatureId, selectedTaskId, addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1470,8 +1471,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await unlinkFeatureFromTask(featureId, projectId);
|
||||
addToast("Feature unlinked from task", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to unlink feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to unlink feature", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, selectedMission, projectId]);
|
||||
|
||||
@@ -1482,8 +1483,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await triageFeature(featureId, undefined, undefined, projectId);
|
||||
addToast("Feature triaged — task created", "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to triage feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to triage feature", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1522,8 +1523,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
const result = await triageAllSliceFeatures(sliceId, projectId);
|
||||
addToast(`Triaged ${result.count} feature${result.count !== 1 ? "s" : ""}`, "success");
|
||||
await loadMissionDetail(selectedMission!.id);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to triage slice features", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to triage slice features", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1574,8 +1575,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await loadValidationRollup(milestoneId);
|
||||
setIsCreatingAssertion(false);
|
||||
setAssertionForm({ title: "", assertion: "", status: "pending" });
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create assertion", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create assertion", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1612,8 +1613,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await loadAssertionsForMilestone(milestoneId);
|
||||
await loadValidationRollup(milestoneId);
|
||||
handleCancelAssertion();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to update assertion", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to update assertion", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -1658,8 +1659,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Feature linked to assertion", "success");
|
||||
await loadLinkedFeaturesForAssertion(assertionId);
|
||||
setFeaturePickerOpenForAssertion(null);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to link feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to link feature", "error");
|
||||
} finally {
|
||||
setLinkingAssertions((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -1676,8 +1677,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
await unlinkFeatureFromAssertion(featureId, assertionId, projectId);
|
||||
addToast("Feature unlinked from assertion", "success");
|
||||
await loadLinkedFeaturesForAssertion(assertionId);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to unlink feature", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to unlink feature", "error");
|
||||
} finally {
|
||||
setUnlinkingFeatures((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -1701,8 +1702,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
next.set(featureId, snapshot);
|
||||
return next;
|
||||
});
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to trigger validation", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to trigger validation", "error");
|
||||
} finally {
|
||||
setValidatingFeatures((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -1822,8 +1823,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Mission resumed", "success");
|
||||
await loadMissionDetail(missionId);
|
||||
loadMissions();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to resume mission", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to resume mission", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, loadMissions, projectId]);
|
||||
|
||||
@@ -1835,8 +1836,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast(`Mission stopped (${count} task${count !== 1 ? "s" : ""} paused)`, "success");
|
||||
await loadMissionDetail(missionId);
|
||||
loadMissions();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to stop mission", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to stop mission", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, loadMissions, projectId]);
|
||||
|
||||
@@ -1847,8 +1848,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
addToast("Mission started — first slice activated", "success");
|
||||
await loadMissionDetail(missionId);
|
||||
loadMissions();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to start mission", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to start mission", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, loadMissions, projectId]);
|
||||
|
||||
@@ -1861,8 +1862,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
// Reload mission detail to reflect updated fields
|
||||
await loadMissionDetail(missionId);
|
||||
loadMissions();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to update autopilot", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to update autopilot", "error");
|
||||
}
|
||||
}, [addToast, loadMissionDetail, loadMissions, projectId]);
|
||||
|
||||
@@ -3454,9 +3455,8 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
)}
|
||||
|
||||
{/* Mission items */}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
|
||||
{missions.map((mission: any) => {
|
||||
const m = mission as { id: string; title: string; description?: string; status: string; summary?: { totalMilestones: number; completedMilestones: number; totalFeatures: number; completedFeatures: number; progressPercent: number } };
|
||||
{missions.map((mission) => {
|
||||
const m = mission;
|
||||
const selId = selectedMission as { id: string } | null;
|
||||
const isSelected = selId && selId.id === m.id;
|
||||
const statusColors = missionStatusColors[m.status as MissionStatus] || { bg: "", text: "" };
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||
import { fetchModels, updateTask, updateGlobalSettings } from "../api";
|
||||
import type { ModelInfo } from "../api";
|
||||
import type { Settings, Task, TaskDetail } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import { CustomModelDropdown } from "./CustomModelDropdown";
|
||||
import { ProviderIcon } from "./ProviderIcon";
|
||||
@@ -289,7 +290,7 @@ export function ModelSelectorTab({ task, addToast, onTaskUpdated, settings }: Mo
|
||||
getSuccessToastMessage(target, targetSelections[target]),
|
||||
"success",
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (activeTaskIdRef.current !== requestTaskId) {
|
||||
return;
|
||||
}
|
||||
@@ -302,7 +303,7 @@ export function ModelSelectorTab({ task, addToast, onTaskUpdated, settings }: Mo
|
||||
setSelectedPlanning(previousSavedPlanning);
|
||||
}
|
||||
|
||||
addToast(err.message || "Failed to save model settings", "error");
|
||||
addToast(getErrorMessage(err) || "Failed to save model settings", "error");
|
||||
} finally {
|
||||
if (activeTaskIdRef.current === requestTaskId) {
|
||||
setSavingTarget(null);
|
||||
@@ -390,13 +391,13 @@ export function ModelSelectorTab({ task, addToast, onTaskUpdated, settings }: Mo
|
||||
"success",
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (activeTaskIdRef.current !== requestTaskId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedThinking(previousThinking);
|
||||
addToast(err.message || "Failed to save thinking level", "error");
|
||||
addToast(getErrorMessage(err) || "Failed to save thinking level", "error");
|
||||
} finally {
|
||||
if (activeTaskIdRef.current === requestTaskId) {
|
||||
setSavingTarget(null);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useEffect, useRef } from "react";
|
||||
import type { Task, TaskCreateInput } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import { uploadAttachment, fetchAgents } from "../api";
|
||||
import type { Agent } from "../api";
|
||||
@@ -73,8 +74,9 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
|
||||
const result = await fetchAgents(undefined, projectId);
|
||||
setAgents(result);
|
||||
setShowAgentPicker(true);
|
||||
} catch (err: any) {
|
||||
addToast(err?.message ? `Failed to load agents: ${err.message}` : "Failed to load agents", "error");
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
addToast(msg ? `Failed to load agents: ${msg}` : "Failed to load agents", "error");
|
||||
setShowAgentPicker(false);
|
||||
} finally {
|
||||
setAgentsLoading(false);
|
||||
@@ -237,8 +239,8 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
|
||||
|
||||
addToast(`Created ${task.id}`, "success");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create task", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create task", "error");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
||||
import type { Task, PlanningQuestion, PlanningSummary } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
startPlanningStreaming,
|
||||
respondToPlanning,
|
||||
@@ -145,8 +146,8 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
setLoadedModels(response.models);
|
||||
setFavoriteProviders(response.favoriteProviders);
|
||||
setFavoriteModels(response.favoriteModels);
|
||||
} catch (err: any) {
|
||||
setModelsError(err?.message || "Failed to load models");
|
||||
} catch (err) {
|
||||
setModelsError(getErrorMessage(err) || "Failed to load models");
|
||||
} finally {
|
||||
setModelsLoading(false);
|
||||
}
|
||||
@@ -311,9 +312,9 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
|
||||
connectToPlanningStream(sessionId);
|
||||
setResponseHistory([]);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setIsReconnecting(false);
|
||||
setError(err.message || "Failed to start planning session");
|
||||
setError(getErrorMessage(err) || "Failed to start planning session");
|
||||
setView({ type: "initial" });
|
||||
currentSessionIdRef.current = null;
|
||||
setLockSessionId(null);
|
||||
@@ -584,8 +585,8 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
// Submit response - AI will broadcast events via the already-connected stream
|
||||
await respondToPlanning(sessionId, responses, projectId, sessionTabId);
|
||||
// Events (question/summary) will arrive via the existing SSE stream
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to submit response");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to submit response");
|
||||
setView({ type: "question", session });
|
||||
}
|
||||
},
|
||||
@@ -609,9 +610,9 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
currentSessionIdRef.current = retryTarget.sessionId;
|
||||
setLockSessionId(retryTarget.sessionId);
|
||||
await retryPlanningSession(retryTarget.sessionId, projectId, sessionTabId);
|
||||
} catch (err: any) {
|
||||
let retryError = err;
|
||||
const retryErrorMessage = err?.message || "";
|
||||
} catch (err) {
|
||||
let retryError: unknown = err;
|
||||
const retryErrorMessage = getErrorMessage(err) || "";
|
||||
|
||||
if (retryErrorMessage.includes("not in an error state")) {
|
||||
try {
|
||||
@@ -661,7 +662,7 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
|
||||
setIsReconnecting(false);
|
||||
return;
|
||||
} catch (sessionRefreshError: any) {
|
||||
} catch (sessionRefreshError) {
|
||||
retryError = sessionRefreshError;
|
||||
}
|
||||
}
|
||||
@@ -671,7 +672,7 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
setView({
|
||||
type: "error",
|
||||
session: retryTarget,
|
||||
errorMessage: retryError?.message || "Retry failed. Please try again.",
|
||||
errorMessage: getErrorMessage(retryError) || "Retry failed. Please try again.",
|
||||
});
|
||||
setIsReconnecting(false);
|
||||
} finally {
|
||||
@@ -689,8 +690,8 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
const task = await createTaskFromPlanning(view.session.sessionId, editedSummary ?? undefined, projectId);
|
||||
onTaskCreated(task);
|
||||
handleCancel();
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to create task");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to create task");
|
||||
setView({ type: "summary", session: view.session, summary: view.summary });
|
||||
}
|
||||
}, [editedSummary, view, projectId, onTaskCreated, handleCancel]);
|
||||
@@ -710,8 +711,8 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
subtasks: result.subtasks,
|
||||
dirty: false,
|
||||
});
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to start breakdown");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to start breakdown");
|
||||
setView({ type: "summary", session: view.session, summary: view.summary });
|
||||
}
|
||||
}, [editedSummary, view, projectId]);
|
||||
@@ -738,8 +739,8 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
|
||||
currentSessionIdRef.current = null;
|
||||
setLockSessionId(null);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to create tasks");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to create tasks");
|
||||
setView({ type: "breakdown", sessionId: view.sessionId, subtasks: view.subtasks, dirty: view.dirty });
|
||||
}
|
||||
}, [view, onTasksCreated, onClose, projectId]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { GitPullRequest, ExternalLink, RefreshCw, Plus, MessageSquare, CircleDot, XCircle, GitMerge } from "lucide-react";
|
||||
import type { PrInfo } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { createPr, refreshPrStatus, type PrRefreshResponse } from "../api";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
|
||||
@@ -54,8 +55,8 @@ export function PrSection({
|
||||
setPrTitle("");
|
||||
setPrBody("");
|
||||
addToast(`Created PR #${newPr.number}`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create PR", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create PR", "error");
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
@@ -70,8 +71,8 @@ export function PrSection({
|
||||
setRefreshState(updated);
|
||||
onPrUpdated(updated.prInfo);
|
||||
addToast("PR status refreshed", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to refresh PR", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to refresh PR", "error");
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useCallback, useRef, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import type { Task, TaskCreateInput, Settings } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ModelInfo, RefinementType, Agent } from "../api";
|
||||
import { fetchModels, fetchSettings, refineText, getRefineErrorMessage, updateGlobalSettings, fetchAgents, uploadAttachment } from "../api";
|
||||
import { Link, Paperclip, Brain, Lightbulb, ListTree, Sparkles, Save, ChevronDown, ChevronUp, ChevronRight, Bot } from "lucide-react";
|
||||
@@ -461,8 +462,8 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
// Clear input for rapid entry
|
||||
resetForm();
|
||||
// Note: Focus restoration is handled by useEffect when isSubmitting becomes false
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create task", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create task", "error");
|
||||
// Keep input content on failure so user can retry
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
@@ -1019,7 +1020,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
textareaRef.current.style.height = "auto";
|
||||
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
const errorMessage = getRefineErrorMessage(err);
|
||||
addToast(errorMessage, "error");
|
||||
} finally {
|
||||
@@ -1050,8 +1051,8 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
if (!parentFavoriteModels) {
|
||||
setFavoriteModels(response.favoriteModels);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setModelsError(err?.message || "Failed to load models");
|
||||
} catch (err) {
|
||||
setModelsError(getErrorMessage(err) || "Failed to load models");
|
||||
} finally {
|
||||
setModelsLoading(false);
|
||||
}
|
||||
@@ -1071,8 +1072,9 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
setAgentsProjectId(projectId);
|
||||
setShowAgentPicker(true);
|
||||
updateAgentPickerPosition();
|
||||
} catch (err: any) {
|
||||
addToast(err?.message ? `Failed to load agents: ${err.message}` : "Failed to load agents", "error");
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
addToast(msg ? `Failed to load agents: ${msg}` : "Failed to load agents", "error");
|
||||
setShowAgentPicker(false);
|
||||
} finally {
|
||||
setAgentsLoading(false);
|
||||
|
||||
@@ -155,7 +155,7 @@ export function RoutineCard({ routine, onEdit, onDelete, onRun, onToggle, runnin
|
||||
|
||||
// Get cron expression if available (from trigger or direct field)
|
||||
const cronExpression = routine.trigger.type === "cron"
|
||||
? (routine.trigger as any).cronExpression || routine.cronExpression || ""
|
||||
? (("cronExpression" in routine.trigger ? routine.trigger.cronExpression : undefined) as string | undefined) || routine.cronExpression || ""
|
||||
: routine.cronExpression || "";
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useMemo } from "react";
|
||||
import { Plus, Zap, Globe, Folder, X } from "lucide-react";
|
||||
import type { Routine, RoutineCreateInput } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
fetchRoutines,
|
||||
createRoutine,
|
||||
@@ -46,8 +47,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
try {
|
||||
const data = await fetchRoutines(scopeOptions);
|
||||
setRoutines(data);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load routines", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load routines", "error");
|
||||
}
|
||||
}, [addToast, scopeOptions]);
|
||||
|
||||
@@ -95,8 +96,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
addToast("Routine created", "success");
|
||||
setRoutineView("list");
|
||||
await loadRoutines();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create routine", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create routine", "error");
|
||||
}
|
||||
},
|
||||
[addToast, loadRoutines, scopeOptions],
|
||||
@@ -116,8 +117,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
setRoutineView("list");
|
||||
setEditingRoutine(undefined);
|
||||
await loadRoutines();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to update routine", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to update routine", "error");
|
||||
}
|
||||
},
|
||||
[editingRoutine, addToast, loadRoutines, scopeOptions],
|
||||
@@ -129,8 +130,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
await deleteRoutine(routine.id, scopeOptions);
|
||||
addToast(`Deleted "${routine.name}"`, "success");
|
||||
await loadRoutines();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete routine", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete routine", "error");
|
||||
}
|
||||
},
|
||||
[addToast, loadRoutines, scopeOptions],
|
||||
@@ -147,8 +148,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
addToast(`"${routine.name}" failed: ${result.error || "Unknown error"}`, "error");
|
||||
}
|
||||
await loadRoutines();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to run routine", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to run routine", "error");
|
||||
} finally {
|
||||
setRunningRoutineId(null);
|
||||
}
|
||||
@@ -165,8 +166,8 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT
|
||||
"success",
|
||||
);
|
||||
await loadRoutines();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to toggle routine", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to toggle routine", "error");
|
||||
}
|
||||
},
|
||||
[addToast, loadRoutines, scopeOptions],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { fetchScripts, addScript, removeScript, type ScriptEntry } from "../api";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import {
|
||||
@@ -55,8 +56,8 @@ export function ScriptsModal({ isOpen, onClose, addToast, projectId, onRunScript
|
||||
setLoading(true);
|
||||
const data = await fetchScripts(projectId);
|
||||
setScripts(data);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load scripts", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load scripts", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -126,11 +127,12 @@ export function ScriptsModal({ isOpen, onClose, addToast, projectId, onRunScript
|
||||
setForm(EMPTY_FORM);
|
||||
setNameError(null);
|
||||
await loadScripts();
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("already exists")) {
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg?.includes("already exists")) {
|
||||
addToast("A script with this name already exists", "error");
|
||||
} else {
|
||||
addToast(err.message || "Failed to save script", "error");
|
||||
addToast(msg || "Failed to save script", "error");
|
||||
}
|
||||
} finally {
|
||||
setSaving(false);
|
||||
@@ -147,8 +149,8 @@ export function ScriptsModal({ isOpen, onClose, addToast, projectId, onRunScript
|
||||
setForm(EMPTY_FORM);
|
||||
}
|
||||
await loadScripts();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete script", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete script", "error");
|
||||
}
|
||||
}, [isEditing, addToast, loadScripts, projectId]);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { Globe, Folder } from "lucide-react";
|
||||
import { THINKING_LEVELS, isGlobalSettingsKey, isProjectSettingsKey } from "@fusion/core";
|
||||
import type { Settings, GlobalSettings, ThemeMode, ColorTheme, ModelPreset, NtfyNotificationEvent, AgentPromptsConfig } from "@fusion/core";
|
||||
import { THINKING_LEVELS, isGlobalSettingsKey, isProjectSettingsKey, getErrorMessage } from "@fusion/core";
|
||||
import type { Settings, GlobalSettings, ThemeMode, ColorTheme, ModelPreset, NtfyNotificationEvent, AgentPromptsConfig, ThinkingLevel } from "@fusion/core";
|
||||
import { fetchSettings, fetchSettingsByScope, updateSettings, updateGlobalSettings, fetchAuthStatus, loginProvider, logoutProvider, saveApiKey, clearApiKey, fetchModels, testNtfyNotification, fetchBackups, createBackup, exportSettings, importSettings, fetchMemoryFile, fetchMemoryFiles, saveMemoryFile, compactMemory, fetchGlobalConcurrency, updateGlobalConcurrency, installQmd, testMemoryRetrieval, fetchGitRemotesDetailed } from "../api";
|
||||
import type { AuthProvider, ModelInfo, BackupListResponse, SettingsExportData, MemoryFileInfo, MemoryRetrievalTestResult, GitRemoteDetailed } from "../api";
|
||||
import { useMemoryBackendStatus } from "../hooks/useMemoryBackendStatus";
|
||||
@@ -95,6 +95,9 @@ const KNOWN_EXPERIMENTAL_FEATURES: Record<string, string> = {
|
||||
|
||||
export type SectionId = SettingsSection["id"];
|
||||
|
||||
/** Local form state extends Settings with a worktreeInitCommand override and lets tokenCap carry null (delete semantic). */
|
||||
type SettingsFormState = Settings & { worktreeInitCommand?: string; tokenCap?: number | null };
|
||||
|
||||
interface SettingsModalProps {
|
||||
onClose: () => void;
|
||||
addToast: (message: string, type?: ToastType) => void;
|
||||
@@ -124,7 +127,7 @@ export function SettingsModal({
|
||||
onColorThemeChange,
|
||||
onReopenOnboarding,
|
||||
}: SettingsModalProps) {
|
||||
const [form, setForm] = useState<Settings & { worktreeInitCommand?: string }>({
|
||||
const [form, setForm] = useState<SettingsFormState>({
|
||||
maxConcurrent: 2,
|
||||
maxTriageConcurrent: 2,
|
||||
maxWorktrees: 4,
|
||||
@@ -256,7 +259,7 @@ export function SettingsModal({
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
addToast(err.message, "error");
|
||||
addToast(getErrorMessage(err), "error");
|
||||
setLoading(false);
|
||||
});
|
||||
}, [addToast, projectId]);
|
||||
@@ -343,9 +346,9 @@ export function SettingsModal({
|
||||
setMemoryContent(content);
|
||||
setMemoryDirty(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
addToast(err?.message || "Failed to load project memory", "error");
|
||||
addToast(getErrorMessage(err) || "Failed to load project memory", "error");
|
||||
setMemoryContent("");
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -397,8 +400,8 @@ export function SettingsModal({
|
||||
// Continue polling on transient errors
|
||||
}
|
||||
}, 2000);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Login failed", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Login failed", "error");
|
||||
setAuthActionInProgress(null);
|
||||
}
|
||||
}, [addToast]);
|
||||
@@ -409,8 +412,8 @@ export function SettingsModal({
|
||||
await logoutProvider(providerId);
|
||||
await loadAuthStatus();
|
||||
addToast("Logged out", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Logout failed", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Logout failed", "error");
|
||||
} finally {
|
||||
setAuthActionInProgress(null);
|
||||
}
|
||||
@@ -437,8 +440,8 @@ export function SettingsModal({
|
||||
});
|
||||
await loadAuthStatus();
|
||||
addToast("API key saved", "success");
|
||||
} catch (err: any) {
|
||||
setApiKeyErrors((prev) => ({ ...prev, [providerId]: err.message || "Failed to save API key" }));
|
||||
} catch (err) {
|
||||
setApiKeyErrors((prev) => ({ ...prev, [providerId]: getErrorMessage(err) || "Failed to save API key" }));
|
||||
} finally {
|
||||
setAuthActionInProgress(null);
|
||||
}
|
||||
@@ -460,8 +463,8 @@ export function SettingsModal({
|
||||
});
|
||||
await loadAuthStatus();
|
||||
addToast("API key cleared", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to clear API key", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to clear API key", "error");
|
||||
} finally {
|
||||
setAuthActionInProgress(null);
|
||||
}
|
||||
@@ -484,8 +487,8 @@ export function SettingsModal({
|
||||
} else {
|
||||
addToast("Failed to send test notification", "error");
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to send test notification", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to send test notification", "error");
|
||||
} finally {
|
||||
setTestNotificationLoading(false);
|
||||
}
|
||||
@@ -503,8 +506,8 @@ export function SettingsModal({
|
||||
} else {
|
||||
addToast(result.error || "Failed to create backup", "error");
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to create backup", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to create backup", "error");
|
||||
} finally {
|
||||
setBackupLoading(false);
|
||||
}
|
||||
@@ -532,8 +535,8 @@ export function SettingsModal({
|
||||
|
||||
const scopeLabel = scope === "global" ? "global" : scope === "project" ? "project" : "all";
|
||||
addToast(`Settings exported (${scopeLabel} scope)`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to export settings", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to export settings", "error");
|
||||
}
|
||||
}, [addToast, activeSectionScope, projectId]);
|
||||
|
||||
@@ -549,8 +552,8 @@ export function SettingsModal({
|
||||
const data = JSON.parse(text) as SettingsExportData;
|
||||
setImportPreview(data);
|
||||
setImportDialogOpen(true);
|
||||
} catch (err: any) {
|
||||
addToast(`Invalid JSON file: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Invalid JSON file: ${getErrorMessage(err)}`, "error");
|
||||
setImportFile(null);
|
||||
} finally {
|
||||
setImportLoading(false);
|
||||
@@ -577,8 +580,8 @@ export function SettingsModal({
|
||||
} else {
|
||||
addToast(result.error || "Import failed", "error");
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to import settings", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to import settings", "error");
|
||||
} finally {
|
||||
setImportLoading(false);
|
||||
}
|
||||
@@ -803,9 +806,9 @@ export function SettingsModal({
|
||||
// if current value is undefined AND initial was defined, use null
|
||||
const initialValue = initialValues?.[key as keyof GlobalSettings];
|
||||
if (value === undefined && initialValue !== undefined) {
|
||||
(globalPatch as any)[key] = null; // null means "explicitly clear"
|
||||
(globalPatch as Record<string, unknown>)[key] = null; // null means "explicitly clear"
|
||||
} else {
|
||||
(globalPatch as any)[key] = value;
|
||||
(globalPatch as Record<string, unknown>)[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -837,14 +840,14 @@ export function SettingsModal({
|
||||
if (value !== initialProjectValue) {
|
||||
// Detect explicit reset: current is undefined/null but initial was set
|
||||
if ((value === undefined || value === null) && initialProjectValue !== undefined && initialProjectValue !== null) {
|
||||
(projectPatch as any)[key] = null; // null-as-delete
|
||||
(projectPatch as Record<string, unknown>)[key] = null; // null-as-delete
|
||||
} else if (value !== undefined) {
|
||||
(projectPatch as any)[key] = value;
|
||||
(projectPatch as Record<string, unknown>)[key] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For non-model settings: existing behavior
|
||||
(projectPatch as any)[key] = value;
|
||||
(projectPatch as Record<string, unknown>)[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,8 +865,8 @@ export function SettingsModal({
|
||||
|
||||
addToast("Settings saved", "success");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [form, globalMaxConcurrent, prefixError, presetDraft, initialValues, initialScopedValues, onClose, addToast, projectId]);
|
||||
|
||||
@@ -872,8 +875,8 @@ export function SettingsModal({
|
||||
await saveMemoryFile(selectedMemoryPath, memoryContent, projectId);
|
||||
setMemoryDirty(false);
|
||||
addToast("Memory saved", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err?.message || "Failed to save memory", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save memory", "error");
|
||||
}
|
||||
}, [selectedMemoryPath, memoryContent, projectId, addToast]);
|
||||
|
||||
@@ -893,8 +896,8 @@ export function SettingsModal({
|
||||
setMemoryFiles(files);
|
||||
|
||||
addToast("Memory file compacted", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err?.message || "Failed to compact memory", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to compact memory", "error");
|
||||
} finally {
|
||||
setMemoryCompactLoading(false);
|
||||
}
|
||||
@@ -910,8 +913,8 @@ export function SettingsModal({
|
||||
result.qmdAvailable ? "Memory retrieval test complete" : "qmd is not installed; local fallback was used",
|
||||
result.qmdAvailable ? "success" : "warning",
|
||||
);
|
||||
} catch (err: any) {
|
||||
addToast(err?.message || "Failed to test memory retrieval", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to test memory retrieval", "error");
|
||||
} finally {
|
||||
setMemoryTestLoading(false);
|
||||
}
|
||||
@@ -926,8 +929,8 @@ export function SettingsModal({
|
||||
result.qmdAvailable ? "qmd installed successfully" : "qmd install finished, but qmd is still unavailable",
|
||||
result.qmdAvailable ? "success" : "warning",
|
||||
);
|
||||
} catch (err: any) {
|
||||
addToast(err?.message || "Failed to install qmd", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to install qmd", "error");
|
||||
} finally {
|
||||
setQmdInstallLoading(false);
|
||||
}
|
||||
@@ -1143,7 +1146,7 @@ export function SettingsModal({
|
||||
value={form.defaultThinkingLevel || ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, defaultThinkingLevel: val || undefined } as any));
|
||||
setForm((f) => ({ ...f, defaultThinkingLevel: (val as ThinkingLevel) || undefined }));
|
||||
}}
|
||||
>
|
||||
<option value="">Default</option>
|
||||
@@ -1254,18 +1257,18 @@ export function SettingsModal({
|
||||
id="tokenCap"
|
||||
type="number"
|
||||
placeholder="No cap"
|
||||
value={(form as any).tokenCap ?? ""}
|
||||
value={form.tokenCap ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, tokenCap: val ? parseInt(val, 10) : null } as any));
|
||||
setForm((f) => ({ ...f, tokenCap: val ? parseInt(val, 10) : null } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
{(form as any).tokenCap != null && (
|
||||
{form.tokenCap != null && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-sm"
|
||||
title="Reset to default (no cap)"
|
||||
onClick={() => setForm((f) => ({ ...f, tokenCap: null } as any))}
|
||||
onClick={() => setForm((f) => ({ ...f, tokenCap: null } as unknown as SettingsFormState))}
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
>
|
||||
Reset
|
||||
@@ -1758,7 +1761,7 @@ export function SettingsModal({
|
||||
value={form.maxConcurrent ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, maxConcurrent: val === "" ? undefined : Number(val) } as any));
|
||||
setForm((f) => ({ ...f, maxConcurrent: val === "" ? undefined : Number(val) } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1772,7 +1775,7 @@ export function SettingsModal({
|
||||
value={form.maxTriageConcurrent ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, maxTriageConcurrent: val === "" ? undefined : Number(val) } as any));
|
||||
setForm((f) => ({ ...f, maxTriageConcurrent: val === "" ? undefined : Number(val) } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
<small>Maximum concurrent triage/specification agents</small>
|
||||
@@ -1787,7 +1790,7 @@ export function SettingsModal({
|
||||
value={form.pollIntervalMs ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, pollIntervalMs: val === "" ? undefined : Number(val) } as any));
|
||||
setForm((f) => ({ ...f, pollIntervalMs: val === "" ? undefined : Number(val) } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1975,7 +1978,7 @@ export function SettingsModal({
|
||||
value={form.maxWorktrees ?? ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setForm((f) => ({ ...f, maxWorktrees: val === "" ? undefined : Number(val) } as any));
|
||||
setForm((f) => ({ ...f, maxWorktrees: val === "" ? undefined : Number(val) } as SettingsFormState));
|
||||
}}
|
||||
/>
|
||||
<small>Limits total git worktrees including in-review tasks</small>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { Task } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
startSubtaskBreakdown,
|
||||
retrySubtaskSession,
|
||||
@@ -242,8 +243,8 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
const { sessionId } = await startSubtaskBreakdown(localDescription.trim(), projectId);
|
||||
setView({ type: "generating", sessionId });
|
||||
connectToSubtaskStream(sessionId);
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to start subtask breakdown");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to start subtask breakdown");
|
||||
setView({ type: "initial" });
|
||||
}
|
||||
}, [connectToSubtaskStream, localDescription, projectId]);
|
||||
@@ -295,8 +296,8 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
errorMessage: session.error ?? "Session encountered an error",
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to resume session");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to resume session");
|
||||
}
|
||||
})();
|
||||
}, [connectToSubtaskStream, isOpen, resumeSessionId, view.type, projectId]);
|
||||
@@ -478,8 +479,8 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
onTasksCreated(result.tasks);
|
||||
resetState();
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to create tasks");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to create tasks");
|
||||
setView({ type: "editing", sessionId });
|
||||
}
|
||||
}, [isInvalid, onClose, onTasksCreated, parentTaskId, projectId, resetState, sessionId, subtasks]);
|
||||
@@ -498,9 +499,9 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
|
||||
try {
|
||||
await retrySubtaskSession(retrySessionId, projectId, sessionTabId);
|
||||
} catch (err: any) {
|
||||
let retryError = err;
|
||||
const retryErrorMessage = err?.message || "";
|
||||
} catch (err) {
|
||||
let retryError: unknown = err;
|
||||
const retryErrorMessage = getErrorMessage(err) || "";
|
||||
|
||||
if (retryErrorMessage.includes("not in an error state")) {
|
||||
try {
|
||||
@@ -536,7 +537,7 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
|
||||
setIsReconnecting(false);
|
||||
return;
|
||||
} catch (sessionRefreshError: any) {
|
||||
} catch (sessionRefreshError) {
|
||||
retryError = sessionRefreshError;
|
||||
}
|
||||
}
|
||||
@@ -546,7 +547,7 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
setView({
|
||||
type: "error",
|
||||
sessionId: retrySessionId,
|
||||
errorMessage: retryError?.message || "Retry failed. Please try again.",
|
||||
errorMessage: getErrorMessage(retryError) || "Retry failed. Please try again.",
|
||||
});
|
||||
setIsReconnecting(false);
|
||||
} finally {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo, useCallback, useState, useRef, useEffect, useMemo } from "react";
|
||||
import { Link, Clock, Layers, Pencil, ChevronDown, Folder, Target, Bot, Trash2 } from "lucide-react";
|
||||
import type { Task, TaskDetail, Column, PrInfo, IssueInfo } from "@fusion/core";
|
||||
import { COLUMN_LABELS, VALID_TRANSITIONS } from "@fusion/core";
|
||||
import { COLUMN_LABELS, VALID_TRANSITIONS, getErrorMessage } from "@fusion/core";
|
||||
import { fetchTaskDetail, uploadAttachment, fetchMission, fetchAgent } from "../api";
|
||||
import { GitHubBadge } from "./GitHubBadge";
|
||||
import { pickPreferredBadge } from "./TaskCardBadge";
|
||||
@@ -438,8 +438,8 @@ function TaskCardComponent({
|
||||
try {
|
||||
await uploadAttachment(task.id, file, projectId);
|
||||
addToast(`Attached ${file.name} to ${task.id}`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to attach ${file.name}: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to attach ${file.name}: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
}
|
||||
}, [task.id, isFileDrag, addToast]);
|
||||
@@ -640,8 +640,8 @@ function TaskCardComponent({
|
||||
});
|
||||
addToast(`Updated ${task.id}`, "success");
|
||||
setIsEditing(false);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update ${task.id}: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update ${task.id}: ${getErrorMessage(err)}`, "error");
|
||||
// Stay in edit mode on error so user can retry
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { FileCode, ChevronDown, ChevronRight, ChevronLeft, AlertCircle, GitCommit, WrapText, Maximize2 } from "lucide-react";
|
||||
import type { MergeDetails, Column } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { fetchTaskDiff, type TaskDiff } from "../api";
|
||||
import { highlightDiff } from "../utils/highlightDiff";
|
||||
import { truncateMiddle } from "../utils/truncatePath";
|
||||
@@ -90,8 +91,8 @@ export function TaskChangesTab({ taskId, worktree, projectId, column, mergeDetai
|
||||
setExpandedFiles(new Set([normalized[0].path]));
|
||||
setCurrentFileIndex(0);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to load diff");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to load diff");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import type { Task, TaskComment } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { addSteeringComment, updateTaskComment, deleteTaskComment } from "../api";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
|
||||
@@ -47,8 +48,8 @@ export function TaskComments({ task, onTaskUpdated, addToast, currentAuthor = "u
|
||||
setDraft("");
|
||||
onTaskUpdated?.(updated);
|
||||
addToast("Comment added", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to add comment", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to add comment", "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -64,8 +65,8 @@ export function TaskComments({ task, onTaskUpdated, addToast, currentAuthor = "u
|
||||
setEditingText("");
|
||||
onTaskUpdated?.(updated);
|
||||
addToast("Comment updated", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to update comment", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to update comment", "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
@@ -77,8 +78,8 @@ export function TaskComments({ task, onTaskUpdated, addToast, currentAuthor = "u
|
||||
const updated = await deleteTaskComment(task.id, commentId, projectId);
|
||||
onTaskUpdated?.(updated);
|
||||
addToast("Comment deleted", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to delete comment", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to delete comment", "error");
|
||||
} finally {
|
||||
setDeletingId(null);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Pencil, Bot, X, ChevronDown } from "lucide-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import type { Task, TaskDetail, TaskAttachment, Column, MergeResult, Settings, AgentLogEntry, Agent } from "@fusion/core";
|
||||
import { COLUMN_LABELS, VALID_TRANSITIONS } from "@fusion/core";
|
||||
import { COLUMN_LABELS, VALID_TRANSITIONS, getErrorMessage } from "@fusion/core";
|
||||
import { uploadAttachment, deleteAttachment, updateTask, pauseTask, unpauseTask, fetchTaskDetail, fetchSettings, requestSpecRevision, rebuildTaskSpec, approvePlan, rejectPlan, refineTask, fetchWorkflowResults, assignTask, fetchAgents, fetchAgent } from "../api";
|
||||
import type { WorkflowStepResult } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
@@ -371,9 +371,9 @@ export function TaskDetailModal({
|
||||
.then((results) => {
|
||||
if (!cancelled) setWorkflowResults(results);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
.catch((err) => {
|
||||
if (!cancelled) {
|
||||
addToast(`Failed to load workflow results: ${err.message}`, "error");
|
||||
addToast(`Failed to load workflow results: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -601,8 +601,8 @@ export function TaskDetailModal({
|
||||
setEditPendingImages([]);
|
||||
addToast(`Updated ${task.id}`, "success");
|
||||
setIsEditing(false);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to update ${task.id}: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to update ${task.id}: ${getErrorMessage(err)}`, "error");
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
setIsSaving(false);
|
||||
@@ -614,8 +614,8 @@ export function TaskDetailModal({
|
||||
try {
|
||||
await updateTask(task.id, { description }, projectId);
|
||||
addToast("Description saved", "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to save: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to save: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
}, [task.id, addToast, projectId]);
|
||||
|
||||
@@ -671,8 +671,8 @@ export function TaskDetailModal({
|
||||
await onMoveTask(task.id, column);
|
||||
onClose();
|
||||
addToast(`Moved to ${COLUMN_LABELS[column]}`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
},
|
||||
[task.id, onMoveTask, onClose, addToast],
|
||||
@@ -684,8 +684,8 @@ export function TaskDetailModal({
|
||||
await onDeleteTask(task.id);
|
||||
onClose();
|
||||
addToast(`Deleted ${task.id}`, "info");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, onDeleteTask, onClose, addToast]);
|
||||
|
||||
@@ -700,8 +700,8 @@ export function TaskDetailModal({
|
||||
: `Closed ${task.id} (${result.error || "no branch to merge"})`;
|
||||
addToast(msg, "success");
|
||||
})
|
||||
.catch((err: any) => {
|
||||
addToast(err.message, "error");
|
||||
.catch((err) => {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
});
|
||||
}, [task.id, onMergeTask, onClose, addToast]);
|
||||
|
||||
@@ -712,8 +712,8 @@ export function TaskDetailModal({
|
||||
.then(() => {
|
||||
addToast(`Retried ${task.id}`, "success");
|
||||
})
|
||||
.catch((err: any) => {
|
||||
addToast(err.message, "error");
|
||||
.catch((err) => {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
});
|
||||
}, [task.id, onRetryTask, onClose, addToast]);
|
||||
|
||||
@@ -724,8 +724,8 @@ export function TaskDetailModal({
|
||||
const newTask = await onDuplicateTask(task.id);
|
||||
onClose();
|
||||
addToast(`Duplicated ${task.id} → ${newTask.id}`, "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, onDuplicateTask, onClose, addToast]);
|
||||
|
||||
@@ -739,8 +739,8 @@ export function TaskDetailModal({
|
||||
addToast(`Paused ${task.id}`, "success");
|
||||
}
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, task.paused, onClose, addToast]);
|
||||
|
||||
@@ -749,8 +749,8 @@ export function TaskDetailModal({
|
||||
await approvePlan(task.id, projectId);
|
||||
addToast(`Plan approved — ${task.id} moved to Todo`, "success");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, onClose, addToast]);
|
||||
|
||||
@@ -760,8 +760,8 @@ export function TaskDetailModal({
|
||||
await rejectPlan(task.id, projectId);
|
||||
addToast(`Plan rejected — ${task.id} returned to Triage for re-specification`, "info");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, onClose, addToast]);
|
||||
|
||||
@@ -771,8 +771,8 @@ export function TaskDetailModal({
|
||||
await rebuildTaskSpec(task.id, projectId);
|
||||
onClose();
|
||||
addToast(`Respecifying ${task.id}...`, "info");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, projectId, onClose, addToast]);
|
||||
|
||||
@@ -823,8 +823,8 @@ export function TaskDetailModal({
|
||||
const newTask = await refineTask(task.id, refineFeedback.trim(), projectId);
|
||||
addToast(`Refinement task created: ${newTask.id}`, "success");
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
} finally {
|
||||
setIsRefining(false);
|
||||
}
|
||||
@@ -836,8 +836,8 @@ export function TaskDetailModal({
|
||||
const attachment = await uploadAttachment(task.id, file, projectId);
|
||||
setAttachments((prev) => [...prev, attachment]);
|
||||
addToast("Screenshot attached", "success");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
@@ -891,8 +891,8 @@ export function TaskDetailModal({
|
||||
await deleteAttachment(task.id, filename, projectId);
|
||||
setAttachments((prev) => prev.filter((a) => a.filename !== filename));
|
||||
addToast("Attachment deleted", "info");
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, addToast]);
|
||||
|
||||
@@ -904,9 +904,9 @@ export function TaskDetailModal({
|
||||
const updatedTask = await updateTask(task.id, { enabledWorkflowSteps }, projectId);
|
||||
addToast("Workflow steps updated", "success");
|
||||
onTaskUpdated?.(updatedTask);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setWorkflowEnabledSteps(previousSteps);
|
||||
addToast(`Failed to update workflow steps: ${err.message}`, "error");
|
||||
addToast(`Failed to update workflow steps: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
}, [task.id, projectId, workflowEnabledSteps, onTaskUpdated, addToast]);
|
||||
|
||||
@@ -916,8 +916,8 @@ export function TaskDetailModal({
|
||||
const loadedAgents = await fetchAgents(undefined, projectId);
|
||||
setAgents(loadedAgents);
|
||||
setShowAgentPicker(true);
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to load agents: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to load agents: ${getErrorMessage(err)}`, "error");
|
||||
setShowAgentPicker(false);
|
||||
} finally {
|
||||
setAgentsLoading(false);
|
||||
@@ -936,8 +936,8 @@ export function TaskDetailModal({
|
||||
setShowAgentPicker(false);
|
||||
onTaskUpdated?.(updatedTask);
|
||||
addToast("Assigned agent updated", "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to assign agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to assign agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
}, [task.id, projectId, agents, onTaskUpdated, addToast]);
|
||||
|
||||
@@ -948,8 +948,8 @@ export function TaskDetailModal({
|
||||
setShowAgentPicker(false);
|
||||
onTaskUpdated?.(updatedTask);
|
||||
addToast("Agent unassigned", "success");
|
||||
} catch (err: any) {
|
||||
addToast(`Failed to unassign agent: ${err.message}`, "error");
|
||||
} catch (err) {
|
||||
addToast(`Failed to unassign agent: ${getErrorMessage(err)}`, "error");
|
||||
}
|
||||
}, [task.id, projectId, onTaskUpdated, addToast]);
|
||||
|
||||
@@ -958,9 +958,9 @@ export function TaskDetailModal({
|
||||
setDependencies(newDeps);
|
||||
try {
|
||||
await updateTask(task.id, { dependencies: newDeps }, projectId);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setDependencies(dependencies);
|
||||
addToast(err.message, "error");
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, dependencies, addToast]);
|
||||
|
||||
@@ -970,9 +970,9 @@ export function TaskDetailModal({
|
||||
setDependencies(newDeps);
|
||||
try {
|
||||
await updateTask(task.id, { dependencies: newDeps }, projectId);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setDependencies(dependencies);
|
||||
addToast(err.message, "error");
|
||||
addToast(getErrorMessage(err), "error");
|
||||
}
|
||||
}, [task.id, dependencies, addToast]);
|
||||
|
||||
@@ -995,8 +995,8 @@ export function TaskDetailModal({
|
||||
if (fullDetail) {
|
||||
fullDetail.prompt = newContent;
|
||||
}
|
||||
} catch (err: any) {
|
||||
addToast(err.message, "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err), "error");
|
||||
throw err;
|
||||
} finally {
|
||||
setIsSavingSpec(false);
|
||||
@@ -1010,11 +1010,12 @@ export function TaskDetailModal({
|
||||
addToast("AI revision requested. Task moved to triage.", "success");
|
||||
// Task has been moved to triage, close modal
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("in-review") || err.message?.includes("done")) {
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg.includes("in-review") || msg.includes("done")) {
|
||||
addToast("Cannot request revision: Task must be in 'todo' or 'in-progress' column.", "error");
|
||||
} else {
|
||||
addToast(err.message, "error");
|
||||
addToast(msg, "error");
|
||||
}
|
||||
} finally {
|
||||
setIsRequestingRevision(false);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FileText, ChevronDown, ChevronUp, Plus, Trash2, History } from "lucide-
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import type { Task, TaskDocument, TaskDocumentRevision } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import {
|
||||
fetchTaskDocuments,
|
||||
@@ -61,8 +62,8 @@ export function TaskDocumentsTab({
|
||||
try {
|
||||
const docs = await fetchTaskDocuments(taskId, projectId);
|
||||
setDocuments(docs);
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to load documents", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to load documents", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -102,8 +103,8 @@ export function TaskDocumentsTab({
|
||||
try {
|
||||
const revs = await fetchTaskDocumentRevisions(taskId, docKey, projectId);
|
||||
setRevisions(revs);
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to load revisions", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to load revisions", "error");
|
||||
} finally {
|
||||
setLoadingRevisions(false);
|
||||
}
|
||||
@@ -136,8 +137,8 @@ export function TaskDocumentsTab({
|
||||
setExpandedContent(updated.content);
|
||||
}
|
||||
addToast("Document saved", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to save document", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to save document", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -169,8 +170,8 @@ export function TaskDocumentsTab({
|
||||
setNewDocContent("");
|
||||
await loadDocuments();
|
||||
addToast("Document created", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to create document", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to create document", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -192,8 +193,8 @@ export function TaskDocumentsTab({
|
||||
}
|
||||
await loadDocuments();
|
||||
addToast("Document deleted", "success");
|
||||
} catch (error: any) {
|
||||
addToast(error.message || "Failed to delete document", "error");
|
||||
} catch (error) {
|
||||
addToast(getErrorMessage(error) || "Failed to delete document", "error");
|
||||
} finally {
|
||||
setDeletingKey(null);
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ export function TaskForm({
|
||||
descTextareaRef.current.style.height = "auto";
|
||||
descTextareaRef.current.style.height = descTextareaRef.current.scrollHeight + "px";
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
const errorMessage = getRefineErrorMessage(err);
|
||||
addToast(errorMessage, "error");
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { X, Trash2, Terminal as TerminalIcon, RefreshCw } from "lucide-react";
|
||||
import { useTerminal } from "../hooks/useTerminal";
|
||||
import { useTerminalSessions } from "../hooks/useTerminalSessions";
|
||||
@@ -786,8 +787,8 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
||||
// Restart the active tab's session
|
||||
try {
|
||||
await restartActiveTab();
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to restart terminal session");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to restart terminal session");
|
||||
}
|
||||
}, [restartActiveTab]);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import type { WorkflowStep, WorkflowStepInput, WorkflowStepMode, WorkflowStepPhase } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import {
|
||||
fetchWorkflowSteps,
|
||||
createWorkflowStep,
|
||||
@@ -138,8 +139,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setLoading(true);
|
||||
const data = await fetchWorkflowSteps(projectId);
|
||||
setSteps(data);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load workflow steps", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load workflow steps", "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -168,8 +169,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setTemplatesLoading(true);
|
||||
const response = await fetchWorkflowStepTemplates();
|
||||
setTemplates(response.templates);
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to load templates", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to load templates", "error");
|
||||
} finally {
|
||||
setTemplatesLoading(false);
|
||||
}
|
||||
@@ -261,8 +262,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setEditingId(null);
|
||||
setForm(EMPTY_FORM);
|
||||
await loadSteps();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to save workflow step", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to save workflow step", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -278,8 +279,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setForm(EMPTY_FORM);
|
||||
}
|
||||
await loadSteps();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to delete workflow step", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to delete workflow step", "error");
|
||||
}
|
||||
}, [editingId, addToast, loadSteps]);
|
||||
|
||||
@@ -320,8 +321,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setForm((prev) => ({ ...prev, prompt: result.prompt }));
|
||||
addToast("Prompt refined with AI", "success");
|
||||
await loadSteps();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to refine prompt", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to refine prompt", "error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
setRefining(false);
|
||||
@@ -337,8 +338,8 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
setForm((prev) => ({ ...prev, prompt: result.prompt }));
|
||||
addToast("Prompt refined with AI", "success");
|
||||
await loadSteps();
|
||||
} catch (err: any) {
|
||||
addToast(err.message || "Failed to refine prompt", "error");
|
||||
} catch (err) {
|
||||
addToast(getErrorMessage(err) || "Failed to refine prompt", "error");
|
||||
} finally {
|
||||
setRefining(false);
|
||||
}
|
||||
@@ -352,11 +353,12 @@ export function WorkflowStepManager({ isOpen, onClose, addToast, projectId }: Wo
|
||||
await loadSteps();
|
||||
// Switch to "My Workflow Steps" tab to show the newly added step
|
||||
setActiveTab("my-steps");
|
||||
} catch (err: any) {
|
||||
if (err.message?.includes("already exists")) {
|
||||
} catch (err) {
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg?.includes("already exists")) {
|
||||
addToast(`A workflow step named '${template.name}' already exists`, "error");
|
||||
} else {
|
||||
addToast(err.message || "Failed to add workflow step from template", "error");
|
||||
addToast(msg || "Failed to add workflow step from template", "error");
|
||||
}
|
||||
} finally {
|
||||
setAddingTemplateId(null);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { fetchBatchStatus } from "../api";
|
||||
import type { BatchStatusResult } from "@fusion/core";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
|
||||
// Module-level store to share batch data across hook instances
|
||||
const batchBadgeStore = {
|
||||
@@ -68,11 +69,12 @@ export function useBatchBadgeFetch(projectId?: string): UseBatchBadgeFetchResult
|
||||
try {
|
||||
const results = await fetchBatchStatus(taskIds, projectId);
|
||||
return results;
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
lastError = err instanceof Error ? err : new Error(String(err));
|
||||
|
||||
// If it's a 429 rate limit error, wait before retrying with exponential backoff
|
||||
if (err?.message?.includes("429") || err?.message?.toLowerCase().includes("rate limit")) {
|
||||
const errMsg = getErrorMessage(err);
|
||||
if (errMsg?.includes("429") || errMsg?.toLowerCase().includes("rate limit")) {
|
||||
const delayMs = Math.min(1000 * Math.pow(2, attempt), 30000); // Max 30s delay
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
continue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileNode, FileListResponse } from "../api";
|
||||
import { fetchFileList } from "../api";
|
||||
|
||||
@@ -56,9 +57,9 @@ export function useFileBrowser(taskId: string, enabled: boolean, projectId?: str
|
||||
if (!cancelled) {
|
||||
setEntries(response.entries);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load files");
|
||||
setError(getErrorMessage(err) || "Failed to load files");
|
||||
setEntries([]);
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileContentResponse, SaveFileResponse } from "../api";
|
||||
import { fetchFileContent, saveFileContent } from "../api";
|
||||
|
||||
@@ -65,9 +66,9 @@ export function useFileEditor(
|
||||
setOriginalContent(response.content);
|
||||
setMtime(response.mtime);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load file");
|
||||
setError(getErrorMessage(err) || "Failed to load file");
|
||||
setContentState("");
|
||||
setOriginalContent("");
|
||||
setMtime(null);
|
||||
@@ -100,8 +101,8 @@ export function useFileEditor(
|
||||
const response: SaveFileResponse = await saveFileContent(taskId, filePath, content, projectId);
|
||||
setOriginalContent(content);
|
||||
setMtime(response.mtime);
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to save file");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to save file");
|
||||
throw err;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileNode, FileListResponse } from "../api";
|
||||
import { fetchWorkspaceFileList } from "../api";
|
||||
|
||||
@@ -54,9 +55,9 @@ export function useProjectFileBrowser(rootPath: string, enabled: boolean): UsePr
|
||||
if (!cancelled) {
|
||||
setEntries(response.entries);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load files");
|
||||
setError(getErrorMessage(err) || "Failed to load files");
|
||||
setEntries([]);
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileContentResponse, SaveFileResponse } from "../api";
|
||||
import { fetchWorkspaceFileContent, saveWorkspaceFileContent } from "../api";
|
||||
|
||||
@@ -63,9 +64,9 @@ export function useProjectFileEditor(
|
||||
setOriginalContent(response.content);
|
||||
setMtime(response.mtime);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load file");
|
||||
setError(getErrorMessage(err) || "Failed to load file");
|
||||
setContentState("");
|
||||
setOriginalContent("");
|
||||
setMtime(null);
|
||||
@@ -98,8 +99,8 @@ export function useProjectFileEditor(
|
||||
const response: SaveFileResponse = await saveWorkspaceFileContent("project", filePath, content);
|
||||
setOriginalContent(content);
|
||||
setMtime(response.mtime);
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to save file");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to save file");
|
||||
throw err;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { fetchUsageData, type ProviderUsage } from "../api";
|
||||
|
||||
interface UsageDataState {
|
||||
@@ -57,14 +58,14 @@ export function useUsageData(options: UseUsageDataOptions = {}) {
|
||||
error: null,
|
||||
lastUpdated: new Date(),
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Don't update state if the request was aborted
|
||||
if (err.name === "AbortError") return;
|
||||
if (err instanceof Error && err.name === "AbortError") return;
|
||||
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: err.message || "Failed to fetch usage data",
|
||||
error: getErrorMessage(err) || "Failed to fetch usage data",
|
||||
}));
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileNode, FileListResponse } from "../api";
|
||||
import { fetchWorkspaceFileList } from "../api";
|
||||
|
||||
@@ -65,9 +66,9 @@ export function useWorkspaceFileBrowser(
|
||||
if (!cancelled) {
|
||||
setEntries(response.entries);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load files");
|
||||
setError(getErrorMessage(err) || "Failed to load files");
|
||||
setEntries([]);
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import type { FileContentResponse, SaveFileResponse } from "../api";
|
||||
import { fetchWorkspaceFileContent, saveWorkspaceFileContent } from "../api";
|
||||
|
||||
@@ -63,9 +64,9 @@ export function useWorkspaceFileEditor(
|
||||
setOriginalContent(response.content);
|
||||
setMtime(response.mtime);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load file");
|
||||
setError(getErrorMessage(err) || "Failed to load file");
|
||||
setContentState("");
|
||||
setOriginalContent("");
|
||||
setMtime(null);
|
||||
@@ -98,8 +99,8 @@ export function useWorkspaceFileEditor(
|
||||
const response: SaveFileResponse = await saveWorkspaceFileContent(workspace, filePath, content, projectId);
|
||||
setOriginalContent(content);
|
||||
setMtime(response.mtime);
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to save file");
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err) || "Failed to save file");
|
||||
throw err;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { fetchWorkspaces, type WorkspaceTaskInfo } from "../api";
|
||||
|
||||
export interface WorkspaceInfo {
|
||||
@@ -56,9 +57,9 @@ export function useWorkspaces(projectId?: string): UseWorkspacesReturn {
|
||||
setProjectName(getProjectName(response.project));
|
||||
setWorkspaces(response.tasks.map(mapTaskWorkspace));
|
||||
setError(null);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err.message || "Failed to load workspaces");
|
||||
setError(getErrorMessage(err) || "Failed to load workspaces");
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
|
||||
Reference in New Issue
Block a user