diff --git a/backend/src/models/config.rs b/backend/src/models/config.rs index d995200d..a6cc6a11 100644 --- a/backend/src/models/config.rs +++ b/backend/src/models/config.rs @@ -8,6 +8,7 @@ use ts_rs::TS; pub struct Config { pub theme: ThemeMode, pub executor: ExecutorConfig, + pub disclaimer_acknowledged: bool, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] @@ -24,6 +25,7 @@ impl Default for Config { Self { theme: ThemeMode::System, executor: ExecutorConfig::Claude, + disclaimer_acknowledged: false, } } } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5be2957a..43571f4e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -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(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 = 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 = await response.json(); + + if (data.success) { + setConfig(updatedConfig); + setShowDisclaimer(false); + } + } catch (err) { + console.error("Error saving config:", err); + } + }; + + if (loading) { + return ( +
+
+
+

Loading...

+
+
+ ); + } + return (
+ {showNavbar && }
diff --git a/frontend/src/components/DisclaimerDialog.tsx b/frontend/src/components/DisclaimerDialog.tsx new file mode 100644 index 00000000..78660865 --- /dev/null +++ b/frontend/src/components/DisclaimerDialog.tsx @@ -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 ( + {}}> + + +
+ + Important Safety Warning +
+ +

+ Please read and acknowledge the following before proceeding: +

+
+

+ Coding agents have full access to your computer and can execute any terminal commands, including: +

+
    +
  • Installing, modifying, or deleting software
  • +
  • Accessing, creating, or removing files and directories
  • +
  • Making network requests and connections
  • +
  • Running system-level commands with your permissions
  • +
+

+ This software is experimental and may cause catastrophic damage to your system, data, or projects. By using this software, you acknowledge that: +

+
    +
  • You use this software entirely at your own risk
  • +
  • The developers are not responsible for any damage, data loss, or security issues
  • +
  • You should have proper backups of important data before using this software
  • +
  • You understand the potential consequences of granting unrestricted system access
  • +
+
+
+
+
+ setAcknowledged(checked === true)} + /> + +
+ + + +
+
+ ); +} diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..50691a56 --- /dev/null +++ b/frontend/src/components/ui/checkbox.tsx @@ -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( + ({ className, checked = false, onCheckedChange, disabled, ...props }, ref) => { + return ( + + ) + } +) +Checkbox.displayName = "Checkbox" + +export { Checkbox } diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index b25813de..2cf62562 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -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 (
@@ -183,6 +189,40 @@ export function Settings() {
+ + + + Safety & Disclaimers + + Manage safety warnings and acknowledgments. + + + +
+
+
+ +

+ {config.disclaimer_acknowledged + ? "You have acknowledged the safety disclaimer." + : "The safety disclaimer has not been acknowledged."} +

+
+ +
+

+ Resetting the disclaimer will require you to acknowledge the safety warning again on next app start. +

+
+
+
diff --git a/shared/types.ts b/shared/types.ts index 7b983f2f..4046a0d7 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -3,7 +3,7 @@ export type ApiResponse = { success: boolean, data: T | null, message: string | null, }; -export type Config = { theme: ThemeMode, executor: ExecutorConfig, }; +export type Config = { theme: ThemeMode, executor: ExecutorConfig, disclaimer_acknowledged: boolean, }; export type ThemeMode = "light" | "dark" | "system";