fix: tool page race condition on refresh and install error reporting

- Subscribe to features store reactively in ToolPage so refresh shows
  install prompt correctly instead of the tool UI
- Show loading state while features are being fetched for AI tools
- Capture stdout from install script for better error messages
- Keep last 20 stderr lines for error context instead of just exit code
- Clear install progress on success (was leaving stale state)
- Pass PIP_CACHE_DIR to install subprocess
This commit is contained in:
ashim-hq
2026-04-18 10:55:22 +08:00
parent 3b2612ac72
commit 3eab3d5391
2 changed files with 41 additions and 11 deletions
+25 -5
View File
@@ -1,4 +1,4 @@
import { PYTHON_SIDECAR_TOOLS, TOOLS } from "@ashim/shared";
import { PYTHON_SIDECAR_TOOLS, TOOL_BUNDLE_MAP, TOOLS } from "@ashim/shared";
import {
CheckCircle2,
ChevronLeft,
@@ -110,12 +110,22 @@ export function ToolPage() {
[toolId],
);
const isAiTool = toolId ? (PYTHON_SIDECAR_TOOLS as readonly string[]).includes(toolId) : false;
const getBundleForTool = useFeaturesStore((s) => s.getBundleForTool);
const isToolInstalled = useFeaturesStore((s) => s.isToolInstalled);
const featureBundle = toolId ? getBundleForTool(toolId) : null;
const toolInstalled = toolId ? isToolInstalled(toolId) : true;
const featuresLoaded = useFeaturesStore((s) => s.loaded);
const featureBundles = useFeaturesStore((s) => s.bundles);
const fetchFeatures = useFeaturesStore((s) => s.fetch);
const featureBundle = useMemo(() => {
if (!toolId) return null;
const bundleId = TOOL_BUNDLE_MAP[toolId];
if (!bundleId) return null;
return featureBundles.find((b) => b.id === bundleId) ?? null;
}, [toolId, featureBundles]);
const toolInstalled = featureBundle ? featureBundle.status === "installed" : !isAiTool;
const { hasPermission } = useAuth();
const isAdmin = hasPermission("settings:write");
useEffect(() => {
if (isAiTool) fetchFeatures();
}, [isAiTool, fetchFeatures]);
const {
files,
entries,
@@ -244,6 +254,16 @@ export function ToolPage() {
);
}
if (isAiTool && !featuresLoaded) {
return (
<AppLayout>
<div className="flex items-center justify-center h-full text-muted-foreground">
Loading...
</div>
</AppLayout>
);
}
if (isAiTool && !toolInstalled && featureBundle) {
return (
<AppLayout>