Merge task attempt f8a712e0-423c-4f72-b6e6-bbdac2088c19 into main

This commit is contained in:
Louis Knight-Webb
2025-06-19 19:16:55 -04:00
6 changed files with 237 additions and 1 deletions

View File

@@ -1,3 +1,4 @@
import { useState, useEffect } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Navbar } from "@/components/layout/navbar";
import { Projects } from "@/pages/projects";
@@ -5,12 +6,77 @@ import { ProjectTasks } from "@/pages/project-tasks";
import { TaskDetailsPage } from "@/pages/task-details";
import { TaskAttemptComparePage } from "@/pages/task-attempt-compare";
import { Settings } from "@/pages/Settings";
import { DisclaimerDialog } from "@/components/DisclaimerDialog";
import type { Config, ApiResponse } from "shared/types";
function AppContent() {
const [config, setConfig] = useState<Config | null>(null);
const [loading, setLoading] = useState(true);
const [showDisclaimer, setShowDisclaimer] = useState(false);
const showNavbar = true;
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch("/api/config");
const data: ApiResponse<Config> = await response.json();
if (data.success && data.data) {
setConfig(data.data);
setShowDisclaimer(!data.data.disclaimer_acknowledged);
}
} catch (err) {
console.error("Error loading config:", err);
} finally {
setLoading(false);
}
};
loadConfig();
}, []);
const handleDisclaimerAccept = async () => {
if (!config) return;
const updatedConfig = { ...config, disclaimer_acknowledged: true };
try {
const response = await fetch("/api/config", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updatedConfig),
});
const data: ApiResponse<Config> = await response.json();
if (data.success) {
setConfig(updatedConfig);
setShowDisclaimer(false);
}
} catch (err) {
console.error("Error saving config:", err);
}
};
if (loading) {
return (
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-background">
<DisclaimerDialog
open={showDisclaimer}
onAccept={handleDisclaimerAccept}
/>
{showNavbar && <Navbar />}
<div className={showNavbar ? "max-w-7xl mx-auto p-6 sm:p-8" : ""}>
<Routes>

View File

@@ -0,0 +1,87 @@
import { useState } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { AlertTriangle } from "lucide-react";
interface DisclaimerDialogProps {
open: boolean;
onAccept: () => void;
}
export function DisclaimerDialog({ open, onAccept }: DisclaimerDialogProps) {
const [acknowledged, setAcknowledged] = useState(false);
const handleAccept = () => {
if (acknowledged) {
onAccept();
}
};
return (
<Dialog open={open} onOpenChange={() => {}}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<div className="flex items-center gap-3">
<AlertTriangle className="h-6 w-6 text-destructive" />
<DialogTitle>Important Safety Warning</DialogTitle>
</div>
<DialogDescription className="text-left space-y-4 pt-4">
<p className="font-semibold text-foreground">
Please read and acknowledge the following before proceeding:
</p>
<div className="space-y-3">
<p>
<strong>Coding agents have full access to your computer</strong> and can execute any terminal commands, including:
</p>
<ul className="list-disc list-inside space-y-1 ml-4">
<li>Installing, modifying, or deleting software</li>
<li>Accessing, creating, or removing files and directories</li>
<li>Making network requests and connections</li>
<li>Running system-level commands with your permissions</li>
</ul>
<p>
<strong>This software is experimental and may cause catastrophic damage</strong> to your system, data, or projects. By using this software, you acknowledge that:
</p>
<ul className="list-disc list-inside space-y-1 ml-4">
<li>You use this software entirely at your own risk</li>
<li>The developers are not responsible for any damage, data loss, or security issues</li>
<li>You should have proper backups of important data before using this software</li>
<li>You understand the potential consequences of granting unrestricted system access</li>
</ul>
</div>
</DialogDescription>
</DialogHeader>
<div className="flex items-center space-x-2 py-4">
<Checkbox
id="acknowledge"
checked={acknowledged}
onCheckedChange={(checked: boolean) => setAcknowledged(checked === true)}
/>
<label
htmlFor="acknowledge"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
I understand and acknowledge the risks described above. I am aware that coding agents have full access to my computer and may cause catastrophic damage.
</label>
</div>
<DialogFooter>
<Button
onClick={handleAccept}
disabled={!acknowledged}
variant="destructive"
>
I Accept the Risks and Want to Proceed
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,41 @@
import * as React from "react"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
interface CheckboxProps {
id?: string;
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
className?: string;
disabled?: boolean;
}
const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
({ className, checked = false, onCheckedChange, disabled, ...props }, ref) => {
return (
<button
type="button"
role="checkbox"
aria-checked={checked}
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
checked && "bg-primary text-primary-foreground",
className
)}
disabled={disabled}
onClick={() => onCheckedChange?.(!checked)}
{...props}
>
{checked && (
<div className="flex items-center justify-center text-current">
<Check className="h-4 w-4" />
</div>
)}
</button>
)
}
)
Checkbox.displayName = "Checkbox"
export { Checkbox }

View File

@@ -78,6 +78,12 @@ export function Settings() {
setConfig((prev: Config | null) => prev ? { ...prev, ...updates } : null);
};
const resetDisclaimer = async () => {
if (!config) return;
updateConfig({ disclaimer_acknowledged: false });
};
if (loading) {
return (
<div className="container mx-auto px-4 py-8">
@@ -183,6 +189,40 @@ export function Settings() {
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Safety & Disclaimers</CardTitle>
<CardDescription>
Manage safety warnings and acknowledgments.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<div>
<Label>Disclaimer Status</Label>
<p className="text-sm text-muted-foreground">
{config.disclaimer_acknowledged
? "You have acknowledged the safety disclaimer."
: "The safety disclaimer has not been acknowledged."}
</p>
</div>
<Button
onClick={resetDisclaimer}
variant="outline"
size="sm"
disabled={!config.disclaimer_acknowledged}
>
Reset Disclaimer
</Button>
</div>
<p className="text-xs text-muted-foreground">
Resetting the disclaimer will require you to acknowledge the safety warning again on next app start.
</p>
</div>
</CardContent>
</Card>
</div>
<div className="flex justify-end">